All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.ejb.containers.StatelessSessionContainer Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 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://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/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 packager/legal/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-2019] [Payara Foundation and/or its affiliates]

package com.sun.ejb.containers;

import com.sun.ejb.ComponentContext;
import com.sun.ejb.EjbInvocation;
import com.sun.ejb.containers.util.pool.AbstractPool;
import com.sun.ejb.containers.util.pool.BlockingPool;
import com.sun.ejb.containers.util.pool.NonBlockingPool;
import com.sun.ejb.containers.util.pool.ObjectFactory;
import com.sun.ejb.monitoring.stats.EjbMonitoringStatsProvider;
import com.sun.ejb.monitoring.stats.EjbPoolStatsProvider;
import com.sun.ejb.monitoring.stats.StatelessSessionBeanStatsProvider;
import com.sun.enterprise.admin.monitor.callflow.ComponentType;
import com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType;
import com.sun.enterprise.deployment.runtime.BeanPoolDescriptor;
import com.sun.enterprise.security.SecurityManager;

import java.lang.reflect.Method;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;

import javax.ejb.CreateException;
import javax.ejb.EJBException;
import javax.ejb.EJBObject;
import javax.ejb.NoSuchObjectLocalException;
import javax.ejb.RemoveException;
import javax.ejb.SessionBean;
import javax.transaction.Status;
import javax.transaction.Transaction;

import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.ejb.config.EjbContainer;
import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
import org.glassfish.ejb.deployment.descriptor.runtime.IASEjbExtraDescriptors;

