/* * JBoss, Home of Professional Open Source. * Copyright 2006, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.verifier.strategy; // standard imports import org.gjt.lindfors.pattern.StrategyContext; import org.jboss.logging.Logger; import org.jboss.metadata.BeanMetaData; import org.jboss.metadata.EntityMetaData; import org.jboss.metadata.MessageDrivenMetaData; import org.jboss.util.Classes; import org.jboss.verifier.Section; import org.jboss.verifier.event.VerificationEvent; import org.jboss.verifier.factory.DefaultEventFactory; import org.jboss.verifier.factory.VerificationEventFactory; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Member; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.net.URL; import java.net.URLClassLoader; import java.rmi.RemoteException; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import javax.jms.Message; /** * Abstract superclass for verifiers containing a bunch of useful methods. * * @see org.jboss.verifier.strategy.VerificationStrategy * * @author Juha Lindfors * @author Aaron Mulder (ammulder@alumni.princeton.edu) * @author Vinay Menon (menonv@cpw.co.uk) * @author Andreas Schaefer * @author Luke Taylor * @author Jay Walters * @author Scott.Stark@jboss.org * * @version $Revision: 57209 $ * @since JDK 1.3 */ public abstract class AbstractVerifier implements VerificationStrategy { static final Logger log = Logger.getLogger(AbstractVerifier.class); protected final static String EJB_OBJECT_INTERFACE = "javax.ejb.EJBObject"; protected final static String EJB_HOME_INTERFACE = "javax.ejb.EJBHome"; protected final static String EJB_LOCAL_OBJECT_INTERFACE = "javax.ejb.EJBLocalObject"; protected final static String EJB_LOCAL_HOME_INTERFACE = "javax.ejb.EJBLocalHome"; /** * The application classloader. This can be provided by the context * directly via {@link VerificationContext#getClassLoader} method, or * constructed by this object by creating a classloader to the URL * returned by {@link VerificationContext#getJarLocation} method.
* * Initialized in the constructor. */ protected ClassLoader classloader = null; /** * Factory for generating the verifier events.
* * Initialized in the constructor. * * @see org.jboss.verifier.factory.DefaultEventFactory */ private VerificationEventFactory factory = null; /** * Context is used for retrieving application level information, * such as the application meta data, location of the jar file, etc. *
*
* Initialized in the constructor.
*/
private VerificationContext context = null;
/**************************************************************************
*
* CONSTRUCTORS
*
**************************************************************************/
public AbstractVerifier(VerificationContext context)
{
this.context = context;
this.classloader = context.getClassLoader();
this.factory = new DefaultEventFactory(getMessageBundle());
if (this.classloader == null)
{
URL[] list = {context.getJarLocation()};
ClassLoader parent = Thread.currentThread().getContextClassLoader();
this.classloader = new URLClassLoader(list, parent);
}
}
/*
*************************************************************************
*
* PUBLIC INSTANCE METHODS
*
*************************************************************************
*/
public boolean isAssignableFrom(String className, Class assignableFromClass)
{
try
{
Class clazz = this.classloader.loadClass(className);
return clazz.isAssignableFrom(assignableFromClass);
}
catch (ClassNotFoundException e)
{
log.warn("Failed to find class: " + className, e);
}
return false;
}
public boolean isAssignableFrom(Class clazz, String assignableFromClassName)
{
try
{
Class assignableFromClass = this.classloader.loadClass(assignableFromClassName);
return clazz.isAssignableFrom(assignableFromClass);
}
catch (ClassNotFoundException e)
{
log.warn("Failed to find class: " + assignableFromClassName, e);
}
return false;
}
public abstract String getMessageBundle();
public abstract boolean isCreateMethod(Method m);
public abstract boolean isEjbCreateMethod(Method m);
public boolean hasLegalRMIIIOPArguments(Method method)
{
Class[] params = method.getParameterTypes();
for (int i = 0; i < params.length; ++i)
{
if (!isRMIIIOPType(params[i]))
return false;
}
return true;
}
public boolean hasLegalRMIIIOPReturnType(Method method)
{
return isRMIIIOPType(method.getReturnType());
}
public boolean hasLegalRMIIIOPExceptionTypes(Method method)
{
/*
* All checked exception classes used in method declarations
* (other than java.rmi.RemoteException) MUST be conforming
* RMI/IDL exception types.
*
* Spec 28.2.3 (4)
*/
Iterator it = Arrays.asList(method.getExceptionTypes()).iterator();
while (it.hasNext())
{
Class exception = (Class)it.next();
if (!isRMIIDLExceptionType(exception))
return false;
}
return true;
}
/**
* Checks if the method includes java.rmi.RemoteException or its
* subclass in its throws clause.
*
* See bug report #434739 and #607805
*/
public boolean throwsRemoteException(Method method)
{
Class[] exception = method.getExceptionTypes();
for (int i = 0; i < exception.length; ++i)
{
// Fix for bug #607805: an IOException is OK for local interfaces
// Fix for bug #626430: java.lang.Exception is also OK
if (exception[i].equals(java.io.IOException.class)
|| exception[i].equals(java.lang.Exception.class))
{
continue;
}
// Not true see bug report #434739
// if (java.rmi.RemoteException.class.isAssignableFrom(exception[i]))
// According to the RMI spec. a remote interface must throw an RemoteException
// or any of its super classes therefore the check must be done vice versa
if (isAssignableFrom(exception[i], "java.rmi.RemoteException"))
{
return true;
}
}
return false;
}
/**
* checks if the method accepts a single parameter of a specified type.
*/
public boolean hasSingleArgument(Method method, Class argClass)
{
Class[] params = method.getParameterTypes();
if (params.length == 1)
{
if (params[0].equals(argClass))
return true;
}
return false;
}
/**
* checks if the method accepts any parameters.
*/
public boolean hasNoArguments(Method method)
{
Class[] params = method.getParameterTypes();
return (params.length == 0) ? true : false;
}
/**
* checks if the method throws no checked exceptions in its throws clause.
*/
public boolean throwsNoException(Method method)
{
boolean hasCheckedException = false;
Class[] exceptions = method.getExceptionTypes();
for (int e = 0; e < exceptions.length; e++)
{
Class ex = exceptions[e];
boolean isError = Error.class.isAssignableFrom(ex);
boolean isRuntimeException = RuntimeException.class.isAssignableFrom(ex);
boolean isRemoteException = RemoteException.class.isAssignableFrom(ex);
if (isError == false && isRuntimeException == false && isRemoteException == false)
hasCheckedException = true;
}
return hasCheckedException == false;
}
/**
* checks if the method includes java.ejb.CreateException in its
* throws clause.
*/
public boolean throwsCreateException(Method method)
{
Class[] exception = method.getExceptionTypes();
for (int i = 0; i < exception.length; ++i)
{
if (isAssignableFrom("javax.ejb.CreateException", exception[i]))
return true;
}
return false;
}
/**
* checks if the methods includes javax.ejb.FinderException in its
* throws clause.
*/
public boolean throwsFinderException(Method method)
{
Class[] exception = method.getExceptionTypes();
for (int i = 0; i < exception.length; ++i)
{
if (isAssignableFrom("javax.ejb.FinderException", exception[i]))
return true;
}
return false;
}
/**
* checks if a class's member (method, constructor or field) has a
* static
modifier.
*/
public boolean isStatic(Member member)
{
return (Modifier.isStatic(member.getModifiers()));
}
/**
* checks if the given class is declared as static (inner classes only)
*/
public boolean isStatic(Class c)
{
return (Modifier.isStatic(c.getModifiers()));
}
/**
* checks if a class's member (method, constructor or field) has a
* final
modifier.
*/
public boolean isFinal(Member member)
{
return (Modifier.isFinal(member.getModifiers()));
}
/**
* checks if the given class is declared as final
*/
public boolean isFinal(Class c)
{
return (Modifier.isFinal(c.getModifiers()));
}
/**
* checks if a class's member (method, constructor or field) has a
* public
modifier.
*/
public boolean isPublic(Member member)
{
return (Modifier.isPublic(member.getModifiers()));
}
/**
* checks if the given class is declared as public
*/
public boolean isPublic(Class c)
{
return (Modifier.isPublic(c.getModifiers()));
}
/**
* Checks whether all the fields in the class are declared as public.
*/
public boolean isAllFieldsPublic(Class c)
{
try
{
Field list[] = c.getFields();
for (int i = 0; i < list.length; i++)
{
if (!Modifier.isPublic(list[i].getModifiers()))
return false;
}
}
catch (Exception e)
{
return false;
}
return true;
}
/**
* checks if the given class is declared as abstract
*/
public boolean isAbstract(Class c)
{
return (Modifier.isAbstract(c.getModifiers()));
}
/**
* checks if the given method is declared as abstract
*/
public boolean isAbstract(Method m)
{
return (Modifier.isAbstract(m.getModifiers()));
}
/**
* checks if finder returns the primary key type
*/
public boolean isSingleObjectFinder(EntityMetaData entity,
Method finder)
{
return hasPrimaryKeyReturnType(entity, finder);
}
/**
* checks if finder method returns either Collection or Enumeration
*/
public boolean isMultiObjectFinder(Method f)
{
return (java.util.Collection.class.isAssignableFrom(f.getReturnType())
|| java.util.Enumeration.class.isAssignableFrom(f.getReturnType()));
}
/**
* checks the return type of method matches the bean's remote interface
*/
public boolean hasRemoteReturnType(BeanMetaData bean, Method m)
{
try
{
Class clazz = classloader.loadClass(bean.getRemote());
return m.getReturnType().isAssignableFrom(clazz);
}
catch (Exception e)
{
// e.printStackTrace();
return false;
}
}
/**
* checks the return type of method matches the bean's local interface
*/
public boolean hasLocalReturnType(BeanMetaData bean, Method m)
{
try
{
Class clazz = classloader.loadClass(bean.getLocal());
return m.getReturnType().isAssignableFrom(clazz);
}
catch (Exception e)
{
// e.printStackTrace();
return false;
}
}
/**
* checks if a method has a void return type
*/
public boolean hasVoidReturnType(Method method)
{
return (method.getReturnType() == Void.TYPE);
}
/**
* Finds java.ejb.MessageDrivenBean interface from the class
*/
public boolean hasMessageDrivenBeanInterface(Class c)
{
return isAssignableFrom("javax.ejb.MessageDrivenBean", c);
}
/**
* Finds javax.jms.MessageListener interface from the class
*/
public boolean hasMessageListenerInterface(Class c)
{
return isAssignableFrom("javax.jms.MessageListener", c);
}
/**
* Finds java.ejb.SessionBean interface from the class
*/
public boolean hasSessionBeanInterface(Class c)
{
return isAssignableFrom("javax.ejb.SessionBean", c);
}
/**
* Finds java.ejb.EntityBean interface from the class
*/
public boolean hasEntityBeanInterface(Class c)
{
return isAssignableFrom("javax.ejb.EntityBean", c);
}
/**
* Finds java.ejb.EJBObject interface from the class
*/
public boolean hasEJBObjectInterface(Class c)
{
return isAssignableFrom("javax.ejb.EJBObject", c);
}
/**
* Finds java.ejb.EJBLocalObject interface from the class
*/
public boolean hasEJBLocalObjectInterface(Class c)
{
return isAssignableFrom("javax.ejb.EJBLocalObject", c);
}
/**
* Finds javax.ejb.EJBHome interface from the class or its
* superclasses
*/
public boolean hasEJBHomeInterface(Class c)
{
return isAssignableFrom("javax.ejb.EJBHome", c);
}
/**
* Finds javax.ejb.EJBLocalHome interface from the class or its
* superclasses
*/
public boolean hasEJBLocalHomeInterface(Class c)
{
return isAssignableFrom("javax.ejb.EJBLocalHome", c);
}
/**
* Finds javax.ejb.SessionSynchronization interface from the class
*/
public boolean hasSessionSynchronizationInterface(Class c)
{
return isAssignableFrom("javax.ejb.SessionSynchronization", c);
}
/**
* Checks if a class has a default (no args) constructor
*/
public boolean hasDefaultConstructor(Class c)
{
try
{
Constructor ctr = c.getConstructor(new Class[0]);
}
catch (NoSuchMethodException e)
{
if( log.isTraceEnabled() )
{
StringBuffer tmp = new StringBuffer("hasDefaultConstructor(");
tmp.append(") failure, ");
Classes.displayClassInfo(c, tmp);
log.trace(tmp.toString(), e);
}
return false;
}
return true;
}
/**
* Checks of the class defines a finalize() method
*/
public boolean hasFinalizer(Class c)
{
try
{
Method finalizer = c.getDeclaredMethod(FINALIZE_METHOD, new Class[0]);
}
catch (NoSuchMethodException e)
{
return false;
}
return true;
}
/**
* check if a class has one or more finder methods
*/
public boolean hasFinderMethod(Class c)
{
Method[] method = c.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (method[i].getName().startsWith("ejbFind"))
return true;
}
return false;
}
/**
* Check if this is a finder method
*/
public boolean isFinderMethod(Method m)
{
return (m.getName().startsWith("find"));
}
/**
* Check if the given message is the onMessage() method
*/
public boolean isOnMessageMethod(Method m)
{
if ("onMessage".equals(m.getName()))
{
Class[] paramTypes = m.getParameterTypes();
if (paramTypes.length == 1)
{
if (Message.class.equals(paramTypes[0]))
return true;
}
}
return false;
}
/**
* Checks for at least one non-static field.
*/
public boolean hasANonStaticField(Class c)
{
try
{
Field list[] = c.getFields();
for (int i = 0; i < list.length; i++)
{
if (!Modifier.isStatic(list[i].getModifiers()))
return true;
}
}
catch (Exception ignored)
{
}
return false;
}
/**
* Searches for an instance of a public onMessage method from the class
*/
public boolean hasOnMessageMethod(Class c)
{
Method[] method = c.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (isOnMessageMethod(method[i]))
return true;
}
return false;
}
/**
* Searches for an instance of a public create method from the class
*/
public boolean hasCreateMethod(Class c)
{
Method[] method = c.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (isCreateMethod(method[i]))
return true;
}
return false;
}
/**
* Searches for an instance of a public ejbCreate method from the class
*/
public boolean hasEJBCreateMethod(Class c, boolean isSession)
{
Method[] method = c.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (isEjbCreateMethod(method[i]))
{
if (!isStatic(method[i]) && !isFinal(method[i])
&& ((isSession && hasVoidReturnType(method[i]))
|| (!isSession))
)
{
return true;
}
}
}
return false;
}
/**
* Searches the class or interface, and its superclass or superinterface
* for a create() method that takes no arguments
*/
public boolean hasDefaultCreateMethod(Class home)
{
Method[] method = home.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (isCreateMethod(method[i]))
{
Class[] params = method[i].getParameterTypes();
if (params.length == 0)
return true;
}
}
return false;
}
/**
* checks if the class has an ejbFindByPrimaryKey method
*/
public boolean hasEJBFindByPrimaryKey(Class c)
{
Method[] method = c.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (method[i].getName().equals(EJB_FIND_BY_PRIMARY_KEY))
return true;
}
return false;
}
/**
* checks the return type of method matches the entity's primary key
* class or is a super class of the primary key class
*/
public boolean hasPrimaryKeyReturnType(EntityMetaData entity, Method m)
{
try
{
return
m.getReturnType().isAssignableFrom(classloader.loadClass(entity.getPrimaryKeyClass()));
}
catch (ClassNotFoundException cnfe)
{
// Only check equality
return
m.getReturnType().getName().equals(entity.getPrimaryKeyClass());
}
}
/**
* @return Returns the default create method or null
* if none is found
*/
public Method getDefaultCreateMethod(Class c)
{
Method method = null;
try
{
method = c.getMethod(CREATE_METHOD, null);
}
catch (NoSuchMethodException ignored)
{
}
return method;
}
/**
* Returns the ejbFindByPrimaryKey method
*/
public Method getEJBFindByPrimaryKey(Class c)
{
Method[] method = c.getMethods();
for (int i = 0; i < method.length; ++i)
{
if (method[i].getName().equals(EJB_FIND_BY_PRIMARY_KEY))
return method[i];
}
return null;
}
/**
* returns the ejbFind