/*
* 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.ejb3;
import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.ApplicationException;
import javax.ejb.EJBException;
import javax.ejb.EJBHome;
import javax.ejb.EJBLocalHome;
import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Timeout;
import javax.ejb.Timer;
import javax.ejb.TimerService;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.LinkRef;
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;
import javax.transaction.TransactionManager;
import javax.transaction.UserTransaction;
import org.jboss.aop.Advisor;
import org.jboss.aop.Domain;
import org.jboss.aop.MethodInfo;
import org.jboss.aop.advice.AdviceStack;
import org.jboss.aop.advice.Interceptor;
import org.jboss.aop.annotation.AnnotationRepository;
import org.jboss.aop.joinpoint.Joinpoint;
import org.jboss.aop.microcontainer.annotations.DisableAOP;
import org.jboss.aop.util.MethodHashing;
import org.jboss.beans.metadata.api.annotations.Inject;
import org.jboss.ejb.AllowedOperationsAssociation;
import org.jboss.ejb3.annotation.Clustered;
import org.jboss.ejb3.annotation.SecurityDomain;
import org.jboss.ejb3.annotation.defaults.PoolDefaults;
import org.jboss.ejb3.annotation.impl.ApplicationExceptionImpl;
import org.jboss.ejb3.aop.BeanContainer;
import org.jboss.ejb3.async.spi.AsyncInvocationMap;
import org.jboss.ejb3.cluster.metadata.ClusteredMetaDataBridge;
import org.jboss.ejb3.common.spi.ErrorCodes;
import org.jboss.ejb3.deployers.JBoss5DependencyPolicy;
import org.jboss.ejb3.effigy.ApplicationExceptionEffigy;
import org.jboss.ejb3.effigy.EnterpriseBeanEffigy;
import org.jboss.ejb3.effigy.common.JBossBeanEffigyInfo;
import org.jboss.ejb3.effigy.spi.BeanEffigyFactory;
import org.jboss.ejb3.injection.InjectionInvocation;
import org.jboss.ejb3.instantiator.impl.Ejb31SpecBeanInstantiator;
import org.jboss.ejb3.instantiator.spi.BeanInstantiationException;
import org.jboss.ejb3.instantiator.spi.BeanInstantiator;
import org.jboss.ejb3.instantiator.spi.InvalidConstructionParamsException;
import org.jboss.ejb3.interceptor.InterceptorInfoRepository;
import org.jboss.ejb3.interceptor.InterceptorInjector;
import org.jboss.ejb3.interceptors.container.ManagedObjectAdvisor;
import org.jboss.ejb3.interceptors.direct.DirectContainer;
import org.jboss.ejb3.interceptors.direct.IndirectContainer;
import org.jboss.ejb3.interceptors.metadata.AdditiveBeanInterceptorMetaDataBridge;
import org.jboss.ejb3.interceptors.metadata.InterceptorComponentMetaDataLoaderFactory;
import org.jboss.ejb3.interceptors.metadata.InterceptorMetaDataBridge;
import org.jboss.ejb3.javaee.JavaEEComponent;
import org.jboss.ejb3.javaee.JavaEEComponentHelper;
import org.jboss.ejb3.javaee.JavaEEModule;
import org.jboss.ejb3.metadata.MetaDataBridge;
import org.jboss.ejb3.metadata.annotation.AnnotationRepositoryToMetaData;
import org.jboss.ejb3.pool.Pool;
import org.jboss.ejb3.pool.PoolFactory;
import org.jboss.ejb3.pool.PoolFactoryRegistry;
import org.jboss.ejb3.proxy.factory.ProxyFactoryHelper;
import org.jboss.ejb3.security.SecurityDomainManager;
import org.jboss.ejb3.security.bridge.RunAsMetaDataBridge;
import org.jboss.ejb3.security.bridge.SecurityDomainMetaDataBridge;
import org.jboss.ejb3.statistics.InvocationStatistics;
import org.jboss.ejb3.timeout.spi.TimeoutMethodCallbackRequirements;
import org.jboss.ejb3.tx.UserTransactionImpl;
import org.jboss.ejb3.tx.metadata.ApplicationExceptionComponentMetaDataLoaderFactory;
import org.jboss.ejb3.tx.metadata.ApplicationExceptionMetaDataBridge;
import org.jboss.ejb3.util.Service;
import org.jboss.ejb3.vfs.spi.VirtualFile;
import org.jboss.injection.DependsHandler;
import org.jboss.injection.EJBHandler;
import org.jboss.injection.EJBInjectionContainer;
import org.jboss.injection.EncInjector;
import org.jboss.injection.ExtendedInjectionContainer;
import org.jboss.injection.InjectionHandler;
import org.jboss.injection.InjectionUtil;
import org.jboss.injection.Injector;
import org.jboss.injection.JndiInjectHandler;
import org.jboss.injection.PersistenceContextHandler;
import org.jboss.injection.manager.spi.InjectionManager;
import org.jboss.jca.spi.ComponentStack;
import org.jboss.logging.Logger;
import org.jboss.metadata.ejb.jboss.JBossAssemblyDescriptorMetaData;
import org.jboss.metadata.ejb.jboss.JBossEnterpriseBeanMetaData;
import org.jboss.metadata.ejb.jboss.JBossMetaData;
import org.jboss.metadata.ejb.spec.ApplicationExceptionMetaData;
import org.jboss.metadata.ejb.spec.InterceptorMetaData;
import org.jboss.metadata.ejb.spec.InterceptorsMetaData;
import org.jboss.metadata.ejb.spec.MethodParametersMetaData;
import org.jboss.metadata.ejb.spec.NamedMethodMetaData;
import org.jboss.metadata.javaee.spec.Environment;
import org.jboss.metadata.javaee.spec.ServiceReferenceMetaData;
import org.jboss.util.StringPropertyReplacer;
import org.jboss.util.naming.Util;
/**
* Comment
*
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
* @version $Revision: 109819 $
*/
@DisableAOP
public abstract class EJBContainer
implements Container, IndirectContainer<EJBContainer, DirectContainer<EJBContainer>>,
EJBInjectionContainer, ExtendedInjectionContainer, JavaEEComponent
{
private static final Logger log = Logger.getLogger(EJBContainer.class);
private static TimeoutMethodCallbackRequirements timeoutMethodCallbackRequirements = Service.loadService(TimeoutMethodCallbackRequirements.class);
/**
* Empty parameter set used to create new EJB3 bean instances
*/
private static final Object[] BEAN_INSTANCE_CONSTRUCTION_PARAMS = new Object[]{};
private final String name;
private final BeanContainer beanContainer;
private DirectContainer<EJBContainer> directContainer;
protected EjbEncFactory encFactory;
protected Pool pool;
protected String ejbName;
protected ObjectName objectName;
protected final String beanClassName;
private final Class<?> beanClass;
protected final ClassLoader classloader;
// for performance there is an array.
protected List<Injector> injectors = new ArrayList<Injector>();
protected Context enc;
protected Hashtable initialContextProperties;
protected Map<String, EncInjector> encInjectors = new HashMap<String, EncInjector>();
protected JBossEnterpriseBeanMetaData xml;
protected JBossAssemblyDescriptorMetaData assembly;
protected Map<String, Map<AccessibleObject, Injector>> encInjections = new HashMap<String, Map<AccessibleObject, Injector>>();
// protected List<InterceptorInfo> classInterceptors = new ArrayList<InterceptorInfo>();
//
// protected LinkedHashSet<InterceptorInfo> applicableInterceptors;
private HashMap<Class<?>, InterceptorInjector> interceptorInjectors = new HashMap<Class<?>, InterceptorInjector>();
private Ejb3Deployment deployment;
/**
* Used to construct bean instances
*/
private BeanInstantiator beanInstantiator;
private DependencyPolicy dependencyPolicy;
private String jaccContextId;
protected InvocationStatistics invokeStats = new InvocationStatistics();
private String partitionName;
private List<Class<?>> businessAndComponentInterfaces;
private ThreadLocalStack<BeanContext<?>> currentBean = new ThreadLocalStack<BeanContext<?>>();
protected boolean reinitialize = false;
/**
* Metadata based annotation repository
*/
protected AnnotationRepositoryToMetaData metadataBasedAnnotationRepo;
/**
* Manages injection into beans and interceptors
*/
private InjectionManager injectionManager;
private static final int TOTAL_PERMITS = Integer.MAX_VALUE;
// To support clean startup/shutdown
private final Semaphore semaphore = new Semaphore(TOTAL_PERMITS, true);
private final Lock invocationLock = new SemaphoreLock(this.semaphore);
private final Interceptor[] injectionCallbackStack;
private ComponentStack cachedConnectionManager;
private boolean resurrectMetaData = false;
private final EnterpriseBeanEffigy effigy;
/**
* @param name Advisor name
* @param manager Domain to get interceptor bindings from
* @param cl the EJB's classloader
* @param beanClassName
* @param ejbName
* @param ctxProperties
* @param interceptorRepository
* @param deployment
* @param beanMetaData the meta data for this bean or null
*/
protected EJBContainer(String name, Domain domain, ClassLoader cl,
String beanClassName, String ejbName, Hashtable ctxProperties,
Ejb3Deployment deployment, JBossEnterpriseBeanMetaData beanMetaData) throws ClassNotFoundException
{
assert name != null : "name is null";
this.name = name;
this.deployment = deployment;
this.beanClassName = beanClassName;
this.classloader = cl;
this.xml = beanMetaData;
this.beanClass = classloader.loadClass(beanClassName);
// We can't type cast the direct container, because we just loaded the beanClass
// so assuming we have an object is a safe bet.
this.beanContainer = new BeanContainer(this);
this.ejbName = ejbName;
String on = createObjectName(ejbName);
try
{
objectName = new ObjectName(on);
}
catch (MalformedObjectNameException e)
{
throw new RuntimeException("failed to create object name for: " + on, e);
}
// Because interceptors will query back the EJBContainer for annotations
// we must have set beanContainer first and then do the advisor.
try
{
beanContainer.initialize(ejbName, domain, beanClass, beanMetaData, cl);
}
catch(Exception e)
{
throw new RuntimeException("failed to initialize bean container ",e);
}
//annotations = new AnnotationRepositoryToMetaData(this);
initialContextProperties = ctxProperties;
// pull out some info from the deployment (if present)
if(deployment != null)
{
// set the dependency policy
this.dependencyPolicy = deployment.createDependencyPolicy(this);
}
Advisor advisor = getAdvisor();
AdviceStack stack = advisor.getManager().getAdviceStack("InjectionCallbackStack");
if(stack == null)
throw new IllegalStateException("EJBTHREE-2020: No InjectionCallbackStack defined for domain " + domain + " of " + this);
injectionCallbackStack = stack.createInterceptors(advisor, null);
this.effigy = effigy(classloader, beanMetaData);
}
/**
* Create an EJBContainer
*
* @param name Name for the EJB container
* @param manager Domain to get interceptor bindings from
* @param cl the EJB's classloader
* @param beanClassName Fully qualified name of the ejb class
* @param ejbName The name of the EJB to which this container corresponds
* @param ctxProperties Context properties for this bean
* @param beanMetaData the meta data for this bean or null
*/
protected EJBContainer(String name, Domain domain, ClassLoader cl,
String beanClassName, String ejbName, Hashtable ctxProperties,
JBossEnterpriseBeanMetaData beanMetaData) throws ClassNotFoundException
{
this(name, domain, cl, beanClassName, ejbName, ctxProperties, null, beanMetaData);
}
/**
* @deprecated {@link EJBContainer} is no longer responsible for setting up
* ENC. Instead, SwitchBoard http://community.jboss.org/wiki/Switchboard
* will be setting it up.
*/
@Deprecated
private void bindEJBContext()
{
try
{
// Reference ref = new Reference(EJBContext.class.getName(), EJBContextFactory.class.getName(), null);
// ref.add(new StringRefAddr("containerGuid", Ejb3Registry.guid(this)));
// ref.add(new StringRefAddr("containerClusterUid", Ejb3Registry.clusterUid(this)));
// ref.add(new StringRefAddr("isClustered", Boolean.toString(isClustered())));
// TODO: a temporary measure until jboss-injection is in place
LinkRef ref = new LinkRef("java:internal/EJBContext");
Util.rebind(getEnc(), "EJBContext", ref);
}
catch (NamingException e)
{
throw new RuntimeException(e);
}
}
/**
* @deprecated {@link EJBContainer} is no longer responsible for setting up
* ENC. Instead, SwitchBoard http://community.jboss.org/wiki/Switchboard
* will be setting it up.
*/
@Deprecated
private void bindORB()
{
try
{
Util.rebind(getEnc(), "ORB", new LinkRef("java:/JBossCorbaORB"));
}
catch(NamingException e)
{
throw new RuntimeException(e);
}
}
/**
* Creates a {@link LinkRef} for java:comp/TimerService to
* an internal jndi name for {@link TimerService}
* @throws NamingException
* @deprecated {@link EJBContainer} is no longer responsible for setting up
* ENC. Instead, SwitchBoard http://community.jboss.org/wiki/Switchboard
* will be setting it up.
*/
@Deprecated
private void bindTimerService() throws NamingException
{
// link to java:internal/TimerService (which is setup by
// org.jboss.ejb3.timerservice.naming.TimerServiceBinder)
LinkRef timerServiceLinkRef = new LinkRef("java:internal/TimerService");
// bind to java:comp/TimerService
Util.rebind(getEnc(), "TimerService", timerServiceLinkRef);
}
@Deprecated
public boolean canResolveEJB()
{
return deployment.canResolveEJB();
}
public abstract BeanContext<?> createBeanContext();
public String createObjectName(String ejbName)
{
return JavaEEComponentHelper.createObjectName(deployment, ejbName);
}
/**
* Do not call, for BeanContainer.
* @throws IllegalAccessException
* @throws InstantiationException
*/
public Object createInterceptor(Class<?> interceptorClass) throws InstantiationException, IllegalAccessException
{
Object instance = interceptorClass.newInstance();
// TODO: This needs to be removed once we have all the RPs functioning
// and the InjectionManager will solely be responsible for injection
InterceptorInjector interceptorInjector = interceptorInjectors.get(interceptorClass);
assert interceptorInjector != null : "interceptorInjector not found for " + interceptorClass;
interceptorInjector.inject(null, instance);
// inject into the interceptor instance using InjectionManager.
// the injection manager in a real environment should never
// be null. But there are various legacy unit tests which
// start up a container in various different ways and it's
// a real big task to set a mock injection manager in all those places
if (this.injectionManager != null)
{
this.injectionManager.inject(instance);
}
return instance;
}
public String createObjectName(String unitName, String ejbName)
{
return JavaEEComponentHelper.createObjectName(deployment, unitName, ejbName);
}
/**
* Do nothing.
* @param ctx
*/
public void destroyBeanContext(org.jboss.ejb3.interceptors.container.BeanContext<?> ctx)
{
}
private static EnterpriseBeanEffigy effigy(ClassLoader cl, JBossEnterpriseBeanMetaData beanMetaData)
throws ClassNotFoundException
{
JBossBeanEffigyInfo info = new JBossBeanEffigyInfo(cl, beanMetaData);
return service(BeanEffigyFactory.class).create(info, EnterpriseBeanEffigy.class);
}
// TODO: re-evaluate this exposure
@Deprecated
public Advisor getAdvisor()
{
return beanContainer._getAdvisor();
}
/*
* TODO: re-evalute this exposure
*/
@Deprecated
public AnnotationRepository getAnnotations()
{
return beanContainer.getAnnotationRepository();
}
public ApplicationException getApplicationException(Class<?> exceptionType, Method invokedMethod)
{
ApplicationExceptionEffigy appEx = effigy.getApplicationException(exceptionType);
if(appEx != null)
{
if(appEx.isInherited() || appEx.getExceptionClass().equals(exceptionType))
return new ApplicationExceptionImpl(appEx.isRollback());
else
return null;
}
// Everything after this line is a hack.
// TODO: we have no facility that picks up annotations from shared libs, so lets do some emergency annotation scanning.
ApplicationException annotation = exceptionType.getAnnotation(ApplicationException.class);
if(annotation != null)
return annotation;
// An unchecked-exception is only an application exception if annotated (or described) as such.
// (see EJB 3.1 FR 14.2.1)
if (RuntimeException.class.isAssignableFrom(exceptionType) || Error.class.isAssignableFrom(exceptionType))
return null;
// This feels like a hack around shortcomings in jboss-metadata
Class[] exceptionTypes = invokedMethod.getExceptionTypes();
for (Class exceptionClass : exceptionTypes)
{
if (exceptionClass.isAssignableFrom(exceptionType))
return new ApplicationExceptionImpl(false);
}
return null;
}
protected BeanContainer getBeanContainer()
{
return beanContainer;
}
public Collection<Class<?>> getInterceptorClasses()
{
return this.beanContainer.getInterceptorClasses();
}
public Map<Class<?>, InterceptorInjector> getInterceptorInjectors()
{
return this.interceptorInjectors;
}
/**
*
* @return the bean class of this container
* @deprecated use getBeanClass
*/
public Class<?> getClazz()
{
return getBeanClass();
}
protected EnterpriseBeanEffigy getEffigy()
{
return effigy;
}
@SuppressWarnings("unchecked")
public static <C extends EJBContainer> C getEJBContainer(Advisor advisor)
{
try
{
return (C) ((ManagedObjectAdvisor<Object, BeanContainer>) advisor).getContainer().getEJBContainer();
}
catch(ClassCastException e)
{
throw new ClassCastException(e.getMessage() + " using " + advisor);
}
}
public String getName()
{
return name;
}
public void pushContext(BeanContext<?> beanContext)
{
currentBean.push(beanContext);
}
/**
* Makes sure that EJB's ENC is available
* Delegates to whatever implementation is used to push the ENC of the EJB
* onto the stack
*
*/
protected void pushEnc()
{
encFactory.pushEnc(this);
}
public BeanContext<?> peekContext()
{
BeanContext<?> ctx = currentBean.get();
assert ctx != null : "ctx is null";
return ctx;
}
public BeanContext<?> popContext()
{
return currentBean.pop();
}
/**
* Pops EJB's ENC from the stack. Delegates to whatever implementation
* is used to pop the EJB's ENC from the stock
*
*/
protected void popEnc()
{
encFactory.popEnc(this);
}
private static <S> S service(Class<S> service)
{
ServiceLoader<S> loader = ServiceLoader.load(service);
Iterator<S> it = loader.iterator();
if(!it.hasNext())
throw new IllegalArgumentException("Unable to find a service for " + service);
S instance = it.next();
if(it.hasNext())
throw new IllegalArgumentException("Multiple services found for " + service + ", " + it);
return instance;
}
public Environment getEnvironmentRefGroup()
{
return xml;
}
public List<Injector> getInjectors()
{
return injectors;
}
public String getJaccContextId()
{
return jaccContextId;
}
/**
* Do not call, used by BeanContainer.
* @return
*/
public List<Method> getVirtualMethods()
{
return null;
}
public void setJaccContextId(String jaccContextId)
{
this.jaccContextId = jaccContextId;
}
public VirtualFile getRootFile()
{
return getDeploymentUnit().getRootFile();
}
/**
* Return all the business interfaces implemented by this bean.
*
* Available after the meta data has been processed.
*
* @return an array of business interfaces or empty if no interface is provided
*/
public List<Class<?>> getBusinessAndComponentInterfaces()
{
if(businessAndComponentInterfaces == null) throw new IllegalStateException("businessAndComponentInterfaces not yet initialized");
return businessAndComponentInterfaces;
}
public String getDeploymentQualifiedName()
{
return objectName.getCanonicalName();
}
/**
* Returns a String identifier for this bean that is qualified by the
* deployment, and hence should be unique across deployments. Name is of the
* form "ear=foo.ear,jar=foo.jar,name=Bar", where "Bar" is the value
* returned by {@link #getEjbName()}. The "ear=foo.ear" portion is ommitted
* if the bean is not packaged in an ear.
*/
public String getDeploymentPropertyListString()
{
return objectName.getCanonicalKeyPropertyListString();
}
public DeploymentUnit getDeploymentUnit()
{
return deployment.getDeploymentUnit();
}
public Ejb3Deployment getDeployment()
{
return deployment;
}
public DependencyPolicy getDependencyPolicy()
{
return dependencyPolicy;
}
public boolean isAnnotationPresent(Class<? extends Annotation> annotationType)
{
return beanContainer.isAnnotationPresent(annotationType);
}
/**
* Is the method a business method of this container.
*
* @param businessMethod the method in question
* @return true if so, otherwise false
*/
public boolean isBusinessMethod(Method businessMethod)
{
for(Class<?> businessInterface : getBusinessAndComponentInterfaces())
{
for(Method method : businessInterface.getMethods())
{
if(isCallable(method, businessMethod))
return true;
}
}
return false;
}
/**
* Can method definition method be used to call method other.
* For example if the method is defined in an interface it can be used to call a method
* in a class.
*
* @param method
* @param other
* @return
*/
private static boolean isCallable(Method method, Method other)
{
if ((method.getDeclaringClass().isAssignableFrom(other.getDeclaringClass())) && (method.getName().equals(other.getName())))
{
if (!method.getReturnType().equals(other.getReturnType()))
return false;
Class<?>[] params1 = method.getParameterTypes();
Class<?>[] params2 = other.getParameterTypes();
if (params1.length == params2.length)
{
for (int i = 0; i < params1.length; i++)
{
if (params1[i] != params2[i])
return false;
}
return true;
}
}
return false;
}
/**
* introspects EJB container to find all dependencies
* and initialize any extra metadata.
* <p/>
* This must be called before container is registered with any microcontainer
*
* @param dependencyPolicy
*/
public void processMetadata()
{
// XML must be done first so that any annotation overrides are initialized
// todo injection handlers should be pluggable from XML
Collection<InjectionHandler<Environment>> handlers = this.getInjectionHandlers();
if(handlers == null)
{
handlers = new ArrayList<InjectionHandler<Environment>>();
handlers.add(new EJBHandler<Environment>());
handlers.add(new DependsHandler<Environment>());
handlers.add(new JndiInjectHandler<Environment>());
handlers.add(new PersistenceContextHandler<Environment>());
}
ClassLoader old = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(classloader);
try
{
// EJB container's XML must be processed before interceptor's as it may override interceptor's references
for (InjectionHandler<Environment> handler : handlers) handler.loadXml(xml, this);
Map<AccessibleObject, Injector> tmp = InjectionUtil.processAnnotations(this, handlers, getBeanClass());
injectors.addAll(tmp.values());
/*
initialiseInterceptors();
*/
for (Class<?> interceptorClass : beanContainer.getInterceptorClasses())
{
InterceptorMetaData interceptorMetaData = findInterceptor(interceptorClass);
if(interceptorMetaData == null)
continue;
for (InjectionHandler<Environment> handler : handlers)
{
handler.loadXml(interceptorMetaData, this);
}
}
for (Class<?> interceptorClass : beanContainer.getInterceptorClasses())
{
Map<AccessibleObject, Injector> injections = InjectionUtil.processAnnotations(this, handlers, interceptorClass);
InterceptorInjector injector = new InterceptorInjector(injections);
interceptorInjectors.put(interceptorClass, injector);
}
// When @WebServiceRef is not used service-ref won't be processed
// In this case we process them late
if(xml != null && xml.getServiceReferences() != null)
{
for(ServiceReferenceMetaData sref : xml.getServiceReferences())
{
// FIXME: fix WS metadata
/*
if(!sref.isProcessed())
{
try
{
String name = sref.getServiceRefName();
String encName = "env/" + name;
Context encCtx = getEnc();
UnifiedVirtualFile vfsRoot = new VirtualFileAdaptor(getRootFile());
new ServiceRefDelegate().bindServiceRef(encCtx, encName, vfsRoot, getClassloader(), sref);
}
catch (Exception e)
{
log.error("Failed to bind service-ref", e);
}
}
*/
}
}
// EJBTHREE-1025
this.checkForDuplicateLocalAndRemoteInterfaces();
for(Class<?> businessInterface : getBusinessAndComponentInterfaces())
((JBoss5DependencyPolicy) getDependencyPolicy()).addSupply(businessInterface);
Class localHomeInterface = ProxyFactoryHelper.getLocalHomeInterface(this);
if(localHomeInterface != null)
((JBoss5DependencyPolicy) getDependencyPolicy()).addSupply(localHomeInterface);
Class remoteHomeInterface = ProxyFactoryHelper.getRemoteHomeInterface(this);
if(remoteHomeInterface != null)
((JBoss5DependencyPolicy) getDependencyPolicy()).addSupply(remoteHomeInterface);
}
finally
{
Thread.currentThread().setContextClassLoader(old);
}
}
protected Collection<InjectionHandler<Environment>> getInjectionHandlers()
{
if (this.deployment != null)
{
return this.deployment.getHandlers();
}
return null;
}
/**
* Ensures that the bean does not implement any one interface as both @Local and @Remote
*
* EJBTHREE-1025
*
* @throws EJBException If the bean does implements any one interface as both @Local and @Remote
*/
protected void checkForDuplicateLocalAndRemoteInterfaces() throws EJBException
{
// Initialize issue used in Error Message
String issue = "[" + ErrorCodes.ERROR_CODE_EJBTHREE1025 + "]";
// Obtain annotations, if found
Local local = (Local) resolveAnnotation(Local.class);
Remote remote = (Remote) resolveAnnotation(Remote.class);
// If either local or remote is unspecified, return safely - there can be no overlap
if (local == null || remote == null)
{
return;
}
// Ensure "value" attribute of both local and remote are not blank
if (local.value().length < 1 && local.value().length < 1)
{
throw new EJBException("Cannot designate both " + Local.class.getName() + " and " + Remote.class.getName()
+ " annotations without 'value' attribute on " + this.getEjbName() + ". " + issue);
}
// Iterate through local and remote interfaces, ensuring any one interface is not being used for both local and remote exposure
for (Class<?> localClass : local.value())
{
for (Class<?> remoteClass : remote.value())
{
if (localClass.equals(remoteClass))
{
throw new EJBException("Cannot designate " + localClass.getName() + " as both " + Local.class.getName()
+ " and " + Remote.class.getName() + " on " + this.getEjbName() + ". " + issue);
}
}
}
}
public JBossEnterpriseBeanMetaData getXml()
{
return xml;
}
public JBossAssemblyDescriptorMetaData getAssemblyDescriptor()
{
return assembly;
}
// FIXME: remove
@Deprecated
public void setAssemblyDescriptor(JBossAssemblyDescriptorMetaData assembly)
{
this.assembly = assembly;
}
protected abstract List<Class<?>> resolveBusinessInterfaces();
public InterceptorInfoRepository getInterceptorRepository()
{
throw new RuntimeException("invalid");
}
public Map<String, EncInjector> getEncInjectors()
{
return encInjectors;
}
public ComponentStack getCachedConnectionManager()
{
return cachedConnectionManager;
}
public ClassLoader getClassloader()
{
return classloader;
}
public InitialContext getInitialContext()
{
try
{
return InitialContextFactory.getInitialContext(initialContextProperties);
}
catch (NamingException e)
{
throw new RuntimeException(e);
}
}
public Map<String, Map<AccessibleObject, Injector>> getEncInjections()
{
return encInjections;
}
public Context getEnc()
{
if (enc == null)
{
if(encFactory == null)
{
// postpone the inevitable
//ClassLoader cl = AccessController.doPrivileged(currentContextClassLoader());
ClassLoader cl = Thread.currentThread().getContextClassLoader();
Class<?> interfaces[] = { Context.class };
InvocationHandler handler = new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
try
{
if(encFactory == null)
throw new IllegalStateException("EJBTHREE-2056: EjbEncFactory is null, usage of java:comp is not allowed before CREATE");
return method.invoke(encFactory.getEnc(EJBContainer.this), args);
}
catch(InvocationTargetException e)
{
throw e.getTargetException();
}
}
};
enc = (Context) Proxy.newProxyInstance(cl, interfaces, handler);
}
else
enc = encFactory.getEnc(this);
}
return enc;
}
public Hashtable getInitialContextProperties()
{
return initialContextProperties;
}
public ObjectName getObjectName()
{
return objectName;
}
public String getEjbName()
{
return ejbName;
}
public String getBeanClassName()
{
return beanClassName;
}
public Class<?> getBeanClass()
{
return beanClass;
}
public Pool getPool()
{
return pool;
}
/**
* Gets the name of the cluster partition with which this container is
* associated. Not available until <code>EJBContainer.start()</code>
* is completed.
*
* @return the name of the cluster partition with which this container is
* associated, or <code>null</code> if the container is not clustered
*/
public String getPartitionName()
{
if (partitionName == null)
this.findPartitionName();
return partitionName;
}
/**
* Delegates bean instance construction to the
* {@link EJBContainer#beanInstantiator}
* @return
*/
protected Object construct()
{
try
{
return beanInstantiator.create(beanClass, BEAN_INSTANCE_CONSTRUCTION_PARAMS);
}
catch (final InvalidConstructionParamsException e)
{
throw new RuntimeException(e);
}
catch (final BeanInstantiationException e)
{
throw new RuntimeException(e);
}
}
protected void reinitialize()
{
// FIXME: is this correct?
beanContainer.reinitializeAdvisor();
/*
initClassMetaDataBindingsList();
adviceBindings.clear();
doesHaveAspects = false;
constructorInfos = null;
rebuildInterceptors();
*/
bindEJBContext();
reinitialize = false;
}
public void create() throws Exception
{
// Blocking invocations until start()
this.blockInvocations();
if(encFactory == null)
{
log.debug("EJBTHREE-2056: Failed to get an EjbEncFactory, will create the default.");
encFactory = new DefaultEjbEncFactory();
}
if(log.isDebugEnabled())
log.debug("Current context class loader is " + Thread.currentThread().getContextClassLoader());
if(resurrectMetaData)
{
processMetadata();
resurrectMetaData = false;
}
}
/**
* This should have been final, but ServiceContainer wants to butt in.
*/
public void start() throws Exception
{
this.lockedStart();
// Allow invocations until stop()
this.allowInvocations();
// Now that invocations are allowed on the containers,
// let us invoke this callback so that individual
// containers can do an startup invocations if required
this.afterStart();
}
// Everything must be done in start to make sure all dependencies have been satisfied
protected void lockedStart() throws Exception
{
if (reinitialize)
reinitialize();
initializePool();
// TODO: Should be removed after switchboard (backed by ResourceProviders)
// is fully functional
for (EncInjector injector : encInjectors.values())
{
injector.inject(this);
}
// creating of injector array should come after injection into ENC as an ENC injector
// may add additional injectors into the injector list. An example is an extended persistence
// context which mush be created and added to the SFSB bean context.
Injector[] injectors2 = injectors.toArray(new Injector[injectors.size()]);
if (pool != null) pool.setInjectors(injectors2);
// createCallbackHandler();
// If we're clustered, find our partition name
findPartitionName();
if(this.beanInstantiator == null)
{
log.warn("EJBTHREE-2046: beanInstantior not set, using default on " + this);
// TODO: should not introduce an EJB 3.1 dependency
this.beanInstantiator = new Ejb31SpecBeanInstantiator();
}
log.info("STARTED EJB: " + beanClass.getName() + " ejbName: " + ejbName);
}
/**
* Invoked after all the dependencies on the containers have satisfied and the
* container is completely started and open for invocations.
*
* Containers can use this method as a callback point where they want to do
* some startup invocations on the container, which requires the container
* to be open for invocations (ex: restoring timers - see StatelessContainer)
*
* @see #start()
*/
protected void afterStart()
{
// do nothing, let the specific containers (ex: StatelessContainer) do anything
// specific they want to.
}
/**
* This should have been final, but ServiceContainer wants to butt in.
*/
public void stop() throws Exception
{
// Wait for active invocations to complete - and block new invocations
this.blockInvocations();
this.lockedStop();
}
protected void lockedStop() throws Exception
{
reinitialize = true;
//encFactory.cleanupEnc(this);
if (pool != null)
{
pool.destroy();
pool = null;
}
log.info("STOPPED EJB: " + beanClass.getName() + " ejbName: " + ejbName);
}
public void destroy() throws Exception
{
// Once enc is destroyed, the encInjectors are invalid as well.
injectors = new ArrayList<Injector>();
encInjectors = new HashMap<String, EncInjector>();
encFactory.cleanupEnc(this);
enc = null;
// TODO: clean up BeanContainer?
//super.cleanup();
/*
* EJBTHREE-1984: Leave invocations blocked
*/
// Restore to pre- create() state
// this.allowInvocations();
// EJBTHREE-1889: make the bean resurrectable
resurrectMetaData = true;
}
@SuppressWarnings("unchecked")
public <T> T getSecurityManager(Class<T> type)
{
try
{
InitialContext ctx = getInitialContext();
SecurityDomain securityAnnotation = (SecurityDomain) resolveAnnotation(SecurityDomain.class);
if (securityAnnotation != null && securityAnnotation.value().length() > 0)
{
return (T) SecurityDomainManager.getSecurityManager(securityAnnotation.value(),ctx);
}
return null;
}
catch (NamingException e)
{
throw new RuntimeException(e);
}
}
protected Method getTimeoutCallback(NamedMethodMetaData timeoutMethodMetaData, Class<?> beanClass)
{
String methodName = null;
Class<?>[] timeoutMethodParams = null;
JBossEnterpriseBeanMetaData metaData = xml;
if(metaData != null)
{
if(timeoutMethodMetaData != null)
{
// timeout method name
methodName = timeoutMethodMetaData.getMethodName();
// timeout method params
MethodParametersMetaData methodParams = timeoutMethodMetaData.getMethodParams();
String[] paramTypes = methodParams == null ? null : methodParams.toArray(new String[methodParams.size()]);
timeoutMethodParams = this.loadTimeoutMethodParamTypes(beanClass.getClassLoader(), paramTypes);
}
}
Method timeoutMethod = timeoutMethodCallbackRequirements.getTimeoutMethod(beanClass, methodName, timeoutMethodParams);
if(timeoutMethod != null)
return timeoutMethod;
// TODO: should not be needed
// TODO: ServiceContainer does not container correct metadata
if(metaData != null)
{
// TODO: cross cutting concern
if(metaData.getEjbJarMetaData().isMetadataComplete())
return null;
}
for (Method method : beanClass.getMethods())
{
if (getAnnotation(Timeout.class, method) != null)
{
if (Modifier.isPublic(method.getModifiers()) &&
method.getReturnType().equals(Void.TYPE) &&
method.getParameterTypes().length == 1 &&
method.getParameterTypes()[0].equals(Timer.class))
{
// TODO: check for multiples
log.warn("EJBTHREE-2017: found a timeout method using legacy annotation scan (bug in JBMETA)");
return method;
}
else
{
throw new RuntimeException("@Timeout method " + method + " must have signature: void <METHOD>(javax.ejb.Timer timer) (EJB3 18.2.2)");
}
}
}
return null;
}
/**
* Loads the classes passed through the <code>paramTypes</code>, using the <code>cl</code> {@link ClassLoader}
*
* @param cl {@link ClassLoader} which will be used to load the param types
* @param paramTypes The fully qualified type names
* @return
*/
private Class<?>[] loadTimeoutMethodParamTypes(ClassLoader cl, String[] paramTypes)
{
if (paramTypes == null)
{
return null;
}
// load the method param classes
List<Class<?>> timeoutMethodParamTypes = new ArrayList<Class<?>>(paramTypes.length);
for (String paramClassName : paramTypes)
{
Class<?> methodParamClass = null;
try
{
methodParamClass = Class.forName(paramClassName, false, cl);
}
catch (ClassNotFoundException cnfe)
{
throw new RuntimeException("Could not load method param class: " + paramClassName + " of timeout method");
}
timeoutMethodParamTypes.add(methodParamClass);
}
return timeoutMethodParamTypes.toArray(new Class<?>[paramTypes.length]);
}
protected void initializePool() throws Exception
{
org.jboss.ejb3.annotation.Pool poolAnnotation = getAnnotation(org.jboss.ejb3.annotation.Pool.class);
if (poolAnnotation == null)
throw new IllegalStateException("No pool annotation");
String registeredPoolName = poolAnnotation.value();
// EJBTHREE-1119
if(registeredPoolName==null||registeredPoolName.trim().equals(""))
{
// Default the Pool Implementation
registeredPoolName = PoolDefaults.POOL_IMPLEMENTATION_THREADLOCAL;
}
int maxSize = poolAnnotation.maxSize();
long timeout = poolAnnotation.timeout();
PoolFactoryRegistry registry = deployment.getPoolFactoryRegistry();
PoolFactory factory = registry.getPoolFactory(registeredPoolName);
pool = factory.createPool();
pool.initialize(this, maxSize, timeout);
pool.setInjectors(injectors.toArray(new Injector[injectors.size()]));
}
/**
* Note this method is a WIP.
*
* In actuality ejb3-interceptors should perform the injection itself,
* but this requires a rewrite of all injectors.
*/
public void injectBeanContext(BeanContext<?> beanContext)
{
try
{
// TODO: Remove the rest of this stuff from here, once we
// have fully integrated SwitchBoard and jboss-injection in place
if(injectors == null)
{
this.injectors = new ArrayList<Injector>();
}
Advisor advisor = getAdvisor();
InjectionInvocation invocation = new InjectionInvocation(beanContext, injectors, injectionCallbackStack, this.injectionManager);
invocation.setAdvisor(advisor);
invocation.setTargetObject(beanContext.getInstance());
invocation.invokeNext();
}
catch(Throwable t)
{
if(t instanceof Error)
throw (Error) t;
if(t instanceof RuntimeException)
throw (RuntimeException) t;
throw new RuntimeException(t);
}
}
/**
* Note that this method is a WIP.
*
* @param beanContext
* @param callbackAnnotationClass on of PostConstruct, PreDestroy, PostActivate or PrePassivate
*/
protected void invokeCallback(BeanContext<?> beanContext, Class<? extends Annotation> callbackAnnotationClass)
{
// it's the BeanContainer's responsibility to invoke the callback
// through the correct interceptors. So let's pass the call to the beanContainer
this.getBeanContainer().invokeCallback(beanContext, callbackAnnotationClass);
}
public void invokePostConstruct(BeanContext<?> beanContext)
{
// FIXME: This is a dirty hack to notify AS EJBTimerService about what's going on
AllowedOperationsAssociation.pushInMethodFlag(AllowedOperationsAssociation.IN_EJB_CREATE);
try
{
this.pushContext(beanContext);
invokeCallback(beanContext, PostConstruct.class);
}
finally
{
this.popContext();
AllowedOperationsAssociation.popInMethodFlag();
}
}
@Deprecated
public void invokePostConstruct(BeanContext beanContext, Object[] params)
{
invokePostConstruct(beanContext);
}
public void invokePreDestroy(BeanContext beanContext)
{
invokeCallback(beanContext, PreDestroy.class);
}
public void invokePostActivate(BeanContext beanContext)
{
throw new RuntimeException("PostActivate not implemented for container");
}
public void invokePrePassivate(BeanContext beanContext)
{
throw new RuntimeException("PrePassivate not implemented for container");
}
/*
public void invokeInit(Object bean, Class[] initParameterTypes,
Object[] initParameterValues)
{
// do nothing, only useful on a stateful session bean
}
*/
public static final String MANAGED_ENTITY_MANAGER_FACTORY = "ManagedEntityManagerFactory";
public static final String ENTITY_MANAGER_FACTORY = "EntityManagerFactory";
/**
* @deprecated {@link EJBContainer} is no longer responsible for setting up
* ENC. Instead, SwitchBoard http://community.jboss.org/wiki/Switchboard
* will be setting it up.
*/
@Deprecated
protected void resolveInjectors() throws Exception
{
pushEnc();
try
{
Thread.currentThread().setContextClassLoader(classloader);
try
{
Util.rebind(getEnc(), "UserTransaction", new UserTransactionImpl());
}
catch (NamingException e)
{
NamingException namingException = new NamingException("Could not bind user transaction for ejb name " + ejbName + " into JNDI under jndiName: " + getEnc().getNameInNamespace() + "/" + "UserTransaction");
namingException.setRootCause(e);
throw namingException;
}
try
{
Util.rebind(getEnc(), "TransactionSynchronizationRegistry", new LinkRef("java:TransactionSynchronizationRegistry"));
log.debug("Linked java:comp/TransactionSynchronizationRegistry to JNDI name: java:TransactionSynchronizationRegistry");
}
catch (NamingException e)
{
NamingException namingException = new NamingException("Could not bind TransactionSynchronizationRegistry for ejb name " + ejbName + " into JNDI under jndiName: " + getEnc().getNameInNamespace() + "/" + "TransactionSynchronizationRegistry");
namingException.setRootCause(e);
throw namingException;
}
}
finally
{
popEnc();
}
}
protected Class[] getHandledCallbacks()
{
return new Class[]
{PostConstruct.class, PreDestroy.class, Timeout.class};
}
// TODO: once injection is finalized this method will disappear
private InterceptorMetaData findInterceptor(Class<?> interceptorClass)
{
if(xml == null)
return null;
JBossMetaData ejbJarMetaData = xml.getEjbJarMetaData();
if(ejbJarMetaData == null)
return null;
InterceptorsMetaData interceptors = ejbJarMetaData.getInterceptors();
if(interceptors == null)
return null;
for(InterceptorMetaData interceptorMetaData : interceptors)
{
if(interceptorMetaData.getInterceptorClass().equals(interceptorClass.getName()))
return interceptorMetaData;
}
return null;
}
protected void findPartitionName()
{
Clustered clustered = (Clustered) getAnnotation(Clustered.class);
if (clustered == null)
{
partitionName = null;
return;
}
String value = clustered.partition();
try
{
String replacedValue = StringPropertyReplacer.replaceProperties(value);
if (value != replacedValue)
{
log.debug("Replacing @Clustered partition attribute " + value + " with " + replacedValue);
value = replacedValue;
}
}
catch (Exception e)
{
log.warn("Unable to replace @Clustered partition attribute " + value +
". Caused by " + e.getClass() + " " + e.getMessage());
}
partitionName = value;
}
public <T> T getBusinessObject(BeanContext<?> beanContext, Class<T> businessInterface) throws IllegalStateException
{
throw new IllegalStateException("Not implemented");
}
public Object getInvokedBusinessInterface(BeanContext beanContext) throws IllegalStateException
{
throw new IllegalStateException("Not implemented");
}
protected Object getInvokedInterface(Method method)
{
Remote remoteAnnotation = (Remote) resolveAnnotation(Remote.class);
if (remoteAnnotation != null)
{
Class[] remotes = remoteAnnotation.value();
for (int i = 0; i < remotes.length; ++i)
{
try
{
remotes[i].getMethod(method.getName(), method.getParameterTypes());
return remotes[i];
}
catch (NoSuchMethodException e)
{
}
}
}
Local localAnnotation = (Local) resolveAnnotation(Local.class);
if (localAnnotation != null)
{
Class[] locals = localAnnotation.value();
for (int i = 0; i < locals.length; ++i)
{
Method[] interfaceMethods = locals[i].getMethods();
for (int j = 0; j < interfaceMethods.length; ++j)
{
if (interfaceMethods[j].equals(method))
return locals[i];
}
}
}
return null;
}
// todo these method overrides for aop are for performance reasons
private Class loadPublicAnnotation(String annotation)
{
try
{
Class ann = classloader.loadClass(annotation);
if (!ann.isAnnotation()) return null;
Retention retention = (Retention) ann.getAnnotation(Retention.class);
if (retention != null && retention.value() == RetentionPolicy.RUNTIME) return ann;
}
catch (ClassNotFoundException ignored)
{
}
return null;
}
/*
@Override
public boolean hasAnnotation(Class tgt, String annotation)
{
if (annotations.hasClassAnnotation(annotation)) return true;
if (tgt == null) return false;
try
{
Class ann = loadPublicAnnotation(annotation);
// it is metadata or CLASS annotation
if (ann == null) return AnnotationElement.isAnyAnnotationPresent(tgt, annotation);
return tgt.isAnnotationPresent(ann);
}
catch (Exception e)
{
throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
}
}
@Override
public boolean hasAnnotation(Method m, String annotation)
{
if (annotations.hasAnnotation(m, annotation)) return true;
try
{
Class ann = loadPublicAnnotation(annotation);
// it is metadata or CLASS annotation
if (ann == null) return AnnotationElement.isAnyAnnotationPresent(m, annotation);
return m.isAnnotationPresent(ann);
}
catch (Exception e)
{
throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
}
}
@Override
public boolean hasAnnotation(Field m, String annotation)
{
if (annotations.hasAnnotation(m, annotation)) return true;
try
{
Class ann = loadPublicAnnotation(annotation);
// it is metadata or CLASS annotation
if (ann == null) return AnnotationElement.isAnyAnnotationPresent(m, annotation);
return m.isAnnotationPresent(ann);
}
catch (Exception e)
{
throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
}
}
@Override
public boolean hasAnnotation(Constructor m, String annotation)
{
if (annotations.hasAnnotation(m, annotation)) return true;
try
{
Class ann = loadPublicAnnotation(annotation);
// it is metadata or CLASS annotation
if (ann == null) return AnnotationElement.isAnyAnnotationPresent(m, annotation);
return m.isAnnotationPresent(ann);
}
catch (Exception e)
{
throw new RuntimeException(e); //To change body of catch statement use Options | File Templates.
}
}
*/
public String resolveEJB(String link, Class<?> beanInterface, String mappedName)
{
return deployment.resolveEJB(link, beanInterface, mappedName);
}
public Container resolveEjbContainer(String link, Class businessIntf)
{
return deployment.getEjbContainer(link, businessIntf);
}
public Container resolveEjbContainer(Class businessIntf) throws NameNotFoundException
{
return deployment.getEjbContainer(businessIntf);
}
public String resolveMessageDestination(String link)
{
return deployment.resolveMessageDestination(link);
}
public String resolvePersistenceUnitSupplier(String unitName)
{
return getDeployment().resolvePersistenceUnitSupplier(unitName);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType)
{
if (this.getAnnotations().isDisabled(annotationType))
return null;
return beanContainer.getAnnotation(annotationType);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz)
{
return beanContainer.getAnnotation(clazz, annotationType);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz, Method method)
{
return beanContainer.getAnnotation(annotationType, clazz, method);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType, Method method)
{
if (this.getAnnotations().isDisabled(method, annotationType))
return null;
return beanContainer.getAnnotation(annotationType, method);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType, Class<?> clazz, Field field)
{
return beanContainer.getAnnotation(annotationType, clazz, field);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationType, Field field)
{
return beanContainer.getAnnotation(annotationType, field);
}
/**
* @deprecated use getAnnotation
*/
@SuppressWarnings("unchecked")
public Object resolveAnnotation(Class annotationType)
{
return getAnnotation(annotationType);
}
/**
* @deprecated use getAnnotation
*/
@SuppressWarnings("unchecked")
public Object resolveAnnotation(Field field, Class annotationType)
{
return getAnnotation(annotationType, field);
}
/**
* @deprecated use getAnnotation
*/
@SuppressWarnings("unchecked")
public Object resolveAnnotation(Method method, Class annotationType)
{
return getAnnotation(annotationType, method);
}
/**
* @deprecated this is going to be gone soon
*/
@SuppressWarnings("unchecked")
public Object resolveAnnotation(Method m, Class[] annotationChoices)
{
Object value = null;
int i = 0;
while (value == null && i < annotationChoices.length){
value = resolveAnnotation(m, annotationChoices[i++]);
}
return value;
}
public String getIdentifier()
{
return getEjbName();
}
public String getDeploymentDescriptorType()
{
return "ejb-jar.xml";
}
public String getEjbJndiName(Class businessInterface) throws NameNotFoundException
{
return deployment.getEjbJndiName(businessInterface);
}
public String getEjbJndiName(String link, Class businessInterface)
{
return deployment.getEjbJndiName(link, businessInterface);
}
public InvocationStatistics getInvokeStats()
{
return invokeStats;
}
@Deprecated
protected MethodInfo getMethodInfo(Method method)
{
long hash = MethodHashing.calculateHash(method);
MethodInfo info = getAdvisor().getMethodInfo(hash);
if (info == null)
{
throw new RuntimeException("Could not resolve beanClass method from proxy call: " + method.toString());
}
return info;
}
public boolean isClustered()
{
return false;
}
public JavaEEModule getModule()
{
return deployment;
}
public void setEjbEncFactory(EjbEncFactory encFactory)
{
this.encFactory = encFactory;
}
public abstract boolean hasJNDIBinding(String jndiName);
/**
* After XML processing has been done this allows the container
* to further initialize the meta data.
*/
public void instantiated()
{
this.businessAndComponentInterfaces = resolveBusinessInterfaces();
// Before we start to process annotations, make sure we also have the ones from interceptors-aop.
// FIXME: because of the flaked life cycle of an EJBContainer (we add annotations after it's been
// constructed), we must reinitialize the whole thing.
beanContainer.reinitializeAdvisor();
}
@Inject
public void setCachedConnectionManager(ComponentStack ccm)
{
this.cachedConnectionManager = ccm;
}
public void setDirectContainer(DirectContainer<EJBContainer> container)
{
this.directContainer = container;
}
protected Method getNonBridgeMethod(Method bridgeMethod)
{
Class clazz = bridgeMethod.getDeclaringClass();
Method[] methods = clazz.getMethods();
for (Method method : methods)
{
if (!method.isBridge() && method.getParameterTypes().length == bridgeMethod.getParameterTypes().length)
{
return method;
}
}
return bridgeMethod;
}
public Lock getInvocationLock()
{
return this.invocationLock;
}
// to make sure we have a dependency on the TransactionManager
// note that the actual tx interceptors don't make use of the injected tm
@Inject
public void setTransactionManager(TransactionManager tm)
{
}
public void setInjectionManager(InjectionManager injectionManager)
{
if (injectionManager == null)
{
throw new IllegalArgumentException("Cannot set null InjectionManager for container of bean " + this.getEjbName());
}
this.injectionManager = injectionManager;
}
/**
* Returns {@link AnnotationRepositoryToMetaData} corresponding to this {@link EJBContainer}.
* The returned {@link AnnotationRepositoryToMetaData} is an implementation of {@link AnnotationRepository}
* and is capable of resolving annotations from bean metadata through the use of {@link MetaDataBridge}s
*
* @return
*/
public AnnotationRepositoryToMetaData getMetaDataBasedAnnotationRepository()
{
if (this.metadataBasedAnnotationRepo == null)
{
this.initMetaDataBasedAnnotationRepository();
}
return this.metadataBasedAnnotationRepo;
}
/**
* Initializes the metadata based annotation repository {@link AnnotationRepositoryToMetaData},
* by setting up the appropriate metadata bridges.
* <p>
* Individual containers can then override this method to add more metadata bridges to the
* metadata based annotation repository.
* </p>
* <p>
* Sets up metadata bridges for
* <ul>
* <li>SecurityDomain metadata</li>
* <li>RunAs metadata</li>
* <li>Clustered metadata</li>
* <li>ApplicationException metadata</li>
* <li>Various metadata bridges for processing lifecycle callbacks on bean an interceptors</li>
* </ul>
* </p>
*
*/
protected void initMetaDataBasedAnnotationRepository()
{
this.metadataBasedAnnotationRepo = new AnnotationRepositoryToMetaData(this.beanClass, this.xml, name, this.classloader);
List<MetaDataBridge<InterceptorMetaData>> interceptorBridges = new ArrayList<MetaDataBridge<InterceptorMetaData>>();
interceptorBridges.add(new InterceptorMetaDataBridge());
this.metadataBasedAnnotationRepo.addComponentMetaDataLoaderFactory(new InterceptorComponentMetaDataLoaderFactory(interceptorBridges));
this.metadataBasedAnnotationRepo.addMetaDataBridge(new AdditiveBeanInterceptorMetaDataBridge(this.beanClass, this.classloader, this.xml));
List<MetaDataBridge<ApplicationExceptionMetaData>> appExceptionBridges = new ArrayList<MetaDataBridge<ApplicationExceptionMetaData>>();
appExceptionBridges.add(new ApplicationExceptionMetaDataBridge());
this.metadataBasedAnnotationRepo.addComponentMetaDataLoaderFactory(new ApplicationExceptionComponentMetaDataLoaderFactory(appExceptionBridges));
this.metadataBasedAnnotationRepo.addMetaDataBridge(new RunAsMetaDataBridge());
//Add a security domain bridge
this.metadataBasedAnnotationRepo.addMetaDataBridge(new SecurityDomainMetaDataBridge());
// Ensure that an @Clustered annotation is visible to AOP if the XML says the bean is clustered.
this.metadataBasedAnnotationRepo.addMetaDataBridge(new ClusteredMetaDataBridge());
}
public String toString()
{
return getObjectName().getCanonicalName();
}
private void blockInvocations() throws InterruptedException
{
// Allow re-entrance
if (this.semaphore.tryAcquire())
{
try
{
// Acquire all remaining permits, blocking invocation lock
this.semaphore.acquire(TOTAL_PERMITS - 1);
}
catch (InterruptedException e)
{
this.semaphore.release();
throw e;
}
}
}
private void allowInvocations()
{
// Allow re-entrance
if (!this.semaphore.tryAcquire())
{
// Make all permits available to invocation lock
this.semaphore.release(TOTAL_PERMITS);
}
else
{
// Release the one we just acquired
this.semaphore.release();
}
}
/**
* {@link java.util.concurrent.locks.Lock} facade for this container's semaphore
* @author Paul Ferraro
*/
private static class SemaphoreLock implements Lock
{
private final Semaphore semaphore;
SemaphoreLock(Semaphore semaphore)
{
this.semaphore = semaphore;
}
/**
* @see java.util.concurrent.locks.Lock#lock()
*/
public void lock()
{
this.semaphore.acquireUninterruptibly();
}
/**
* @see java.util.concurrent.locks.Lock#lockInterruptibly()
*/
public void lockInterruptibly() throws InterruptedException
{
this.semaphore.acquire();
}
/**
* @see java.util.concurrent.locks.Lock#newCondition()
*/
public Condition newCondition()
{
throw new UnsupportedOperationException();
}
/**
* @see java.util.concurrent.locks.Lock#tryLock()
*/
public boolean tryLock()
{
return this.semaphore.tryAcquire();
}
/**
* @see java.util.concurrent.locks.Lock#tryLock(long, java.util.concurrent.TimeUnit)
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException
{
return this.semaphore.tryAcquire(timeout, unit);
}
/**
* @see java.util.concurrent.locks.Lock#unlock()
*/
public void unlock()
{
this.semaphore.release();
}
}
/**
* {@inheritDoc}
*/
@Override
public EJBHome getEJBHome() throws IllegalStateException
{
throw new UnsupportedOperationException("NYI");
}
/**
* {@inheritDoc}
*/
public EJBLocalHome getEJBLocalHome() throws IllegalStateException
{
throw new UnsupportedOperationException("NYI");
}
/**
* {@inheritDoc}
*/
public boolean getRollbackOnly() throws IllegalStateException
{
throw new UnsupportedOperationException("NYI");
}
/**
* {@inheritDoc}
*/
public UserTransaction getUserTransaction() throws IllegalStateException
{
throw new UnsupportedOperationException("NYI");
}
/**
* {@inheritDoc}
*/
public boolean isCallerInRole(Principal callerPrincipal, String roleName) throws IllegalStateException
{
throw new UnsupportedOperationException("NYI");
}
/**
* {@inheritDoc}
*/
public Object lookup(String name) throws IllegalArgumentException
{
try
{
Object obj = this.getEnc().lookup(name);
return obj;
}
catch (NamingException ne)
{
throw new RuntimeException(ne);
}
}
/**
* {@inheritDoc}
*/
public void setRollbackOnly() throws IllegalStateException
{
throw new UnsupportedOperationException("NYI");
}
/**
*
* @param joinPoint
* @param interceptorStackName
* @return
*/
protected Interceptor[] getInterceptors(Joinpoint joinPoint, String interceptorStackName)
{
AdviceStack stack = this.getAdvisor().getManager().getAdviceStack(interceptorStackName);
if (stack == null)
{
log.debug("No AOP interceptor stack with name : " + interceptorStackName + " available for EJB container: " + this);
return new Interceptor[0];
}
List<Interceptor> interceptors = new ArrayList<Interceptor>();
interceptors.addAll(Arrays.asList(stack.createInterceptors(this.getAdvisor(), joinPoint)));
return interceptors.toArray(new Interceptor[]{});
}
protected BeanInstantiator getBeanInstantiator()
{
return this.beanInstantiator;
}
public void setBeanInstantiator(BeanInstantiator beanInstantiator)
{
this.beanInstantiator = beanInstantiator;
}
}