/**
 * This class provides container functionality specific to stateless SessionBeans.
 * 

* At deployment time, one instance of the StatelessSessionContainer is created * for each stateless SessionBean type (i.e. deployment descriptor) in a JAR. *

* The three states of a Stateless EJB (an EJB can be in only one state at a time): *

    *
  1. POOLED : ready for invocations, no transaction in progress *
  2. INVOKING : processing an invocation *
  3. DESTROYED : does not exist *
*

* This container services invocations using a pool of EJB instances. * An instance is returned to the pool immediately after the invocation * completes, so the # of instances needed = # of concurrent invocations. *

* A Stateless Bean can hold open DB connections across invocations. * It is assumed that the Resource Manager can handle multiple incomplete transactions * on the same connection. */ public class StatelessSessionContainer extends BaseContainer { private static final byte[] statelessInstanceKey = {0, 0, 0, 1}; // All stateless EJBs have the same instanceKey, since all stateless EJBs // are identical. Note: the first byte of instanceKey must be left empty. // All stateless EJB instances of a particular class (i.e. all bean // instances created by this container instance) have the same // EJBObject/EJBLocalObject instance since they are all identical. private EJBLocalObjectImpl theEJBLocalObjectImpl = null; private EJBLocalObjectImpl theEJBLocalBusinessObjectImpl = null; private EJBLocalObjectImpl theOptionalEJBLocalBusinessObjectImpl = null; // Data members for RemoteHome view private EJBObjectImpl theEJBObjectImpl = null; private EJBObject theEJBStub = null; // Data members for Remote business view. Any objects representing the // Remote business interface are not subtypes of EJBObject. private EJBObjectImpl theRemoteBusinessObjectImpl = null; private final Map theRemoteBusinessStubs = new HashMap<>(); private AbstractPool pool; private IASEjbExtraDescriptors iased = null; private BeanPoolDescriptor beanPoolDes = null; private EjbContainer ejbContainer = null; private PoolProperties poolProp = null; /** * This constructor is called from the JarManager when a Jar is deployed. * @exception Exception on error */ StatelessSessionContainer(EjbDescriptor desc, ClassLoader loader, SecurityManager sm) throws Exception { this(ContainerType.STATELESS, desc, loader, sm); } protected StatelessSessionContainer( // ContainerType conType, EjbDescriptor desc, ClassLoader loader, SecurityManager sm) throws Exception { super(conType, desc, loader, sm); ejbContainer = ejbContainerUtilImpl.getEjbContainer(); super.createCallFlowAgent(ComponentType.SLSB); } public String getMonitorAttributeValues() { StringBuilder sbuf = new StringBuilder(); sbuf.append("STATELESS ").append(ejbDescriptor.getName()); sbuf.append(pool.getAllAttrValues()); sbuf.append("]"); return sbuf.toString(); } @Override protected boolean suspendTransaction(EjbInvocation inv) throws Exception { // EJB2.0 section 7.5.7 says that ejbCreate/ejbRemove etc are called // without a Tx. So suspend the client's Tx if any. // Note: ejbRemove cannot be called when EJB is associated with // a Tx, according to EJB2.0 section 7.6.4. This check is done in // the container's implementation of removeBean(). return !inv.invocationInfo.isBusinessMethod; } @Override protected boolean resumeTransaction(EjbInvocation inv) throws Exception { return !inv.invocationInfo.isBusinessMethod; } @Override public boolean scanForEjbCreateMethod() { return true; } @Override protected EjbMonitoringStatsProvider getMonitoringStatsProvider(String appName, String modName, String ejbName) { return new StatelessSessionBeanStatsProvider(this, getContainerId(), appName, modName, ejbName); } @Override protected void initializeHome() throws Exception { super.initializeHome(); if (isRemote) { if (hasRemoteHomeView) { // Create theEJBObjectImpl theEJBObjectImpl = instantiateEJBObjectImpl(); // connect the EJBObject to the ProtocolManager (creates the stub too). // Note: cant do this in constructor above because // beanId is not set at that time. theEJBStub = (EJBObject) remoteHomeRefFactory.createRemoteReference(statelessInstanceKey); theEJBObjectImpl.setStub(theEJBStub); } if (hasRemoteBusinessView) { theRemoteBusinessObjectImpl = instantiateRemoteBusinessObjectImpl(); for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { java.rmi.Remote stub = next.referenceFactory.createRemoteReference(statelessInstanceKey); theRemoteBusinessStubs.put(next.generatedRemoteIntf.getName(), stub); theRemoteBusinessObjectImpl.setStub(next.generatedRemoteIntf.getName(), stub); } } } if (isLocal) { if (hasLocalHomeView) { theEJBLocalObjectImpl = instantiateEJBLocalObjectImpl(); } if (hasLocalBusinessView) { theEJBLocalBusinessObjectImpl = instantiateEJBLocalBusinessObjectImpl(); } if (hasOptionalLocalBusinessView) { theOptionalEJBLocalBusinessObjectImpl = instantiateOptionalEJBLocalBusinessObjectImpl(); } } createBeanPool(); registerMonitorableComponents(); } protected void createBeanPool() { ObjectFactory sessionCtxFactory = new SessionContextFactory(); iased = ejbDescriptor.getIASEjbExtraDescriptors(); if (iased != null) { beanPoolDes = iased.getBeanPool(); } poolProp = new PoolProperties(ejbContainer, beanPoolDes); String val = ejbDescriptor.getEjbBundleDescriptor().getEnterpriseBeansProperty(SINGLETON_BEAN_POOL_PROP); if (poolProp.maxWaitTimeInMillis != -1) { pool = new BlockingPool( // getContainerId(), ejbDescriptor.getName(), sessionCtxFactory, // poolProp.steadyPoolSize, poolProp.poolResizeQuantity, poolProp.maxPoolSize, // poolProp.poolIdleTimeoutInSeconds, loader, Boolean.parseBoolean(val), poolProp.maxWaitTimeInMillis); } else { pool = new NonBlockingPool( // getContainerId(), ejbDescriptor.getName(), sessionCtxFactory, // poolProp.steadyPoolSize, poolProp.poolResizeQuantity, poolProp.maxPoolSize, // poolProp.poolIdleTimeoutInSeconds, loader, Boolean.parseBoolean(val)); } } @Override protected void registerMonitorableComponents() { super.registerMonitorableComponents(); poolProbeListener = new EjbPoolStatsProvider( // pool, getContainerId(), containerInfo.appName, containerInfo.modName, containerInfo.ejbName); poolProbeListener.register(); _logger.log(Level.FINE, "[SLSB Container] registered monitorable"); } @Override public void onReady() { } @Override public EJBObjectImpl createRemoteBusinessObjectImpl() throws CreateException, RemoteException { // No access check since this is an internal operation. ejbBeanCreatedEvent(); return theRemoteBusinessObjectImpl; } private void ejbBeanCreatedEvent() { ejbProbeNotifier.ejbBeanCreatedEvent( // getContainerId(), containerInfo.appName, containerInfo.modName, containerInfo.ejbName); } @Override public EJBObjectImpl createEJBObjectImpl() throws CreateException, RemoteException { // Need to do access control check here because BaseContainer.preInvoke // is not called for stateless sessionbean creates. authorizeRemoteMethod(EJBHome_create); ejbBeanCreatedEvent(); // For stateless EJBs, EJB2.0 Section 7.8 says that // Home.create() need not do any real creation. // If necessary, a stateless bean is created below during getContext(). return theEJBObjectImpl; } /** * Called during client creation request through EJB LocalHome view. */ @Override public EJBLocalObjectImpl createEJBLocalObjectImpl() throws CreateException { // Need to do access control check here because BaseContainer.preInvoke // is not called for stateless sessionbean creates. authorizeLocalMethod(EJBLocalHome_create); ejbBeanCreatedEvent(); // For stateless EJBs, EJB2.0 Section 7.8 says that // Home.create() need not do any real creation. // If necessary, a stateless bean is created below during getContext(). return theEJBLocalObjectImpl; } /** * Called during internal creation of session bean */ @Override public EJBLocalObjectImpl createEJBLocalBusinessObjectImpl(boolean localBeanView) throws CreateException { ejbBeanCreatedEvent(); // No access checks needed because this is called as a result // of an internal creation, not a user-visible create method. return (localBeanView) ? theOptionalEJBLocalBusinessObjectImpl : theEJBLocalBusinessObjectImpl; } // Called from EJBObjectImpl.remove, EJBLocalObjectImpl.remove, // EJBHomeImpl.remove(Handle). @Override protected void removeBean(EJBLocalRemoteObject ejbo, Method removeMethod, boolean local) throws RemoveException, EJBException, RemoteException { if (local) { authorizeLocalMethod(BaseContainer.EJBLocalObject_remove); } else { authorizeRemoteMethod(BaseContainer.EJBObject_remove); } ejbProbeNotifier.ejbBeanDestroyedEvent( // getContainerId(), containerInfo.appName, containerInfo.modName, containerInfo.ejbName); } /** * Force destroy the EJB. Called from postInvokeTx. * Note: EJB2.0 section 18.3.1 says that discarding an EJB * means that no methods other than finalize() should be invoked on it. */ @Override protected void forceDestroyBean(EJBContextImpl sc) { if (sc.getState() == EJBContextImpl.BeanState.DESTROYED) { return; } // mark context as destroyed sc.setState(EJBContextImpl.BeanState.DESTROYED); //sessionCtxPool.destroyObject(sc); pool.destroyObject(sc); } /** * Called when a remote invocation arrives for an EJB. */ @Override protected EJBObjectImpl getEJBObjectImpl(byte[] instanceKey) { return theEJBObjectImpl; } @Override EJBObjectImpl getEJBRemoteBusinessObjectImpl(byte[] instanceKey) { return theRemoteBusinessObjectImpl; } /** * Called from EJBLocalObjectImpl.getLocalObject() while deserializing * a local object reference. */ @Override protected EJBLocalObjectImpl getEJBLocalObjectImpl(Object key) { return theEJBLocalObjectImpl; } /** * Called from EJBLocalObjectImpl.getLocalObject() while deserializing * a local business object reference. */ @Override EJBLocalObjectImpl getEJBLocalBusinessObjectImpl(Object key) { return theEJBLocalBusinessObjectImpl; } /** * Called from EJBLocalObjectImpl.getLocalObject() while deserializing * a local business object reference. */ @Override EJBLocalObjectImpl getOptionalEJBLocalBusinessObjectImpl(Object key) { return theOptionalEJBLocalBusinessObjectImpl; } /** * Called from preInvoke which is called from the EJBObject * for local and remote invocations. */ @Override protected ComponentContext _getContext(EjbInvocation inv) { try { SessionContextImpl sessionCtx = (SessionContextImpl) pool.getObject(null); sessionCtx.setState(EJBContextImpl.BeanState.INVOKING); return sessionCtx; } catch (Exception ex) { throw new EJBException(ex); } } @Override protected SessionContextImpl _constructEJBContextImpl(Object instance) { return new SessionContextImpl(instance, this); } /** * called when an invocation arrives and there are no instances * left to deliver the invocation to. * Called from SessionContextFactory.create() ! */ private SessionContextImpl createStatelessEJB() throws CreateException { EjbInvocation ejbInv = null; SessionContextImpl context; try { context = (SessionContextImpl) createEjbInstanceAndContext(); Object ejb = context.getEJB(); // this allows JNDI lookups from setSessionContext, ejbCreate ejbInv = super.createEjbInvocation(ejb, context); invocationManager.preInvoke(ejbInv); // setSessionContext will be called without a Tx as required // by the spec, because the EJBHome.create would have been called // after the container suspended any client Tx. // setSessionContext is also called before context.setEJBStub // because the bean is not allowed to do EJBContext.getEJBObject setSessionContext(ejb, context); // Perform injection right after where setSessionContext // would be called. This is important since injection methods // have the same "operations allowed" permissions as // setSessionContext. injectEjbInstance(context); if (isRemote) { if (hasRemoteHomeView) { context.setEJBObjectImpl(theEJBObjectImpl); context.setEJBStub(theEJBStub); } if (hasRemoteBusinessView) { context.setEJBRemoteBusinessObjectImpl(theRemoteBusinessObjectImpl); } } if (isLocal) { if (hasLocalHomeView) { context.setEJBLocalObjectImpl(theEJBLocalObjectImpl); } if (hasLocalBusinessView) { context.setEJBLocalBusinessObjectImpl(theEJBLocalBusinessObjectImpl); } if (hasOptionalLocalBusinessView) { context.setOptionalEJBLocalBusinessObjectImpl(theOptionalEJBLocalBusinessObjectImpl); } } // all stateless beans have the same id and same InstanceKey context.setInstanceKey(statelessInstanceKey); //Call ejbCreate() or @PostConstruct method intercept(CallbackType.POST_CONSTRUCT, context); // Set the state to POOLED after ejbCreate so that // EJBContext methods not allowed will throw exceptions context.setState(EJBContextImpl.BeanState.POOLED); } catch (Throwable th) { _logger.log(Level.SEVERE, "ejb.stateless_ejbcreate_exception", logParams); CreateException creEx = new CreateException("Could not create stateless EJB"); creEx.initCause(th); throw creEx; } finally { if (ejbInv != null) { invocationManager.postInvoke(ejbInv); } } context.touch(); return context; } private void setSessionContext(Object ejb, SessionContextImpl context) throws Exception { if (ejb instanceof SessionBean) { ((SessionBean) ejb).setSessionContext(context); } } @Override protected void doTimerInvocationInit(EjbInvocation inv, Object primaryKey) throws Exception { if (isRemote) { inv.isLocal = false; } else { inv.ejbObject = theEJBLocalObjectImpl; inv.isLocal = true; } } @Override public boolean userTransactionMethodsAllowed(ComponentInvocation inv) { boolean utMethodsAllowed = false; if (isBeanManagedTran) { if (inv instanceof EjbInvocation) { EjbInvocation ejbInv = (EjbInvocation) inv; EJBContextImpl sc = (EJBContextImpl) ejbInv.context; // If Invocation, only ejbRemove not allowed. utMethodsAllowed = !sc.isInEjbRemove(); } else { // This will prevent setSessionContext/ejbCreate access utMethodsAllowed = false; } } return utMethodsAllowed; } /** * Called from preInvoke which is called from the EJBObject * for local and remote invocations. */ @Override public void releaseContext(EjbInvocation inv) { SessionContextImpl sc = (SessionContextImpl) inv.context; // check if the bean was destroyed if (sc.getState() == EJBContextImpl.BeanState.DESTROYED) { return; } sc.setState(EJBContextImpl.BeanState.POOLED); // prevent java.util.ConcurrentModificationException from JMS // or others that hold on to invocation references, and thus possibly // reference to the contexts that are out of scope sc.resetResourceList(); // Stateless beans cant have transactions across invocations sc.setTransaction(null); sc.touch(); pool.returnObject(sc); } @Override protected boolean isIdentical(EJBObjectImpl ejbo, EJBObject other) throws RemoteException { if (other == ejbo.getStub()) { return true; } try { // other may be a stub for a remote object. // Although all stateless sessionbeans for a bean type // are identical, we dont know whether other is of the // same bean type as ejbo. return getProtocolManager().isIdentical(ejbo.getStub(), other); } catch (Exception ex) { if (_logger.isLoggable(Level.SEVERE)) { _logger.log(Level.SEVERE, "ejb.ejb_getstub_exception", logParams); _logger.log(Level.SEVERE, "", ex); } throw new RemoteException("Error during isIdentical.", ex); } } /** * Check if the given EJBObject/LocalObject has been removed. * @exception NoSuchObjectLocalException if the object has been removed. */ @Override protected void checkExists(EJBLocalRemoteObject ejbObj) { // For stateless session beans, EJBObject/EJBLocalObj are never removed. // So do nothing. } @Override protected void afterBegin(EJBContextImpl context) { // Stateless SessionBeans cannot implement SessionSynchronization!! // EJB2.0 Spec 7.8. } @Override protected void beforeCompletion(EJBContextImpl context) { // Stateless SessionBeans cannot implement SessionSynchronization!! // EJB2.0 Spec 7.8. } @Override protected void afterCompletion(EJBContextImpl ctx, int status) { // Stateless SessionBeans cannot implement SessionSynchronization!! // EJB2.0 Spec 7.8. // We dissociate the transaction from the bean in releaseContext above } // default @Override public boolean passivateEJB(ComponentContext context) { return false; } /** * @param ctx * @param instanceKey * @deprecated not called and not used in Payara 5 */ @Deprecated // default public void activateEJB(Object ctx, Object instanceKey) { // nothing to do } @Override protected void doConcreteContainerShutdown(boolean appBeingUndeployed) { try { if (hasRemoteHomeView) { // destroy EJBObject refs // XXX invocations still in progress will get exceptions ?? remoteHomeRefFactory.destroyReference(theEJBObjectImpl.getStub(), theEJBObjectImpl.getEJBObject()); } if (hasRemoteBusinessView) { for (RemoteBusinessIntfInfo next : remoteBusinessIntfInfo.values()) { next.referenceFactory.destroyReference( theRemoteBusinessObjectImpl.getStub(next.generatedRemoteIntf.getName()), theRemoteBusinessObjectImpl.getEJBObject(next.generatedRemoteIntf.getName())); } } if (pool != null) { pool.close(); poolProbeListener.unregister(); } } catch (Throwable t) { _logger.log(Level.FINE, "Exception during conrete StatelessSessionBean cleanup", t); } } public long getMethodReadyCount() { return pool.getSize(); } protected class SessionContextFactory implements ObjectFactory { @Override public Object create(Object param) { try { return createStatelessEJB(); } catch (CreateException ex) { throw new EJBException(ex); } } @Override public void destroy(Object obj) { SessionContextImpl sessionCtx = (SessionContextImpl) obj; // Note: stateless SessionBeans cannot have incomplete transactions // in progress. So it is ok to destroy the EJB. Object sb = sessionCtx.getEJB(); if (sessionCtx.getState() != EJBContextImpl.BeanState.DESTROYED) { //Called from pool implementation to reduce the pool size. //So need to call ejb.ejbRemove() // mark context as destroyed sessionCtx.setState(EJBContextImpl.BeanState.DESTROYED); EjbInvocation ejbInv = null; try { // NOTE : Context class-loader is already set by Pool ejbInv = createEjbInvocation(sb, sessionCtx); invocationManager.preInvoke(ejbInv); sessionCtx.setInEjbRemove(true); intercept(CallbackType.PRE_DESTROY, sessionCtx); } catch ( Throwable t ) { _logger.log(Level.FINE, "ejbRemove exception", t); } finally { sessionCtx.setInEjbRemove(false); if( ejbInv != null ) { invocationManager.postInvoke(ejbInv); } } } else { //Called from forceDestroyBean //So NO need to call ejb.ejbRemove() // mark the context's transaction for rollback Transaction tx = sessionCtx.getTransaction(); try { if ( (tx != null) && (tx.getStatus() != Status.STATUS_NO_TRANSACTION ) ) { tx.setRollbackOnly(); } } catch ( Exception ex ) { _logger.log(Level.FINE,"forceDestroyBean exception", ex); } } cleanupInstance(sessionCtx); // tell the TM to release resources held by the bean transactionManager.componentDestroyed(sessionCtx); sessionCtx.setTransaction(null); sessionCtx.deleteAllReferences(); } } // SessionContextFactory{} private static class PoolProperties { int maxPoolSize; int maxWaitTimeInMillis; int poolIdleTimeoutInSeconds; int poolResizeQuantity; int steadyPoolSize; public PoolProperties(EjbContainer ejbContainer, BeanPoolDescriptor beanPoolDes) { maxWaitTimeInMillis = Integer.parseInt(ejbContainer.getMaxWaitTimeInMillis()); if (!Boolean.parseBoolean(ejbContainer.getLimitInstancesEnabled())) { maxWaitTimeInMillis = -1; } maxPoolSize = Integer.parseInt(ejbContainer.getMaxPoolSize()); poolIdleTimeoutInSeconds = Integer.parseInt(ejbContainer.getPoolIdleTimeoutInSeconds()); poolResizeQuantity = Integer.parseInt(ejbContainer.getPoolResizeQuantity()); steadyPoolSize = Integer.parseInt(ejbContainer.getSteadyPoolSize()); if (beanPoolDes != null) { int temp; if ((temp = beanPoolDes.getMaxPoolSize()) != -1) { maxPoolSize = temp; } if ((temp = beanPoolDes.getPoolIdleTimeoutInSeconds()) != -1) { poolIdleTimeoutInSeconds = temp; } if ((temp = beanPoolDes.getPoolResizeQuantity()) != -1) { poolResizeQuantity = temp; } if ((temp = beanPoolDes.getSteadyPoolSize()) != -1) { steadyPoolSize = temp; } if ((temp = beanPoolDes.getMaxWaitTimeInMillis()) != -1) { maxWaitTimeInMillis = temp; } } } } // PoolProperties{} //Methods for StatelessSessionBeanStatsProvider public int getMaxPoolSize() { return (poolProp.maxPoolSize <= 0) ? Integer.MAX_VALUE : poolProp.maxPoolSize; } public int getSteadyPoolSize() { return (poolProp.steadyPoolSize <= 0) ? 0 : poolProp.steadyPoolSize; } } // StatelessSessionContainer.java





© 2015 - 2024 Weber Informatics LLC | Privacy Policy