Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.sun.ejb.containers.BaseContainer Maven / Gradle / Ivy
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 1997-2017 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* https://oss.oracle.com/licenses/CDDL+GPL-1.1
* or LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
// Portions Copyright [2016-2021] [Payara Foundation and/or its affiliates]
package com.sun.ejb.containers;
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.codegen.ServiceInterfaceGenerator;
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.EjbThreadPoolExecutorStatsProvider;
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 fish.payara.cluster.DistributedLockType;
import fish.payara.notification.requesttracing.RequestTraceSpanLog;
import java.io.Serializable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.rmi.AccessException;
import java.rmi.RemoteException;
import java.util.*;
import java.util.Map.Entry;
import java.util.concurrent.Future;
import java.util.logging.Level;
import java.util.logging.Logger;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.ejb.*;
import jakarta.enterprise.inject.Vetoed;
import jakarta.interceptor.AroundConstruct;
import javax.naming.NamingException;
import javax.naming.Reference;
import javax.naming.StringRefAddr;
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;
import fish.payara.nucleus.requesttracing.RequestTracingService;
import fish.payara.opentracing.OpenTracingService;
import io.opentracing.Span;
import io.opentracing.Tracer;
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 static com.sun.enterprise.deployment.MethodDescriptor.EJB_WEB_SERVICE;
import com.sun.enterprise.loader.ASURLClassLoader;
import static java.util.logging.Level.FINE;
import static java.util.stream.Collectors.toList;
/**
* This class implements part of the com.sun.ejb.Container interface.
* It implements the container's side of the EJB-to-Container
* contract defined 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;
private final RequestTracingService requestTracingService;
private final OpenTracingService openTracingService;
// 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 ClassLoader loader = null;
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;
private boolean hasPersistenceTimer;
private boolean hasNonPersistenceTimer;
/*****************************************
* 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;
// Constructor used to instantiate ejb local object proxy.
private Constructor ejbLocalObjectProxyCtor;
//
// 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 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;
// Constructor used to instantiate local business object proxy.
private Constructor ejbLocalBusinessObjectProxyCtor;
// Constructor used to instantiate local business object proxy.
private Constructor ejbOptionalLocalBusinessObjectProxyCtor;
private Collection 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;
// Remote interface proxy constructor.
private Constructor ejbObjectProxyCtor;
// 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 = null;
// 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 final Map 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 final Map invocationInfoMap = new HashMap<>();
protected final Map scheduleIds = new HashMap<>();
private final Map> 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 final 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 volatile int containerState = CONTAINER_INITIALIZING;
protected EjbMonitoringStatsProvider ejbProbeListener;
protected EjbMonitoringProbeProvider ejbProbeNotifier;
protected EjbTimedObjectStatsProvider timerProbeListener;
protected EjbTimedObjectProbeProvider timerProbeNotifier;
private boolean timersStarted = false;
protected EjbPoolStatsProvider poolProbeListener;
protected EjbCacheProbeProvider cacheProbeNotifier;
protected EjbCacheStatsProvider cacheProbeListener;
protected EjbThreadPoolExecutorStatsProvider executorProbeListener;
protected ContainerInfo containerInfo;
private final String _debugDescription;
//protected Agent callFlowAgent;
protected CallFlowInfo callFlowInfo;
protected InterceptorManager interceptorManager;
private final Set pendingInterceptors = new HashSet<>();
// 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 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 publishedPortableGlobalJndiNames = new HashSet<>();
private final Set publishedNonPortableGlobalJndiNames = new HashSet<>();
private final Set publishedInternalGlobalJndiNames = new HashSet<>();
private final Map 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;
protected JCDIService jcdiService;
/**
* This constructor is called from ContainerFactoryImpl when an
* EJB Jar is deployed.
*/
protected BaseContainer(ContainerType type, EjbDescriptor ejbDesc, ClassLoader loader, SecurityManager sm)
throws Exception
{
this.containerType = type;
this.securityManager = sm;
this.requestTracingService = Globals.getDefaultHabitat().getService(RequestTracingService.class);
this.openTracingService = Globals.getDefaultHabitat().getService(OpenTracingService.class);
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();
// URLClassLoader now uses DirWatcher because negative lookups are expensive (on Windows).
// However, DirWatcher, since it relies on OS events, may not actually be up to date
// and may not know that classes already exist (race condition)
// here we tell URLClassLoader that the class must exist in this loader, and
// ignore DirWatcher for this loader. Internally, this is stored in a boolean variable
// and the Auto-Closeable will clear out that value
try (AutoCloseable closeable = ASURLClassLoader.mustLoadFrom(loader)) {
// 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(ejbClass.getClassLoader(),
ejbClass.getName());
}
}
}
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);
for(String next : ejbDescriptor.getRemoteBusinessClassNames()) {
// The generated remote business interface and the
// client wrapper for the business interface are
// produced dynamically. The following call must be
// made before any EJB 3.0 Remote business interface
// runtime behavior is needed for a particular
// classloader.
EJBUtils.loadGeneratedRemoteBusinessClasses(loader, next);
String nextGen = EJBUtils.getGeneratedRemoteIntfName(next);
Class genRemoteIntf = loader.loadClass(nextGen);
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(nextGen, 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(next, clz);
}
}
if ( ejbDescriptor.isLocalBean() ) {
isLocal = true;
hasOptionalLocalBusinessView = true;
ejbOptionalLocalBusinessHomeIntf = GenericEJBLocalHome.class;
Class clz = loader.loadClass(ejbDescriptor.getEjbClassName());
addToGeneratedMonitoredMethodInfo(ejbDescriptor.getEjbClassName(), clz);
this.optIntfClassName = EJBUtils.getGeneratedOptionalInterfaceName(ejbClass.getName());
optIntfClassLoader = new EjbOptionalIntfGenerator(loader);
ejbGeneratedOptionalLocalBusinessIntfClass = optIntfClassLoader.loadClass(
ejbClass.getName(),
optIntfClassName,
() -> optIntfClassLoader.generateOptionalLocalInterface(ejbClass, 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
}
hasPersistenceTimer = false;
hasNonPersistenceTimer = false;
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);
// timers defined in runtime
hasPersistenceTimer = true;
hasNonPersistenceTimer = true;
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 {0}", method);
}
if(schd.getPersistent()) {
hasPersistenceTimer = true;
} else {
hasNonPersistenceTimer = true;
}
processEjbTimeoutMethod(method);
List list = schedules.get(method);
if (list == null) {
list = new ArrayList<>();
schedules.put(method, list);
}
list.add(schd);
}
}
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);
}
@Override
public void initialize() {
if (isTimedObject_) {
if (!isStatefulSession) {
// EJBTimerService should be accessed only if needed
// not to cause it to be loaded if it's not used.
if (hasPersistenceTimer) {
EJBTimerService timerService = EJBTimerService.getEJBTimerService(null, true, true);
if (timerService != null) {
timerService.timedObjectCount();
timersStarted = true;
}
}
if (hasNonPersistenceTimer) {
EJBTimerService timerService = EJBTimerService.getEJBTimerService(null, true, false);
if (timerService != null) {
timerService.timedObjectCount();
timersStarted = true;
}
}
} 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()));
}
}
}
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(String qualifiedClassName,
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 (InvocationInfo info : invocationInfoMap.values()) {
setInterceptorChain(info);
}
for (InvocationInfo info : this.webServiceInvocationInfoMap.values()) {
setInterceptorChain(info);
}
} catch (Exception e) {
throw new IllegalStateException(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 == null || !serviceEndpointIntfClass.isInterface()) {
ServiceInterfaceGenerator generator = new ServiceInterfaceGenerator(loader, ejbClass);
serviceEndpointIntfClass = EJBUtils.generateSEI(generator, generator.getGeneratedClass(),
loader, this.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));
}
}
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
final Class> tieClass = getTieClass();
// Create a facade for container services to be used by web services runtime.
EjbEndpointFacade endpointFacade = new EjbEndpointFacadeImpl(this, ejbContainerUtilImpl);
wsejbEndpointRegistry = Globals.getDefaultBaseServiceLocator().getService(WSEjbEndpointRegistry.class);
if (wsejbEndpointRegistry == null) {
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"));
}
wsejbEndpointRegistry.registerEndpoint(webServiceEndpoint,endpointFacade,servant,tieClass);
}
Map 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.parseBoolean(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();
// Since some containers might create multiple EJBObjects for
// the same ejb, make sure we use the same Proxy class to
// instantiate all the proxy instances.
ejbObjectProxyClass = Proxy.getProxyClass(loader, remoteIntf);
ejbObjectProxyCtor = ejbObjectProxyClass.getConstructor(InvocationHandler.class);
//
// 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()) {
next.proxyClass = Proxy.getProxyClass(loader, next.generatedRemoteIntf);
next.proxyCtor = next.proxyClass.getConstructor(InvocationHandler.class);
// 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 #. 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();
// Since some containers might create multiple EJBLocalObjects
// for the same ejb, make sure we use the same Proxy class to
// instantiate all the proxy instances.
Class> ejbLocalObjectProxyClass = Proxy.getProxyClass(loader, IndirectlySerializable.class, localIntf);
ejbLocalObjectProxyCtor = ejbLocalObjectProxyClass.getConstructor(InvocationHandler.class);
// 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();
Class>[] proxyInterfaces = new Class[localBusinessIntfs.size() + 1];
proxyInterfaces[0] = IndirectlySerializable.class;
int index = 1;
for (Class> next : localBusinessIntfs) {
proxyInterfaces[index] = next;
index++;
}
Class> proxyClass = Proxy.getProxyClass(loader, proxyInterfaces);
ejbLocalBusinessObjectProxyCtor = proxyClass.getConstructor(InvocationHandler.class);
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();
Class>[] proxyInterfaces = new Class[2];
proxyInterfaces[0] = IndirectlySerializable.class;
String optionalIntfName = EJBUtils.getGeneratedOptionalInterfaceName(ejbClass.getName());
proxyInterfaces[1] = ejbGeneratedOptionalLocalBusinessIntfClass = //
optIntfClassLoader.loadClass(optionalIntfName);
Class> proxyClass = Proxy.getProxyClass(loader, proxyInterfaces);
ejbOptionalLocalBusinessObjectProxyCtor = proxyClass.getConstructor(InvocationHandler.class);
// 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 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 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();
StringBuilder javaGlobalPrefix = new StringBuilder("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 context) throws Exception {
Object instance;
if (isJCDIEnabled()) {
// ejb creation for cdi is handled in JCDIServiceImpl not here.
instance = context.getJCDIInjectionContext().createEjbAfterAroundConstruct();
} else {
// this is only for non-cdi case.
instance = _constructEJBInstance();
}
context.setEJB(instance);
}
protected void createEmptyContextAndInterceptors(EJBContextImpl context) throws Exception {
JCDIService.JCDIInjectionContext> cdiContext = null;
if (isJCDIEnabled()) {
// 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.
cdiContext = jcdiService.createEmptyJCDIInjectionContext();
context.setJCDIInjectionContext(cdiContext);
}
// Interceptors must be created before the ejb so they're available for around construct.
createEjbInterceptors(context, cdiContext);
}
protected EJBContextImpl createEjbInstanceAndContext() throws Exception {
JCDIService.JCDIInjectionContext> jcdiCtx = null;
Object instance = null;
EJBContextImpl ctx = _constructEJBContextImpl(null);
EjbInvocation ejbInv = null;
boolean success = false;
try {
ejbInv = createEjbInvocation(null, ctx);
invocationManager.preInvoke(ejbInv);
createEmptyContextAndInterceptors(ctx);
if (isJCDIEnabled()) {
ctx.setJCDIInjectionContext(_createJCDIInjectionContext(ctx, null, jcdiCtx));
jcdiCtx = ctx.getJCDIInjectionContext();
if (jcdiCtx != null) {
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 boolean isJCDIEnabled() {
return jcdiService != null && jcdiService.isJCDIEnabled(ejbDescriptor.getEjbBundleDescriptor()) && (this.ejbClass.getAnnotation(Vetoed.class) == null);
}
protected JCDIService.JCDIInjectionContext> _createJCDIInjectionContext(EJBContextImpl ctx, Object instance) {
return _createJCDIInjectionContext(ctx, instance, null);
}
protected JCDIService.JCDIInjectionContext> _createJCDIInjectionContext(EJBContextImpl ejbContext, Object instance, JCDIService.JCDIInjectionContext> cdiInjectionContext) {
JCDIService.JCDIInjectionContext> context = jcdiService.createJCDIInjectionContext(ejbDescriptor,
instance, buildJCDIInjectionEjbInfo(ejbContext, cdiInjectionContext));
if (context == null) {
jcdiService = null;
}
return context;
}
private HashMap, Object> buildJCDIInjectionEjbInfo(EJBContextImpl ejbContext, JCDIService.JCDIInjectionContext> cdiInjectionContext) {
HashMap, Object> ejbInfo = new HashMap<>();
ejbInfo.put(BaseContainer.class, this);
ejbInfo.put(EJBContextImpl.class, ejbContext);
ejbInfo.put(JCDIService.JCDIInjectionContext.class, cdiInjectionContext == null? jcdiService.createEmptyJCDIInjectionContext() : cdiInjectionContext);
return ejbInfo;
}
/**
* Must be overridden for any container that supports injection
*
* @param instance
* @return {@link EJBContextImpl}
*/
protected EJBContextImpl _constructEJBContextImpl(Object instance) {
throw new IllegalStateException("Not implemented by this container: " + getClass());
}
protected Object _constructEJBInstance() throws Exception {
return ejbClass.newInstance();
}
private void createEjbInterceptors(EJBContextImpl context,
JCDIService.JCDIInjectionContext> ejbInterceptorsJCDIInjectionContext) throws Exception {
Object[] interceptorInstances;
//sanitizing the null reference of interceptorManager
if (interceptorManager == null) {
_logger.severe("The reference for interceptorManager is not available, this is an un-sync state of the container");
return;
}
if (isJCDIEnabled()) {
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 {
if (isJCDIEnabled()) {
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 {
//sanitizing the null reference of interceptorManager
if (interceptorManager == null) {
_logger.severe("The reference for interceptorManager is not available, this is an un-sync state of the container");
return false;
}
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 = inv.exception.getCause();
}
// 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)) {
final 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"));
}
}
}
/**
* 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> 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 accessible
*/
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));
}
}
private Class> getTieClass() throws ClassNotFoundException {
if (webServiceEndpoint.getTieClassName() == null) {
return null;
}
return loader.loadClass(webServiceEndpoint.getTieClassName());
}
/**
* 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 ? webServiceInvocationInfoMap.get(inv.method) : invocationInfoMap.get(inv.method);
}
private Throwable mapRemoteException(EjbInvocation inv) {
final Throwable originalException = inv.exception;
final boolean remoteClient = inv.isClientInterfaceAssignableToOneOf(java.rmi.Remote.class);
final Throwable mappedException = mapRemoteException( //
inv.exception, inv.invocationInfo.isAsynchronous(), inv.isBusinessInterface, remoteClient);
_logger.log(Level.FINE, () -> "Mapped original remote exception " + originalException //
+ " to exception " + mappedException + " for " + inv);
return mappedException;
}
private Throwable mapRemoteException(final Throwable remoteException, final boolean async,
final boolean businessIface, boolean remoteClient) {
final Throwable originalException = remoteException;
// 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 (async) {
if (remoteClient) {
final Throwable protocolException = protocolMgr.mapException(originalException);
if (protocolException != originalException) {
return protocolException;
}
return wrapEjbExceptionToRemote(originalException, protocolException);
}
return mapLocal3xException(originalException);
}
// Synchronous invocation. First let the protocol manager perform its mapping.
final Throwable protocolException = protocolMgr.mapException(originalException);
if (protocolException != originalException) {
return protocolException;
}
if (businessIface) {
// Wrap it up in a special exception so the
// client can unwrap it and ensure that the client receives EJBException.
return wrapEjbExceptionTInternal(originalException, protocolException);
}
return wrapEjbExceptionToRemote(originalException, protocolException);
}
private Throwable wrapEjbExceptionToRemote(final Throwable originalException, final Throwable protocolException) {
if (originalException instanceof EJBException) {
return new RemoteException(originalException.getMessage(), originalException);
}
return protocolException;
}
private Throwable wrapEjbExceptionTInternal(final Throwable originalException, final Throwable protocolException) {
if (originalException instanceof EJBException) {
return new InternalEJBContainerException(originalException.getMessage(), originalException);
}
return protocolException;
}
private Throwable mapLocal3xException(final Throwable t) {
if (t instanceof TransactionRolledbackLocalException) {
return new EJBTransactionRolledbackException(t.getMessage(), (Exception) t);
}
if (t instanceof TransactionRequiredLocalException) {
final EJBTransactionRequiredException exception = new EJBTransactionRequiredException(t.getMessage());
exception.initCause(t);
return exception;
}
if (t instanceof NoSuchObjectLocalException) {
return new NoSuchEJBException(t.getMessage(), (Exception) t);
}
if (t instanceof AccessLocalException) {
final EJBAccessException exception = new EJBAccessException(t.getMessage());
exception.initCause(t);
return exception;
}
return t;
}
/**
* Common code to handle EJB security manager authorization call.
*/
@Override
public boolean authorize(EjbInvocation inv) {
// 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 (inv.invocationInfo == null) {
inv.invocationInfo = getInvocationInfo(inv);
}
// Internal methods for 3.0 bean creation so there won't
// be corresponding permissions in the security policy file.
if ( (inv.method.getDeclaringClass() == localBusinessHomeIntf)
||
(inv.method.getDeclaringClass() == remoteBusinessHomeIntf) ) {
return true;
}
boolean authorized = securityManager.authorize(inv);
if (!authorized) {
if (inv.context != null) {
// This means that an enterprise bean context was created
// during the authorization call because of a callback from
// a JACC enterprise bean handler. Since the invocation will
// not proceed due to the authorization failure, we need
// to release the enterprise bean context.
releaseContext(inv);
}
}
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", jakarta.ejb.Handle.class);
ejbIntfMethods[EJBHome_remove_Pkey] = EJBHome.class.getMethod("remove", 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", 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", 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", 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.
if (hasPersistenceTimer) {
EJBTimerService timerService = EJBTimerService.getPersistentTimerService();
if (timerService != null) {
timerService.cancelTimersByKey(getContainerId(), key);
}
}
if (hasNonPersistenceTimer) {
EJBTimerService timerService = EJBTimerService.getNonPersistentTimerService();
if (timerService != null) {
timerService.cancelTimersByKey(getContainerId(), key);
}
}
}
}
private void stopTimers() {
if ( isTimedObject() && timersStarted ) {
if (hasPersistenceTimer) {
EJBTimerService ejbTimerService = EJBTimerService.getPersistentTimerService();
if (ejbTimerService != null) {
ejbTimerService.stopTimers(getContainerId());
}
}
if (hasNonPersistenceTimer) {
EJBTimerService ejbTimerService = EJBTimerService.getNonPersistentTimerService();
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 inv) {
Object bean = null;
// Access to an enterprise bean instance is undefined for
// anything but business method invocations through
// Remote , Local, and ServiceEndpoint interfaces.
if ( ( (inv.invocationInfo != null) &&
inv.invocationInfo.isBusinessMethod )
||
inv.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 ( inv.context == null ) {
try {
inv.context = getContext(inv);
bean = inv.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 = inv.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)
? "A null parameter is not a valid remote interface of bean " + ejbDescriptor.getName()
: "Parameter instance of class '" + o.getClass().getName()
+ "' is not a valid remote interface instance for bean " + ejbDescriptor.getName();
}
if (!valid) {
if (causeException == null) {
throw new EJBException(errorMsg);
} else {
throw new EJBException(errorMsg, causeException);
}
}
}
protected final int getTxAttr(Method method, String methodIntf) throws EJBException {
InvocationInfo invInfo = methodIntf.equals(EJB_WEB_SERVICE) //
? webServiceInvocationInfoMap.get(method)
: invocationInfoMap.get(method);
if (invInfo == null) {
throw new EJBException("Transaction Attribute not found for method " + method);
}
return invInfo.txAttr;
}
// 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;
}
inv.transactionAttribute = getTxAttr(inv.method, inv.getMethodInterface());
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 && beanMethod != null) {
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");
}
} 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;
if (!bothVoid && !bothFuture) {
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) {
if (methodIntf.equals(MethodDescriptor.EJB_LOCAL) || methodIntf.equals(MethodDescriptor.EJB_REMOTE)) {
// ejb1/2
return !EJBObject.class.isAssignableFrom(originalIntf) //
&& !EJBLocalObject.class.isAssignableFrom(originalIntf);
}
return false;
}
/**
* 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 readLockMethods = singletonDesc.getReadLockMethods();
List writeLockMethods = singletonDesc.getWriteLockMethods();
DistributedLockType distLockType = singletonDesc.isClustered()?
singletonDesc.getClusteredLockType() : DistributedLockType.LOCK_NONE;
for(MethodDescriptor readLockMethodDesc : readLockMethods) {
Method readLockMethod = readLockMethodDesc.getMethod(singletonDesc);
if (implMethodMatchesInvInfoMethod(invInfoMethod, methodIntf, readLockMethod)) {
lockInfo = new MethodLockInfo();
switch(distLockType) {
case INHERIT:
{
_logger.log(Level.WARNING, "Distributed Read Lock for Method {0} Upgraded to Read/Write", readLockMethod.getName());
lockInfo.setLockType(LockType.WRITE, true);
break;
}
case LOCK_NONE:
{
lockInfo.setLockType(LockType.READ, false);
}
}
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, distLockType != DistributedLockType.LOCK_NONE);
break;
}
}
}
}
// Set AccessTimeout info
if ( isSingleton || isStatefulSession ) {
EjbSessionDescriptor sessionDesc = (EjbSessionDescriptor) ejbDescriptor;
List 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 synchronized void initializeInterceptorManager() throws Exception {
this.interceptorManager = new InterceptorManager( //
_logger, this, lifecycleCallbackAnnotationClasses, getPre30LifecycleMethodNames());
if (!pendingInterceptors.isEmpty()) {
pendingInterceptors.forEach(this::registerSystemInterceptor);
pendingInterceptors.clear();
}
}
void registerSystemInterceptor(Object interceptor) {
if (needSystemInterceptorProxy()) {
synchronized (this) {
if (interceptorManager == null) {
pendingInterceptors.add(interceptor);
} else {
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> 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] = 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) ejbLocalObjectProxyCtor.newInstance(new Object[]{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;
ejbLocalBusinessObjectProxyCtor.newInstance( new Object[] { 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;
ejbOptionalLocalBusinessObjectProxyCtor.newInstance( new Object[] { 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__";
Class subClass = optIntfClassLoader.loadClass(
ejbClass.getName(),
beanSubClassName,
() -> optIntfClassLoader.generateOptionalLocalInterfaceSubClass(
ejbClass, beanSubClassName, businessIntfClass
)
);
optIntfClassLoader.loadClass(businessIntfClass.getName());
OptionalLocalInterfaceProvider provider =
(OptionalLocalInterfaceProvider) subClass.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) ejbObjectProxyCtor.newInstance(new Object[]{handler});
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);
java.rmi.Remote ejbBusinessObjectProxy = //
(java.rmi.Remote) next.proxyCtor.newInstance(new Object[] {delegate});
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(FINE, "Application deployment successful : {0}", this);
// By now all existing timers should have been restored.
if ( isTimedObject_ ) {
scheduleIds.clear();
// EJBTimerService should be accessed only if needed
// not to cause it to be loaded if it's not used.
if (hasPersistenceTimer) {
Map result;
EJBTimerService timerService = EJBTimerService.getPersistentTimerService();
if (timerService != null) {
boolean deploy0 = deploy; //avoid modifying param
if (deploy0 && ejbDescriptor.getApplication().getKeepStateResolved()) {
deploy0 = false;
_logger.log(Level.INFO, KEEPSTATE_IS_TRUE);
}
result = timerService.recoverAndCreateSchedules(
getContainerId(),
getApplicationId(),
getPersistentSchedules(),
deploy0
);
scheduleIds.putAll(result);
} else {
throw new RuntimeException("EJB Timer Service is not available");
}
}
if (hasNonPersistenceTimer) {
Map result;
EJBTimerService timerService = EJBTimerService.getNonPersistentTimerService();
if (timerService != null) {
boolean deploy0 = deploy;
if (deploy0 && ejbDescriptor.getApplication().getKeepStateResolved()) {
deploy0 = false;
_logger.log(Level.INFO, KEEPSTATE_IS_TRUE);
}
result = timerService.recoverAndCreateSchedules(
getContainerId(),
getApplicationId(),
getNonPersistentSchedules(),
deploy0
);
scheduleIds.putAll(result);
} else {
throw new RuntimeException("EJB Timer Service is not available");
}
}
}
setStartedState();
}
private Map> getPersistentSchedules() {
Map> persistentSchedules = new HashMap<>();
for (Entry> entry : schedules.entrySet()) {
Method method = entry.getKey();
List descriptors
= entry.getValue()
.stream()
.filter(descriptor -> descriptor.getPersistent())
.collect(toList());
if (!descriptors.isEmpty()) {
persistentSchedules.put(method, descriptors);
}
}
return persistentSchedules;
}
private Map> getNonPersistentSchedules() {
Map> nonPersistentSchedules = new HashMap<>();
for (Entry> entry : schedules.entrySet()) {
Method method = entry.getKey();
List descriptors
= entry.getValue()
.stream()
.filter(descriptor -> !descriptor.getPersistent())
.collect(toList());
if (!descriptors.isEmpty()) {
nonPersistentSchedules.put(method, descriptors);
}
}
return nonPersistentSchedules;
}
/**
*
* @param timerState
* @param timerService
* @return
* @throws java.lang.Exception
*/
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);
addEjbMethodTraceLog(callFlowInfo, true);
}
final void onEjbMethodEnd(String method_sig, Throwable th) {
ejbProbeNotifier.ejbMethodEndEvent(getContainerId(),
callFlowInfo.getApplicationName(),
callFlowInfo.getModuleName(),
callFlowInfo.getComponentName(),
th,
method_sig);
addEjbMethodTraceLog(callFlowInfo, false);
}
private void addEjbMethodTraceLog(CallFlowInfo info, boolean callEnter) {
if (openTracingService.isEnabled()) {
Tracer tracer = openTracingService.getTracer(openTracingService.getApplicationName(invocationManager));
RequestTraceSpanLog spanLog = constructEjbMethodSpanLog(info, callEnter);
if (tracer != null) {
Span span = tracer.activeSpan();
if (span != null) {
span.log(spanLog.getTimeMillis(), spanLog.getLogEntries());
} else {
// Traces started in the pre-OpenTracing style won't have an active span, so just attempt to add as
// is to thread local trace if there is one
requestTracingService.addSpanLog(spanLog);
}
} else {
// If we couldn't get a tracer here, it's because we couldn't get a name from the invocation manager.
// In such a case, just try to add the span log to the currently active thread local request trace
requestTracingService.addSpanLog(spanLog);
}
}
}
private RequestTraceSpanLog constructEjbMethodSpanLog(CallFlowInfo info, boolean callEnter) {
String eventName="enterEjbMethodEvent";
if (!callEnter) {
eventName="exitEjbMethodEvent";
}
RequestTraceSpanLog spanLog = new RequestTraceSpanLog(eventName);
spanLog.addLogEntry("ApplicationName", info.getApplicationName());
spanLog.addLogEntry("ComponentName", info.getComponentName());
spanLog.addLogEntry("ComponentType",info.getComponentType().toString());
spanLog.addLogEntry("ModuleName",info.getModuleName());
spanLog.addLogEntry("EJBClass", this.ejbClass.getCanonicalName());
spanLog.addLogEntry("EJBMethod", info.getMethod().getName());
spanLog.addLogEntry("CallerPrincipal", info.getCallerPrincipal());
spanLog.addLogEntry("TX-ID", info.getTransactionId());
return spanLog;
}
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 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());
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);
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);
}
}
executorProbeListener.unregister();
} 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
}
/**
* Checks if the exception is not null and if it is checked exception
* - uses ejb descriptor to resolve it.
*
* @param exception can be null
* @return true if the exception is not null and if it is not an unchecked exception
*/
protected boolean isApplicationException(Throwable exception) {
return exception != null && !isSystemUncheckedException(exception);
}
/**
* Checks if the exception is not null and if it is a system unchecked exception
* - uses ejb descriptor to resolve it.
*
* @param exception can be null
* @return true if the exception is not null and if it is an unchecked exception
*/
protected boolean isSystemUncheckedException(Throwable exception) {
if (exception == null) {
return false;
}
if (exception instanceof RuntimeException //
|| exception instanceof Error //
|| exception instanceof RemoteException) {
Class clazz = exception.getClass();
final String exceptionClassName = clazz.getName();
final Map appExceptions = //
ejbDescriptor.getEjbBundleDescriptor().getApplicationExceptions();
while (clazz != null) {
final 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 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.
* 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 inv)
throws Throwable
{
try {
return securityManager.invoke(inv.getBeanMethod(), inv.isLocal, inv.ejb,
inv.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 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();
executorProbeListener = new EjbThreadPoolExecutorStatsProvider(null);
executorProbeListener.register();
}
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.
*
* Use usual {@link PreInvokeException#getCause()} to get wrapped exception.
*/
public final static class PreInvokeException extends EJBException {
private static final long serialVersionUID = -1214474588837446339L;
public PreInvokeException(Exception ex) {
super(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;
Constructor proxyCtor;
}
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{}