| /* |
| * Copyright (c) 1997, 2021 Oracle and/or its affiliates. All rights reserved. |
| * Copyright (c) 2022 Contributors to the Eclipse Foundation |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0, which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * This Source Code may also be made available under the following Secondary |
| * Licenses when the conditions for such availability set forth in the |
| * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, |
| * version 2 with the GNU Classpath Exception, which is available at |
| * https://www.gnu.org/software/classpath/license.html. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 |
| */ |
| |
| package com.sun.ejb.containers; |
| |
| import java.io.Serializable; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.lang.reflect.Proxy; |
| import java.rmi.AccessException; |
| import java.rmi.Remote; |
| import java.rmi.RemoteException; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Locale; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| import java.util.Vector; |
| import java.util.concurrent.Future; |
| import java.util.logging.Level; |
| import java.util.logging.Logger; |
| |
| import javax.naming.NamingException; |
| import javax.naming.Reference; |
| import javax.naming.StringRefAddr; |
| |
| import org.glassfish.api.invocation.ComponentInvocation; |
| import org.glassfish.api.invocation.InvocationManager; |
| import org.glassfish.api.naming.GlassfishNamingManager; |
| import org.glassfish.deployment.common.DeploymentException; |
| import org.glassfish.deployment.common.Descriptor; |
| import org.glassfish.ejb.LogFacade; |
| import org.glassfish.ejb.api.EjbEndpointFacade; |
| import org.glassfish.ejb.deployment.descriptor.EjbApplicationExceptionInfo; |
| import org.glassfish.ejb.deployment.descriptor.EjbBundleDescriptorImpl; |
| import org.glassfish.ejb.deployment.descriptor.EjbDescriptor; |
| import org.glassfish.ejb.deployment.descriptor.EjbInitInfo; |
| import org.glassfish.ejb.deployment.descriptor.EjbSessionDescriptor; |
| import org.glassfish.ejb.deployment.descriptor.ScheduledTimerDescriptor; |
| import org.glassfish.ejb.spi.EjbContainerInterceptor; |
| import org.glassfish.ejb.spi.WSEjbEndpointRegistry; |
| import org.glassfish.enterprise.iiop.api.GlassFishORBHelper; |
| import org.glassfish.enterprise.iiop.api.ProtocolManager; |
| import org.glassfish.enterprise.iiop.api.RemoteReferenceFactory; |
| import org.glassfish.enterprise.iiop.spi.EjbContainerFacade; |
| import org.glassfish.flashlight.provider.ProbeProviderFactory; |
| import org.glassfish.hk2.api.ServiceLocator; |
| import org.glassfish.internal.api.Globals; |
| import org.glassfish.logging.annotation.LogMessageInfo; |
| |
| import com.sun.ejb.ComponentContext; |
| import com.sun.ejb.Container; |
| import com.sun.ejb.EJBUtils; |
| import com.sun.ejb.EjbInvocation; |
| import com.sun.ejb.EjbInvocationFactory; |
| import com.sun.ejb.InvocationInfo; |
| import com.sun.ejb.MethodLockInfo; |
| import com.sun.ejb.codegen.EjbOptionalIntfGenerator; |
| import com.sun.ejb.containers.interceptors.InterceptorManager; |
| import com.sun.ejb.containers.interceptors.SystemInterceptorProxy; |
| import com.sun.ejb.containers.util.MethodMap; |
| import com.sun.ejb.monitoring.probes.EjbCacheProbeProvider; |
| import com.sun.ejb.monitoring.probes.EjbMonitoringProbeProvider; |
| import com.sun.ejb.monitoring.probes.EjbTimedObjectProbeProvider; |
| import com.sun.ejb.monitoring.stats.EjbCacheStatsProvider; |
| import com.sun.ejb.monitoring.stats.EjbMonitoringStatsProvider; |
| import com.sun.ejb.monitoring.stats.EjbMonitoringUtils; |
| import com.sun.ejb.monitoring.stats.EjbPoolStatsProvider; |
| import com.sun.ejb.monitoring.stats.EjbTimedObjectStatsProvider; |
| import com.sun.ejb.portable.EJBMetaDataImpl; |
| import com.sun.ejb.spi.container.OptionalLocalInterfaceProvider; |
| import com.sun.enterprise.admin.monitor.callflow.CallFlowInfo; |
| import com.sun.enterprise.admin.monitor.callflow.ComponentType; |
| import com.sun.enterprise.container.common.spi.JCDIService; |
| import com.sun.enterprise.container.common.spi.JavaEEContainer; |
| import com.sun.enterprise.container.common.spi.util.ComponentEnvManager; |
| import com.sun.enterprise.container.common.spi.util.IndirectlySerializable; |
| import com.sun.enterprise.container.common.spi.util.InjectionManager; |
| import com.sun.enterprise.deployment.Application; |
| import com.sun.enterprise.deployment.EnvironmentProperty; |
| import com.sun.enterprise.deployment.InterceptorDescriptor; |
| import com.sun.enterprise.deployment.LifecycleCallbackDescriptor; |
| import com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType; |
| import com.sun.enterprise.deployment.MethodDescriptor; |
| import com.sun.enterprise.deployment.WebServiceEndpoint; |
| import com.sun.enterprise.deployment.WebServicesDescriptor; |
| import com.sun.enterprise.deployment.util.TypeUtil; |
| import com.sun.enterprise.deployment.xml.RuntimeTagNames; |
| import com.sun.enterprise.security.SecurityManager; |
| import com.sun.enterprise.transaction.api.JavaEETransaction; |
| import com.sun.enterprise.transaction.api.JavaEETransactionManager; |
| import com.sun.enterprise.util.LocalStringManagerImpl; |
| import com.sun.enterprise.util.Utility; |
| |
| import jakarta.annotation.PostConstruct; |
| import jakarta.annotation.PreDestroy; |
| import jakarta.ejb.AccessLocalException; |
| import jakarta.ejb.CreateException; |
| import jakarta.ejb.EJBAccessException; |
| import jakarta.ejb.EJBContext; |
| import jakarta.ejb.EJBException; |
| import jakarta.ejb.EJBHome; |
| import jakarta.ejb.EJBLocalHome; |
| import jakarta.ejb.EJBLocalObject; |
| import jakarta.ejb.EJBMetaData; |
| import jakarta.ejb.EJBObject; |
| import jakarta.ejb.EJBTransactionRequiredException; |
| import jakarta.ejb.EJBTransactionRolledbackException; |
| import jakarta.ejb.FinderException; |
| import jakarta.ejb.LockType; |
| import jakarta.ejb.NoSuchEJBException; |
| import jakarta.ejb.NoSuchObjectLocalException; |
| import jakarta.ejb.PostActivate; |
| import jakarta.ejb.PrePassivate; |
| import jakarta.ejb.RemoveException; |
| import jakarta.ejb.TransactionRequiredLocalException; |
| import jakarta.ejb.TransactionRolledbackLocalException; |
| import jakarta.interceptor.AroundConstruct; |
| import jakarta.persistence.EntityManager; |
| import jakarta.persistence.EntityManagerFactory; |
| import jakarta.transaction.RollbackException; |
| import jakarta.transaction.Status; |
| import jakarta.transaction.SystemException; |
| import jakarta.transaction.Transaction; |
| import jakarta.transaction.UserTransaction; |
| |
| /** |
| * This class implements part of the com.sun.ejb.Container interface. It implements the container's side of the EJB-to-Container |
| * contract definweed by the EJB 2.0 spec. It contains code shared by SessionBeans, EntityBeans and MessageDrivenBeans. Its |
| * subclasses provide the remaining implementation of the container functionality. |
| * |
| */ |
| |
| public abstract class BaseContainer implements Container, EjbContainerFacade, JavaEEContainer { |
| public enum ContainerType { |
| STATELESS, STATEFUL, SINGLETON, MESSAGE_DRIVEN, ENTITY, READ_ONLY |
| } |
| |
| protected static final Logger _logger = LogFacade.getLogger(); |
| |
| @LogMessageInfo(message = "The feature {0} requires Full Java EE Profile to be supported", level = "WARNING") |
| private static final String WARN_FEATURE_REQUIRES_FULL_PROFILE = "AS-EJB-00053"; |
| |
| @LogMessageInfo(message = "Portable JNDI names for EJB {0}: {1}", level = "INFO") |
| private static final String PORTABLE_JNDI_NAMES = "AS-EJB-00054"; |
| |
| @LogMessageInfo(message = "Glassfish-specific (Non-portable) JNDI names for EJB {0}: {1}", level = "INFO") |
| private static final String GLASSFISH_SPECIFIC_JNDI_NAMES = "AS-EJB-00055"; |
| |
| @LogMessageInfo(message = "A system exception occurred during an invocation on EJB {0}, method: {1}", level = "WARNING") |
| private static final String SYSTEM_EXCEPTION = "AS-EJB-00056"; |
| |
| @LogMessageInfo(message = "Error while creating enterprise bean context for {0} during jacc callback", level = "WARNING") |
| private static final String CONTEXT_FAILURE_JACC = "AS-EJB-00057"; |
| |
| @LogMessageInfo(message = "Attempt to override reserved ejb interface method [{0}] in [{1}]. Override will be ignored.", level = "WARNING") |
| private static final String ILLEGAL_EJB_INTERFACE_OVERRIDE = "AS-EJB-00058"; |
| |
| @LogMessageInfo(message = "Bean class for ejb [{0}] does not define a method corresponding to [{1}] interface method [{2}]", level = "WARNING") |
| private static final String BEAN_CLASS_METHOD_NOT_FOUND = "AS-EJB-00059"; |
| |
| @LogMessageInfo(message = "keepstate is true and will not create new auto timers during deployment.", level = "INFO") |
| private static final String KEEPSTATE_IS_TRUE = "AS-EJB-00060"; |
| |
| @LogMessageInfo(message = "Failed to initialize the interceptor", level = "SEVERE", cause = "Error during initializing the interceptor", action = "Try to restart the server") |
| private static final String FAILED_TO_INITIALIZE_INTERCEPTOR = "AS-EJB-00061"; |
| |
| @LogMessageInfo(message = "[**BaseContainer**] Could not create MonitorRegistryMediator. [{0}]", level = "SEVERE", cause = "Fail to create MonitorRegistryMediator", action = "Check the exception stack") |
| private static final String COULD_NOT_CREATE_MONITORREGISTRYMEDIATOR = "AS-EJB-00062"; |
| |
| @LogMessageInfo(message = "Internal Error", level = "WARNING", cause = "Error during invoke the ejb application", action = "Trying to invoke the ejb application") |
| private static final String INTERNAL_ERROR = "AS-EJB-00052"; |
| |
| protected static final Class[] NO_PARAMS = new Class[] {}; |
| |
| protected Object[] logParams = null; |
| |
| protected ContainerType containerType; |
| |
| // constants for EJB(Local)Home/EJB(Local)Object methods, |
| // used in authorizeRemoteMethod and authorizeLocalMethod |
| private static final int EJB_INTF_METHODS_LENGTH = 16; |
| static final int EJBHome_remove_Handle = 0; |
| static final int EJBHome_remove_Pkey = 1; |
| static final int EJBHome_getEJBMetaData = 2; |
| static final int EJBHome_getHomeHandle = 3; |
| static final int EJBLocalHome_remove_Pkey = 4; |
| static final int EJBObject_getEJBHome = 5; |
| protected static final int EJBObject_getPrimaryKey = 6; //TODO - move related to entity-container |
| static final int EJBObject_remove = 7; |
| static final int EJBObject_getHandle = 8; |
| static final int EJBObject_isIdentical = 9; |
| static final int EJBLocalObject_getEJBLocalHome = 10; |
| protected static final int EJBLocalObject_getPrimaryKey = 11; //TODO - move related to entity-container |
| static final int EJBLocalObject_remove = 12; |
| static final int EJBLocalObject_isIdentical = 13; |
| static final int EJBHome_create = 14; |
| static final int EJBLocalHome_create = 15; |
| |
| // true if home method, false if component intf method. |
| // Used for setting info on invocation object during authorization. |
| private static final boolean[] EJB_INTF_METHODS_INFO = { true, true, true, true, true, false, false, false, false, false, false, false, |
| false, false, true, true }; |
| |
| private static final byte HOME_KEY = (byte) 0xff; |
| private static final byte[] homeInstanceKey = { HOME_KEY }; |
| |
| protected static final String SINGLETON_BEAN_POOL_PROP = "singleton-bean-pool"; |
| |
| protected final ClassLoader loader; |
| protected Class ejbClass = null; |
| protected Class sfsbSerializedClass = null; |
| protected Method ejbPassivateMethod = null; |
| protected Method ejbActivateMethod = null; |
| protected Method ejbRemoveMethod = null; |
| private Method ejbTimeoutMethod = null; |
| |
| protected Class webServiceEndpointIntf = null; |
| |
| // true if exposed as a web service endpoint. |
| protected boolean isWebServiceEndpoint = false; |
| |
| private boolean isTimedObject_ = false; |
| |
| /***************************************** |
| * Data members for Local views * |
| *****************************************/ |
| |
| // True if bean has a LocalHome/Local view |
| // OR a Local business view OR both. |
| protected boolean isLocal = false; |
| |
| // True if bean exposes a local home view |
| protected boolean hasLocalHomeView = false; |
| |
| // True if bean exposes a local business view |
| protected boolean hasLocalBusinessView = false; |
| |
| protected boolean hasOptionalLocalBusinessView = false; |
| |
| protected Class ejbGeneratedOptionalLocalBusinessIntfClass; |
| // |
| // Data members for LocalHome/Local view |
| // |
| |
| // LocalHome interface written by developer |
| protected Class localHomeIntf = null; |
| |
| // Local interface written by developer |
| private Class localIntf = null; |
| |
| // Client reference to ejb local home |
| protected EJBLocalHome ejbLocalHome; |
| |
| // Implementation of ejb local home. May or may not be the same |
| // object as ejbLocalHome, for example in the case of dynamic proxies. |
| protected EJBLocalHomeImpl ejbLocalHomeImpl; |
| |
| // |
| // Data members for 3.x Local business view |
| // |
| |
| // Internal interface describing operation used to create an |
| // instance of a local business object. (GenericEJBLocalHome) |
| protected Class localBusinessHomeIntf = null; |
| protected Class ejbOptionalLocalBusinessHomeIntf = null; |
| |
| // Local business interface written by developer |
| protected Set<Class> localBusinessIntfs = new HashSet(); |
| |
| // Client reference to internal local business home interface. |
| // This is only seen by internal ejb code that instantiates local |
| // business objects during lookups. |
| protected GenericEJBLocalHome ejbLocalBusinessHome; |
| |
| protected GenericEJBLocalHome ejbOptionalLocalBusinessHome; |
| |
| // Implementation of internal local business home interface. |
| protected EJBLocalHomeImpl ejbLocalBusinessHomeImpl; |
| |
| // Implementation of internal local business home interface. |
| protected EJBLocalHomeImpl ejbOptionalLocalBusinessHomeImpl; |
| |
| private Collection<EjbContainerInterceptor> interceptors = null; |
| |
| /***************************************** |
| * Data members for Remote views * |
| *****************************************/ |
| |
| // True if bean has a RemoteHome/Remote view |
| // OR a Remote business view OR both. |
| protected boolean isRemote = false; |
| |
| // True if bean exposes a RemoteHome view |
| protected boolean hasRemoteHomeView = false; |
| |
| // True if bean exposes a Remote Business view. |
| protected boolean hasRemoteBusinessView = false; |
| |
| // |
| // Data members for RemoteHome/Remote view |
| // |
| |
| // Home interface written by developer. |
| protected Class homeIntf = null; |
| |
| // Remote interface written by developer. |
| protected Class remoteIntf = null; |
| |
| // Container implementation of EJB Home. May or may not be the same |
| // object as ejbHome, for example in the case of dynamic proxies. |
| protected EJBHomeImpl ejbHomeImpl; |
| |
| // EJB Home reference used by ORB Tie within server to deliver |
| // invocation. |
| protected EJBHome ejbHome; |
| |
| // Client reference to EJB Home. |
| protected EJBHome ejbHomeStub; |
| |
| // Remote interface proxy class |
| private Class ejbObjectProxyClass; |
| |
| // RemoteReference Factory for RemoteHome view |
| protected RemoteReferenceFactory remoteHomeRefFactory = null; |
| |
| // |
| // Data members for 3.x Remote business view |
| // |
| |
| // Internal interface describing operation used to create an |
| // instance of a remote business object. |
| protected Class remoteBusinessHomeIntf; |
| |
| // Container implementation of internal EJB Business Home. May or may |
| // not be same object as ejbRemoteBusinessHome, for example in the |
| // case of dynamic proxies. |
| protected EJBHomeImpl ejbRemoteBusinessHomeImpl; |
| |
| // EJB Remote Business Home reference used by ORB Tie within server |
| // to deliver invocation. |
| protected EJBHome ejbRemoteBusinessHome; |
| |
| // Client reference to internal Remote EJB Business Home. This is |
| // only seen by internal EJB code that instantiates remote business |
| // objects during lookups. |
| protected EJBHome ejbRemoteBusinessHomeStub; |
| |
| // Holds information such as remote reference factory that are associated |
| // with a particular remote business interface |
| protected Map<String, RemoteBusinessIntfInfo> remoteBusinessIntfInfo = new HashMap<>(); |
| |
| // |
| // END -- Data members for Remote views |
| // |
| |
| protected EJBMetaData metadata = null; |
| |
| protected final SecurityManager securityManager; |
| |
| protected boolean isSession; |
| protected boolean isStatelessSession; |
| protected boolean isStatefulSession; |
| protected boolean isMessageDriven; |
| protected boolean isSingleton; |
| |
| protected EjbDescriptor ejbDescriptor; |
| protected String componentId; // unique id for java:comp namespace lookup |
| |
| protected Map invocationInfoMap = new HashMap(); |
| |
| protected Map<TimerPrimaryKey, Method> scheduleIds = new HashMap<>(); |
| |
| Map<Method, List<ScheduledTimerDescriptor>> schedules = new HashMap<>(); |
| |
| // Need a separate map for web service methods since it's possible for |
| // an EJB Remote interface to be a subtype of the Service Endpoint |
| // Interface. In that case, it's ambiguous to do a lookup based only |
| // on a java.lang.reflect.Method |
| protected Map webServiceInvocationInfoMap = new HashMap(); |
| |
| // optimized method map for proxies to resolve invocation info |
| private MethodMap proxyInvocationInfoMap; |
| |
| protected Method[] ejbIntfMethods; |
| protected InvocationInfo[] ejbIntfMethodInfo; |
| |
| protected Properties envProps; |
| protected boolean isBeanManagedTran = false; |
| |
| protected boolean debugMonitorFlag = false; |
| |
| private static LocalStringManagerImpl localStrings = new LocalStringManagerImpl(BaseContainer.class); |
| |
| private final ThreadLocal threadLocalContext = new ThreadLocal(); |
| |
| protected static final int CONTAINER_INITIALIZING = -1; |
| protected static final int CONTAINER_STARTED = 0; |
| protected static final int CONTAINER_STOPPED = 1; |
| protected static final int CONTAINER_UNDEPLOYED = 3; |
| protected static final int CONTAINER_ON_HOLD = 4; |
| |
| protected int containerState = CONTAINER_INITIALIZING; |
| |
| protected HashMap methodMonitorMap; |
| protected boolean monitorOn = false; |
| |
| protected EjbMonitoringStatsProvider ejbProbeListener; |
| protected EjbMonitoringProbeProvider ejbProbeNotifier; |
| protected EjbTimedObjectStatsProvider timerProbeListener; |
| protected EjbTimedObjectProbeProvider timerProbeNotifier; |
| protected EjbPoolStatsProvider poolProbeListener; |
| protected EjbCacheProbeProvider cacheProbeNotifier; |
| protected EjbCacheStatsProvider cacheProbeListener; |
| |
| protected ContainerInfo containerInfo; |
| |
| private final String _debugDescription; |
| |
| //protected Agent callFlowAgent; |
| |
| protected CallFlowInfo callFlowInfo; |
| |
| protected InterceptorManager interceptorManager; |
| |
| // the order must be the same as CallbackType and getPre30LifecycleMethodNames |
| private static final Class[] lifecycleCallbackAnnotationClasses = { AroundConstruct.class, PostConstruct.class, PreDestroy.class, |
| PrePassivate.class, PostActivate.class }; |
| |
| private final Set<Class> monitoredGeneratedClasses = new HashSet<>(); |
| |
| protected InvocationManager invocationManager; |
| |
| protected InjectionManager injectionManager; |
| |
| protected GlassfishNamingManager namingManager; |
| |
| protected JavaEETransactionManager transactionManager; |
| |
| private EjbInvocationFactory invFactory; |
| |
| private ProtocolManager protocolMgr; |
| |
| protected EjbContainerUtil ejbContainerUtilImpl = EjbContainerUtilImpl.getInstance(); |
| |
| protected EjbOptionalIntfGenerator optIntfClassLoader; |
| |
| private final Set<String> publishedPortableGlobalJndiNames = new HashSet<>(); |
| |
| private final Set<String> publishedNonPortableGlobalJndiNames = new HashSet<>(); |
| |
| private final Set<String> publishedInternalGlobalJndiNames = new HashSet<>(); |
| |
| private final Map<String, JndiInfo> jndiInfoMap = new HashMap<>(); |
| |
| private String optIntfClassName; |
| |
| // Used to track whether we've done the base container cleanup (JNDI entries, etc.) |
| // Only. Not applicable to concrete containers. |
| private boolean baseContainerCleanupDone = false; |
| |
| // True if there is at least one asynchronous method exposed from the bean. |
| private boolean hasAsynchronousInvocations = false; |
| |
| // Information about a web service ejb endpoint. Used as a conduit |
| // between webservice runtime and ejb container. Contains a Remote |
| // servant used by jaxrpc to call web service business method. |
| private WebServiceEndpoint webServiceEndpoint; |
| |
| //The Webservices Ejb Endpoint Registry contract |
| // used to register and unregister ejb webservices endpoints |
| private WSEjbEndpointRegistry wsejbEndpointRegistry; |
| |
| protected EJBContainerStateManager containerStateManager; |
| protected EJBContainerTransactionManager containerTransactionManager; |
| |
| private final JCDIService jcdiService; |
| |
| private Class[] ejbLocalBusinessProxyInterfaces; |
| |
| private Class[] ejbOptionalLocalBusinessHomeProxyInterfaces; |
| |
| /** |
| * This constructor is called from ContainerFactoryImpl when an EJB Jar is deployed. |
| */ |
| protected BaseContainer(final ContainerType type, final EjbDescriptor ejbDesc, final ClassLoader loader, |
| final SecurityManager sm) throws Exception { |
| this.containerType = type; |
| this.securityManager = sm; |
| |
| try { |
| this.loader = loader; |
| this.ejbDescriptor = ejbDesc; |
| //this.callFlowAgent = ejbContainerUtilImpl.getCallFlowAgent(); |
| |
| logParams = new Object[1]; |
| logParams[0] = ejbDesc.getName(); |
| invocationManager = ejbContainerUtilImpl.getInvocationManager(); |
| injectionManager = ejbContainerUtilImpl.getInjectionManager(); |
| namingManager = ejbContainerUtilImpl.getGlassfishNamingManager(); |
| transactionManager = ejbContainerUtilImpl.getTransactionManager(); |
| |
| // get Class objects for creating new EJBs |
| ejbClass = loader.loadClass(ejbDescriptor.getEjbImplClassName()); |
| |
| containerStateManager = new EJBContainerStateManager(this); |
| containerTransactionManager = new EJBContainerTransactionManager(this, ejbDesc); |
| |
| isBeanManagedTran = ejbDescriptor.getTransactionType().equals("Bean"); |
| |
| if (ejbDescriptor instanceof EjbSessionDescriptor) { |
| isSession = true; |
| EjbSessionDescriptor sd = (EjbSessionDescriptor) ejbDescriptor; |
| |
| if (!sd.isSessionTypeSet()) { |
| throw new RuntimeException(localStrings.getLocalString("ejb.session_type_not_set", |
| "Invalid ejb Descriptor. Session type not set for {0}: {1}", sd.getName(), sd)); |
| } |
| |
| if (sd.isSingleton()) { |
| isSingleton = true; |
| } else { |
| isStatelessSession = sd.isStateless(); |
| isStatefulSession = !isStatelessSession; |
| |
| if (isStatefulSession) { |
| |
| /** |
| * If bean class isn't explicitly marked Serializable, generate a subclass that is. We do this with a generator that |
| * uses ASM directly instead of the CORBA codegen library since none of the corba .jars are part of the Web Profile. |
| */ |
| if (!Serializable.class.isAssignableFrom(ejbClass)) { |
| sfsbSerializedClass = EJBUtils.loadGeneratedSerializableClass(loader, ejbClass); |
| } |
| |
| } |
| } |
| |
| hasAsynchronousInvocations = sd.hasAsynchronousMethods(); |
| } |
| |
| if (ejbDescriptor.isRemoteInterfacesSupported() || ejbDescriptor.isRemoteBusinessInterfacesSupported()) { |
| |
| assertFullProfile("exposes a Remote client view"); |
| |
| initializeProtocolManager(); |
| |
| } |
| |
| if (ejbDescriptor.isRemoteInterfacesSupported()) { |
| |
| isRemote = true; |
| hasRemoteHomeView = true; |
| |
| String homeClassName = ejbDescriptor.getHomeClassName(); |
| |
| homeIntf = loader.loadClass(homeClassName); |
| remoteIntf = loader.loadClass(ejbDescriptor.getRemoteClassName()); |
| |
| String id = Long.toString(ejbDescriptor.getUniqueId()) + "_RHome"; |
| |
| remoteHomeRefFactory = getProtocolManager().getRemoteReferenceFactory(this, true, id); |
| |
| } |
| |
| if (ejbDescriptor.isRemoteBusinessInterfacesSupported()) { |
| |
| isRemote = true; |
| hasRemoteBusinessView = true; |
| |
| remoteBusinessHomeIntf = EJBUtils.loadGeneratedGenericEJBHomeClass(loader, ejbClass); |
| |
| for (String next : ejbDescriptor.getRemoteBusinessClassNames()) { |
| Class<?> genRemoteIntf = EJBUtils.loadGeneratedRemoteBusinessClasses(loader, next); |
| RemoteBusinessIntfInfo info = new RemoteBusinessIntfInfo(); |
| info.generatedRemoteIntf = genRemoteIntf; |
| info.remoteBusinessIntf = loader.loadClass(next); |
| |
| // One remote reference factory for each remote |
| // business interface. Id must be unique across |
| // all ejb containers. |
| String id = Long.toString(ejbDescriptor.getUniqueId()) + "_RBusiness" + "_" + genRemoteIntf.getName(); |
| |
| info.referenceFactory = getProtocolManager().getRemoteReferenceFactory(this, false, id); |
| |
| remoteBusinessIntfInfo.put(genRemoteIntf.getName(), info); |
| |
| addToGeneratedMonitoredMethodInfo(genRemoteIntf); |
| } |
| |
| } |
| |
| if (ejbDescriptor.isLocalInterfacesSupported()) { |
| // initialize class objects for LocalHome/LocalIntf etc. |
| isLocal = true; |
| hasLocalHomeView = true; |
| |
| String localHomeClassName = ejbDescriptor.getLocalHomeClassName(); |
| |
| localHomeIntf = loader.loadClass(localHomeClassName); |
| localIntf = loader.loadClass(ejbDescriptor.getLocalClassName()); |
| } |
| |
| if (ejbDescriptor.isLocalBusinessInterfacesSupported()) { |
| isLocal = true; |
| hasLocalBusinessView = true; |
| |
| localBusinessHomeIntf = GenericEJBLocalHome.class; |
| |
| for (String next : ejbDescriptor.getLocalBusinessClassNames()) { |
| Class clz = loader.loadClass(next); |
| localBusinessIntfs.add(clz); |
| addToGeneratedMonitoredMethodInfo(clz); |
| } |
| } |
| |
| if (ejbDescriptor.isLocalBean()) { |
| isLocal = true; |
| hasOptionalLocalBusinessView = true; |
| |
| ejbOptionalLocalBusinessHomeIntf = GenericEJBLocalHome.class; |
| Class clz = loader.loadClass(ejbDescriptor.getEjbClassName()); |
| addToGeneratedMonitoredMethodInfo(clz); |
| |
| this.optIntfClassName = EJBUtils.getGeneratedOptionalInterfaceName(ejbClass.getName()); |
| optIntfClassLoader = new EjbOptionalIntfGenerator(loader); |
| optIntfClassLoader.generateOptionalLocalInterface(ejbClass, optIntfClassName); |
| ejbGeneratedOptionalLocalBusinessIntfClass = optIntfClassLoader.loadClass(optIntfClassName); |
| } |
| |
| if (isStatelessSession || isSingleton) { |
| EjbBundleDescriptorImpl bundle = ejbDescriptor.getEjbBundleDescriptor(); |
| WebServicesDescriptor webServices = bundle.getWebServices(); |
| Collection endpoints = webServices.getEndpointsImplementedBy(ejbDescriptor); |
| // JSR 109 doesn't require support for a single ejb |
| // implementing multiple port ex. |
| if (endpoints.size() == 1) { |
| |
| assertFullProfile("is a Web Service Endpoint"); |
| |
| webServiceEndpointIntf = loader.loadClass(ejbDescriptor.getWebServiceEndpointInterfaceName()); |
| isWebServiceEndpoint = true; |
| } |
| } |
| |
| try { |
| // get Method objects for ejbPassivate/Activate/ejbRemove |
| ejbPassivateMethod = ejbClass.getMethod("ejbPassivate", NO_PARAMS); |
| ejbActivateMethod = ejbClass.getMethod("ejbActivate", NO_PARAMS); |
| ejbRemoveMethod = ejbClass.getMethod("ejbRemove", NO_PARAMS); |
| } catch (NoSuchMethodException nsme) { |
| // ignore. Will happen for EJB 3.0 session beans |
| } |
| |
| if (ejbDescriptor.isTimedObject()) { |
| |
| warnIfNotFullProfile("use of persistent EJB Timer Service"); |
| |
| MethodDescriptor ejbTimeoutMethodDesc = ejbDescriptor.getEjbTimeoutMethod(); |
| // Can be a @Timeout or @Schedule or TimedObject |
| if (ejbTimeoutMethodDesc != null) { |
| Method method = ejbTimeoutMethodDesc.getMethod(ejbDescriptor); |
| processEjbTimeoutMethod(method); |
| |
| ejbTimeoutMethod = method; |
| } |
| |
| for (ScheduledTimerDescriptor schd : ejbDescriptor.getScheduledTimerDescriptors()) { |
| Method method = schd.getTimeoutMethod().getMethod(ejbDescriptor); |
| if (method == null) { |
| // This should've been caught in EjbBundleValidator |
| throw new EJBException( |
| localStrings.getLocalString("ejb.no_timeout_method", "Class {0} does not define timeout method {1}", |
| ejbClass.getName(), schd.getTimeoutMethod().getFormattedString())); |
| } |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "... processing " + method); |
| } |
| processEjbTimeoutMethod(method); |
| |
| List<ScheduledTimerDescriptor> list = schedules.get(method); |
| if (list == null) { |
| list = new ArrayList<>(); |
| schedules.put(method, list); |
| } |
| list.add(schd); |
| } |
| |
| } |
| if (isTimedObject_) { |
| if (!isStatefulSession) { |
| // EJBTimerService should be accessed only if needed |
| // not to cause it to be loaded if it's not used. |
| EJBTimerService timerService = EJBTimerService.getEJBTimerService(); |
| if (timerService != null) { |
| timerService.timedObjectCount(); |
| } |
| } else { |
| isTimedObject_ = false; |
| throw new EJBException(localStrings.getLocalString("ejb.stateful_cannot_be_timed_object", |
| "EJB {0} is invalid. Stateful session ejbs cannot be Timed Objects", ejbDescriptor.getName())); |
| } |
| } |
| |
| preInitialize(ejbDesc, loader); |
| |
| initializeEjbInterfaceMethods(); |
| |
| if (needSystemInterceptorProxy()) { |
| addSystemInterceptorProxy(); |
| } |
| |
| // NOTE : InterceptorManager initialization delayed until transition to START state. |
| |
| addLocalRemoteInvocationInfo(); |
| addWSOrTimedObjectInvocationInfo(); |
| |
| initializeInvocationInfo(); |
| |
| setupEnvironment(); |
| |
| ServiceLocator services = ejbContainerUtilImpl.getServices(); |
| |
| jcdiService = services.getService(JCDIService.class); |
| |
| initEjbInterceptors(); |
| } catch (Exception ex) { |
| _logger.log(Level.FINE, "Exception creating BaseContainer : [{0}]", logParams); |
| _logger.log(Level.FINE, "", ex); |
| throw ex; |
| } |
| |
| _debugDescription = "ejbName: " + ejbDescriptor.getName() + "; containerId: " + ejbDescriptor.getUniqueId(); |
| _logger.log(Level.FINE, "Instantiated container for: " + _debugDescription); |
| } |
| |
| protected ProtocolManager getProtocolManager() { |
| return protocolMgr; |
| } |
| |
| public ContainerType getContainerType() { |
| return containerType; |
| } |
| |
| protected void doEJBHomeRemove(Object pk, Method m, boolean isLocal) throws RemoteException, RemoveException { |
| throw new UnsupportedOperationException( |
| localStrings.getLocalString("ejb.ejbhome_remove_on_nonentity", "EJBHome.remove() called on non entity container")); |
| } |
| |
| private void addToGeneratedMonitoredMethodInfo(Class generatedClass) { |
| monitoredGeneratedClasses.add(generatedClass); |
| } |
| |
| protected void initializeProtocolManager() { |
| |
| try { |
| |
| GlassFishORBHelper orbHelper = ejbContainerUtilImpl.getORBHelper(); |
| protocolMgr = orbHelper.getProtocolManager(); |
| |
| } catch (Throwable t) { |
| throw new RuntimeException( |
| "IIOP Protocol Manager initialization failed. " + "Possible cause is that ORB is not available in this " |
| + ((ejbContainerUtilImpl.isEmbeddedServer()) |
| ? "embedded container, or server instance is running and required ports are in use" |
| : "container"), |
| t); |
| } |
| |
| } |
| |
| protected void preInitialize(EjbDescriptor ejbDesc, ClassLoader loader) { |
| //Overridden in sub classes |
| } |
| |
| public void checkUserTransactionLookup(ComponentInvocation inv) throws javax.naming.NameNotFoundException { |
| if (!this.isBeanManagedTran) { |
| throw new javax.naming.NameNotFoundException(localStrings.getLocalString("ejb.ut_lookup_not_allowed", |
| "Lookup of java:comp/UserTransaction not allowed for Container managed Transaction beans")); |
| } |
| } |
| |
| protected final void createCallFlowAgent(ComponentType compType) { |
| |
| this.callFlowInfo = new CallFlowInfoImpl(this, ejbDescriptor, compType); |
| } |
| |
| @Override |
| public String toString() { |
| return _debugDescription; |
| } |
| |
| @Override |
| public final void setStartedState() { |
| |
| if (containerState == CONTAINER_STARTED) { |
| return; |
| } |
| |
| // NOTE : we used to initialize interceptor manager in the ctor but we need to delay |
| // the initialization to account for the possiblity of a 299-enabled app. In |
| // that case, the 299-defined ejb interceptors are not added until the |
| // deployment load() phase. That's ok, as long as everything is initialized |
| // before any bean instances are created or any ejb invocations take place. |
| // Therefore, moving the initialization to the point that we transition into the |
| // ejb container START state. |
| |
| try { |
| |
| initializeInterceptorManager(); |
| |
| for (Object o : invocationInfoMap.values()) { |
| InvocationInfo next = (InvocationInfo) o; |
| setInterceptorChain(next); |
| } |
| for (Object o : this.webServiceInvocationInfoMap.values()) { |
| InvocationInfo next = (InvocationInfo) o; |
| setInterceptorChain(next); |
| } |
| |
| } catch (Exception e) { |
| throw new RuntimeException(e); |
| } |
| |
| containerState = CONTAINER_STARTED; |
| } |
| |
| private void setInterceptorChain(InvocationInfo info) { |
| if (info.aroundMethod != null) { |
| if (info.isEjbTimeout) { |
| MethodDescriptor md = new MethodDescriptor(info.aroundMethod, MethodDescriptor.TIMER_METHOD); |
| info.interceptorChain = interceptorManager.getAroundTimeoutChain(md, info.aroundMethod); |
| } else { |
| MethodDescriptor md = new MethodDescriptor(info.aroundMethod, MethodDescriptor.EJB_BEAN); |
| info.interceptorChain = interceptorManager.getAroundInvokeChain(md, info.aroundMethod); |
| } |
| } |
| } |
| |
| @Override |
| public final void setStoppedState() { |
| containerState = CONTAINER_STOPPED; |
| } |
| |
| public final boolean isStopped() { |
| return containerState == CONTAINER_STOPPED; |
| } |
| |
| @Override |
| public final void setUndeployedState() { |
| containerState = CONTAINER_UNDEPLOYED; |
| } |
| |
| public final boolean isUndeployed() { |
| return (containerState == CONTAINER_UNDEPLOYED); |
| } |
| |
| @Override |
| public final boolean isTimedObject() { |
| return isTimedObject_; |
| } |
| |
| @Override |
| public final boolean isLocalObject() { |
| return isLocal; |
| } |
| |
| @Override |
| public final boolean isRemoteObject() { |
| return isRemote; |
| } |
| |
| @Override |
| public final ClassLoader getContainerClassLoader() { |
| return loader; |
| } |
| |
| @Override |
| public final ClassLoader getClassLoader() { |
| return loader; |
| } |
| |
| @Override |
| public final String getUseThreadPoolId() { |
| return ejbDescriptor.getIASEjbExtraDescriptors().getUseThreadPoolId(); |
| } |
| |
| @Override |
| public final boolean getPassByReference() { |
| return ejbDescriptor.getIASEjbExtraDescriptors().getPassByReference(); |
| } |
| |
| protected final long getContainerId() { |
| return ejbDescriptor.getUniqueId(); |
| } |
| |
| public final long getApplicationId() { |
| return ejbDescriptor.getApplication().getUniqueId(); |
| } |
| |
| @Override |
| public final EjbDescriptor getEjbDescriptor() { |
| return ejbDescriptor; |
| } |
| |
| /** |
| * Method defined on JavaEEContainer |
| */ |
| @Override |
| public final Descriptor getDescriptor() { |
| return getEjbDescriptor(); |
| } |
| |
| @Override |
| public final EJBMetaData getEJBMetaData() { |
| return metadata; |
| } |
| |
| public final UserTransaction getUserTransaction() { |
| return containerTransactionManager.getUserTransaction(); |
| } |
| |
| public boolean isHAEnabled() { |
| return false; |
| } |
| |
| /** |
| * EJB spec makes a distinction between access to the UserTransaction object itself and access to its methods. getUserTransaction |
| * covers the first check and this method covers the second. It is called by the UserTransaction implementation to verify access. |
| */ |
| @Override |
| public boolean userTransactionMethodsAllowed(ComponentInvocation inv) { |
| // Overridden by containers that allowed BMT; |
| return false; |
| } |
| |
| public final EJBHome getEJBHomeStub() { |
| return ejbHomeStub; |
| } |
| |
| @Override |
| public final EJBHome getEJBHome() { |
| return ejbHome; |
| } |
| |
| /** |
| * Return an object that implements ejb's local home interface. If dynamic proxies are being used, this is the proxy itself, it |
| * can't be directly cast to an EJBLocalHomeImpl. |
| */ |
| public final EJBLocalHome getEJBLocalHome() { |
| return ejbLocalHome; |
| } |
| |
| /** |
| * Return an object that implements ejb's local business home interface. |
| */ |
| public final GenericEJBLocalHome getEJBLocalBusinessHome(String clientViewClassName) { |
| |
| return isLocalBeanClass(clientViewClassName) ? ejbOptionalLocalBusinessHome : ejbLocalBusinessHome; |
| } |
| |
| boolean isLocalBeanClass(String className) { |
| |
| return hasOptionalLocalBusinessView |
| && (className.equals(ejbClass.getName()) || className.equals(ejbGeneratedOptionalLocalBusinessIntfClass.getName())); |
| } |
| |
| public final Class getEJBClass() { |
| return ejbClass; |
| } |
| |
| @Override |
| public final SecurityManager getSecurityManager() { |
| return securityManager; |
| } |
| |
| final Properties getEnvironmentProperties() { |
| return envProps; |
| } |
| |
| /** |
| * Create an EJBObject reference from the instanceKey Called from EJBObjectOutputStream.SerializableRemoteRef during |
| * deserialization of a remote-ref |
| * |
| * @param instanceKey instanceKey of the ejbobject |
| * @param generatedRemoteBusinessIntf non-null, this is a remote business view and the param is the name of the generated remote |
| * business interface. Otherwise, this is for the RemoteHome view |
| */ |
| public java.rmi.Remote createRemoteReferenceWithId(byte[] instanceKey, String generatedRemoteBusinessIntf) { |
| |
| final Thread currentThread = Thread.currentThread(); |
| final ClassLoader previousClassLoader = currentThread.getContextClassLoader(); |
| final ClassLoader myClassLoader = loader; |
| try { |
| if (System.getSecurityManager() == null) { |
| currentThread.setContextClassLoader(myClassLoader); |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { |
| @Override |
| public java.lang.Object run() { |
| currentThread.setContextClassLoader(myClassLoader); |
| return null; |
| } |
| }); |
| } |
| java.rmi.Remote remoteRef = null; |
| if (generatedRemoteBusinessIntf == null) { |
| remoteRef = remoteHomeRefFactory.createRemoteReference(instanceKey); |
| } else { |
| RemoteReferenceFactory remoteBusinessRefFactory = remoteBusinessIntfInfo.get(generatedRemoteBusinessIntf).referenceFactory; |
| |
| remoteRef = remoteBusinessRefFactory.createRemoteReference(instanceKey); |
| } |
| return remoteRef; |
| } finally { |
| if (System.getSecurityManager() == null) { |
| currentThread.setContextClassLoader(previousClassLoader); |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { |
| @Override |
| public java.lang.Object run() { |
| currentThread.setContextClassLoader(previousClassLoader); |
| return null; |
| } |
| }); |
| } |
| } |
| } |
| |
| private void assertFullProfile(String description) { |
| if (ejbContainerUtilImpl.isEJBLite()) { |
| throw new RuntimeException(localStrings.getLocalString("ejb.assert_full_profile", |
| "Invalid application. EJB {0} {1}. This feature is not part of the EJB 3.1 Lite API", ejbDescriptor.getName(), |
| description)); |
| } |
| } |
| |
| private void warnIfNotFullProfile(String description) { |
| if (ejbContainerUtilImpl.isEJBLite()) { |
| _logger.log(Level.WARNING, WARN_FEATURE_REQUIRES_FULL_PROFILE, description); |
| } |
| } |
| |
| /** |
| * Called from the ContainerFactory during initialization. |
| */ |
| protected void initializeHome() throws Exception { |
| |
| if (isWebServiceEndpoint) { |
| |
| EjbBundleDescriptorImpl bundle = ejbDescriptor.getEjbBundleDescriptor(); |
| WebServicesDescriptor webServices = bundle.getWebServices(); |
| Collection myEndpoints = webServices.getEndpointsImplementedBy(ejbDescriptor); |
| |
| // An ejb can only be exposed through 1 web service endpoint |
| Iterator iter = myEndpoints.iterator(); |
| webServiceEndpoint = (com.sun.enterprise.deployment.WebServiceEndpoint) iter.next(); |
| |
| Class serviceEndpointIntfClass = loader.loadClass(webServiceEndpoint.getServiceEndpointInterface()); |
| |
| if (!serviceEndpointIntfClass.isInterface()) { |
| serviceEndpointIntfClass = EJBUtils.generateSEI(loader, ejbClass); |
| if (serviceEndpointIntfClass == null) { |
| throw new RuntimeException(localStrings.getLocalString("ejb.error_generating_sei", |
| "Error in generating service endpoint interface class for EJB class {0}", this.ejbClass)); |
| } |
| } |
| |
| Class tieClass = null; |
| |
| WebServiceInvocationHandler invocationHandler = new WebServiceInvocationHandler(ejbClass, webServiceEndpoint, |
| serviceEndpointIntfClass, ejbContainerUtilImpl, webServiceInvocationInfoMap); |
| |
| invocationHandler.setContainer(this); |
| Object servant = Proxy.newProxyInstance(loader, new Class[] { serviceEndpointIntfClass }, invocationHandler); |
| |
| // starting in 2.0, there is no more generated Ties |
| if (webServiceEndpoint.getTieClassName() != null) { |
| tieClass = loader.loadClass(webServiceEndpoint.getTieClassName()); |
| } |
| |
| // Create a facade for container services to be used by web services runtime. |
| EjbEndpointFacade endpointFacade = new EjbEndpointFacadeImpl(this, ejbContainerUtilImpl); |
| |
| wsejbEndpointRegistry = Globals.getDefaultHabitat().getService(WSEjbEndpointRegistry.class); |
| if (wsejbEndpointRegistry != null) { |
| wsejbEndpointRegistry.registerEndpoint(webServiceEndpoint, endpointFacade, servant, tieClass); |
| } else { |
| throw new DeploymentException(localStrings.getLocalString("ejb.no_webservices_module", |
| "EJB-based Webservice endpoint is detected but there is no webservices module installed to handle it")); |
| } |
| } |
| |
| Map<String, Object> intfsForPortableJndi = new HashMap<>(); |
| |
| // Root of portable global JNDI name for this bean |
| String javaGlobalName = getJavaGlobalJndiNamePrefix(); |
| |
| if (isRemote) { |
| boolean disableNonPortableJndiName = false; |
| Boolean disableInDD = ejbDescriptor.getEjbBundleDescriptor().getDisableNonportableJndiNames(); |
| if (disableInDD != null) { // explicitly set in glassfish-ejb-jar.xml |
| disableNonPortableJndiName = disableInDD; |
| } else { |
| String disableInServer = ejbContainerUtilImpl.getEjbContainer() |
| .getPropertyValue(RuntimeTagNames.DISABLE_NONPORTABLE_JNDI_NAMES); |
| disableNonPortableJndiName = Boolean.valueOf(disableInServer); |
| } |
| |
| String glassfishSpecificJndiName = null; |
| if (!disableNonPortableJndiName) { |
| // This is either the default glassfish-specific (non-portable) |
| // global JNDI name or the one specified via mappedName(), sun-ejb-jar.xml, |
| // etc. |
| glassfishSpecificJndiName = ejbDescriptor.getJndiName(); |
| |
| // If the explicitly specified name is the same as the portable name, |
| // don't register any of the glassfish-specific names to prevent |
| // clashes. |
| if ((glassfishSpecificJndiName != null) |
| && (glassfishSpecificJndiName.equals("") || glassfishSpecificJndiName.equals(javaGlobalName))) { |
| glassfishSpecificJndiName = null; |
| } |
| } |
| |
| if (hasRemoteHomeView) { |
| this.ejbHomeImpl = instantiateEJBHomeImpl(); |
| this.ejbHome = ejbHomeImpl.getEJBHome(); |
| |
| // |
| // Make sure all Home/Remote interfaces conform to RMI-IIOP |
| // rules. Checking for conformance here keeps the exposed |
| // deployment/startup error behavior consistent since when |
| // rmic is used during codegen it makes equivalent checks and |
| // treats any validation problems as fatal errors. |
| // |
| // These same checks will be made when setTarget is called |
| // in POARemoteReferenceFactory.preinvoke, but that happens |
| // only when the actual invocation is made, so it's better to |
| // know at container initialization time if there is a problem. |
| // |
| |
| getProtocolManager().validateTargetObjectInterfaces(this.ejbHome); |
| |
| // Unlike the Home, each of the concrete containers are |
| // responsible for creating the EJBObjects, so just create |
| // a dummy EJBObjectImpl for validation purposes. |
| EJBObjectImpl dummyEJBObjectImpl = instantiateEJBObjectImpl(); |
| EJBObject dummyEJBObject = (EJBObject) dummyEJBObjectImpl.getEJBObject(); |
| getProtocolManager().validateTargetObjectInterfaces(dummyEJBObject); |
| |
| // Remotereference factory needs instances of |
| // Home and Remote to get repository Ids since it doesn't have |
| // stubs and ties. This must be done before any Home or Remote |
| // references are created. |
| remoteHomeRefFactory.setRepositoryIds(homeIntf, remoteIntf); |
| |
| // get a remote ref for the EJBHome |
| ejbHomeStub = (EJBHome) remoteHomeRefFactory.createHomeReference(homeInstanceKey); |
| |
| // Add 2.x Home for later portable JNDI name processing. |
| intfsForPortableJndi.put(ejbDescriptor.getHomeClassName(), ejbHomeStub); |
| |
| // If there's a glassfish-specific JNDI name, any 2.x Home object is always |
| // regsitered under that name. This preserves backward compatibility since |
| // this was the original use of the jndi name. |
| if (glassfishSpecificJndiName != null) { |
| |
| JndiInfo jndiInfo = JndiInfo.newNonPortableRemote(glassfishSpecificJndiName, ejbHomeStub); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| } |
| |
| } |
| |
| if (hasRemoteBusinessView) { |
| this.ejbRemoteBusinessHomeImpl = instantiateEJBRemoteBusinessHomeImpl(); |
| |
| this.ejbRemoteBusinessHome = ejbRemoteBusinessHomeImpl.getEJBHome(); |
| |
| // RMI-IIOP validation |
| getProtocolManager().validateTargetObjectInterfaces(this.ejbRemoteBusinessHome); |
| |
| for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { |
| // Remotereference factory needs instances of |
| // Home and Remote to get repository Ids since it |
| // doesn't have stubs and ties. This must be done before |
| // any Home or Remote references are created. |
| next.referenceFactory.setRepositoryIds(remoteBusinessHomeIntf, next.generatedRemoteIntf); |
| |
| // Create home stub from the remote reference factory |
| // associated with one of the remote business interfaces. |
| // It doesn't matter which remote reference factory is |
| // selected, so just do it the first time through the loop. |
| if (ejbRemoteBusinessHomeStub == null) { |
| ejbRemoteBusinessHomeStub = (EJBHome) next.referenceFactory.createHomeReference(homeInstanceKey); |
| } |
| |
| } |
| |
| EJBObjectImpl dummyEJBObjectImpl = instantiateRemoteBusinessObjectImpl(); |
| |
| // Internal jndi name under which remote business home is registered for |
| // glassfish-specific remote business JNDI names |
| String remoteBusinessHomeJndiName = null; |
| |
| if (glassfishSpecificJndiName != null) { |
| |
| remoteBusinessHomeJndiName = EJBUtils.getRemote30HomeJndiName(glassfishSpecificJndiName); |
| } |
| |
| // Convenience location for common case of 3.0 session bean with only |
| // 1 remote business interface and no adapted remote home. Allows a |
| // stand-alone client to access 3.0 business interface by using simple |
| // jndi name. Each remote business interface is also always available |
| // at <jndi-name>#<business_interface_name>. This is needed for the |
| // case where the bean has an adapted remote home and/or multiple business |
| // interfaces. |
| String simpleRemoteBusinessJndiName = null; |
| |
| if ((glassfishSpecificJndiName != null) && !hasRemoteHomeView && remoteBusinessIntfInfo.size() == 1) { |
| |
| simpleRemoteBusinessJndiName = glassfishSpecificJndiName; |
| } |
| |
| // We need a separate name for the internal generated home object to |
| // support the portable global JNDI names for business interfaces. |
| // There won't necessarily be a glassfish-specific name specified so |
| // it's cleaner to just always use a separate ones. |
| String internalHomeJndiNameForPortableRemoteNames = EJBUtils.getRemote30HomeJndiName(javaGlobalName); |
| |
| for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { |
| |
| java.rmi.Remote dummyEJBObject = dummyEJBObjectImpl.getEJBObject(next.generatedRemoteIntf.getName()); |
| |
| getProtocolManager().validateTargetObjectInterfaces(dummyEJBObject); |
| |
| if (glassfishSpecificJndiName != null) { |
| |
| next.jndiName = EJBUtils.getRemoteEjbJndiName(true, next.remoteBusinessIntf.getName(), glassfishSpecificJndiName); |
| |
| Reference remoteBusRef = new Reference(next.remoteBusinessIntf.getName(), |
| new StringRefAddr("url", remoteBusinessHomeJndiName), "com.sun.ejb.containers.RemoteBusinessObjectFactory", |
| null); |
| |
| // Glassfish-specific JNDI name for fully-qualified 3.0 Remote business interface. |
| JndiInfo jndiInfo = JndiInfo.newNonPortableRemote(next.jndiName, remoteBusRef); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| } |
| |
| if (simpleRemoteBusinessJndiName != null) { |
| |
| Reference remoteBusRef = new Reference(next.remoteBusinessIntf.getName(), |
| new StringRefAddr("url", remoteBusinessHomeJndiName), "com.sun.ejb.containers.RemoteBusinessObjectFactory", |
| null); |
| |
| // Glassfish-specific JNDI name for simple 3.0 Remote business interface lookup. |
| // Applicable when the bean exposes only a single Remote 3.x client view. |
| JndiInfo jndiInfo = JndiInfo.newNonPortableRemote(simpleRemoteBusinessJndiName, remoteBusRef); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| } |
| |
| Reference remoteBusRef = new Reference(next.remoteBusinessIntf.getName(), |
| new StringRefAddr("url", internalHomeJndiNameForPortableRemoteNames), |
| "com.sun.ejb.containers.RemoteBusinessObjectFactory", null); |
| |
| // Always register portable JNDI name for each remote business view |
| intfsForPortableJndi.put(next.remoteBusinessIntf.getName(), remoteBusRef); |
| |
| } |
| |
| if (remoteBusinessHomeJndiName != null) { |
| // Glassfish-specific JNDI name for internal generated |
| // home object used by container |
| JndiInfo jndiInfo = JndiInfo.newNonPortableRemote(remoteBusinessHomeJndiName, ejbRemoteBusinessHomeStub); |
| jndiInfo.setInternal(true); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| } |
| |
| // Always registeer internal name for home in support of portable global |
| // remote business JNDI names. |
| JndiInfo jndiInfo = JndiInfo.newPortableRemote(internalHomeJndiNameForPortableRemoteNames, ejbRemoteBusinessHomeStub); |
| jndiInfo.setInternal(true); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| |
| // If there isn't any jndi name from the descriptor, set one so the |
| // lookup logic that depends on ejbDescriptor.getJndiName() will work. |
| if (glassfishSpecificJndiName == null) { |
| ejbDescriptor.setJndiName(javaGlobalName); |
| } |
| } |
| } |
| |
| if (isLocal) { |
| |
| if (hasLocalHomeView) { |
| this.ejbLocalHomeImpl = instantiateEJBLocalHomeImpl(); |
| this.ejbLocalHome = ejbLocalHomeImpl.getEJBLocalHome(); |
| |
| // Portable JNDI name for EJB 2.x LocalHome. We don't provide a |
| // glassfish-specific way of accessing Local EJBs. |
| |
| JavaGlobalJndiNamingObjectProxy namingProxy = new JavaGlobalJndiNamingObjectProxy(this, localHomeIntf.getName()); |
| |
| intfsForPortableJndi.put(localHomeIntf.getName(), namingProxy); |
| } |
| |
| if (hasLocalBusinessView) { |
| ejbLocalBusinessHomeImpl = instantiateEJBLocalBusinessHomeImpl(); |
| ejbLocalBusinessHome = (GenericEJBLocalHome) ejbLocalBusinessHomeImpl.getEJBLocalHome(); |
| |
| ejbLocalBusinessProxyInterfaces = new Class[localBusinessIntfs.size() + 1]; |
| ejbLocalBusinessProxyInterfaces[0] = IndirectlySerializable.class; |
| int index = 1; |
| for (Class next : localBusinessIntfs) { |
| ejbLocalBusinessProxyInterfaces[index] = next; |
| index++; |
| } |
| |
| for (Class next : localBusinessIntfs) { |
| // Portable JNDI name for EJB 3.x Local business interface. |
| // We don't provide a glassfish-specific way of accessing Local EJBs. |
| JavaGlobalJndiNamingObjectProxy namingProxy = new JavaGlobalJndiNamingObjectProxy(this, next.getName()); |
| intfsForPortableJndi.put(next.getName(), namingProxy); |
| } |
| } |
| |
| if (hasOptionalLocalBusinessView) { |
| EJBLocalHomeImpl obj = instantiateEJBOptionalLocalBusinessHomeImpl(); |
| ejbOptionalLocalBusinessHomeImpl = obj; |
| |
| ejbOptionalLocalBusinessHome = (GenericEJBLocalHome) ejbOptionalLocalBusinessHomeImpl.getEJBLocalHome(); |
| |
| ejbOptionalLocalBusinessHomeProxyInterfaces = new Class[2]; |
| ejbOptionalLocalBusinessHomeProxyInterfaces[0] = IndirectlySerializable.class; |
| |
| String optionalIntfName = EJBUtils.getGeneratedOptionalInterfaceName(ejbClass.getName()); |
| ejbGeneratedOptionalLocalBusinessIntfClass = optIntfClassLoader.loadClass(optionalIntfName); |
| ejbOptionalLocalBusinessHomeProxyInterfaces[1] = ejbGeneratedOptionalLocalBusinessIntfClass; |
| |
| // Portable JNDI name for no-interface view. |
| // We don't provide a glassfish-specific way of accessing the |
| // no-interface view of a session bean. |
| JavaGlobalJndiNamingObjectProxy namingProxy = new JavaGlobalJndiNamingObjectProxy(this, ejbClass.getName()); |
| |
| intfsForPortableJndi.put(ejbClass.getName(), namingProxy); |
| |
| } |
| |
| } |
| |
| for (Map.Entry<String, Object> entry : intfsForPortableJndi.entrySet()) { |
| String intf = entry.getKey(); |
| |
| String fullyQualifiedJavaGlobalName = javaGlobalName + "!" + intf; |
| Object namingProxy = entry.getValue(); |
| boolean local = (namingProxy instanceof JavaGlobalJndiNamingObjectProxy); |
| |
| if (intfsForPortableJndi.size() == 1) { |
| |
| JndiInfo jndiInfo = local ? JndiInfo.newPortableLocal(javaGlobalName, namingProxy) |
| : JndiInfo.newPortableRemote(javaGlobalName, namingProxy); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| |
| } |
| |
| JndiInfo jndiInfo = local ? JndiInfo.newPortableLocal(fullyQualifiedJavaGlobalName, namingProxy) |
| : JndiInfo.newPortableRemote(fullyQualifiedJavaGlobalName, namingProxy); |
| jndiInfoMap.put(jndiInfo.name, jndiInfo); |
| |
| } |
| |
| for (Map.Entry<String, JndiInfo> entry : jndiInfoMap.entrySet()) { |
| JndiInfo jndiInfo = entry.getValue(); |
| try { |
| jndiInfo.publish(this.namingManager); |
| |
| if (jndiInfo.internal) { |
| publishedInternalGlobalJndiNames.add(jndiInfo.name); |
| } else { |
| |
| if (jndiInfo.portable) { |
| publishedPortableGlobalJndiNames.add(jndiInfo.name); |
| } else { |
| publishedNonPortableGlobalJndiNames.add(jndiInfo.name); |
| } |
| } |
| } catch (Exception e) { |
| throw new RuntimeException(localStrings.getLocalString("ejb.error_binding_jndi_name", |
| "Error while binding JNDI name {0} for EJB {1}", jndiInfo.name, this.ejbDescriptor.getName()), e); |
| } |
| } |
| |
| if (!publishedPortableGlobalJndiNames.isEmpty()) { |
| _logger.log(Level.INFO, PORTABLE_JNDI_NAMES, new Object[] { this.ejbDescriptor.getName(), publishedPortableGlobalJndiNames }); |
| } |
| |
| if (!publishedNonPortableGlobalJndiNames.isEmpty()) { |
| _logger.log(Level.INFO, GLASSFISH_SPECIFIC_JNDI_NAMES, |
| new Object[] { this.ejbDescriptor.getName(), publishedNonPortableGlobalJndiNames }); |
| } |
| |
| if (!publishedInternalGlobalJndiNames.isEmpty()) { |
| _logger.log(Level.FINE, "Internal container JNDI names for EJB {0}: {1}", |
| new Object[] { this.ejbDescriptor.getName(), publishedInternalGlobalJndiNames }); |
| } |
| |
| // set EJBMetaData |
| setEJBMetaData(); |
| } |
| |
| // default impl |
| protected void setEJBMetaData() throws Exception { |
| metadata = new EJBMetaDataImpl(ejbHomeStub, homeIntf, remoteIntf, isSession, isStatelessSession); |
| } |
| |
| protected String getJavaGlobalJndiNamePrefix() { |
| |
| String appName = null; |
| |
| Application app = ejbDescriptor.getApplication(); |
| if (!app.isVirtual()) { |
| appName = ejbDescriptor.getApplication().getAppName(); |
| } |
| |
| EjbBundleDescriptorImpl ejbBundle = ejbDescriptor.getEjbBundleDescriptor(); |
| String modName = ejbBundle.getModuleDescriptor().getModuleName(); |
| |
| String ejbName = ejbDescriptor.getName(); |
| |
| StringBuffer javaGlobalPrefix = new StringBuffer("java:global/"); |
| |
| if (appName != null) { |
| javaGlobalPrefix.append(appName); |
| javaGlobalPrefix.append("/"); |
| } |
| |
| javaGlobalPrefix.append(modName); |
| javaGlobalPrefix.append("/"); |
| javaGlobalPrefix.append(ejbName); |
| |
| return javaGlobalPrefix.toString(); |
| } |
| |
| // This method is used to create the ejb after the around_construct interceptor chain has completed. |
| public void createEjbInstanceForInterceptors(Object[] params, EJBContextImpl ctx) throws Exception { |
| Object instance; |
| EjbBundleDescriptorImpl ejbBundle = ejbDescriptor.getEjbBundleDescriptor(); |
| if ((jcdiService != null) && jcdiService.isJCDIEnabled(ejbBundle)) { |
| // ejb creation for cdi is handled in JCDIServiceImpl not here. |
| instance = ctx.getJCDIInjectionContext().createEjbAfterAroundConstruct(); |
| } else { |
| // this is only for non-cdi case. |
| instance = _constructEJBInstance(); |
| } |
| ctx.setEJB(instance); |
| } |
| |
| protected EJBContextImpl createEjbInstanceAndContext() throws Exception { |
| EjbBundleDescriptorImpl ejbBundle = ejbDescriptor.getEjbBundleDescriptor(); |
| |
| JCDIService.JCDIInjectionContext<Object> jcdiCtx = null; |
| Object instance = null; |
| |
| EJBContextImpl ctx = _constructEJBContextImpl(null); |
| EjbInvocation ejbInv = null; |
| boolean success = false; |
| try { |
| ejbInv = createEjbInvocation(null, ctx); |
| invocationManager.preInvoke(ejbInv); |
| |
| if ((jcdiService != null) && jcdiService.isJCDIEnabled(ejbBundle)) { |
| // In cdi we need this for the interceptors to store dependent jcdi contexts. We can't assign the |
| // other info as the ejb has not been created yet. |
| jcdiCtx = jcdiService.createEmptyJCDIInjectionContext(); |
| ctx.setJCDIInjectionContext(jcdiCtx); |
| } |
| |
| // Interceptors must be created before the ejb so they're available for around construct. |
| createEjbInterceptors(ctx, jcdiCtx); |
| |
| if ((jcdiService != null) && jcdiService.isJCDIEnabled(ejbBundle)) { |
| HashMap<Class, Object> ejbInfo = new HashMap<>(); |
| ejbInfo.put(BaseContainer.class, this); |
| ejbInfo.put(EJBContextImpl.class, ctx); |
| ejbInfo.put(JCDIService.JCDIInjectionContext.class, jcdiCtx); |
| jcdiService.createJCDIInjectionContext(ejbDescriptor, ejbInfo); |
| instance = jcdiCtx.getInstance(); |
| } else { |
| injectEjbInstance(ctx); |
| intercept(CallbackType.AROUND_CONSTRUCT, ctx); |
| instance = ctx.getEJB(); |
| } |
| success = true; |
| |
| } catch (Throwable th) { |
| try { |
| if (jcdiCtx != null) { |
| // protecte against memory leak |
| jcdiCtx.cleanup(true); |
| } |
| } catch (Throwable ignore) { |
| } |
| throw new InvocationTargetException(th); |
| } finally { |
| try { |
| if (ejbInv != null) { |
| // Complete the dummy invocation |
| invocationManager.postInvoke(ejbInv); |
| } |
| } catch (Throwable t) { |
| if (success) { |
| throw new InvocationTargetException(t); |
| } else { |
| _logger.log(Level.WARNING, "", t); |
| } |
| } |
| |
| } |
| |
| ctx.setEJB(instance); |
| |
| return ctx; |
| } |
| |
| protected EJBContextImpl _constructEJBContextImpl(Object instance) { |
| // Overridden for any container that supports injection |
| throw new IllegalStateException(); |
| } |
| |
| protected Object _constructEJBInstance() throws Exception { |
| return ejbClass.newInstance(); |
| } |
| |
| private void createEjbInterceptors(EJBContextImpl context, JCDIService.JCDIInjectionContext ejbInterceptorsJCDIInjectionContext) |
| throws Exception { |
| EjbBundleDescriptorImpl ejbBundle = ejbDescriptor.getEjbBundleDescriptor(); |
| |
| Object[] interceptorInstances; |
| |
| if ((jcdiService != null) && jcdiService.isJCDIEnabled(ejbBundle)) { |
| Class[] interceptorClasses = interceptorManager.getInterceptorClasses(); |
| |
| interceptorInstances = new Object[interceptorClasses.length]; |
| |
| for (int i = 0; i < interceptorClasses.length; i++) { |
| // 299 impl will instantiate and inject the instance, but PostConstruct |
| // is still our responsibility |
| interceptorInstances[i] = jcdiService.createInterceptorInstance(interceptorClasses[i], ejbDescriptor, |
| ejbInterceptorsJCDIInjectionContext, context.getContainer().getEjbDescriptor().getInterceptorClasses()); |
| } |
| |
| interceptorManager.initializeInterceptorInstances(interceptorInstances); |
| |
| } else { |
| interceptorInstances = interceptorManager.createInterceptorInstances(); |
| |
| for (Object interceptorInstance : interceptorInstances) { |
| injectionManager.injectInstance(interceptorInstance, ejbDescriptor, false); |
| } |
| } |
| |
| context.setInterceptorInstances(interceptorInstances); |
| } |
| |
| protected void injectEjbInstance(EJBContextImpl context) throws Exception { |
| |
| EjbBundleDescriptorImpl ejbBundle = ejbDescriptor.getEjbBundleDescriptor(); |
| |
| if ((jcdiService != null) && jcdiService.isJCDIEnabled(ejbBundle)) { |
| jcdiService.injectEJBInstance(context.getJCDIInjectionContext()); |
| } else { |
| if (context.getEJB() != null) { |
| injectionManager.injectInstance(context.getEJB(), ejbDescriptor, false); |
| } |
| } |
| } |
| |
| protected void cleanupInstance(EJBContextImpl context) { |
| |
| JCDIService.JCDIInjectionContext jcdiCtx = context.getJCDIInjectionContext(); |
| if (jcdiCtx != null) { |
| jcdiCtx.cleanup(false); |
| } |
| |
| } |
| |
| /** |
| * Return the EJBObject/EJBHome Proxy for the given ejbId and instanceKey. Called from the ProtocolManager when a remote |
| * invocation arrives. |
| * |
| * @exception NoSuchObjectLocalException if the target object does not exist |
| */ |
| @Override |
| public java.rmi.Remote getTargetObject(byte[] instanceKey, String generatedRemoteBusinessIntf) { |
| |
| externalPreInvoke(); |
| boolean remoteHomeView = (generatedRemoteBusinessIntf == null); |
| if (instanceKey.length == 1 && instanceKey[0] == HOME_KEY) { |
| return remoteHomeView ? ejbHomeImpl.getEJBHome() : ejbRemoteBusinessHomeImpl.getEJBHome(); |
| } else { |
| |
| java.rmi.Remote targetObject = null; |
| EJBObjectImpl ejbObjectImpl = null; |
| |
| if (remoteHomeView) { |
| ejbObjectImpl = getEJBObjectImpl(instanceKey); |
| // In rare cases for sfsbs and entity beans, this can be null. |
| if (ejbObjectImpl != null) { |
| targetObject = ejbObjectImpl.getEJBObject(); |
| } |
| } else { |
| ejbObjectImpl = getEJBRemoteBusinessObjectImpl(instanceKey); |
| // In rare cases for sfsbs and entity beans, this can be null. |
| if (ejbObjectImpl != null) { |
| targetObject = ejbObjectImpl.getEJBObject(generatedRemoteBusinessIntf); |
| } |
| } |
| |
| return targetObject; |
| } |
| } |
| |
| /** |
| * Release the EJBObject/EJBHome object. Called from the ProtocolManager after a remote invocation completes. |
| */ |
| @Override |
| public void releaseTargetObject(java.rmi.Remote remoteObj) { |
| externalPostInvoke(); |
| } |
| |
| @Override |
| public void externalPreInvoke() { |
| BeanContext bc = new BeanContext(); |
| final Thread currentThread = Thread.currentThread(); |
| bc.previousClassLoader = currentThread.getContextClassLoader(); |
| if (getClassLoader().equals(bc.previousClassLoader) == false) { |
| if (System.getSecurityManager() == null) { |
| currentThread.setContextClassLoader(getClassLoader()); |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { |
| |
| @Override |
| public java.lang.Object run() { |
| currentThread.setContextClassLoader(getClassLoader()); |
| return null; |
| } |
| }); |
| } |
| bc.classLoaderSwitched = true; |
| } |
| |
| ArrayDeque beanContextStack = (ArrayDeque) threadLocalContext.get(); |
| |
| if (beanContextStack == null) { |
| beanContextStack = new ArrayDeque(); |
| threadLocalContext.set(beanContextStack); |
| } |
| beanContextStack.push(bc); |
| } |
| |
| @Override |
| public void externalPostInvoke() { |
| try { |
| ArrayDeque beanContextStack = (ArrayDeque) threadLocalContext.get(); |
| |
| final BeanContext bc = (BeanContext) beanContextStack.pop(); |
| if (bc.classLoaderSwitched == true) { |
| if (System.getSecurityManager() == null) { |
| Thread.currentThread().setContextClassLoader(bc.previousClassLoader); |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { |
| @Override |
| public java.lang.Object run() { |
| Thread.currentThread().setContextClassLoader(bc.previousClassLoader); |
| return null; |
| } |
| }); |
| } |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.FINE, "externalPostInvoke ex", ex); |
| } |
| } |
| |
| private boolean doPreInvokeAuthorization(EjbInvocation inv) { |
| |
| // preInvocation authorization does not apply if this is a timer callback |
| // OR if it's a remove operation initiated via the 299 SPI |
| boolean skipPreInvokeAuth = inv.isTimerCallback || (inv.isLocal && inv.method.equals(ejbIntfMethods[EJBLocalObject_remove]) |
| && !((EJBLocalObjectImpl) inv.ejbObject).isLocalHomeView()); |
| |
| return !skipPreInvokeAuth; |
| } |
| |
| /** |
| * Called from EJBObject/EJBHome before invoking on EJB. Set the EJB instance in the EjbInvocation. |
| * |
| * It must be ensured that the following general pattern is followed by various parts of the EJBContainer code: |
| * |
| * try { container.preInvoke(inv); returnValue = container.intercept(inv); } catch (Exception1 e1) { ... } catch (Exception2 e2) |
| * { ... } finally { container.postInvoke(); } |
| * |
| */ |
| @Override |
| public void preInvoke(EjbInvocation inv) { |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Entering BaseContainer::preInvoke : " + inv); |
| } |
| |
| try { |
| if (containerState != CONTAINER_STARTED) { |
| throw new EJBException(localStrings.getLocalString("ejb.container_not_started", |
| "Attempt to invoke when container is in {0}", containerStateToString(containerState))); |
| } |
| |
| if (inv.method == null) { |
| throw new EJBException( |
| localStrings.getLocalString("ejb.null_invocation_method", "Attempt to invoke container with null invocation method")); |
| } |
| |
| if (inv.invocationInfo == null) { |
| inv.invocationInfo = getInvocationInfo(inv); |
| if (inv.invocationInfo == null) { |
| throw new EJBException(localStrings.getLocalString("ejb.null_invocation_info", |
| "EjbInvocation Info lookup failed for method {0}", inv.method)); |
| } |
| } |
| |
| inv.transactionAttribute = inv.invocationInfo.txAttr; |
| inv.container = this; |
| |
| if (inv.mustInvokeAsynchronously()) { |
| return; |
| } |
| |
| if (doPreInvokeAuthorization(inv)) { |
| if (!authorize(inv)) { |
| throw new AccessLocalException( |
| localStrings.getLocalString("ejb.client_not_authorized", "Client not authorized for this invocation")); |
| } |
| } |
| |
| // Cache value of txManager.getStatus() in invocation to avoid |
| // multiple thread-local accesses of that value during pre-invoke |
| // stage. |
| inv.setPreInvokeTxStatus(transactionManager.getStatus()); |
| |
| ComponentContext ctx = getContext(inv); |
| inv.context = ctx; |
| |
| inv.instance = inv.ejb = ctx.getEJB(); |
| InvocationInfo info = inv.invocationInfo; |
| |
| inv.useFastPath = (info.isTxRequiredLocalCMPField) && (inv.foundInTxCache); |
| // _logger.log(Level.INFO, "Use fastPath() ==> " + info.method); |
| |
| if (!inv.useFastPath) { |
| // Sets thread-specific state for Transaction, Naming, Security, |
| // etc |
| invocationManager.preInvoke(inv); |
| |
| // Do Tx machinery |
| preInvokeTx(inv); |
| |
| // null out invocation preInovkeTxStatus since the cache value |
| // is obsolete |
| inv.setPreInvokeTxStatus(null); |
| |
| enlistExtendedEntityManagers(ctx); |
| } |
| |
| } catch (Exception ex) { |
| _logger.log(Level.FINE, "Exception while running pre-invoke : ejbName = [{0}]", logParams); |
| _logger.log(Level.FINE, "", ex); |
| |
| EJBException ejbEx; |
| if (ex instanceof EJBException) { |
| ejbEx = (EJBException) ex; |
| } else { |
| ejbEx = new EJBException(ex); |
| } |
| |
| throw new PreInvokeException(ejbEx); |
| } |
| } |
| |
| public boolean intercept(CallbackType eventType, EJBContextImpl ctx) throws Throwable { |
| |
| return interceptorManager.intercept(eventType, ctx); |
| } |
| |
| protected void enlistExtendedEntityManagers(ComponentContext ctx) { |
| // Do nothing in general case |
| } |
| |
| protected void delistExtendedEntityManagers(ComponentContext ctx) { |
| // Do nothing in general case |
| } |
| |
| /** |
| * Containers that allow extended EntityManager will override this method. |
| */ |
| @Override |
| public EntityManager lookupExtendedEntityManager(EntityManagerFactory emf) { |
| throw new IllegalStateException(localStrings.getLocalString("ejb.extended_persistence_context_not_supported", |
| "EntityManager with PersistenceContextType.EXTENDED is not supported for this bean type")); |
| } |
| |
| @Override |
| public void webServicePostInvoke(EjbInvocation inv) { |
| // postInvokeTx is handled by WebServiceInvocationHandler. |
| // Invoke postInvoke with instructions to skip tx processing portion. |
| postInvoke(inv, false); |
| } |
| |
| /** |
| * Called from EJBObject/EJBHome after invoking on bean. |
| */ |
| @Override |
| public void postInvoke(EjbInvocation inv) { |
| postInvoke(inv, true); |
| } |
| |
| protected void postInvoke(EjbInvocation inv, boolean doTxProcessing) { |
| if (containerState != CONTAINER_STARTED) { |
| throw new EJBException(localStrings.getLocalString("ejb.container_not_started", "Attempt to invoke when container is in {0}", |
| containerStateToString(containerState))); |
| } |
| |
| inv.setDoTxProcessingInPostInvoke(doTxProcessing); |
| if (inv.mustInvokeAsynchronously()) { |
| EjbAsyncInvocationManager asyncManager = ((EjbContainerUtilImpl) ejbContainerUtilImpl).getEjbAsyncInvocationManager(); |
| asyncManager.submit(inv); |
| return; |
| } |
| |
| if (inv.ejb != null) { |
| // counterpart of invocationManager.preInvoke |
| if (!inv.useFastPath) { |
| invocationManager.postInvoke(inv); |
| delistExtendedEntityManagers(inv.context); |
| } else { |
| doTxProcessing = doTxProcessing && (inv.exception != null); |
| } |
| |
| try { |
| if (doTxProcessing) { |
| postInvokeTx(inv); |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.FINE, "Exception occurred in postInvokeTx : [{0}]", ex); |
| if (ex instanceof EJBException) { |
| inv.exception = ex; |
| } else { |
| inv.exception = new EJBException(ex); |
| } |
| } |
| |
| releaseContext(inv); |
| } |
| |
| if (inv.exception != null) { |
| |
| // Unwrap the PreInvokeException if necessary |
| if (inv.exception instanceof PreInvokeException) { |
| inv.exception = ((PreInvokeException) inv.exception).exception; |
| } |
| |
| // Log system exceptions by default and application exceptions only |
| // when log level is FINE or higher. |
| |
| if (isSystemUncheckedException(inv.exception)) { |
| _logger.log(Level.WARNING, SYSTEM_EXCEPTION, new Object[] { ejbDescriptor.getName(), inv.beanMethod }); |
| _logger.log(Level.WARNING, "", inv.exception); |
| } else { |
| _logger.log(Level.FINE, "An application exception occurred during an invocation on EJB {0}, method: {1}", |
| new Object[] { ejbDescriptor.getName(), inv.beanMethod }); |
| _logger.log(Level.FINE, "", inv.exception); |
| } |
| |
| if (inv.isRemote) { |
| |
| if (protocolMgr != null) { |
| // For remote business case, exception mapping is performed |
| // in client wrapper. |
| // TODO need extra logic to handle implementation-specific ejb exceptions |
| // (ParallelAccessEXCeption etc. that used to be handled by iiop glue code |
| inv.exception = mapRemoteException(inv); |
| } |
| |
| // The most useful portion of the system exception is logged |
| // above. Only log mapped form when log level is FINE or |
| // higher. |
| _logger.log(Level.FINE, "", inv.exception); |
| |
| } else { |
| |
| if (inv.isBusinessInterface) { |
| inv.exception = mapLocal3xException(inv.exception); |
| } |
| |
| } |
| |
| } |
| /*TODO |
| if ( AppVerification.doInstrument()) { |
| // need to pass the method, exception info, |
| // and EJB descriptor to get app info |
| AppVerification.getInstrumentLogger().doInstrumentForEjb( |
| ejbDescriptor, inv.method, inv.exception); |
| |
| } |
| */ |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Leaving BaseContainer::postInvoke : " + inv); |
| } |
| } |
| |
| /** |
| * Check if caller is authorized to invoke the method. Only called for EJBLocalObject and EJBLocalHome methods, from |
| * EJBLocalHome|ObjectImpl classes. |
| * |
| * @param method an integer identifying the method to be checked, must be one of the EJBLocal{Home|Object}_* constants. |
| */ |
| protected void authorizeLocalMethod(int method) { |
| |
| EjbInvocation inv = invFactory.create(); |
| inv.isLocal = true; |
| inv.isHome = EJB_INTF_METHODS_INFO[method]; |
| inv.method = ejbIntfMethods[method]; |
| inv.invocationInfo = ejbIntfMethodInfo[method]; |
| |
| if (!authorize(inv)) { |
| throw new AccessLocalException( |
| localStrings.getLocalString("ejb.client_not_authorized", "Client not authorized for this invocation")); |
| } |
| } |
| |
| /** |
| * Check if caller is authorized to invoke the method. Only called for EJBObject and EJBHome methods, from EJBHome|ObjectImpl |
| * classes. |
| * |
| * @param method an integer identifying the method to be checked, must be one of the EJB{Home|Object}_* constants. |
| */ |
| protected void authorizeRemoteMethod(int method) throws RemoteException { |
| EjbInvocation inv = invFactory.create(); |
| inv.isLocal = false; |
| inv.isHome = EJB_INTF_METHODS_INFO[method]; |
| inv.method = ejbIntfMethods[method]; |
| inv.invocationInfo = ejbIntfMethodInfo[method]; |
| |
| if (!authorize(inv)) { |
| // TODO see note above about additional special exception handling needed |
| Throwable t = mapRemoteException(inv); |
| if (t instanceof RuntimeException) { |
| throw (RuntimeException) t; |
| } else if (t instanceof RemoteException) { |
| throw (RemoteException) t; |
| } |
| else { |
| throw new AccessException( |
| localStrings.getLocalString("ejb.client_not_authorized", "Client not authorized for this invocation")); // throw the AccessException |
| } |
| } |
| } |
| |
| /** |
| * Call back from the timer migration process to add automatic timers to the map of scheduleIds |
| */ |
| void addSchedule(TimerPrimaryKey timerId, EJBTimerSchedule ts) { |
| for (Map.Entry<Method, List<ScheduledTimerDescriptor>> entry : schedules.entrySet()) { |
| Method m = entry.getKey(); |
| if (m.getName().equals(ts.getTimerMethodName()) && m.getParameterTypes().length == ts.getMethodParamCount()) { |
| scheduleIds.put(timerId, m); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Adding schedule: " + ts.getScheduleAsString() + " FOR method: " + m); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Check timeout method and set it accessable |
| */ |
| private void processEjbTimeoutMethod(Method method) throws Exception { |
| Class[] params = method.getParameterTypes(); |
| if ((params.length == 0 || (params.length == 1 && params[0] == jakarta.ejb.Timer.class)) && (method.getReturnType() == Void.TYPE)) { |
| |
| isTimedObject_ = true; |
| |
| final Method ejbTimeoutAccessible = method; |
| // Since timeout method can have any kind of access |
| // setAccessible to true. |
| if (System.getSecurityManager() == null) { |
| if (!ejbTimeoutAccessible.isAccessible()) { |
| ejbTimeoutAccessible.setAccessible(true); |
| } |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedExceptionAction() { |
| @Override |
| public java.lang.Object run() throws Exception { |
| if (!ejbTimeoutAccessible.isAccessible()) { |
| ejbTimeoutAccessible.setAccessible(true); |
| } |
| return null; |
| } |
| }); |
| } |
| } else { |
| throw new EJBException(localStrings.getLocalString("ejb.invalid_timeout_method", |
| "Invalid @Timeout or @Schedule signature for: {0} @Timeout or @Schedule method must return void and be a no-arg method or take a single jakarta.ejb.Timer param", |
| method)); |
| } |
| } |
| |
| /** |
| * Encapsulate logic used to map invocation method to invocation info. At present, we have two different maps, one for webservice |
| * invocation info and one for everything else. That might change in the future. |
| */ |
| private InvocationInfo getInvocationInfo(EjbInvocation inv) { |
| return inv.isWebService ? (InvocationInfo) webServiceInvocationInfoMap.get(inv.method) |
| : (InvocationInfo) invocationInfoMap.get(inv.method); |
| } |
| |
| private Throwable mapRemoteException(EjbInvocation inv) { |
| |
| Throwable originalException = inv.exception; |
| Throwable mappedException = originalException; |
| |
| // If it's an asnyc invocation and we're mapping an exception it |
| // means this is the thread of execution. The exception won't directly |
| // flow over the wire as a remote exception from the orb's perspective. |
| // If it's asychronous we know it's a remote business interface, not the |
| // 2.x client view. |
| if (inv.invocationInfo.isAsynchronous()) { |
| |
| if (java.rmi.Remote.class.isAssignableFrom(inv.clientInterface)) { |
| mappedException = protocolMgr.mapException(originalException); |
| |
| if (mappedException == originalException) { |
| |
| if (originalException instanceof EJBException) { |
| mappedException = new RemoteException(originalException.getMessage(), originalException); |
| } |
| |
| } |
| } else { |
| |
| mappedException = mapLocal3xException(originalException); |
| |
| } |
| |
| } else { |
| |
| // Synchronous invocation. First let the protocol manager perform |
| // its mapping. |
| |
| mappedException = protocolMgr.mapException(originalException); |
| |
| // If no mapping happened |
| if (mappedException == originalException) { |
| |
| if (inv.isBusinessInterface) { |
| |
| // Wrap it up in a special exception so the |
| // client can unwrap it and ensure that the client receives EJBException. |
| if (originalException instanceof EJBException) { |
| mappedException = new InternalEJBContainerException(originalException.getMessage(), originalException); |
| } |
| |
| } else { |
| if (originalException instanceof EJBException) { |
| mappedException = new RemoteException(originalException.getMessage(), originalException); |
| } |
| } |
| } |
| |
| } |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| |
| _logger.log(Level.FINE, |
| "Mapped original remote exception " + originalException + " to exception " + mappedException + " for " + inv); |
| |
| } |
| |
| return mappedException; |
| } |
| |
| private Throwable mapLocal3xException(Throwable t) { |
| |
| Throwable mappedException = null; |
| |
| if (t instanceof TransactionRolledbackLocalException) { |
| mappedException = new EJBTransactionRolledbackException(); |
| mappedException.initCause(t); |
| } else if (t instanceof TransactionRequiredLocalException) { |
| mappedException = new EJBTransactionRequiredException(); |
| mappedException.initCause(t); |
| } else if (t instanceof NoSuchObjectLocalException) { |
| mappedException = new NoSuchEJBException(); |
| mappedException.initCause(t); |
| } else if (t instanceof AccessLocalException) { |
| mappedException = new EJBAccessException(); |
| mappedException.initCause(t); |
| } |
| |
| return (mappedException != null) ? mappedException : t; |
| |
| } |
| |
| /** |
| * Common code to handle EJB security manager authorization call. |
| */ |
| @Override |
| public boolean authorize(EjbInvocation invocation) { |
| |
| // There are a few paths (e.g. authorizeLocalMethod, |
| // authorizeRemoteMethod, Ejb endpoint pre-handler ) |
| // for which invocationInfo is not set. We get better |
| // performance with the security manager on subsequent |
| // invocations of the same method if invocationInfo is |
| // set on the invocation. However, the authorization |
| // does not depend on it being set. So, try to set |
| // invocationInfo but in this case don't treat it as |
| // an error if it's not available. |
| if (invocation.invocationInfo == null) { |
| invocation.invocationInfo = getInvocationInfo(invocation); |
| } |
| |
| // Internal methods for 3.0 bean creation so there won't |
| // be corresponding permissions in the security policy file. |
| if ((invocation.method.getDeclaringClass() == localBusinessHomeIntf) || (invocation.method.getDeclaringClass() == remoteBusinessHomeIntf)) { |
| return true; |
| } |
| |
| boolean authorized = securityManager.authorize(invocation); |
| |
| if (!authorized) { |
| if (invocation.context != null) { |
| // This means that an enterprise bean context was created |
| // during the authorization call because of a callback from |
| // a Jakarta Authorization enterprise bean handler. Since the invocation will |
| // not proceed due to the authorization failure, we need |
| // to release the enterprise bean context. |
| releaseContext(invocation); |
| } |
| } |
| |
| return authorized; |
| } |
| |
| /** |
| * Create an array of all methods in the standard EJB interfaces: jakarta.ejb.EJB(Local){Home|Object} . |
| */ |
| private void initializeEjbInterfaceMethods() throws Exception { |
| ejbIntfMethods = new Method[EJB_INTF_METHODS_LENGTH]; |
| |
| if (isRemote) { |
| ejbIntfMethods[EJBHome_remove_Handle] = EJBHome.class.getMethod("remove", new Class[] { jakarta.ejb.Handle.class }); |
| ejbIntfMethods[EJBHome_remove_Pkey] = EJBHome.class.getMethod("remove", new Class[] { java.lang.Object.class }); |
| ejbIntfMethods[EJBHome_getEJBMetaData] = EJBHome.class.getMethod("getEJBMetaData", NO_PARAMS); |
| ejbIntfMethods[EJBHome_getHomeHandle] = EJBHome.class.getMethod("getHomeHandle", NO_PARAMS); |
| |
| ejbIntfMethods[EJBObject_getEJBHome] = EJBObject.class.getMethod("getEJBHome", NO_PARAMS); |
| ejbIntfMethods[EJBObject_getPrimaryKey] = EJBObject.class.getMethod("getPrimaryKey", NO_PARAMS); |
| ejbIntfMethods[EJBObject_remove] = EJBObject.class.getMethod("remove", NO_PARAMS); |
| ejbIntfMethods[EJBObject_getHandle] = EJBObject.class.getMethod("getHandle", NO_PARAMS); |
| ejbIntfMethods[EJBObject_isIdentical] = EJBObject.class.getMethod("isIdentical", new Class[] { jakarta.ejb.EJBObject.class }); |
| |
| if (isStatelessSession) { |
| if (hasRemoteHomeView) { |
| ejbIntfMethods[EJBHome_create] = homeIntf.getMethod("create", NO_PARAMS); |
| } |
| } |
| } |
| |
| if (isLocal) { |
| ejbIntfMethods[EJBLocalHome_remove_Pkey] = EJBLocalHome.class.getMethod("remove", new Class[] { java.lang.Object.class }); |
| |
| ejbIntfMethods[EJBLocalObject_getEJBLocalHome] = EJBLocalObject.class.getMethod("getEJBLocalHome", NO_PARAMS); |
| ejbIntfMethods[EJBLocalObject_getPrimaryKey] = EJBLocalObject.class.getMethod("getPrimaryKey", NO_PARAMS); |
| ejbIntfMethods[EJBLocalObject_remove] = EJBLocalObject.class.getMethod("remove", NO_PARAMS); |
| ejbIntfMethods[EJBLocalObject_isIdentical] = EJBLocalObject.class.getMethod("isIdentical", |
| new Class[] { jakarta.ejb.EJBLocalObject.class }); |
| |
| if (isStatelessSession) { |
| if (hasLocalHomeView) { |
| Method m = localHomeIntf.getMethod("create", NO_PARAMS); |
| ejbIntfMethods[EJBLocalHome_create] = m; |
| } |
| } |
| } |
| |
| } |
| |
| protected void cancelTimers(Object key) { |
| if (isTimedObject()) { |
| // EJBTimerService should be accessed only if needed |
| // not to cause it to be loaded if it's not used. |
| EJBTimerService timerService = EJBTimerService.getEJBTimerService(); |
| if (timerService != null) { |
| timerService.cancelTimersByKey(getContainerId(), key); |
| } |
| } |
| } |
| |
| private void stopTimers() { |
| if (isTimedObject()) { |
| EJBTimerService ejbTimerService = EJBTimerService.getEJBTimerService(); |
| if (ejbTimerService != null) { |
| ejbTimerService.stopTimers(getContainerId()); |
| } |
| } |
| } |
| |
| protected boolean isEjbTimeoutMethod(Method m) { |
| return schedules.containsKey(m) || m.equals(ejbTimeoutMethod); |
| } |
| |
| // internal API, implemented in subclasses |
| protected abstract EJBObjectImpl createEJBObjectImpl() throws CreateException, RemoteException; |
| |
| // Only applies to concrete session containers |
| EJBObjectImpl createRemoteBusinessObjectImpl() throws CreateException, RemoteException { |
| throw new EJBException("Internal ERROR: BaseContainer.createRemoteBusinessObject called"); |
| } |
| |
| // internal API, implemented in subclasses |
| protected EJBLocalObjectImpl createEJBLocalObjectImpl() throws CreateException { |
| throw new EJBException("Internal ERROR: BaseContainer.createEJBLocalObject called"); |
| } |
| |
| // Only implemented in Stateless , Stateful, and Singleton session containers |
| EJBLocalObjectImpl createEJBLocalBusinessObjectImpl(boolean localBeanView) throws CreateException { |
| throw new EJBException("Internal ERROR: BaseContainer.createEJBLocalBusinessObject called"); |
| } |
| |
| EJBLocalObjectImpl createEJBLocalBusinessObjectImpl(String clientIntf) throws CreateException { |
| boolean useLocalBeanView = isLocalBeanClass(clientIntf); |
| return createEJBLocalBusinessObjectImpl(useLocalBeanView); |
| |
| } |
| |
| /** |
| * Called when a remote invocation arrives for an EJB. Implemented in subclasses. |
| */ |
| protected abstract EJBObjectImpl getEJBObjectImpl(byte[] streamKey); |
| |
| EJBObjectImpl getEJBRemoteBusinessObjectImpl(byte[] streamKey) { |
| throw new EJBException(localStrings.getLocalString("ejb.basecontainer_internal_error", "Internal ERROR: BaseContainer.{0} called", |
| "getRemoteBusinessObjectImpl")); |
| } |
| |
| protected EJBLocalObjectImpl getEJBLocalObjectImpl(Object key) { |
| throw new EJBException(localStrings.getLocalString("ejb.basecontainer_internal_error", "Internal ERROR: BaseContainer.{0} called", |
| "getEJBLocalObjectImpl")); |
| } |
| |
| EJBLocalObjectImpl getEJBLocalBusinessObjectImpl(Object key) { |
| throw new EJBException(localStrings.getLocalString("ejb.basecontainer_internal_error", "Internal ERROR: BaseContainer.{0} called", |
| "getEJBLocalBusinessObjectImpl")); |
| } |
| |
| EJBLocalObjectImpl getOptionalEJBLocalBusinessObjectImpl(Object key) { |
| throw new EJBException(localStrings.getLocalString("ejb.basecontainer_internal_error", "Internal ERROR: BaseContainer.{0} called", |
| "getOptionalEJBLocalBusinessObjectImpl")); |
| } |
| |
| /** |
| * Check if the given EJBObject/LocalObject has been removed. |
| * |
| * @exception NoSuchObjectLocalException if the object has been removed. |
| */ |
| protected void checkExists(EJBLocalRemoteObject ejbObj) { |
| throw new EJBException(localStrings.getLocalString("ejb.basecontainer_internal_error", "Internal ERROR: BaseContainer.{0} called", |
| ("checkExists for bean " + ejbDescriptor.getName()))); |
| } |
| |
| protected final ComponentContext getContext(EjbInvocation inv) throws EJBException { |
| |
| return (inv.context == null) ? _getContext(inv) : inv.context; |
| |
| } |
| |
| protected final Object getInvocationKey(EjbInvocation inv) { |
| return (inv.ejbObject == null) ? null : inv.ejbObject.getKey(); |
| } |
| |
| // internal API, implemented in subclasses |
| protected abstract ComponentContext _getContext(EjbInvocation inv) throws EJBException; |
| |
| // internal API, implemented in subclasses |
| protected abstract void releaseContext(EjbInvocation inv) throws EJBException; |
| |
| protected abstract boolean passivateEJB(ComponentContext context); |
| |
| // internal API, implemented in subclasses |
| protected abstract void forceDestroyBean(EJBContextImpl sc) throws EJBException; |
| |
| protected abstract void removeBean(EJBLocalRemoteObject ejbo, Method removeMethod, boolean local) |
| throws RemoveException, EJBException, RemoteException; |
| |
| // default implementation |
| protected void authorizeLocalGetPrimaryKey(EJBLocalRemoteObject ejbObj) throws EJBException { |
| throw new EJBException(localStrings.getLocalString("containers.invalid_operation", "Invalid operation for Session EJBs.")); |
| } |
| |
| // default implementation |
| protected void authorizeRemoteGetPrimaryKey(EJBLocalRemoteObject ejbObj) throws RemoteException { |
| throw new RemoteException(localStrings.getLocalString("containers.invalid_operation", "Invalid operation for Session EJBs.")); |
| } |
| |
| // default implementation |
| protected Object invokeFindByPrimaryKey(Method method, EjbInvocation inv, Object[] args) throws Throwable { |
| assertSupportedOption("invokeFindByPrimaryKey"); |
| return null; |
| } |
| |
| // default implementation |
| @Override |
| public void removeBeanUnchecked(Object pkey) { |
| assertSupportedOption("removeBeanUnchecked"); |
| } |
| |
| // default implementation |
| @Override |
| public void removeBeanUnchecked(EJBLocalObject bean) { |
| assertSupportedOption("removeBeanUnchecked"); |
| } |
| |
| @Override |
| public void preSelect() { |
| assertSupportedOption("preSelect"); |
| } |
| |
| // default implementation |
| @Override |
| public EJBLocalObject getEJBLocalObjectForPrimaryKey(Object pkey, EJBContext ctx) { |
| assertSupportedOption("getEJBLocalObjectForPrimaryKey(pkey, ctx)"); |
| return null; |
| } |
| |
| // default implementation |
| @Override |
| public EJBLocalObject getEJBLocalObjectForPrimaryKey(Object pkey) { |
| assertSupportedOption("getEJBLocalObjectForPrimaryKey"); |
| return null; |
| } |
| |
| // default implementation |
| @Override |
| public EJBObject getEJBObjectForPrimaryKey(Object pkey) { |
| assertSupportedOption("getEJBObjectForPrimaryKey"); |
| return null; |
| } |
| |
| private void assertSupportedOption(String name) { |
| throw new EJBException(localStrings.getLocalString("ejb.entity_container_only", "{0} only works for EntityContainer", name)); |
| } |
| |
| // internal API, implemented in subclasses |
| protected boolean isIdentical(EJBObjectImpl ejbo, EJBObject other) throws RemoteException { |
| throw new EJBException( |
| localStrings.getLocalString("ejb.basecontainer_internal_error", "Internal ERROR: BaseContainer.{0} called", "isIdentical")); |
| } |
| |
| /** |
| * Called-back from security implementation through EjbInvocation when a jacc policy provider wants an enterprise bean instance. |
| */ |
| @Override |
| public Object getJaccEjb(EjbInvocation ejbInvocation) { |
| Object bean = null; |
| |
| // Access to an enterprise bean instance is undefined for |
| // anything but business method invocations through |
| // Remote , Local, and ServiceEndpoint interfaces. |
| if (((ejbInvocation.invocationInfo != null) && ejbInvocation.invocationInfo.isBusinessMethod) || ejbInvocation.isWebService) { |
| |
| // In the typical case the context will not have been |
| // set when the policy provider invokes this callback. |
| // There are some cases where it is ok for it to have been |
| // set, e.g. if the policy provider invokes the callback |
| // twice within the same authorization decision. |
| if (ejbInvocation.context == null) { |
| |
| try { |
| ejbInvocation.context = getContext(ejbInvocation); |
| bean = ejbInvocation.context.getEJB(); |
| |
| // NOTE : inv.ejb is not set here. Post-invoke logic for |
| // BaseContainer and webservices uses the fact that |
| // inv.ejb is non-null as an indication that that |
| // BaseContainer.preInvoke() proceeded past a certain |
| // point, which affects which cleanup needs to be |
| // performed. |
| |
| // It would be better to have explicit |
| // state in the invocation that says which cleanup |
| // steps are necessary(e.g. for invocationMgr.postInvoke |
| // , postInvokeTx, etc) but I'm keeping the logic the |
| // same for now. |
| |
| // BaseContainer.authorize() will |
| // explicitly handle the case where a context was |
| // created as a result of this call and the |
| // authorization failed, which means the context needs |
| // be released. |
| |
| } catch (EJBException e) { |
| _logger.log(Level.WARNING, CONTEXT_FAILURE_JACC, logParams[0]); |
| _logger.log(Level.WARNING, "", e); |
| } |
| |
| } else { |
| bean = ejbInvocation.context.getEJB(); |
| } |
| } |
| |
| return bean; |
| } |
| |
| @Override |
| public void assertValidLocalObject(Object o) throws EJBException { |
| boolean valid = false; |
| String errorMsg = ""; |
| |
| if ((o != null) && (o instanceof EJBLocalObject)) { |
| // Given object is always the client view EJBLocalObject. |
| // Use utility method to translate it to EJBLocalObjectImpl |
| // so we handle both the generated and proxy case. |
| EJBLocalObjectImpl ejbLocalObjImpl = EJBLocalObjectImpl.toEJBLocalObjectImpl((EJBLocalObject) o); |
| BaseContainer otherContainer = (BaseContainer) ejbLocalObjImpl.getContainer(); |
| if (otherContainer.getContainerId() == getContainerId()) { |
| valid = true; |
| } else { |
| errorMsg = "Local objects of ejb-name " + otherContainer.ejbDescriptor.getName() + " and ejb-name " |
| + ejbDescriptor.getName() + " are from different containers"; |
| |
| } |
| } else { |
| errorMsg = (o != null) |
| ? "Parameter instance of class '" + o.getClass().getName() + "' is not a valid local interface instance for bean " |
| + ejbDescriptor.getName() |
| : "A null parameter is not a valid local interface of bean " + ejbDescriptor.getName(); |
| } |
| |
| if (!valid) { |
| throw new EJBException(errorMsg); |
| } |
| |
| } |
| |
| /** |
| * Asserts validity of RemoteHome objects. This was defined for the J2EE 1.4 implementation and is exposed through Container SPI. |
| */ |
| @Override |
| public void assertValidRemoteObject(Object o) throws EJBException { |
| boolean valid = false; |
| String errorMsg = ""; |
| Exception causeException = null; |
| |
| if ((o != null) && (o instanceof EJBObject)) { |
| String className = o.getClass().getName(); |
| |
| // Given object must be an instance of the remote stub class for |
| // this ejb. |
| if (hasRemoteHomeView) { |
| try { |
| valid = remoteHomeRefFactory.hasSameContainerID((org.omg.CORBA.Object) o); |
| } catch (Exception ex) { |
| causeException = ex; |
| errorMsg = "Parameter instance of class '" + className + "' is not a valid remote interface instance for bean " |
| + ejbDescriptor.getName(); |
| } |
| } else { |
| errorMsg = "Parameter instance of class '" + className + "' is not a valid remote interface instance for bean " |
| + ejbDescriptor.getName(); |
| |
| } |
| } else { |
| errorMsg = (o != null) |
| ? "Parameter instance of class '" + o.getClass().getName() + "' is not a valid remote interface instance for bean " |
| + ejbDescriptor.getName() |
| : "A null parameter is not a valid remote interface of bean " + ejbDescriptor.getName(); |
| } |
| |
| if (!valid) { |
| if (causeException != null) { |
| throw new EJBException(errorMsg, causeException); |
| } else { |
| throw new EJBException(errorMsg); |
| } |
| } |
| } |
| |
| /** |
| * |
| */ |
| protected final int getTxAttr(Method method, String methodIntf) throws EJBException { |
| |
| InvocationInfo invInfo = methodIntf.equals(MethodDescriptor.EJB_WEB_SERVICE) |
| ? (InvocationInfo) webServiceInvocationInfoMap.get(method) |
| : (InvocationInfo) invocationInfoMap.get(method); |
| |
| if (invInfo != null) { |
| return invInfo.txAttr; |
| } else { |
| throw new EJBException("Transaction Attribute not found for method" + method); |
| } |
| } |
| |
| // Get the transaction attribute for a method. |
| // Note: this method object is of the remote/EJBHome interface |
| // class, not the EJB class. (except for MDB's message listener |
| // callback method or TimedObject ejbTimeout method) |
| protected final int getTxAttr(EjbInvocation inv) throws EJBException { |
| if (inv.transactionAttribute != TX_NOT_INITIALIZED) { |
| return inv.transactionAttribute; |
| } |
| |
| int txAttr = getTxAttr(inv.method, inv.getMethodInterface()); |
| inv.transactionAttribute = txAttr; |
| return inv.transactionAttribute; |
| |
| } |
| |
| // Check if a method is a business method. |
| // Note: this method object is of the EJB's remote/home/local interfaces, |
| // not the EJB class. |
| final boolean isBusinessMethod(Method method) { |
| Class methodClass = method.getDeclaringClass(); |
| |
| // All methods on the Home/LocalHome & super-interfaces |
| // are not business methods. |
| // All methods on jakarta.ejb.EJBObject and EJBLocalObject |
| // (e.g. remove) are not business methods. |
| // All remaining methods are business methods |
| |
| if (isRemote) { |
| if ((hasRemoteHomeView && ((methodClass == homeIntf) || methodClass.isAssignableFrom(homeIntf))) |
| || (hasRemoteBusinessView |
| && ((methodClass == remoteBusinessHomeIntf) || methodClass.isAssignableFrom(remoteBusinessHomeIntf))) |
| || (methodClass == EJBObject.class)) { |
| return false; |
| } |
| } |
| if (isLocal) { |
| if ((hasLocalHomeView && ((methodClass == localHomeIntf) || methodClass.isAssignableFrom(localHomeIntf))) |
| || (hasLocalBusinessView && ((methodClass == localBusinessHomeIntf) || methodClass.isAssignableFrom(localBusinessHomeIntf))) |
| || (methodClass == EJBLocalObject.class)) { |
| return false; |
| } |
| } |
| // NOTE : Web Service client view contains ONLY |
| // business methods |
| |
| return true; |
| } |
| |
| // Check if a method is a create / finder / home method. |
| // Note: this method object is of the EJB's remote/home/local interfaces, |
| // not the EJB class. |
| protected boolean isCreateHomeFinder(Method method) { |
| Class methodClass = method.getDeclaringClass(); |
| |
| if (hasRemoteHomeView && methodClass.isAssignableFrom(homeIntf) && (methodClass != EJBHome.class)) { |
| return true; |
| } |
| |
| if (hasRemoteBusinessView && methodClass.isAssignableFrom(remoteBusinessHomeIntf) && (methodClass != EJBHome.class)) { |
| return true; |
| } |
| |
| if (hasLocalHomeView && methodClass.isAssignableFrom(localHomeIntf) && (methodClass != EJBLocalHome.class)) { |
| return true; |
| } |
| |
| if (hasLocalBusinessView && methodClass.isAssignableFrom(localBusinessHomeIntf) && (methodClass != EJBLocalHome.class)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| protected InvocationInfo addInvocationInfo(Method method, String methodIntf, Class originalIntf) throws EJBException { |
| |
| return addInvocationInfo(method, methodIntf, originalIntf, false, false); |
| } |
| |
| private InvocationInfo addInvocationInfo(Method method, String methodIntf, Class originalIntf, boolean isEjbTimeout) |
| throws EJBException { |
| |
| return addInvocationInfo(method, methodIntf, originalIntf, isEjbTimeout, false); |
| } |
| |
| private InvocationInfo addInvocationInfo(Method method, String methodIntf, Class originalIntf, boolean isEjbTimeout, |
| boolean optionalLocalBusView) throws EJBException |
| |
| { |
| MethodDescriptor md = new MethodDescriptor(method, methodIntf); |
| boolean flushEnabled = findFlushEnabledAttr(md); |
| int txAttr = containerTransactionManager.findTxAttr(md); |
| InvocationInfo info = createInvocationInfo(method, txAttr, flushEnabled, methodIntf, originalIntf); |
| boolean isHomeIntf = (methodIntf.equals(MethodDescriptor.EJB_HOME) || methodIntf.equals(MethodDescriptor.EJB_LOCALHOME)); |
| if (!isHomeIntf) { |
| Method beanMethod = null; |
| if (!isEjbTimeout) { |
| try { |
| beanMethod = getEJBClass().getMethod(method.getName(), method.getParameterTypes()); |
| } catch (NoSuchMethodException nsmEx) { |
| //TODO |
| } |
| } else { |
| // For a timeout it is the method |
| beanMethod = method; |
| } |
| |
| if (beanMethod != null) { |
| // Can't set AroundInvoke/AroundTimeout chains here, but set up some |
| // state on info object so it can be done right after InterceptorManager |
| // is initialized. |
| info.aroundMethod = beanMethod; |
| info.isEjbTimeout = isEjbTimeout; |
| } |
| |
| // Asynchronous method initialization |
| if (isEligibleForAsync(originalIntf, methodIntf)) { |
| |
| Method targetMethod = optionalLocalBusView ? beanMethod : method; |
| |
| boolean isAsync = ((EjbSessionDescriptor) ejbDescriptor).isAsynchronousMethod(targetMethod); |
| |
| if (isAsync) { |
| |
| // Check return type |
| if (optionalLocalBusView) { |
| |
| boolean beanMethodReturnTypeVoid = beanMethod.getReturnType().equals(Void.TYPE); |
| boolean beanMethodReturnTypeFuture = beanMethod.getReturnType().equals(Future.class); |
| |
| if (!beanMethodReturnTypeVoid && !beanMethodReturnTypeFuture) { |
| throw new RuntimeException("Invalid no-interface view asynchronous method '" + beanMethod + "' for bean " |
| + ejbDescriptor.getName() + ". Async method exposed through no-interface view must " |
| + " have return type void or java.lang.concurrent.Future<V>"); |
| } |
| |
| } else { |
| |
| // Use actual interface method instead of method from generated interface |
| Method intfMethod = null; |
| try { |
| intfMethod = originalIntf.getMethod(method.getName(), method.getParameterTypes()); |
| } catch (NoSuchMethodException nsmEx) { |
| throw new RuntimeException( |
| "No matching async intf method for method '" + beanMethod + "' on bean " + ejbDescriptor.getName()); |
| } |
| |
| if (beanMethod == null) { |
| |
| throw new RuntimeException( |
| "No matching bean class method for async method '" + intfMethod + "' on bean " + ejbDescriptor.getName()); |
| } |
| |
| boolean beanMethodReturnTypeVoid = beanMethod.getReturnType().equals(Void.TYPE); |
| boolean beanMethodReturnTypeFuture = beanMethod.getReturnType().equals(Future.class); |
| |
| boolean intfMethodReturnTypeVoid = intfMethod.getReturnType().equals(Void.TYPE); |
| boolean intfMethodReturnTypeFuture = intfMethod.getReturnType().equals(Future.class); |
| |
| boolean bothVoid = intfMethodReturnTypeVoid && beanMethodReturnTypeVoid; |
| boolean bothFuture = intfMethodReturnTypeFuture && beanMethodReturnTypeFuture; |
| |
| boolean valid = false; |
| |
| if (bothVoid) { |
| valid = true; |
| } else if (bothFuture) { |
| valid = true; |
| } |
| |
| if (!valid) { |
| throw new RuntimeException( |
| "Invalid asynchronous bean class / interface " + "method signatures for bean " + ejbDescriptor.getName() |
| + ". beanMethod = '" + beanMethod + "' , interface method = '" + intfMethod + "'"); |
| } |
| } |
| |
| info.setIsAsynchronous(true); |
| |
| } |
| } |
| } |
| |
| if (methodIntf.equals(MethodDescriptor.EJB_WEB_SERVICE)) { |
| webServiceInvocationInfoMap.put(method, info); |
| } else { |
| invocationInfoMap.put(method, info); |
| } |
| |
| return info; |
| } |
| |
| private boolean isEligibleForAsync(Class originalIntf, String methodIntf) { |
| |
| boolean eligibleForAsync = false; |
| |
| if (methodIntf.equals(MethodDescriptor.EJB_LOCAL) || methodIntf.equals(MethodDescriptor.EJB_REMOTE)) { |
| |
| boolean is2xClientView = (EJBObject.class.isAssignableFrom(originalIntf) |
| || EJBLocalObject.class.isAssignableFrom(originalIntf)); |
| eligibleForAsync = !is2xClientView; |
| } |
| |
| return eligibleForAsync; |
| } |
| |
| /** |
| * Create invocation info for one method. |
| * |
| * @param originalIntf Leaf interface for the given view. Not set for methodIntf == bean. |
| */ |
| private final InvocationInfo createInvocationInfo(Method method, int txAttr, boolean flushEnabled, String methodIntf, |
| Class originalIntf) throws EJBException { |
| |
| InvocationInfo invInfo = new InvocationInfo(method); |
| invInfo.str_method_sig = EjbMonitoringUtils.stringify(method); |
| |
| invInfo.ejbName = ejbDescriptor.getName(); |
| invInfo.txAttr = txAttr; |
| invInfo.methodIntf = methodIntf; |
| |
| invInfo.isBusinessMethod = isBusinessMethod(method); |
| invInfo.isCreateHomeFinder = isCreateHomeFinder(method); |
| |
| invInfo.startsWithCreate = method.getName().startsWith("create"); |
| invInfo.startsWithFind = method.getName().startsWith("find"); |
| invInfo.startsWithRemove = method.getName().startsWith("remove"); |
| invInfo.startsWithFindByPrimaryKey = method.getName().startsWith("findByPrimaryKey"); |
| invInfo.flushEnabled = flushEnabled; |
| |
| if (methodIntf.equals(MethodDescriptor.EJB_LOCALHOME)) { |
| if (method.getDeclaringClass() != EJBLocalHome.class) { |
| setHomeTargetMethodInfo(invInfo, true); |
| } |
| } else if (methodIntf.equals(MethodDescriptor.EJB_HOME)) { |
| if (method.getDeclaringClass() != EJBHome.class) { |
| setHomeTargetMethodInfo(invInfo, false); |
| } |
| } else if (methodIntf.equals(MethodDescriptor.EJB_LOCAL)) { |
| if (method.getDeclaringClass() != EJBLocalObject.class) { |
| setEJBObjectTargetMethodInfo(invInfo, true, originalIntf); |
| } |
| } else if (methodIntf.equals(MethodDescriptor.EJB_REMOTE)) { |
| if (method.getDeclaringClass() != EJBObject.class) { |
| setEJBObjectTargetMethodInfo(invInfo, false, originalIntf); |
| } |
| } |
| |
| setConcurrencyInvInfo(method, methodIntf, invInfo); |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, invInfo.toString()); |
| } |
| |
| adjustInvocationInfo(invInfo, method, txAttr, flushEnabled, methodIntf, originalIntf); |
| |
| return invInfo; |
| } |
| |
| // default impl |
| protected void adjustInvocationInfo(InvocationInfo invInfo, Method method, int txAttr, boolean flushEnabled, String methodIntf, |
| Class originalIntf) throws EJBException { |
| // Nothing todo |
| } |
| |
| private void setConcurrencyInvInfo(Method invInfoMethod, String methodIntf, InvocationInfo invInfo) { |
| |
| MethodLockInfo lockInfo = null; |
| |
| // Set READ/WRITE lock info. Only applies to singleton beans. |
| if (isSingleton) { |
| EjbSessionDescriptor singletonDesc = (EjbSessionDescriptor) ejbDescriptor; |
| List<MethodDescriptor> readLockMethods = singletonDesc.getReadLockMethods(); |
| List<MethodDescriptor> writeLockMethods = singletonDesc.getWriteLockMethods(); |
| |
| for (MethodDescriptor readLockMethodDesc : readLockMethods) { |
| Method readLockMethod = readLockMethodDesc.getMethod(singletonDesc); |
| if (implMethodMatchesInvInfoMethod(invInfoMethod, methodIntf, readLockMethod)) { |
| |
| lockInfo = new MethodLockInfo(); |
| lockInfo.setLockType(LockType.READ); |
| break; |
| } |
| } |
| |
| if (lockInfo == null) { |
| for (MethodDescriptor writeLockMethodDesc : writeLockMethods) { |
| Method writeLockMethod = writeLockMethodDesc.getMethod(singletonDesc); |
| if (implMethodMatchesInvInfoMethod(invInfoMethod, methodIntf, writeLockMethod)) { |
| |
| lockInfo = new MethodLockInfo(); |
| lockInfo.setLockType(LockType.WRITE); |
| break; |
| } |
| } |
| } |
| } |
| |
| // Set AccessTimeout info |
| if (isSingleton || isStatefulSession) { |
| |
| EjbSessionDescriptor sessionDesc = (EjbSessionDescriptor) ejbDescriptor; |
| List<EjbSessionDescriptor.AccessTimeoutHolder> accessTimeoutInfo = sessionDesc.getAccessTimeoutInfo(); |
| |
| for (EjbSessionDescriptor.AccessTimeoutHolder accessTimeoutHolder : accessTimeoutInfo) { |
| MethodDescriptor accessTimeoutMethodDesc = accessTimeoutHolder.method; |
| Method accessTimeoutMethod = accessTimeoutMethodDesc.getMethod(sessionDesc); |
| if (implMethodMatchesInvInfoMethod(invInfoMethod, methodIntf, accessTimeoutMethod)) { |
| |
| if (lockInfo == null) { |
| lockInfo = new MethodLockInfo(); |
| } |
| |
| lockInfo.setTimeout(accessTimeoutHolder.value, accessTimeoutHolder.unit); |
| |
| break; |
| } |
| } |
| } |
| |
| if (lockInfo != null) { |
| invInfo.methodLockInfo = lockInfo; |
| } |
| |
| } |
| |
| private boolean implMethodMatchesInvInfoMethod(Method invInfoMethod, String methodIntf, Method implMethod) { |
| |
| boolean match = false; |
| |
| if (methodIntf.equals(MethodDescriptor.EJB_BEAN)) { |
| // Declaring class must match in addition to signature |
| match = (implMethod.getDeclaringClass().equals(invInfoMethod.getDeclaringClass()) |
| && TypeUtil.sameMethodSignature(implMethod, invInfoMethod)); |
| |
| } else { |
| match = Modifier.isPublic(implMethod.getModifiers()) && Modifier.isPublic(invInfoMethod.getModifiers()) |
| && TypeUtil.sameMethodSignature(implMethod, invInfoMethod); |
| } |
| |
| return match; |
| } |
| |
| protected InvocationInfo postProcessInvocationInfo(InvocationInfo invInfo) { |
| return invInfo; |
| } |
| |
| // default impl |
| protected void adjustHomeTargetMethodInfo(InvocationInfo invInfo, String methodName, Class[] paramTypes) throws NoSuchMethodException { |
| // Nothing todo |
| } |
| |
| private void setHomeTargetMethodInfo(InvocationInfo invInfo, boolean isLocal) throws EJBException { |
| |
| Class homeIntfClazz = isLocal ? jakarta.ejb.EJBLocalHome.class : jakarta.ejb.EJBHome.class; |
| |
| Class methodClass = invInfo.method.getDeclaringClass(); |
| Class[] paramTypes = invInfo.method.getParameterTypes(); |
| String methodName = invInfo.method.getName(); |
| |
| try { |
| Method m = homeIntfClazz.getMethod(methodName, paramTypes); |
| // Attempt to override Home/LocalHome method. Print warning |
| // but don't treat it as a fatal error. At runtime, |
| // the EJBHome/EJBLocalHome method will be called. |
| String[] params = { m.toString(), invInfo.method.toString() }; |
| _logger.log(Level.WARNING, ILLEGAL_EJB_INTERFACE_OVERRIDE, params); |
| invInfo.ejbIntfOverride = true; |
| return; |
| } catch (NoSuchMethodException nsme) { |
| } |
| |
| try { |
| if (invInfo.startsWithCreate) { |
| |
| String extraCreateChars = methodName.substring("create".length()); |
| invInfo.targetMethod1 = ejbClass.getMethod("ejbCreate" + extraCreateChars, paramTypes); |
| |
| adjustHomeTargetMethodInfo(invInfo, methodName, paramTypes); |
| |
| } else if (invInfo.startsWithFind) { |
| |
| String extraFinderChars = methodName.substring("find".length()); |
| invInfo.targetMethod1 = ejbClass.getMethod("ejbFind" + extraFinderChars, paramTypes); |
| |
| } else { |
| |
| // HOME method |
| |
| String upperCasedName = methodName.substring(0, 1).toUpperCase(Locale.US) + methodName.substring(1); |
| invInfo.targetMethod1 = ejbClass.getMethod("ejbHome" + upperCasedName, paramTypes); |
| } |
| } catch (NoSuchMethodException nsme) { |
| |
| if ((methodClass == localBusinessHomeIntf) || (methodClass == remoteBusinessHomeIntf) |
| || (methodClass == ejbOptionalLocalBusinessHomeIntf || (methodClass == GenericEJBHome.class))) { |
| // Not an error. This is the case where the EJB 3.0 |
| // client view is being used and there is no corresponding |
| // create/init method. |
| } else if (isStatelessSession || isSingleton) { |
| // Ignore. Not an error. |
| // EJB 3.0 Stateless session ejbCreate/PostConstruct |
| // is decoupled from RemoteHome/LocalHome create(). |
| } else { |
| |
| Method initMethod = null; |
| if (isSession) { |
| EjbSessionDescriptor sessionDesc = (EjbSessionDescriptor) ejbDescriptor; |
| |
| for (EjbInitInfo next : sessionDesc.getInitMethods()) { |
| MethodDescriptor beanMethod = next.getBeanMethod(); |
| Method m = beanMethod.getMethod(sessionDesc); |
| if (next.getCreateMethod().getName().equals(methodName) && TypeUtil.sameParamTypes(m, invInfo.method)) { |
| initMethod = m; |
| break; |
| } |
| } |
| } |
| |
| if (initMethod != null) { |
| invInfo.targetMethod1 = initMethod; |
| } else { |
| Object[] params = { logParams[0], (isLocal ? "LocalHome" : "Home"), invInfo.method.toString() }; |
| _logger.log(Level.WARNING, BEAN_CLASS_METHOD_NOT_FOUND, params); |
| // Treat this as a warning instead of a fatal error. |
| // That matches the behavior of the generated code. |
| // Mark the target methods as null. If this method is |
| // invoked at runtime it will be result in an exception |
| // from the invocation handlers. |
| invInfo.targetMethod1 = null; |
| invInfo.targetMethod2 = null; |
| } |
| } |
| } |
| } |
| |
| private void setEJBObjectTargetMethodInfo(InvocationInfo invInfo, boolean isLocal, Class originalIntf) throws EJBException { |
| |
| Class ejbIntfClazz = isLocal ? jakarta.ejb.EJBLocalObject.class : jakarta.ejb.EJBObject.class; |
| |
| Class[] paramTypes = invInfo.method.getParameterTypes(); |
| String methodName = invInfo.method.getName(); |
| |
| // Check for 2.x Remote/Local bean attempts to override |
| // EJBObject/EJBLocalObject operations. |
| if (ejbIntfClazz.isAssignableFrom(originalIntf)) { |
| try { |
| Method m = ejbIntfClazz.getMethod(methodName, paramTypes); |
| // Attempt to override EJBObject/EJBLocalObject method. Print |
| // warning but don't treat it as a fatal error. At runtime, the |
| // EJBObject/EJBLocalObject method will be called. |
| String[] params = { m.toString(), invInfo.method.toString() }; |
| _logger.log(Level.WARNING, ILLEGAL_EJB_INTERFACE_OVERRIDE, params); |
| invInfo.ejbIntfOverride = true; |
| return; |
| } catch (NoSuchMethodException nsme) { |
| } |
| } |
| |
| try { |
| invInfo.targetMethod1 = ejbClass.getMethod(methodName, paramTypes); |
| |
| if (isSession && isStatefulSession) { |
| MethodDescriptor methodDesc = new MethodDescriptor(invInfo.targetMethod1, MethodDescriptor.EJB_BEAN); |
| |
| // Assign removal info to inv info. If this method is not |
| // an @Remove method, result will be null. |
| invInfo.removalInfo = ((EjbSessionDescriptor) ejbDescriptor).getRemovalInfo(methodDesc); |
| } |
| |
| } catch (NoSuchMethodException nsme) { |
| Object[] params = { logParams[0] + ":" + nsme.toString(), (isLocal ? "Local" : "Remote"), invInfo.method.toString() }; |
| _logger.log(Level.WARNING, BEAN_CLASS_METHOD_NOT_FOUND, params); |
| // Treat this as a warning instead of a fatal error. |
| // That matches the behavior of the generated code. |
| // Mark the target methods as null. If this method is |
| // invoked at runtime it will be result in an exception from |
| // the invocation handlers. |
| invInfo.targetMethod1 = null; |
| } |
| } |
| |
| //Overridden in StatefulContainerOnly |
| protected String[] getPre30LifecycleMethodNames() { |
| // null to match AroundConstruct |
| return new String[] { null, "ejbCreate", "ejbRemove", "ejbPassivate", "ejbActivate" }; |
| } |
| |
| private void initializeInterceptorManager() throws Exception { |
| this.interceptorManager = new InterceptorManager(_logger, this, lifecycleCallbackAnnotationClasses, getPre30LifecycleMethodNames()); |
| } |
| |
| /** |
| * Can be called after original interceptor initialization. |
| * Install the given interceptor class instance before any application level interceptors. |
| * |
| * @param interceptor optionally specified delegate to be set on SystemInterceptorProxy |
| */ |
| public void registerSystemInterceptor(Object interceptor) { |
| if (needSystemInterceptorProxy()) { |
| interceptorManager.registerRuntimeInterceptor(interceptor); |
| } |
| } |
| |
| private boolean needSystemInterceptorProxy() { |
| // TODO only really needed if JAX-RS needs to dynamically register an |
| // interceptor during web application init. Can optimize this out |
| // by checking for the existence of any JAX-RS resources in module. |
| // Only applies to stateless and singleton session beans. |
| return isSession && !isStatefulSession; |
| |
| } |
| |
| private void addSystemInterceptorProxy() { |
| |
| InterceptorDescriptor interceptorDesc = SystemInterceptorProxy.createInterceptorDesc(); |
| ejbDescriptor.addFrameworkInterceptor(interceptorDesc); |
| |
| } |
| |
| protected void addLocalRemoteInvocationInfo() throws Exception { |
| if (isRemote) { |
| |
| if (hasRemoteHomeView) { |
| // Process Remote intf |
| Method[] methods = remoteIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_REMOTE, remoteIntf); |
| } |
| |
| // Process EJBHome intf |
| methods = homeIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_HOME, homeIntf); |
| } |
| } |
| |
| if (hasRemoteBusinessView) { |
| |
| for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { |
| // Get methods from generated remote intf but pass |
| // actual business interface as original interface. |
| Method[] methods = next.generatedRemoteIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_REMOTE, next.remoteBusinessIntf); |
| } |
| } |
| |
| // Process internal EJB RemoteBusinessHome intf |
| Method[] methods = remoteBusinessHomeIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_HOME, remoteBusinessHomeIntf); |
| } |
| } |
| } |
| |
| if (isLocal) { |
| if (hasLocalHomeView) { |
| // Process Local interface |
| Method[] methods = localIntf.getMethods(); |
| for (Method method : methods) { |
| InvocationInfo info = addInvocationInfo(method, MethodDescriptor.EJB_LOCAL, localIntf); |
| postProcessInvocationInfo(info); |
| } |
| |
| // Process LocalHome interface |
| methods = localHomeIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_LOCALHOME, localHomeIntf); |
| } |
| } |
| |
| if (hasLocalBusinessView) { |
| |
| // Process Local Business interfaces |
| for (Class localBusinessIntf : localBusinessIntfs) { |
| Method[] methods = localBusinessIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_LOCAL, localBusinessIntf); |
| } |
| } |
| |
| // Process (internal) Local Business Home interface |
| Method[] methods = localBusinessHomeIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_LOCALHOME, localBusinessHomeIntf); |
| } |
| } |
| |
| if (hasOptionalLocalBusinessView) { |
| |
| // Process generated Optional Local Business interface |
| String optClassName = EJBUtils.getGeneratedOptionalInterfaceName(ejbClass.getName()); |
| ejbGeneratedOptionalLocalBusinessIntfClass = optIntfClassLoader.loadClass(optClassName); |
| Method[] methods = ejbGeneratedOptionalLocalBusinessIntfClass.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_LOCAL, ejbGeneratedOptionalLocalBusinessIntfClass, false, true); |
| } |
| |
| // Process generated Optional Local Business interface |
| Method[] optHomeMethods = ejbOptionalLocalBusinessHomeIntf.getMethods(); |
| for (Method method : optHomeMethods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_LOCALHOME, ejbOptionalLocalBusinessHomeIntf); |
| } |
| |
| } |
| |
| if (!hasLocalHomeView) { |
| // Add dummy local business interface remove method so that internal |
| // container remove operations will work. (needed for internal 299 contract) |
| addInvocationInfo(this.ejbIntfMethods[EJBLocalObject_remove], MethodDescriptor.EJB_LOCAL, jakarta.ejb.EJBLocalObject.class); |
| } |
| } |
| } |
| |
| private void addWSOrTimedObjectInvocationInfo() throws Exception { |
| |
| if (isWebServiceEndpoint) { |
| // Process Service Endpoint interface |
| Method[] methods = webServiceEndpointIntf.getMethods(); |
| for (Method method : methods) { |
| addInvocationInfo(method, MethodDescriptor.EJB_WEB_SERVICE, webServiceEndpointIntf); |
| } |
| } |
| |
| if (isTimedObject()) { |
| if (ejbTimeoutMethod != null) { |
| processTxAttrForScheduledTimeoutMethod(ejbTimeoutMethod); |
| } |
| |
| for (Map.Entry<Method, List<ScheduledTimerDescriptor>> entry : schedules.entrySet()) { |
| processTxAttrForScheduledTimeoutMethod(entry.getKey()); |
| } |
| } |
| } |
| |
| private void initializeInvocationInfo() throws Exception { |
| // Create a map implementation that is optimized |
| // for method lookups. This is especially important for local |
| // invocations through dynamic proxies, where the overhead of the |
| // the (method -> invocationInfo) lookup has been measured to be |
| // 6X greater than the overhead of the reflective call itself. |
| proxyInvocationInfoMap = new MethodMap(invocationInfoMap); |
| |
| // Store InvocationInfo by standard ejb interface method type |
| // to avoid an invocation info map lookup during authorizeLocalMethod |
| // and authorizeRemoteMethod. |
| ejbIntfMethodInfo = new InvocationInfo[EJB_INTF_METHODS_LENGTH]; |
| for (int i = 0; i < ejbIntfMethods.length; i++) { |
| Method m = ejbIntfMethods[i]; |
| ejbIntfMethodInfo[i] = (InvocationInfo) invocationInfoMap.get(m); |
| } |
| } |
| |
| /** |
| * Validate transaction attribute value. Allow subclasses to add their own validation. |
| */ |
| protected void validateTxAttr(MethodDescriptor md, int txAttr) throws EJBException { |
| } |
| |
| /** |
| * Verify transaction attribute on the timeout or schedule method and process this method if it's correct. |
| */ |
| private void processTxAttrForScheduledTimeoutMethod(Method m) { |
| int txAttr = containerTransactionManager.findTxAttr(new MethodDescriptor(m, MethodDescriptor.TIMER_METHOD)); |
| if (isBeanManagedTran || txAttr == TX_REQUIRED || txAttr == TX_REQUIRES_NEW || txAttr == TX_NOT_SUPPORTED) { |
| addInvocationInfo(m, MethodDescriptor.TIMER_METHOD, null, true); |
| } else { |
| throw new EJBException("Timeout method " + m + "must have TX attribute of " + "TX_REQUIRES_NEW or TX_REQUIRED or " |
| + "TX_NOT_SUPPORTED for ejb " + ejbDescriptor.getName()); |
| } |
| } |
| |
| // Check if the user has enabled flush at end of method flag |
| // This is only used during container initialization and set into |
| // the invocation info object. This method is over-riden in the |
| // EntityContainer. |
| protected boolean findFlushEnabledAttr(MethodDescriptor md) { |
| |
| //Get the flushMethodDescriptor and then find if flush has been |
| //enabled for this method |
| boolean flushEnabled = ejbDescriptor.getIASEjbExtraDescriptors().isFlushEnabledFor(md); |
| |
| return flushEnabled; |
| } |
| |
| // default impl |
| protected void addProxyInterfacesSetClass(Set proxyInterfacesSet, boolean local) { |
| // no-op |
| } |
| |
| // default impl |
| protected EJBHomeInvocationHandler getEJBHomeInvocationHandler(Class homeIntfClass) throws Exception { |
| return new EJBHomeInvocationHandler(ejbDescriptor, homeIntfClass); |
| } |
| |
| private EJBHomeImpl instantiateEJBHomeImpl() throws Exception { |
| EJBHomeInvocationHandler handler = getEJBHomeInvocationHandler(homeIntf); |
| handler.setMethodMap(proxyInvocationInfoMap); |
| |
| EJBHomeImpl homeImpl = handler; |
| |
| // Maintain insertion order |
| Set proxyInterfacesSet = new LinkedHashSet(); |
| |
| addProxyInterfacesSetClass(proxyInterfacesSet, false); |
| |
| proxyInterfacesSet.add(homeIntf); |
| |
| Class[] proxyInterfaces = (Class[]) proxyInterfacesSet.toArray(new Class[proxyInterfacesSet.size()]); |
| |
| try { |
| EJBHome ejbHomeProxy = (EJBHome) Proxy.newProxyInstance(loader, proxyInterfaces, handler); |
| handler.setProxy(ejbHomeProxy); |
| } catch (ClassCastException e) { |
| String msg = localStrings.getLocalString("ejb.basecontainer_invalid_home_interface", |
| "Home interface [{0}] is invalid since it does not extend jakarta.ejb.EJBHome.", homeIntf); |
| throw new IllegalArgumentException(msg, e); |
| } |
| |
| homeImpl.setContainer(this); |
| return homeImpl; |
| } |
| |
| private EJBHomeImpl instantiateEJBRemoteBusinessHomeImpl() throws Exception { |
| EJBHomeInvocationHandler handler = getEJBHomeInvocationHandler(remoteBusinessHomeIntf); |
| handler.setMethodMap(proxyInvocationInfoMap); |
| |
| EJBHomeImpl remoteBusinessHomeImpl = handler; |
| EJBHome ejbRemoteBusinessHomeProxy = (EJBHome) Proxy.newProxyInstance( |
| loader, new Class[] {remoteBusinessHomeIntf}, handler); |
| handler.setProxy(ejbRemoteBusinessHomeProxy); |
| |
| remoteBusinessHomeImpl.setContainer(this); |
| return remoteBusinessHomeImpl; |
| |
| } |
| |
| protected EjbInvocation createEjbInvocation() { |
| return invFactory.create(); |
| } |
| |
| protected EjbInvocation createEjbInvocation(Object ejb, ComponentContext context) { |
| return invFactory.create(ejb, context); |
| } |
| |
| // default impl |
| protected EJBLocalHomeInvocationHandler getEJBLocalHomeInvocationHandler(Class homeIntfClass) throws Exception { |
| return new EJBLocalHomeInvocationHandler(ejbDescriptor, homeIntfClass); |
| } |
| |
| private EJBLocalHomeImpl instantiateEJBLocalHomeImpl() throws Exception { |
| |
| // LocalHome impl |
| EJBLocalHomeInvocationHandler invHandler = getEJBLocalHomeInvocationHandler(localHomeIntf); |
| invHandler.setMethodMap(proxyInvocationInfoMap); |
| |
| EJBLocalHomeImpl homeImpl = invHandler; |
| |
| // Maintain insertion order |
| Set proxyInterfacesSet = new LinkedHashSet(); |
| |
| proxyInterfacesSet.add(IndirectlySerializable.class); |
| addProxyInterfacesSetClass(proxyInterfacesSet, true); |
| proxyInterfacesSet.add(localHomeIntf); |
| |
| Class[] proxyInterfaces = (Class[]) proxyInterfacesSet.toArray(new Class[proxyInterfacesSet.size()]); |
| |
| // Client's EJBLocalHome object |
| try { |
| EJBLocalHome proxy = (EJBLocalHome) Proxy.newProxyInstance(loader, proxyInterfaces, invHandler); |
| invHandler.setProxy(proxy); |
| } catch (ClassCastException e) { |
| String msg = localStrings.getLocalString("ejb.basecontainer_invalid_local_home_interface", |
| "Local home interface [{0}] is invalid since it does not extend jakarta.ejb.EJBLocalHome.", localHomeIntf); |
| throw new IllegalArgumentException(msg, e); |
| } |
| |
| homeImpl.setContainer(this); |
| return homeImpl; |
| } |
| |
| private EJBLocalHomeImpl instantiateEJBLocalBusinessHomeImpl() throws Exception { |
| |
| EJBLocalHomeInvocationHandler invHandler = getEJBLocalHomeInvocationHandler(localBusinessHomeIntf); |
| invHandler.setMethodMap(proxyInvocationInfoMap); |
| |
| EJBLocalHomeImpl homeImpl = invHandler; |
| |
| EJBLocalHome proxy = (EJBLocalHome) Proxy.newProxyInstance(loader, |
| new Class[] { IndirectlySerializable.class, localBusinessHomeIntf }, invHandler); |
| |
| invHandler.setProxy(proxy); |
| |
| homeImpl.setContainer(this); |
| |
| return homeImpl; |
| } |
| |
| private EJBLocalHomeImpl instantiateEJBOptionalLocalBusinessHomeImpl() throws Exception { |
| |
| EJBLocalHomeInvocationHandler invHandler = getEJBLocalHomeInvocationHandler(localBusinessHomeIntf); |
| invHandler.setMethodMap(proxyInvocationInfoMap); |
| |
| EJBLocalHomeImpl homeImpl = invHandler; |
| |
| EJBLocalHome proxy = (EJBLocalHome) Proxy.newProxyInstance(loader, |
| new Class[] { IndirectlySerializable.class, ejbOptionalLocalBusinessHomeIntf }, invHandler); |
| |
| invHandler.setProxy(proxy); |
| |
| homeImpl.setContainer(this); |
| |
| return homeImpl; |
| } |
| |
| protected EJBLocalObjectImpl instantiateEJBLocalObjectImpl() throws Exception { |
| return instantiateEJBLocalObjectImpl(null); |
| } |
| |
| protected EJBLocalObjectImpl instantiateEJBLocalObjectImpl(Object key) throws Exception { |
| EJBLocalObjectImpl localObjImpl = null; |
| EJBLocalObjectInvocationHandler handler = new EJBLocalObjectInvocationHandler(proxyInvocationInfoMap, localIntf); |
| localObjImpl = handler; |
| |
| try { |
| EJBLocalObject localObjectProxy = (EJBLocalObject) Proxy.newProxyInstance( |
| loader, new Class[] {IndirectlySerializable.class, localIntf}, handler); |
| handler.setProxy(localObjectProxy); |
| } catch (ClassCastException e) { |
| String msg = localStrings.getLocalString("ejb.basecontainer_invalid_local_interface", |
| "Local component interface [{0}] is invalid since it does not extend jakarta.ejb.EJBLocalObject.", localIntf); |
| throw new IllegalArgumentException(msg, e); |
| } |
| |
| localObjImpl.setContainer(this); |
| if (key != null) { |
| // associate the EJBObject with the key |
| localObjImpl.setKey(key); |
| } |
| |
| return localObjImpl; |
| } |
| |
| protected EJBLocalObjectImpl instantiateEJBLocalBusinessObjectImpl() throws Exception { |
| |
| EJBLocalObjectInvocationHandler handler = new EJBLocalObjectInvocationHandler(proxyInvocationInfoMap, false); |
| |
| EJBLocalObjectImpl localBusinessObjImpl = handler; |
| Proxy.newProxyInstance(loader, ejbLocalBusinessProxyInterfaces, handler); |
| localBusinessObjImpl.setContainer(this); |
| |
| for (Class businessIntfClass : localBusinessIntfs) { |
| EJBLocalObjectInvocationHandlerDelegate delegate = new EJBLocalObjectInvocationHandlerDelegate(businessIntfClass, |
| getContainerId(), handler); |
| Proxy proxy = (Proxy) Proxy.newProxyInstance(loader, new Class[] { IndirectlySerializable.class, businessIntfClass }, delegate); |
| localBusinessObjImpl.mapClientObject(businessIntfClass.getName(), proxy); |
| } |
| return localBusinessObjImpl; |
| } |
| |
| protected EJBLocalObjectImpl instantiateOptionalEJBLocalBusinessObjectImpl() throws Exception { |
| |
| EJBLocalObjectInvocationHandler handler = new EJBLocalObjectInvocationHandler(proxyInvocationInfoMap, true); |
| EJBLocalObjectImpl localBusinessObjImpl = handler; |
| Proxy.newProxyInstance(loader, ejbOptionalLocalBusinessHomeProxyInterfaces, handler); |
| localBusinessObjImpl.setContainer(this); |
| |
| Class businessIntfClass = ejbGeneratedOptionalLocalBusinessIntfClass; |
| EJBLocalObjectInvocationHandlerDelegate delegate = new EJBLocalObjectInvocationHandlerDelegate( |
| businessIntfClass, getContainerId(), handler); |
| Proxy proxy = (Proxy) Proxy.newProxyInstance( |
| loader, new Class[] {IndirectlySerializable.class, businessIntfClass}, delegate); |
| |
| String beanSubClassName = ejbGeneratedOptionalLocalBusinessIntfClass.getName() + "__Bean__"; |
| |
| optIntfClassLoader.generateOptionalLocalInterfaceSubClass(ejbClass, beanSubClassName, |
| ejbGeneratedOptionalLocalBusinessIntfClass); |
| |
| optIntfClassLoader.loadClass(ejbGeneratedOptionalLocalBusinessIntfClass.getName()); |
| |
| Class subClass = optIntfClassLoader.loadClass(beanSubClassName); |
| OptionalLocalInterfaceProvider provider = (OptionalLocalInterfaceProvider) subClass.getConstructor().newInstance(); |
| provider.setOptionalLocalIntfProxy(proxy); |
| localBusinessObjImpl.mapClientObject(ejbClass.getName(), provider); |
| |
| return localBusinessObjImpl; |
| } |
| |
| protected EJBObjectImpl instantiateEJBObjectImpl() throws Exception { |
| return instantiateEJBObjectImpl(null, null); |
| } |
| |
| protected EJBObjectImpl instantiateEJBObjectImpl(EJBObject ejbStub, Object key) throws Exception { |
| EJBObjectInvocationHandler handler = new EJBObjectInvocationHandler(proxyInvocationInfoMap, remoteIntf); |
| EJBObjectImpl ejbObjImpl = handler; |
| |
| try { |
| EJBObject ejbObjectProxy = (EJBObject) Proxy.newProxyInstance(loader, new Class[] {remoteIntf}, handler); |
| this.ejbObjectProxyClass = ejbObjectProxyClass == null ? ejbObjectProxy.getClass() : ejbObjectProxyClass; |
| handler.setEJBObject(ejbObjectProxy); |
| } catch (ClassCastException e) { |
| String msg = localStrings.getLocalString("ejb.basecontainer_invalid_remote_interface", |
| "Remote component interface [{0}] is invalid since it does not extend jakarta.ejb.EJBObject.", remoteIntf); |
| throw new IllegalArgumentException(msg, e); |
| } |
| |
| if (ejbStub != null) { |
| // associate the EJBObject with the stub |
| ejbObjImpl.setStub(ejbStub); |
| } |
| |
| if (key != null) { |
| // associate the EJBObject with the key |
| ejbObjImpl.setKey(key); |
| } |
| |
| ejbObjImpl.setContainer(this); |
| return ejbObjImpl; |
| } |
| |
| protected EJBObjectImpl instantiateRemoteBusinessObjectImpl() throws Exception { |
| |
| // There is one EJBObjectImpl instance, which is an instance of |
| // the handler. That handler instance is shared by the dynamic |
| // proxy for each remote business interface. We need to create a |
| // different proxy for each remote business interface because |
| // otherwise the target object given to the orb will be invalid |
| // if the same method happens to be declared on multiple remote |
| // business interfaces. |
| EJBObjectInvocationHandler handler = new EJBObjectInvocationHandler(proxyInvocationInfoMap); |
| |
| EJBObjectImpl ejbBusinessObjImpl = handler; |
| |
| for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { |
| |
| EJBObjectInvocationHandlerDelegate delegate = new EJBObjectInvocationHandlerDelegate(next.remoteBusinessIntf, handler); |
| Remote ejbBusinessObjectProxy = (Remote) Proxy.newProxyInstance( |
| loader, new Class[] {next.generatedRemoteIntf}, delegate); |
| next.proxyClass = ejbBusinessObjectProxy.getClass(); |
| ejbBusinessObjImpl.setEJBObject(next.generatedRemoteIntf.getName(), ejbBusinessObjectProxy); |
| } |
| |
| ejbBusinessObjImpl.setContainer(this); |
| |
| return ejbBusinessObjImpl; |
| } |
| |
| // default implementation |
| public boolean scanForEjbCreateMethod() { |
| return false; |
| } |
| |
| // default implementation |
| @Override |
| public void postCreate(EjbInvocation inv, Object primaryKey) throws CreateException { |
| throw new EJBException("Internal error"); |
| } |
| |
| // default implementation |
| @Override |
| public Object postFind(EjbInvocation inv, Object primaryKeys, Object[] findParams) throws FinderException { |
| throw new EJBException("Internal error"); |
| } |
| |
| private void setupEnvironment() throws javax.naming.NamingException { |
| // call the NamingManager to setup the java:comp/env namespace |
| // for this EJB. |
| |
| ComponentEnvManager envManager = ejbContainerUtilImpl.getComponentEnvManager(); |
| componentId = envManager.bindToComponentNamespace(ejbDescriptor); |
| invFactory = new EjbInvocationFactory(componentId, this); |
| ejbContainerUtilImpl.registerContainer(this); |
| // create envProps object to be returned from EJBContext.getEnvironment |
| Set env = ejbDescriptor.getEnvironmentProperties(); |
| SafeProperties safeProps = new SafeProperties(); |
| safeProps.copy(env); |
| envProps = safeProps; |
| } |
| |
| /** |
| * Called from NamingManagerImpl during java:comp/env lookup. |
| */ |
| @Override |
| public String getComponentId() { |
| return componentId; |
| } |
| |
| /** |
| * Called after all the components in the container's application have deployed successfully. |
| */ |
| @Override |
| public void startApplication(boolean deploy) { |
| _logger.log(Level.FINE, "Application deployment successful : " + this); |
| |
| // By now all existing timers should have been restored. |
| if (isTimedObject_) { |
| // EJBTimerService should be accessed only if needed |
| // not to cause it to be loaded if it's not used. |
| EJBTimerService timerService = EJBTimerService.getEJBTimerService(); |
| if (timerService != null) { |
| boolean deploy0 = deploy; //avoid modifying param |
| if (deploy0 && ejbDescriptor.getApplication().getKeepStateResolved()) { |
| deploy0 = false; |
| _logger.log(Level.INFO, KEEPSTATE_IS_TRUE); |
| } |
| scheduleIds = timerService.recoverAndCreateSchedules(getContainerId(), getApplicationId(), schedules, deploy0); |
| } else { |
| throw new RuntimeException("EJB Timer Service is not available"); |
| } |
| } |
| |
| setStartedState(); |
| } |
| |
| /** |
| * |
| */ |
| protected boolean callEJBTimeout(RuntimeTimerState timerState, EJBTimerService timerService) throws Exception { |
| |
| boolean redeliver = false; |
| |
| if (containerState != CONTAINER_STARTED) { |
| throw new EJBException("Attempt to invoke when container is in " + containerStateToString(containerState)); |
| } |
| |
| EjbInvocation inv = createEjbInvocation(); |
| |
| inv.isTimerCallback = true; |
| |
| // Let preInvoke do tx attribute lookup. |
| inv.transactionAttribute = Container.TX_NOT_INITIALIZED; |
| |
| inv.method = getTimeoutMethod(timerState); |
| inv.beanMethod = inv.method; |
| |
| ClassLoader originalClassLoader = null; |
| try { |
| prepareEjbTimeoutParams(inv, timerState, timerService); |
| |
| // Delegate to subclass for i.ejbObject / i.isLocal setup. |
| doTimerInvocationInit(inv, timerState.getTimedObjectPrimaryKey()); |
| |
| originalClassLoader = Utility.setContextClassLoader(loader); |
| |
| preInvoke(inv); |
| |
| // AroundTimeout interceptors will be checked for timeout methods |
| intercept(inv); |
| |
| if (!isBeanManagedTran && (transactionManager.getStatus() == Status.STATUS_MARKED_ROLLBACK)) { |
| redeliver = true; |
| _logger.log(Level.FINE, "ejbTimeout called setRollbackOnly"); |
| } |
| |
| } catch (InvocationTargetException ite) { |
| // A runtime exception thrown from ejbTimeout, independent of |
| // its transactional setting(CMT, BMT, etc.), should result in |
| // a redelivery attempt. The instance that threw the runtime |
| // exception will be destroyed, as per the EJB spec. |
| redeliver = true; |
| inv.exception = ite.getCause(); |
| _logger.log(Level.FINE, "ejbTimeout threw Runtime exception", inv.exception); |
| } catch (Throwable c) { |
| redeliver = true; |
| _logger.log(Level.FINE, "Exception while processing ejbTimeout", c); |
| inv.exception = c; |
| } finally { |
| |
| // Only call postEjbTimeout if there are no errors so far. |
| if (!redeliver) { |
| boolean success = postEjbTimeout(timerState, timerService); |
| redeliver = !success; |
| } |
| |
| postInvoke(inv); |
| |
| // If transaction commit fails, set redeliver flag. |
| if ((redeliver == false) && (inv.exception != null)) { |
| redeliver = true; |
| } |
| |
| if (originalClassLoader != null) { |
| Utility.setContextClassLoader(originalClassLoader); |
| } |
| |
| } |
| |
| return redeliver; |
| } |
| |
| protected Method getTimeoutMethod(RuntimeTimerState timerState) { |
| Method m = scheduleIds.get(timerState.getTimerId()); |
| return (m != null) ? m : ejbTimeoutMethod; |
| } |
| |
| protected boolean postEjbTimeout(RuntimeTimerState timerState, EJBTimerService timerService) { |
| return timerService.postEjbTimeout(timerState.getTimerId()); |
| } |
| |
| protected void prepareEjbTimeoutParams(EjbInvocation inv, RuntimeTimerState timerState, EJBTimerService timerService) { |
| // Create a TimerWrapper for AroundTimeout and as a method argument. |
| jakarta.ejb.Timer timer = new TimerWrapper(timerState.getTimerId(), timerService); |
| inv.timer = timer; |
| |
| if (inv.method.getParameterTypes().length == 1) { |
| Object[] args = { timer }; |
| inv.methodParams = args; |
| } else { |
| inv.methodParams = null; |
| } |
| } |
| |
| public final void onEnteringContainer() { |
| ejbProbeNotifier.ejbContainerEnteringEvent(getContainerId(), containerInfo.appName, containerInfo.modName, containerInfo.ejbName); |
| enteringEjbContainer(); |
| //callFlowAgent.startTime(ContainerTypeOrApplicationType.EJB_CONTAINER); |
| } |
| |
| public final void onLeavingContainer() { |
| ejbProbeNotifier.ejbContainerLeavingEvent(getContainerId(), containerInfo.appName, containerInfo.modName, containerInfo.ejbName); |
| leavingEjbContainer(); |
| //callFlowAgent.endTime(); |
| } |
| |
| private void enteringEjbContainer() { |
| if (interceptors == null) { |
| return; |
| } |
| for (EjbContainerInterceptor interceptor : interceptors) { |
| try { |
| interceptor.preInvoke(ejbDescriptor); |
| } catch (Throwable th) { |
| _logger.log(Level.SEVERE, INTERNAL_ERROR, th); |
| } |
| } |
| } |
| |
| private void leavingEjbContainer() { |
| if (interceptors == null) { |
| return; |
| } |
| for (EjbContainerInterceptor interceptor : interceptors) { |
| try { |
| interceptor.postInvoke(ejbDescriptor); |
| } catch (Throwable th) { |
| _logger.log(Level.SEVERE, INTERNAL_ERROR, th); |
| } |
| } |
| } |
| |
| private void initEjbInterceptors() { |
| try { |
| ServiceLocator services = ejbContainerUtilImpl.getServices(); |
| interceptors = services.getAllServices(EjbContainerInterceptor.class); |
| } catch (Throwable th) { |
| _logger.log(Level.SEVERE, FAILED_TO_INITIALIZE_INTERCEPTOR, th); |
| } |
| } |
| |
| final void onEjbMethodStart(int methodIndex) { |
| InvocationInfo info = ejbIntfMethodInfo[methodIndex]; |
| if (info != null) { |
| onEjbMethodStart(info.str_method_sig); |
| } |
| } |
| |
| final void onEjbMethodEnd(int methodIndex, Throwable th) { |
| InvocationInfo info = ejbIntfMethodInfo[methodIndex]; |
| if (info != null) { |
| onEjbMethodEnd(info.str_method_sig, th); |
| } |
| } |
| |
| final void onEjbMethodStart(String method_sig) { |
| ejbProbeNotifier.ejbMethodStartEvent(getContainerId(), callFlowInfo.getApplicationName(), callFlowInfo.getModuleName(), |
| callFlowInfo.getComponentName(), method_sig); |
| //callFlowAgent.ejbMethodStart(callFlowInfo); |
| } |
| |
| final void onEjbMethodEnd(String method_sig, Throwable th) { |
| ejbProbeNotifier.ejbMethodEndEvent(getContainerId(), callFlowInfo.getApplicationName(), callFlowInfo.getModuleName(), |
| callFlowInfo.getComponentName(), th, method_sig); |
| //callFlowAgent.ejbMethodEnd(callFlowInfo); |
| } |
| |
| protected Object invokeTargetBeanMethod(Method beanClassMethod, EjbInvocation inv, Object target, Object[] params, |
| com.sun.enterprise.security.SecurityManager mgr) throws Throwable { |
| try { |
| onEjbMethodStart(inv.invocationInfo.str_method_sig); |
| if (inv.useFastPath) { |
| return inv.getBeanMethod().invoke(inv.ejb, inv.methodParams); |
| } else { |
| |
| return securityManager.invoke(beanClassMethod, inv.isLocal, target, params); |
| } |
| } catch (InvocationTargetException ite) { |
| inv.exception = ite.getCause(); |
| throw ite; |
| } catch (Throwable c) { |
| inv.exception = c; |
| throw c; |
| } finally { |
| onEjbMethodEnd(inv.invocationInfo.str_method_sig, inv.exception); |
| } |
| } |
| |
| /** |
| * This is implemented by concrete containers that support TimedObjects. |
| */ |
| protected void doTimerInvocationInit(EjbInvocation inv, Object primaryKey) throws Exception { |
| throw new EJBException("This container doesn't support TimedObjects"); |
| } |
| |
| /** |
| * Undeploy event. Code must be able to gracefully handle redundant undeploy/shutdown calls for the same container instance. |
| * |
| */ |
| @Override |
| public final void undeploy() { |
| |
| try { |
| |
| if (!isUndeployed()) { |
| |
| setUndeployedState(); |
| |
| try { |
| stopTimers(); |
| } catch (Exception e) { |
| _logger.log(Level.FINE, "Error destroying timers for " + ejbDescriptor.getName(), e); |
| } |
| |
| // Shutdown with undeploy |
| doConcreteContainerShutdown(true); |
| |
| // BaseContainer cleanup |
| doContainerCleanup(); |
| } |
| } catch (Throwable t) { |
| // Make sure we don't propagate an exception since that could |
| // prevent the cleanup of some other component. |
| _logger.log(Level.FINE, "BsaeContainer::undeploy exception", t); |
| } |
| |
| } |
| |
| /** |
| * Container shutdown event. This happens for every kind of shutdown other than undeploy. It could mean the server is shutting |
| * down or that the app has been disabled while the server is still running. The two cases are handled the same. We must be able |
| * to gracefully handle redundant shutdown calls for the same container instance. |
| */ |
| @Override |
| public final void onShutdown() { |
| |
| try { |
| if (!isStopped()) { |
| |
| setStoppedState(); |
| |
| try { |
| stopTimers(); |
| } catch (Exception e) { |
| _logger.log(Level.FINE, "Error stopping timers for " + ejbDescriptor.getName(), e); |
| } |
| // Cleanup without undeploy |
| doConcreteContainerShutdown(false); |
| |
| // BaseContainer cleanup |
| doContainerCleanup(); |
| } |
| } catch (Throwable t) { |
| // Make sure we don't propagate an exception since that could |
| // prevent the cleanup of some other component. |
| _logger.log(Level.FINE, "BsaeContainer::onShutdown exception", t); |
| } |
| } |
| |
| // Concrete container shutdown actions |
| protected abstract void doConcreteContainerShutdown(boolean appBeingUndeployed); |
| |
| /** |
| * Perform common container shutdown actions. NOTE that this should be done defensively so that we attempt to do as much cleanup |
| * as possible, even in the face of errors. This might be called after an unsuccessful deployment, in which case some of the |
| * services might not have been initialized. |
| */ |
| |
| private void doContainerCleanup() { |
| |
| if (baseContainerCleanupDone) { |
| return; |
| } |
| |
| try { |
| if (isWebServiceEndpoint && (webServiceEndpoint != null)) { |
| String endpointAddress = webServiceEndpoint.getEndpointAddressUri(); |
| if (wsejbEndpointRegistry != null) { |
| wsejbEndpointRegistry.unregisterEndpoint(endpointAddress); |
| } |
| } |
| |
| // NOTE : Pipe cleanup that used to done here is now encapsulated within |
| // endpoint registry unregisterEndpoint operation |
| |
| } catch (Exception e) { |
| _logger.log(Level.FINE, "Error unregistering ejb endpoint for " + ejbDescriptor.getName(), e); |
| } |
| |
| if (hasAsynchronousInvocations) { |
| EjbAsyncInvocationManager asyncManager = ((EjbContainerUtilImpl) ejbContainerUtilImpl).getEjbAsyncInvocationManager(); |
| asyncManager.cleanupContainerTasks(this); |
| } |
| |
| final Thread currentThread = Thread.currentThread(); |
| final ClassLoader previousClassLoader = currentThread.getContextClassLoader(); |
| |
| // Unpublish all portable and non-portable JNDI names |
| for (Map.Entry<String, JndiInfo> entry : jndiInfoMap.entrySet()) { |
| JndiInfo jndiInfo = entry.getValue(); |
| |
| try { |
| jndiInfo.unpublish(this.namingManager); |
| } catch (Exception e) { |
| _logger.log(Level.FINE, "Error while unbinding JNDI name " + jndiInfo.name + " for EJB : " + this.ejbDescriptor.getName(), |
| e); |
| } |
| } |
| |
| try { |
| if (System.getSecurityManager() == null) { |
| currentThread.setContextClassLoader(loader); |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { |
| @Override |
| public java.lang.Object run() { |
| currentThread.setContextClassLoader(loader); |
| return null; |
| } |
| }); |
| } |
| |
| if (isRemote) { |
| try { |
| |
| if (hasRemoteHomeView) { |
| |
| remoteHomeRefFactory.destroyReference(ejbHomeStub, ejbHome); |
| |
| // Hints to release stub-related meta-data in ORB |
| remoteHomeRefFactory.cleanupClass(homeIntf); |
| remoteHomeRefFactory.cleanupClass(remoteIntf); |
| remoteHomeRefFactory.cleanupClass(ejbHome.getClass()); |
| if (ejbObjectProxyClass != null) { |
| remoteHomeRefFactory.cleanupClass(ejbObjectProxyClass); |
| } |
| |
| // destroy the factory itself |
| remoteHomeRefFactory.destroy(); |
| } |
| |
| if (hasRemoteBusinessView) { |
| |
| // Home related cleanup |
| RemoteReferenceFactory remoteBusinessRefFactory = remoteBusinessIntfInfo.values().iterator() |
| .next().referenceFactory; |
| remoteBusinessRefFactory.destroyReference(ejbRemoteBusinessHomeStub, ejbRemoteBusinessHome); |
| |
| remoteBusinessRefFactory.cleanupClass(remoteBusinessHomeIntf); |
| remoteBusinessRefFactory.cleanupClass(ejbRemoteBusinessHome.getClass()); |
| |
| // Cleanup for each remote business interface |
| for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { |
| |
| next.referenceFactory.cleanupClass(next.generatedRemoteIntf); |
| |
| if (next.proxyClass != null) { |
| next.referenceFactory.cleanupClass(next.proxyClass); |
| } |
| |
| // destroy the factory itself |
| next.referenceFactory.destroy(); |
| } |
| |
| } |
| |
| } catch (Exception ex) { |
| _logger.log(Level.FINE, "Exception during undeploy", logParams); |
| _logger.log(Level.FINE, "", ex); |
| } |
| } |
| |
| try { |
| ejbContainerUtilImpl.getComponentEnvManager().unbindFromComponentNamespace(ejbDescriptor); |
| } catch (javax.naming.NamingException namEx) { |
| _logger.log(Level.FINE, "Exception during undeploy", logParams); |
| _logger.log(Level.FINE, "", namEx); |
| } |
| |
| ejbContainerUtilImpl.unregisterContainer(this); |
| |
| unregisterProbeListeners(); |
| |
| } finally { |
| if (System.getSecurityManager() == null) { |
| currentThread.setContextClassLoader(previousClassLoader); |
| } else { |
| java.security.AccessController.doPrivileged(new java.security.PrivilegedAction() { |
| @Override |
| public java.lang.Object run() { |
| currentThread.setContextClassLoader(previousClassLoader); |
| return null; |
| } |
| }); |
| } |
| } |
| |
| baseContainerCleanupDone = true; |
| |
| _logger.log(Level.FINE, "**** [BaseContainer]: Successfully Undeployed " + ejbDescriptor.getName() + " ..."); |
| |
| } |
| |
| private void unregisterProbeListeners() { |
| try { |
| ejbProbeListener.unregister(); |
| ProbeProviderFactory probeFactory = ejbContainerUtilImpl.getProbeProviderFactory(); |
| probeFactory.unregisterProbeProvider(ejbProbeNotifier); |
| |
| if (timerProbeListener != null) { |
| timerProbeListener.unregister(); |
| probeFactory.unregisterProbeProvider(timerProbeNotifier); |
| } |
| if (poolProbeListener != null) { |
| poolProbeListener.unregister(); |
| } |
| if (cacheProbeListener != null) { |
| cacheProbeListener.unregister(); |
| if (cacheProbeNotifier != null) { |
| probeFactory.unregisterProbeProvider(cacheProbeNotifier); |
| } |
| } |
| } catch (Exception ex) { |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Error unregistering the ProbeProvider"); |
| } |
| } |
| } |
| |
| /** |
| * Called when server instance is Ready |
| */ |
| @Override |
| public void onReady() { |
| } |
| |
| /** |
| * Called when server instance is terminating. This method is the last one called during server shutdown. |
| */ |
| @Override |
| public void onTermination() { |
| } |
| |
| /*************************************************************************** |
| * The following methods implement transaction management machinery in a reusable way for both SessionBeans and EntityBeans |
| **************************************************************************/ |
| |
| /** |
| * This is called from preInvoke before every method invocation on the EJB instance, including ejbCreate, ejbFind*, ejbRemove. |
| * Also called from MessageBeanContainer, WebServiceInvocationHandler, etc, so we can't assume that |
| * BaseContainer.preInvoke(EjbInvocation) has run. Therefore, handle inv.invocationInfo defensively since it might not have been |
| * initialized. |
| */ |
| protected final void preInvokeTx(EjbInvocation inv) throws Exception { |
| if (inv.invocationInfo == null) { |
| |
| inv.invocationInfo = getInvocationInfo(inv); |
| |
| if (inv.invocationInfo == null) { |
| throw new EJBException("EjbInvocation Info lookup failed for " + "method " + inv.method); |
| } else { |
| inv.transactionAttribute = inv.invocationInfo.txAttr; |
| } |
| } |
| |
| containerTransactionManager.preInvokeTx(inv); |
| } |
| |
| // Called before invoking a bean with no Tx or with a new Tx. |
| // Check if the bean is associated with an unfinished tx. |
| protected void checkUnfinishedTx(Transaction prevTx, EjbInvocation inv) { |
| } |
| |
| // Called from preInvokeTx to check if transaction needs to be suspended |
| protected boolean suspendTransaction(EjbInvocation inv) throws Exception { |
| // Overridden in subclass that needs it |
| return false; |
| } |
| |
| // Called from postInvokeTx if transaction needs to be resumed |
| protected boolean resumeTransaction(EjbInvocation inv) throws Exception { |
| // Overridden in subclass that needs it |
| return false; |
| } |
| |
| // Called from preInvokeTx before invoking the bean with the client's Tx |
| // Also called from EntityContainer.removeBean for cascaded deletes |
| protected void useClientTx(Transaction prevTx, EjbInvocation inv) { |
| containerTransactionManager.useClientTx(prevTx, inv); |
| } |
| |
| protected void validateEMForClientTx(EjbInvocation inv, JavaEETransaction t) { |
| // Do nothing in general case |
| } |
| |
| /** |
| * postInvokeTx is called after every invocation on the EJB instance, including ejbCreate/ejbFind---/ejbRemove. NOTE: |
| * postInvokeTx is called even if the EJB was not invoked because of an exception thrown from preInvokeTx. |
| */ |
| protected void postInvokeTx(EjbInvocation inv) throws Exception { |
| |
| containerTransactionManager.postInvokeTx(inv); |
| } |
| |
| // this is the counterpart of useClientTx |
| // Called from postInvokeTx after invoking the bean with the client's Tx |
| // Also called from EntityContainer.removeBean for cascaded deletes |
| protected Throwable checkExceptionClientTx(EJBContextImpl context, Throwable exception) throws Exception { |
| return containerTransactionManager.checkExceptionClientTx(context, exception); |
| } |
| |
| // Implementation of Container method. |
| // Called from UserTransactionImpl after the EJB started a Tx, |
| // for TX_BEAN_MANAGED EJBs only. |
| @Override |
| public final void doAfterBegin(ComponentInvocation ci) { |
| EjbInvocation inv = (EjbInvocation) ci; |
| try { |
| // Associate the context with tx so that on subsequent |
| // invocations with the same tx, we can do the appropriate |
| // tx.resume etc. |
| EJBContextImpl sc = (EJBContextImpl) inv.context; |
| Transaction tx = transactionManager.getTransaction(); |
| if (!isSingleton) { |
| sc.setTransaction(tx); |
| } |
| |
| // Register Synchronization with TM so that we can |
| // dissociate the context from tx in afterCompletion |
| ejbContainerUtilImpl.getContainerSync(tx).addBean(sc); |
| |
| enlistExtendedEntityManagers(sc); |
| // Dont call container.afterBegin() because |
| // TX_BEAN_MANAGED EntityBeans are not allowed, |
| // and SessionSync calls on TX_BEAN_MANAGED SessionBeans |
| // are not allowed. |
| } catch (SystemException ex) { |
| throw new EJBException(ex); |
| } catch (RollbackException ex) { |
| throw new EJBException(ex); |
| } catch (IllegalStateException ex) { |
| throw new EJBException(ex); |
| } |
| } |
| |
| // internal APIs, called from ContainerSync, implemented in subclasses |
| protected abstract void afterBegin(EJBContextImpl context); |
| |
| protected abstract void beforeCompletion(EJBContextImpl context); |
| |
| protected abstract void afterCompletion(EJBContextImpl context, int status); |
| |
| protected void preInvokeNoTx(EjbInvocation inv) { |
| // No-op by default |
| } |
| |
| protected void postInvokeNoTx(EjbInvocation inv) { |
| // No-op by default |
| } |
| |
| protected boolean isApplicationException(Throwable exception) { |
| return !isSystemUncheckedException(exception); |
| } |
| |
| protected boolean isSystemUncheckedException(Throwable exception) { |
| if (exception != null |
| && (exception instanceof RuntimeException || exception instanceof Error || exception instanceof RemoteException)) { |
| |
| Class clazz = exception.getClass(); |
| String exceptionClassName = clazz.getName(); |
| Map<String, EjbApplicationExceptionInfo> appExceptions = ejbDescriptor.getEjbBundleDescriptor().getApplicationExceptions(); |
| while (clazz != null) { |
| String eClassName = clazz.getName(); |
| if (appExceptions.containsKey(eClassName)) { |
| if (exceptionClassName.equals(eClassName)) { |
| // Exact exception is specified as an ApplicationException |
| return false; |
| } else { |
| // Superclass exception is not inherited |
| return !appExceptions.get(eClassName).getInherited(); |
| } |
| } |
| clazz = clazz.getSuperclass(); |
| } |
| |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| public boolean getDebugMonitorFlag() { |
| return debugMonitorFlag; |
| } |
| |
| public void setDebugMonitorFlag(boolean flag) { |
| debugMonitorFlag = flag; |
| } |
| |
| protected static final String containerStateToString(int state) { |
| switch (state) { |
| case CONTAINER_INITIALIZING: |
| return "Initializing"; |
| case CONTAINER_STARTED: |
| return "Started"; |
| case CONTAINER_STOPPED: |
| return "STOPPED"; |
| case CONTAINER_UNDEPLOYED: |
| return "Undeployed"; |
| case CONTAINER_ON_HOLD: |
| return "ON_HOLD"; |
| } |
| return "Unknown Container state: " + state; |
| } |
| |
| protected final boolean isRemoteInterfaceSupported() { |
| return hasRemoteHomeView; |
| } |
| |
| protected final boolean isLocalInterfaceSupported() { |
| return hasLocalHomeView; |
| } |
| |
| protected int getTxAttrForLifecycleCallback(Set<LifecycleCallbackDescriptor> lifecycleCallbackDescriptors, int defaultTxAttr, |
| int... validateTxAttr) throws Exception { |
| int txAttr = isBeanManagedTran ? Container.TX_BEAN_MANAGED : defaultTxAttr; |
| |
| if (!isBeanManagedTran) { |
| for (LifecycleCallbackDescriptor lcd : lifecycleCallbackDescriptors) { |
| if (lcd.getLifecycleCallbackClass().equals(ejbDescriptor.getEjbClassName())) { |
| |
| Method callbackMethod = lcd.getLifecycleCallbackMethodObject(loader); |
| int lcTxAttr = containerTransactionManager |
| .findTxAttr(new MethodDescriptor(callbackMethod, MethodDescriptor.LIFECYCLE_CALLBACK)); |
| // Since default attribute is set up, override the value if it's validateTxAttr |
| for (int t : validateTxAttr) { |
| if (lcTxAttr == t) { |
| txAttr = t; |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, |
| "Found callback method " + ejbDescriptor.getEjbClassName() + "<>" + callbackMethod + " : " + txAttr); |
| } |
| break; |
| } |
| } |
| break; |
| } |
| } |
| } |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Returning attr for " + ejbDescriptor.getEjbClassName() + " : " + txAttr); |
| } |
| |
| return txAttr; |
| } |
| |
| /** |
| * Called from various places within the container that are responsible for dispatching invocations to business methods. This |
| * method has the exception semantics of Method.invoke(). Any exception that originated from the business method or application |
| * code within an interceptor will be propagated as the cause within an InvocationTargetException. |
| * |
| */ |
| protected Object intercept(EjbInvocation inv) throws Throwable { |
| Object result = null; |
| if (inv.mustInvokeAsynchronously()) { |
| EjbAsyncInvocationManager asyncManager = ((EjbContainerUtilImpl) ejbContainerUtilImpl).getEjbAsyncInvocationManager(); |
| Future future = inv.isLocal ? asyncManager.createLocalFuture(inv) |
| : asyncManager.createRemoteFuture(inv, this, (GenericEJBHome) ejbRemoteBusinessHomeStub); |
| result = (inv.invocationInfo.method.getReturnType() == void.class) ? null : future; |
| } else { |
| result = __intercept(inv); |
| } |
| return result; |
| } |
| |
| private Object __intercept(EjbInvocation inv) throws Throwable { |
| Object result = null; |
| if (interceptorManager.hasInterceptors()) { |
| try { |
| onEjbMethodStart(inv.invocationInfo.str_method_sig); |
| result = interceptorManager.intercept(inv.getInterceptorChain(), inv); |
| } catch (Throwable t) { |
| inv.exception = t; |
| throw new InvocationTargetException(t); |
| } finally { |
| onEjbMethodEnd(inv.invocationInfo.str_method_sig, inv.exception); |
| } |
| } else { // invoke() has the same exc. semantics as Method.invoke |
| result = this.invokeTargetBeanMethod(inv.getBeanMethod(), inv, inv.ejb, inv.methodParams, null); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Called from Interceptor Chain to invoke the actual bean method. |
| * |
| * <p> |
| * This method must throw any exception from the bean method *as |
| * is*, without being wrapped in an InvocationTargetException. The exception thrown from this method will be propagated through |
| * the application's interceptor code, so it must not be changed in order for any exception handling logic in that code to |
| * function properly. |
| */ |
| public Object invokeBeanMethod(EjbInvocation invocation) throws Throwable { |
| try { |
| return securityManager.invoke(invocation.getBeanMethod(), invocation.isLocal, invocation.ejb, invocation.getParameters()); |
| } catch (InvocationTargetException ite) { |
| throw ite.getCause(); |
| } |
| } |
| |
| protected abstract EjbMonitoringStatsProvider getMonitoringStatsProvider(String appName, String modName, String ejbName); |
| |
| protected void createMonitoringRegistry() { |
| String appName = null; |
| String modName = null; |
| String ejbName = null; |
| boolean isMonitorRegistryMediatorCreated = false; |
| try { |
| appName = (ejbDescriptor.getApplication().isVirtual()) ? null : ejbDescriptor.getApplication().getRegistrationName(); |
| if (appName == null) { |
| modName = ejbDescriptor.getApplication().getRegistrationName(); |
| } else { |
| String archiveuri = ejbDescriptor.getEjbBundleDescriptor().getModuleDescriptor().getArchiveUri(); |
| modName = com.sun.enterprise.util.io.FileUtils.makeFriendlyFilename(archiveuri); |
| } |
| ejbName = ejbDescriptor.getName(); |
| containerInfo = new ContainerInfo(appName, modName, ejbName); |
| |
| isMonitorRegistryMediatorCreated = true; |
| registerEjbMonitoringProbeProvider(appName, modName, ejbName); |
| ejbProbeListener = getMonitoringStatsProvider(appName, modName, ejbName); |
| ejbProbeListener.addMethods(getContainerId(), appName, modName, ejbName, getMonitoringMethodsArray()); |
| ejbProbeListener.register(); |
| |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, |
| "Created MonitoringRegistry: " + EjbMonitoringUtils.getDetailedLoggingName(appName, modName, ejbName)); |
| } |
| } catch (Exception ex) { |
| _logger.log(Level.SEVERE, COULD_NOT_CREATE_MONITORREGISTRYMEDIATOR, |
| new Object[] { EjbMonitoringUtils.getDetailedLoggingName(appName, modName, ejbName), ex }); |
| if (!isMonitorRegistryMediatorCreated) { |
| registerEjbMonitoringProbeProvider(appName, modName, ejbName); |
| } |
| } |
| } |
| |
| private void registerEjbMonitoringProbeProvider(String appName, String modName, String ejbName) { |
| // Always create to avoid NPE |
| try { |
| ProbeProviderFactory probeFactory = ejbContainerUtilImpl.getProbeProviderFactory(); |
| String invokerId = EjbMonitoringUtils.getInvokerId(appName, modName, ejbName); |
| ejbProbeNotifier = probeFactory.getProbeProvider(EjbMonitoringProbeProvider.class, invokerId); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Got ProbeProvider: " + ejbProbeNotifier.getClass().getName()); |
| } |
| } catch (Exception ex) { |
| ejbProbeNotifier = new EjbMonitoringProbeProvider(); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Error getting the EjbMonitoringProbeProvider"); |
| } |
| } |
| |
| } |
| |
| protected String[] getMonitoringMethodsArray() { |
| return getMonitoringMethodsArray((monitoredGeneratedClasses.size() > 0)); |
| } |
| |
| protected String[] getMonitoringMethodsArray(boolean hasGeneratedClasses) { |
| String[] method_sigs = null; |
| if (hasGeneratedClasses) { |
| List<String> methodList = new ArrayList<>(); |
| for (Class clz : monitoredGeneratedClasses) { |
| for (Method m : clz.getDeclaredMethods()) { |
| methodList.add(EjbMonitoringUtils.stringify(m)); |
| } |
| } |
| method_sigs = methodList.toArray(new String[methodList.size()]); |
| } else { |
| Vector methodVec = ejbDescriptor.getMethods(); |
| int sz = methodVec.size(); |
| method_sigs = new String[sz]; |
| for (int i = 0; i < sz; i++) { |
| method_sigs[i] = EjbMonitoringUtils.stringify((Method) methodVec.get(i)); |
| } |
| } |
| |
| return method_sigs; |
| } |
| |
| protected void doFlush(EjbInvocation inv) { |
| } |
| |
| protected void registerMonitorableComponents() { |
| createMonitoringRegistry(); |
| registerTimerMonitorableComponent(); |
| } |
| |
| protected void registerTimerMonitorableComponent() { |
| if (isTimedObject()) { |
| String invokerId = EjbMonitoringUtils.getInvokerId(containerInfo.appName, containerInfo.modName, containerInfo.ejbName); |
| try { |
| ProbeProviderFactory probeFactory = ejbContainerUtilImpl.getProbeProviderFactory(); |
| timerProbeNotifier = probeFactory.getProbeProvider(EjbTimedObjectProbeProvider.class, invokerId); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Got TimerProbeProvider: " + timerProbeNotifier.getClass().getName()); |
| } |
| } catch (Exception ex) { |
| timerProbeNotifier = new EjbTimedObjectProbeProvider(); |
| if (_logger.isLoggable(Level.FINE)) { |
| _logger.log(Level.FINE, "Error getting the TimerProbeProvider"); |
| } |
| } |
| timerProbeListener = new EjbTimedObjectStatsProvider(containerInfo.appName, containerInfo.modName, containerInfo.ejbName); |
| timerProbeListener.register(); |
| } |
| _logger.log(Level.FINE, "[BaseContainer] registered timer monitorable"); |
| } |
| |
| protected void incrementCreatedTimedObject() { |
| timerProbeNotifier.ejbTimerCreatedEvent(); |
| } |
| |
| protected void incrementRemovedTimedObject() { |
| timerProbeNotifier.ejbTimerRemovedEvent(); |
| } |
| |
| protected void incrementDeliveredTimedObject() { |
| timerProbeNotifier.ejbTimerDeliveredEvent(); |
| } |
| |
| private static class JndiInfo { |
| |
| private JndiInfo(String name, Object object) { |
| this.name = name; |
| this.object = object; |
| } |
| |
| static JndiInfo newPortableLocal(String name, Object obj) { |
| JndiInfo jndiInfo = new JndiInfo(name, obj); |
| jndiInfo.portable = true; |
| return jndiInfo; |
| } |
| |
| static JndiInfo newPortableRemote(String name, Object obj) { |
| JndiInfo jndiInfo = new JndiInfo(name, obj); |
| jndiInfo.portable = true; |
| jndiInfo.cosNaming = isCosNamingObject(obj); |
| return jndiInfo; |
| } |
| |
| static JndiInfo newNonPortableRemote(String name, Object obj) { |
| JndiInfo jndiInfo = new JndiInfo(name, obj); |
| jndiInfo.portable = false; |
| jndiInfo.cosNaming = isCosNamingObject(obj); |
| return jndiInfo; |
| } |
| |
| void publish(GlassfishNamingManager nm) throws NamingException { |
| |
| // If it's a portable name, use rebind since the name is guaranteed |
| // to be unique. Otherwise, use bind() so we detect any clashes. |
| // NOTE : Will need to revisit this if we allow a developer-specified |
| // portable JNDI name. |
| boolean rebind = portable; |
| |
| if (cosNaming) { |
| nm.publishCosNamingObject(name, object, rebind); |
| } else { |
| nm.publishObject(name, object, rebind); |
| } |
| |
| publishedSuccessfully = true; |
| |
| } |
| |
| void unpublish(GlassfishNamingManager nm) throws NamingException { |
| |
| if (publishedSuccessfully) { |
| if (cosNaming) { |
| nm.unpublishCosNamingObject(name); |
| } else { |
| nm.unpublishObject(name); |
| } |
| } else { |
| _logger.log(Level.FINE, |
| "Skipping unpublish of " + name + " because it was " + "never published successfully in the first place"); |
| } |
| } |
| |
| public void setInternal(boolean flag) { |
| internal = flag; |
| |
| } |
| |
| private static boolean isCosNamingObject(Object obj) { |
| return ((obj instanceof java.rmi.Remote) || (obj instanceof org.omg.CORBA.Object)); |
| } |
| |
| String name; |
| Object object; |
| boolean cosNaming; |
| boolean portable; |
| boolean internal; |
| boolean publishedSuccessfully; |
| |
| } |
| |
| /** |
| * PreInvokeException is used to wrap exceptions thrown from BaseContainer.preInvoke, so it indicates that the bean's method will |
| * not be called. |
| */ |
| public final static class PreInvokeException extends EJBException { |
| |
| Exception exception; |
| |
| public PreInvokeException(Exception ex) { |
| this.exception = ex; |
| } |
| } //PreInvokeException{} |
| |
| /** |
| * Strings for monitoring info |
| */ |
| public static final class ContainerInfo { |
| public String appName; |
| public String modName; |
| public String ejbName; |
| |
| ContainerInfo(String appName, String modName, String ejbName) { |
| this.appName = appName; |
| this.modName = modName; |
| this.ejbName = ejbName; |
| } |
| } //ContainerInfo |
| |
| private static class BeanContext { |
| ClassLoader previousClassLoader; |
| boolean classLoaderSwitched; |
| } |
| } //BaseContainer{} |
| |
| final class CallFlowInfoImpl implements CallFlowInfo { |
| |
| private final BaseContainer container; |
| |
| private final EjbDescriptor ejbDescriptor; |
| |
| private final String appName; |
| |
| private final String modName; |
| |
| private final String ejbName; |
| |
| private final ComponentType componentType; |
| |
| CallFlowInfoImpl(BaseContainer container, EjbDescriptor descriptor, ComponentType compType) { |
| this.container = container; |
| this.ejbDescriptor = descriptor; |
| |
| this.appName = (ejbDescriptor.getApplication().isVirtual()) ? null : ejbDescriptor.getApplication().getRegistrationName(); |
| String archiveuri = ejbDescriptor.getEjbBundleDescriptor().getModuleDescriptor().getArchiveUri(); |
| this.modName = com.sun.enterprise.util.io.FileUtils.makeFriendlyFilename(archiveuri); |
| this.ejbName = ejbDescriptor.getName(); |
| |
| this.componentType = compType; |
| } |
| |
| @Override |
| public String getApplicationName() { |
| return appName; |
| } |
| |
| @Override |
| public String getModuleName() { |
| return modName; |
| } |
| |
| @Override |
| public String getComponentName() { |
| return ejbName; |
| } |
| |
| @Override |
| public ComponentType getComponentType() { |
| return componentType; |
| } |
| |
| @Override |
| public java.lang.reflect.Method getMethod() { |
| EjbInvocation inv = (EjbInvocation) EjbContainerUtilImpl.getInstance().getCurrentInvocation(); |
| |
| return inv.method; |
| } |
| |
| @Override |
| public String getTransactionId() { |
| JavaEETransaction tx = null; |
| try { |
| tx = (JavaEETransaction) EjbContainerUtilImpl.getInstance().getTransactionManager().getTransaction(); |
| } catch (Exception ex) { |
| //TODO: Log exception |
| } |
| |
| return (tx == null) ? null : "" + tx; //TODO tx.getTransactionId(); |
| } |
| |
| @Override |
| public String getCallerPrincipal() { |
| java.security.Principal principal = container.getSecurityManager().getCallerPrincipal(); |
| |
| return (principal != null) ? principal.getName() : null; |
| } |
| |
| @Override |
| public Throwable getException() { |
| return ((EjbInvocation) EjbContainerUtilImpl.getInstance().getCurrentInvocation()).exception; |
| } |
| } |
| |
| final class RemoteBusinessIntfInfo { |
| Class generatedRemoteIntf; |
| Class remoteBusinessIntf; |
| String jndiName; |
| RemoteReferenceFactory referenceFactory; |
| Class proxyClass; |
| } |
| |
| final class SafeProperties extends Properties { |
| private static final String errstr = "Environment properties cannot be modified"; |
| private static final String ejb10Prefix = "ejb10-properties/"; |
| |
| @Override |
| public void load(java.io.InputStream inStream) { |
| throw new RuntimeException(errstr); |
| } |
| |
| @Override |
| public Object put(Object key, Object value) { |
| throw new RuntimeException(errstr); |
| } |
| |
| @Override |
| public void putAll(Map t) { |
| throw new RuntimeException(errstr); |
| } |
| |
| @Override |
| public Object remove(Object key) { |
| throw new RuntimeException(errstr); |
| } |
| |
| @Override |
| public void clear() { |
| throw new RuntimeException(errstr); |
| } |
| |
| void copy(Set s) { |
| Iterator i = s.iterator(); |
| defaults = new Properties(); |
| while (i.hasNext()) { |
| EnvironmentProperty p = (EnvironmentProperty) i.next(); |
| if (p.getName().startsWith(ejb10Prefix)) { |
| String newName = p.getName().substring(ejb10Prefix.length()); |
| defaults.put(newName, p.getValue()); |
| } |
| } |
| } |
| |
| private void readObject(java.io.ObjectInputStream stream) throws java.io.IOException, ClassNotFoundException { |
| defaults = (Properties) stream.readObject(); |
| } |
| |
| private void writeObject(java.io.ObjectOutputStream stream) throws java.io.IOException { |
| stream.writeObject(defaults); |
| } |
| } //SafeProperties{} |