/* * @(#)ApplicationPoolImpl.java * * Copyright 2000-2002 by Oracle Corporation, * 500 Oracle Parkway, Redwood Shores, California, 94065, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information * of Oracle Corporation. */ package oracle.jbo.common.ampool; import com.sun.java.util.collections.HashMap; import com.sun.java.util.collections.Iterator; import com.sun.java.util.collections.ArrayList; import java.io.IOException; import java.io.ObjectInputStream; import java.util.Date; import java.util.Hashtable; import java.util.Properties; import oracle.jbo.SessionData; import oracle.jbo.ApplicationModule; import oracle.jbo.ConnectionMetadata; import oracle.jbo.JboException; import oracle.jbo.Transaction; import oracle.jbo.Session; import oracle.jbo.client.Configuration; import oracle.jbo.common.Diagnostic; import oracle.jbo.common.DebugDiagnostic; import oracle.jbo.common.JBOClass; import oracle.jbo.common.JboEnvUtil; import oracle.jbo.common.PropertyMetadata; import oracle.jbo.pool.RecentlyUsedLinkedList; import oracle.jbo.pool.RecentlyUsedLinkedListElement; import oracle.jbo.pool.ResourcePool; import oracle.jbo.pool.ResourcePoolLogger; import oracle.jbo.pool.AgeHistogram; import oracle.jbo.pool.ResourceInfo; import oracle.jbo.common.UserAznUtil; //YCHUA 10-09-2002 import oracle.jbo.ApplicationPoolSvcMsgContext; /** * This class provides the default implementation of the ApplicationPool interface. * * A few notes about the locking strategy that has been employed to synchronize * access to the shared application module resources in this pool. These * notes are provided for the benefit of the application pool developer. They * are not required to use the application pool. *

* The pool utilizes a singleton internal monitor object instance to synchronize * read/write access to shared data. *

* In order to provide high pool availability, only those critical blocks that * read/write from the shared data structures have been synchronized. Expensive * application module operations like connect/disconnect, activiate/passivate, * and rollback are performed outside of the pool lock. In order for this to * work the application module is made not available inside of the pool lock * before its state is modified. Using this strategy will prevent other * sessions from attempting to recycle the application module instance while * its state is not consistent. *

* This strategy of course assumes that application module access from the same * session is properly synchronized. Consider the following scenario: *

* 1. Request thread one from session one acquires a pool lock. While holding * the lock the request sets the session application module as not available. * Request thread one from session one then releases the pool lock and begins * modifying the application module state. *

* 2. Request thread two from session one comes along. Before the application * module state has been modified by thread one above, request thread two * invokes useApplicationModule. Request thread two acquires a pool lock * and finds the application module instance associated with the request * session (even though the application module is not available). Request * thread two happily continues using the application module reference and * a possible race condition between request threads one and two has occured. *

* In order to prevent this scenario, the pool assumes that session cookie * access is also properly synchronized. This is indeed the case for the * provided implementations of oracle.jbo.common.ampool.SessionCookie. *

* The discussion above introduces two levels of locking, session locking and pool locking. * The pool's locking strategy utilizes the following rules to prevent deadlock: *

* 1. Session locks are never acquired while pool locks are held. * Please note that this rule does allow a pool lock to be acquired while * a session lock is held. In fact, the pool depends upon this to prevent * the race conditions mentioned above. *

* From an availability standpoint this rule makes sense since the rule implies * that pool locks will be held for shorter duration then a session lock. * However, because of the two levels of locking the application pool developer * must be constantly aware of the potential for deadlock when implementing pool * logic. To further understand the above rule consider the following scenario: *

* 1. Request 1 for session 1 has acquired a pool lock without first acquiring * a session lock. In the critical section the request updates * some shared session state that requires a session lock to be acquired. *

* 2. Concurrently, request 2 for session 1 acquires a session lock. In the * critical section the request requires access to some shared application pool * state that requires an application pool lock to be acquired. *

* Obviously, in the scenario above request one may be blocked waiting for * the session lock that is held by request two and request two may be blocked * waiting for the pool lock that is held by request one; the definition of * deadlock. *

* Most pool methods should be accessed using a session cookie, so that the rule * defined above holds true. However, there are some instances where a cookie * lock could be acquired after a pool lock has been acquired: *

* 1. When a request from session one recycles an application module that is * referenced by session two. If the session two state must be passivated * then the request thread from session one must update the passivation id * of the session two cookie. This requires the request thread from session * one to acquire a session lock for session two. It is safe to assume that a * session lock for the session one has already been acquired because request * one is an active request that must have accessed the pool using the session * cookie API. It is not safe to assume that a session lock for session two * has been acquired because the current pool request thread did not originate * from session two. Attempting to acquire a session lock for session two * while holding a pool lock may cause the deadlock mentioned above. This * scenario is avoided by executing the section that requires the session * lock for session two after the pool lock has been released. *

* 2. When a referenced application module is removed by the high water mark * functionality. This scenario is very similar to the one described in item * one above. However, instead of the request thread from session one recycling * an application module that is referenced by session two the request thread * from session one is discarding the application module that is referenced by * session two. *

*
* View definition of ApplicationPool *
* View Implementation of ApplicationPoolImpl *

*/ public class ApplicationPoolImpl extends ResourcePool implements ApplicationPool { static final long serialVersionUID = -6745281847170690848L; public static int CREATION_POLICY_SERIAL = 0; public static int CREATION_POLICY_ROUND_ROBIN = 1; public static int MAX_HANDLE_POLICY_SPILL = 0; public static int MAX_HANDLE_POLICY_ERROR = 1; private static long mSignatureCounter = 0; private static final Object mStaticLock = new Object(); // Immutable state private final long mSignature; // Mutable state private String mName; private Hashtable mUserData; private int mSessionId = 0; private Hashtable mEnvironment; private String mUserName; private String mPassword; private String mConnectString; // Transient state private transient RecentlyUsedLinkedList mReferencedList = new RecentlyUsedLinkedList(); private transient RecentlyUsedLinkedList mUnrefAvailList = new RecentlyUsedLinkedList(); private transient RecentlyUsedLinkedList mRefAvailList = new RecentlyUsedLinkedList(); private transient ConnectionStrategy mStrategy; private transient SessionCookieFactory mSessionCookieFactory; private transient int mReferencingSessionCount = 0; private transient HashMap mInstanceInfo = new HashMap(10); private transient HashMap mSessionCookieInfo = new HashMap(10); private transient long mTimeCreated = -1; private transient volatile int mUseExclusive = -1; // Checkout operations private static final int REUSE_REFERENCED_INSTANCE = 1; private static final int RECYCLE_UNREFERENCED_INSTANCE = 2; private static final int RECYCLE_REFERENCED_INSTANCE = 3; private static final int CREATE_NEW_INSTANCE = 4; private static final int SHARE_INSTANCE = 5; private static final byte PASSIVATE_STATE = 0; private static final byte ACTIVATE_STATE = 1; private static final byte REMOVE_STATE = 2; static final int RESET_RECONNECT_FLAG = 0x1; static final int RESET_REMOVE_STATE_FLAG = 0x2; static final int RESET_REMOVE_FLAG = 0x4; static final int RESET_MANAGE_SNAPSHOTS_FLAG = 0x8; static final int RESET_RELEASE_FLAG = 0x10; static final int RESET_MANAGED_FLAGS = RESET_RECONNECT_FLAG | RESET_MANAGE_SNAPSHOTS_FLAG; static final int RESET_UNMANAGED_FLAGS = RESET_REMOVE_STATE_FLAG | RESET_RELEASE_FLAG; // Private property which is used to pass the session cookie between // internal pool methods. private static final String SESSION_COOKIE = "jbo.envinfoprovider"; private static final String SVC_MSG_CONTEXT = "_jbo.applicationpoolsvcmsgcontext_"; /** * Constructor */ public ApplicationPoolImpl() { long signatureCounter = 0; synchronized(mStaticLock) { signatureCounter = mSignatureCounter; mSignatureCounter++; } mSignature = System.currentTimeMillis() + signatureCounter; } public long getSignature() { return mSignature; } private String getNextSessionId() { // JRS This method should not be used. Memory generated session // identifers guarantee neither uniqueness nor consistency. String nextSessionId = null; nextSessionId = String.valueOf(mSessionId++); return nextSessionId; } public void initialize() { synchronized(mLock) { Properties props = new Properties(); props.putAll(mEnvironment); super.initialize(props); if (mInstanceInfo == null) { mInstanceInfo = new HashMap(10); } if (mReferencedList == null) { mReferencedList = new RecentlyUsedLinkedList(); } if (mUnrefAvailList == null) { mUnrefAvailList = new RecentlyUsedLinkedList(); } if (mRefAvailList == null) { mRefAvailList = new RecentlyUsedLinkedList(); } if (mSessionCookieInfo == null) { mSessionCookieInfo = new HashMap(10); } mReferencingSessionCount = 0; mTimeCreated = System.currentTimeMillis(); } } public void initialize( String name , String applicationModuleClassName , String connectString , Hashtable env) { synchronized(mLock) { mName = name; mEnvironment = (Hashtable)env.clone(); mConnectString = connectString; initialize(); // Bug 1387996 -- Not properly picking up username password from named // database connections (SPM) if (env != null) { String un = (String) env.get(Configuration.DB_USERNAME_PROPERTY); String pw = (String) env.get(Configuration.DB_PASSWORD_PROPERTY); if (un != null) { setUserName(un); } if (pw != null) { setPassword(pw); } } if (applicationModuleClassName != null) { mEnvironment.put(ConnectionStrategy.APPLICATION_MODULE_CLASS_NAME_PROPERTY, applicationModuleClassName); } } } public ConnectionStrategy getConnectionStrategy() { synchronized(mLock) { if (mStrategy == null) { String className = getConnectionStrategyClassName(); try { Class connectionStrategyClass = JBOClass.forName(className); mStrategy = (ConnectionStrategy)connectionStrategyClass.newInstance(); } catch(ClassNotFoundException cnfex) { throw new JboException(cnfex); } catch(IllegalAccessException iaex) { throw new JboException(iaex); } catch (InstantiationException iex) { throw new JboException(iex); } } return mStrategy; } } public void setConnectionStrategy(ConnectionStrategy strategy) { synchronized(mLock) { if (strategy != null) { mStrategy = strategy; mEnvironment.put( PropertyMetadata.ENV_AMPOOL_CONNECTION_STRATEGY_CLASS_NAME.pName , strategy.getClass().getName()); } } } public SessionCookieFactory getSessionCookieFactory() { synchronized(mLock) { if (mSessionCookieFactory == null) { String className = getSessionCookieFactoryClassName(); try { Class sessionCookieFactoryClass = JBOClass.forName(className); mSessionCookieFactory = (SessionCookieFactory)sessionCookieFactoryClass.newInstance(); } catch(ClassNotFoundException cnfex) { throw new JboException(cnfex); } catch(IllegalAccessException iaex) { throw new JboException(iaex); } catch (InstantiationException iex) { throw new JboException(iex); } } return mSessionCookieFactory; } } public void setSessionCookieFactory(SessionCookieFactory sessionCookieFactory) { synchronized(mLock) { // A null session cookie factory is not allowed if (sessionCookieFactory != null) { mSessionCookieFactory = sessionCookieFactory; mEnvironment.put( PropertyMetadata.ENV_AMPOOL_COOKIE_FACTORY_CLASS_NAME.pName , sessionCookieFactory.getClass().getName()); } } } public int getMaxPoolSize() { return getProperty( PropertyMetadata.ENV_AMPOOL_MAX_POOL_SIZE.pName , getEnvironment() , Integer.valueOf(PropertyMetadata.ENV_AMPOOL_MAX_POOL_SIZE.pDefault) .intValue()); } public int getInitPoolSize() { return getProperty( PropertyMetadata.ENV_AMPOOL_INIT_POOL_SIZE.pName , getEnvironment() , Integer.valueOf(PropertyMetadata.ENV_AMPOOL_INIT_POOL_SIZE.pDefault) .intValue()); } public SessionCookie createSessionCookie(String applicationId, String sessionId, Properties properties) { SessionCookie cookie = getSessionCookieFactory() .createSessionCookie(applicationId, sessionId, this, properties); SessionCookieInfo info = null; boolean cookieExists = false; synchronized(mLock) { // If a session cookie info object already exists for this cookie // then do not replace it with the new cookie. This is necessary if // someone is using the old pool APIs and a new session cookie for // the same session is created for each checkout. info = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (info == null) { mSessionCookieInfo.put(cookie, new SessionCookieInfo(cookie)); } else { cookieExists = true; } } // If the two cookie instances share the same address then this may // be a shared cookie. Go ahead and return the instance without waiting // for the other session to remove the cookie. if ((cookieExists) && (cookie != info.mCookie)) { // Another thread might be updating the cookie (see bug 1814844). // Wait for the cookie lock before preceding. synchronized(info.mCookie.getSyncLock()) { synchronized(mLock) { // If a session cookie info object already exists for this cookie // then do not replace it with the new cookie. This is necessary if // someone is using the old pool APIs and a new session cookie for // the same session is created for each checkout. info = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (info == null) { mSessionCookieInfo.put(cookie, new SessionCookieInfo(cookie)); } else { // At this point an exception should be thrown. Remove should // be invoked before create. ApplicationPoolException apex = new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_COOKIE_ALREADY_EXISTS , new Object[] {cookie.getSessionId(), cookie.getApplicationId(), getName()}); apex.setSessionCookie(info.mCookie); throw apex; } } } } return cookie; } public void addSessionCookie(SessionCookie cookie) { boolean doCookieSynchronization = false; SessionCookieInfo cookieInfo = null; validateSessionCookie(cookie); synchronized(mLock) { // If the session cookie states are out of synch then copy // the state of the newer cookie to the older cookie. cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (cookieInfo == null) { cookieInfo = new SessionCookieInfo(cookie); mSessionCookieInfo.put(cookie, cookieInfo); } else { doCookieSynchronization = true; } } if (doCookieSynchronization) { // Synchronize on the shared cookie instance. This is to prevent // other threads from accessing the shared cookie while its state is // being updated. Ideally, when addSessionCookie is invoked during // cookie de-serialization the cookie in the target pool should be // unused so contention should not occur. synchronized(cookieInfo.mCookie.getSyncLock()) { // If the cookie is referencing an application module and that // application module is not available then the cookie must // currently be using that application module. Throw an exception. // Cookie replication should not target a pool in which that cookie // is active. if ((cookieInfo.mApplicationModule != null) && (!isAvailable(cookieInfo.mApplicationModule))) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_COOKIE_REPL , new Object[] {cookie.getSessionId(), cookie.getApplicationId()}); } if (cookie.getLastUpdate().after(cookieInfo.mCookie.getLastUpdate())) { if (cookieInfo.mApplicationModule != null) { // The cookie that is being added was updated more recently // than the cookie referenced by the pool. Release the pool // cookie's application module statelessly so that activation // may occur properly the next time the session cookie is // used. This may seem expensive, but it should occur very // infrequently. resetApplicationModule(cookieInfo.mCookie, RESET_UNMANAGED_FLAGS | RESET_RECONNECT_FLAG); } // Just in case an old cookie reference is held (would be bad), // but we can't be certain what clustering implementations look // like. This won't hurt. cookie.copyInto(cookieInfo.mCookie); if (cookie.getPassivationId() != SessionCookie.NULL_PASSIVATION_ID) { if (!cookieInfo.mIsReferencingState) { cookieInfo.mIsReferencingState = true; synchronized(mLock) { mReferencingSessionCount++; } } } } else if (cookieInfo.mCookie.getLastUpdate().after(cookie.getLastUpdate())) { cookieInfo.mCookie.copyInto(cookie); } // Replace the existing cookie reference with the new cookie // instance. cookieInfo.mCookie = cookie; } } } public void removeSessionCookie(SessionCookie cookie) { // Check if the cookie is currently reserving an application module // Perform this validation inside the cookie lock. synchronized(cookie.getSyncLock()) { boolean resetAppModule= false; ApplicationModule appModule = null; SessionCookieInfo cookieInfo = null; ApplicationModuleInfo appModuleInfo = null; synchronized(mLock) { validateSessionCookieInPool(cookie); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (useApplicationModule(cookie, false) != null) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_CANNOT_REMOVE_COOKIE , new Object[] {cookie.getSessionId(), cookie.getApplicationId(), getName()}); } // Check if this session cookie is still referencing an application // module instance. If so, reset the application module state. appModule = getApplicationModuleForSession(cookie); if (appModule != null) { setAvailable(appModule, false); appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); resetAppModule = true; } else { mSessionCookieInfo.remove(cookie); } } // Reset the application module outside of the application pool // lock. Okay, the application module is made unavailable above. if (resetAppModule) { try { ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); appModule = resetApplicationModule( cookie , cookieInfo , appModule , appModuleInfo , RESET_MANAGED_FLAGS , ctx); } catch (Throwable t) { // nothing to do. if (Diagnostic.isOn()) { Diagnostic.println( "An exception occured while removing the SessionCookie"); DebugDiagnostic.printStackTrace(t); } } finally { synchronized(mLock) { // bug 2452345 - test if the appModule is still in the pool. // if the application module was stale then // it is possible that it was disassociated and removed // by the resetApplicationModule invocation above. if (mInstanceInfo.get(appModule) != null) { setAvailable(appModule, true); } // JRS Ensure that the cookieInfo gets a hard reset. It is // possible that if the reset fails above that the cookie // info/pool will still be referencing state. resetState is // re-entrant so we should be okay if above did succeed. // see kava.ampool.js28. if (cookieInfo != null) cookieInfo.resetState(); cookie.resetStateInternal(); mSessionCookieInfo.remove(cookie); } } } } } public String getApplicationModuleClass() { return (String)getEnvironment().get(ConnectionStrategy.APPLICATION_MODULE_CLASS_NAME_PROPERTY); } /** * @deprecated This value should be passed to the pool connection strategy as * SessionCookie environment or by implementing an EnvInfoProvider. The * value may be acquired from the ApplicationPool or SessionCookie * environment by using the ConnectionStrategy.DB_CONNECT_STRING_PROPERTY key. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.ConnectionStrategy * @see oracle.jbo.common.ampool.EnvInfoProvider * @see oracle.jbo.common.ampool.SessionCookie */ public String getConnectString() { // Do not synchronize. This should only be written to asynchronously. return mConnectString; } public Hashtable getEnvironment() { // Do not synchronize. This should only be written to asynchronously. return mEnvironment; } private ApplicationModule findUnreferencedAvailInstance() { ApplicationModule appModule = null; RecentlyUsedLinkedListElement elem = mUnrefAvailList.getMRUElement(); // This loop will return the first unreferenced instance in the available // instance list. The available instance list is ordered with the most // recently used instances last. if (elem != null) { appModule = (ApplicationModule)elem.getRefObject(); Diagnostic.ASSERT(!isReferenced(appModule) && isAvailable(appModule) , "ILLEGAL STATE: UNAVAILABLE OR REFERENCED APPMODULE IN AVAILABLE, UNREFERENCED LIST"); } return appModule; } private int findAvailableInstance( SessionCookie cookie , SessionCookieInfo referencingCookieInfo , ApplicationPoolSvcMsgContext ctx , boolean inRetry , Object[] appModuleInfos) { int op = -1; ApplicationModule appModule = null; // perform this outside of the synchronized block. Potentially performs // I/O when creating/connecting an ApplicationModule. if (!isUseExclusive()) { Properties props = new Properties(); props.put(SESSION_COOKIE, cookie); props.put(SVC_MSG_CONTEXT, ctx); appModule = (ApplicationModule)super.useResource(props); } synchronized(mLock) { // bug 3770068 We need to check if the cookie is available in this // block. Another thread may have set it unavailable while // we were waiting to enter the block. // // validateSessionCookie will throw if the SC is unavailable. Throw // is necessary to ensure that both the ApplicationPool lock *and* // the SessionCookie lock is released. If both locks are not released // then the manageReferencingState thread may not be able to passivate. // The outer most useApplicationModule(...) method should handle // this exception and wait until the SC is available. // // Once a cookie has been disassociated it should not become // unavailable again (i.e. a cookie will not cycle b/w available and // unavailable states once it has been disassociated). validateSessionCookieAvailable(cookie); ApplicationModuleInfo appModuleInfo = null; if (!isUseExclusive()) { appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); op = SHARE_INSTANCE; } else if (inRetry) { allocateResource(); op = CREATE_NEW_INSTANCE; } else if ((getApplicationModuleForSession(cookie) != null) // isn't the availability check redundant? why would the // SC still be mapped to the appModule if it was recycled? // if it ain't broke don't fix it. && (isAvailable(getApplicationModuleForSession(cookie)))) { appModule = getApplicationModuleForSession(cookie); appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); mReferencedList.touchElement(appModuleInfo); op = REUSE_REFERENCED_INSTANCE; } else { int recycleThreshold = getRecycleThreshold(); appModule = findUnreferencedAvailInstance(); if (appModule == null && (getResourceCount() >= recycleThreshold)) { appModule = findReferencedAvailInstance(); } // If we could not find a referenced instance if (appModule == null) { // Try to allocate a resource in the resource pool. appModule = (ApplicationModule)allocateResource(); } // If we have successfully recycled an instance if (appModule != null) { // If the instance is not referenced if (!isReferenced(appModule)) { appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); op = RECYCLE_UNREFERENCED_INSTANCE; } else { appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); SessionCookie referencingCookie = getReferencingSessionCookie(appModule); setSessionCookieAvailable(referencingCookie, false); // Remove the application module's session references referencingCookieInfo.mCookie = referencingCookie; disassociateSessionCookie( appModule, referencingCookieInfo.mCookie, false); op = RECYCLE_REFERENCED_INSTANCE; } } else { op = CREATE_NEW_INSTANCE; } } if (appModule != null) { // Do this before associating the instance with the new session // cookie. // // The ConnectionMetadata is cached on the AppModuleInfo in // addition to the SessionCookieInfo because an // ApplicationModule may be disassociated from a SC. The // ApplicationModule ConnectionMetadata is copied to the // AppModuleInfo to allow comparison with the SC // ConnectionMetadata from withing the pool. // // JRS I don't like keeping a copy of the AppModule // ConnectionMetadata in the AppModuleInfo. It just means // we have to do more work keeping the two in synch. Maintaining // this for back compat reasons only (application developer may // override compareConnectionMetadata). The alternative is to // get rid of AppModuleInfo.mConnectionMetadata // and simply perform the comparison between the SC // ConnectionMetadata and the ApplicationModule // ConnectionMetadata in doPoolMessage. referencingCookieInfo.mConnectionMetadata = appModuleInfo.mConnectionMetadata; // Only perform this block if an existing instance was located. // If we need to create a new instance then perform this block in // after the instance has been created below. This is duplicated // so that we can move the instance creation outside of the // section that is blocking the pool. associateSessionCookie(appModule, cookie); // Be sure to set the application module as not available within // the synchronized block. This will prevent other threads from // "stealing" the instance from the current thread once it releases // the lock // // only set not available if use exclusive if (isUseExclusive()) { setAvailable(appModule, false); } // Immediately add the application module to the referenced // instance list. mReferencedList.addElement(appModuleInfo); } referencingCookieInfo.mApplicationModule = appModule; appModuleInfos[0] = appModuleInfo; } // release mLock return op; } private ApplicationModule findReferencedAvailInstance() { ApplicationModule appModule = null; RecentlyUsedLinkedListElement elem = mRefAvailList.getLRUElement(); // This loop will return the first unreferenced instance in the available // instance list. The available instance list is ordered with the most // recently used instances last. if (elem != null) { appModule = (ApplicationModule)elem.getRefObject(); Diagnostic.ASSERT(isAvailable(appModule), "ILLEGAL STATE: UNAVAILABLE APPMODULE IN REF,AVAIL LIST"); } return appModule; } protected final Object getResourceDetails(Object resource) { // always inside pool lock SessionCookieInfo details = null; // if we are not useExclusive then we cannot disassociate the SC // because we do not know which SC has invoked getResourceDetails. // In this instance we must depend upon the SC to release. if (isUseExclusive()) { details = new SessionCookieInfo(); details.mApplicationModule = (ApplicationModule)resource; details.mCookie = getReferencingSessionCookie(details.mApplicationModule); if (details.mCookie != null) { Diagnostic.println( "Removing a referenced, available pool instance"); disassociateSessionCookie( details.mApplicationModule, details.mCookie, false); setSessionCookieAvailable(details.mCookie, false); } else { Diagnostic.println( "Removing an unreferenced, available pool instance"); } } return details; } /** * Return the least recently used available resource in the pool. *

* Applications should not use this method. * * @param refResource if not null returns the resource that was less recently * used than the reference resource. */ protected final Object seekLRUAvailableResource(Object refResource) { synchronized(mLock) { Object resource = null; if (refResource == null) { RecentlyUsedLinkedListElement elem = mUnrefAvailList.getLRUElement(); if (elem == null) { elem = mRefAvailList.getLRUElement(); } if (elem != null) { resource = elem.getRefObject(); } } else { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(refResource); RecentlyUsedLinkedListElement element = info.mUnrefAvailListElement; // if the element is not in one list then check the other. the // two lists should be mutually exclusive. if (!element.isInList()) { element = info.mRefAvailListElement; } if (element.isInList()) { element = element.getLessRecentlyUsedElement(); if (element != null) { resource = element.getRefObject(); } } } return resource; } } /** * Return the most recently used available resource in the pool. *

* Applications should not use this method. * * @param refResource if not null returns the resource that was more recently * used than the reference resource. */ protected final Object seekMRUAvailableResource(Object refResource) { synchronized(mLock) { Object resource = null; if (refResource == null) { RecentlyUsedLinkedListElement elem = mUnrefAvailList.getMRUElement(); if (elem == null) { elem = mRefAvailList.getMRUElement(); } if (elem != null) { resource = elem.getRefObject(); } } else { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(refResource); RecentlyUsedLinkedListElement element = info.mUnrefAvailListElement; // if the element is not in one list then check the other. the // two lists should be mutually exclusive. if (!element.isInList()) { element = info.mRefAvailListElement; } if (element.isInList()) { element = element.getMoreRecentlyUsedElement(); if (element != null) { resource = element.getRefObject(); } } } return resource; } } protected final void finalizeResource(Object resource, Object details) { super.finalizeResource(resource, details); if (!(details instanceof SessionCookieInfo)) { return; } SessionCookieInfo cookieInfo = (SessionCookieInfo)details; ApplicationModule appModule = cookieInfo.mApplicationModule; SessionCookie referencingCookie = cookieInfo.mCookie; try { boolean isInstanceAlive = isInstanceAliveInternal(appModule); if (!isInstanceAlive) { removeDeadInstance(appModule); return; } // If the deferred passivation has been specified then passivate // the referenced application module's state before continuing. // Otherwise, the application module's state has already been // passivated. if (isUseExclusive() && referencingCookie != null) { ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); manageReferencingState(referencingCookie, appModule, false, ctx); } } finally { // the AM may have been disassociated already. Don't bother to // acquire the extra lock in setSessionCookieAvailable. if (referencingCookie != null) { setSessionCookieAvailable(referencingCookie, true); } } } private void manageReferencingState( SessionCookie referencingCookie , ApplicationModule appModule , boolean reconnect , ApplicationPoolSvcMsgContext ctx) { // invoked when a referenced ApplicationModule is about to be finalized // or recycled. // If the deferred passivation has been specified then passivate // the referenced application module's state before continuing. // Otherwise, the application module's state has already been // passivated. There is a slight chance here that the referencing // session returns to the pool after we have released the pool lock // above, but before we can acquire the session cookie lock. // This will not result in an error but may cause the referencing // session to see old state. This is a danger of disabling failover // support. // jrs 08/22/02 - added support to prevent passivation after the pool // lock was released. see validateSessionCookieAvailable. // // Cannot failover inside the application pool lock. Failover // needs to acquire a session cookie lock first. synchronized(referencingCookie.getSyncLock()) { // if isActivationRequired is true then this ApplicationModule // has already been passivated, do not passivate again. Otherwise, // if failover is enabled then the state was managed upon release // so it is not necessary to manage it here if (!referencingCookie.isActivationRequired(appModule) && !referencingCookie.isFailoverEnabled()) { // Reconnect first. This will prevent redundant connect/disconnect // logic from firing b/w doFailover and this method because // doFailover will disconnect if the ApplicationModule was // not connected when passed to doFailover. if (reconnect) { reconnect(appModule, referencingCookie, true, ctx); } doFailover(appModule, referencingCookie, ctx); } // only if work is pending if (ctx.getRequest() != null) { sendPoolMessage(ctx, referencingCookie, appModule); } } } /** * Checks in an application instance that had previously been checked out. * This makes the application instance avalable for subsequent checkout * requests from this pool. *

* @param instance name of the application module instance to check in. *

* @deprecated Replaced by: * {@link oracle.jbo.common.ampool.SessionCookie#releaseApplicationModule(int)}. *

* Application developers should invoke: * SessionCookie.releaseApplicationModule(SessionCookie.SHARED_UNMANAGED_RELEASE_MODE) * instead of this method. A session cookie instance may be acquired by * invoking: * {@link #createSessionCookie(String, String, Properties)}. *

* This change was necessary to support the SessionCookie interface. Please * see: * {@link oracle.jbo.common.ampool.SessionCookie} * for more information about using SessionCookies with the application pool. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.SessionCookie#releaseApplicationModule(int) * @see oracle.jbo.common.ampool.SessionCookie#useApplicationModule() */ public void checkin(ApplicationModule appModule) { // DO NOT ACQUIRE A POOL LOCK. // This may cause deadlock to occur between threads that are waiting // for the pool lock and those waiting for the session cookie lock (see // cookie.releaseApplicationModule(boolean, boolean) below. SessionCookie cookie = getReferencingSessionCookie(appModule); // Use the cookie to release the application module instead of invoking // the private doUnmanagedCheckin directly. This is done to ensure that // the session cookie synchronization between multi-threaded requests // which are using the 3.2 API. cookie.releaseApplicationModule(SessionCookie.SHARED_UNMANAGED_RELEASE_MODE); removeSessionCookie(cookie); } public void releaseApplicationModule( SessionCookie cookie, boolean manageState) { releaseApplicationModule( cookie , manageState ? SessionCookie.SHARED_MANAGED_RELEASE_MODE : SessionCookie.SHARED_UNMANAGED_RELEASE_MODE); } public void releaseApplicationModule(SessionCookie cookie, int releaseFlags) { ApplicationModule appModule = null; ApplicationModuleInfo appModuleInfo = null; SessionCookieInfo cookieInfo = null; if (cookie.isApplicationModuleReserved()) { synchronized(mLock) { appModule = getApplicationModuleForSession(cookie); appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); } } //YCHUA 10-09-2002 // JRS Only logout if use exclusive. If not use exclusive then other // sessions may still be using this resource. boolean isUseExclusive = isUseExclusive(); if (isUseExclusive) { if (appModule != null) { Hashtable env = appModule.getSession().getEnvironment(); if (JboEnvUtil.isUserAuthenticated(env)) { UserAznUtil.logout(env); } } } // turn off the REMOVE_RESOUCE flag int filteredFlags = (~SessionCookie.REMOVE_RESOURCE) & releaseFlags; boolean removeResource = (releaseFlags & SessionCookie.REMOVE_RESOURCE) > 0 || !isAMPoolingEnabled(); boolean failedOver = false; if (!isUseExclusive) { doSharedCheckin(cookie, removeResource); } else { ApplicationPoolSvcMsgContext ctx = null; switch(filteredFlags) { case SessionCookie.SHARED_MANAGED_RELEASE_MODE: { doManagedCheckin(cookie, removeResource); break; } case SessionCookie.SHARED_UNMANAGED_RELEASE_MODE: { doUnmanagedCheckin(cookie, removeResource); break; } case SessionCookie.RESERVED_MANAGED_RELEASE_MODE: { ctx = new ApplicationPoolSvcMsgContext(); if (appModule != null && cookie.isFailoverEnabled()) { doFailover(appModule, cookie, ctx); failedOver = true; } // don't break we want to set the application module as being // inactive for both release modes } case SessionCookie.RESERVED_UNMANAGED_RELEASE_MODE: { if (ctx == null) { ctx = new ApplicationPoolSvcMsgContext(); } if (appModule != null) { if (!failedOver) { doPassivateHint(cookie, ctx); } } if (appModule != null) { sendPoolMessage( ctx, cookie, cookieInfo, appModule, appModuleInfo); } setApplicationModuleReleased(cookie, true); break; } default: throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_RELEASE_MODE , new Object[] {cookie.getSessionId(), cookie.getApplicationId()}); } } cookieInfo.touch(); } private void doPassivateHint( SessionCookie cookie, ApplicationPoolSvcMsgContext ctx) { ((DefaultConnectionStrategy)getConnectionStrategy()) .initMessageJDBCContext(cookie, cookie.getEnvInfoProvider(), ctx); ctx.passivateState(-1, null, ApplicationModule.PASSIVATE_HINT_FLAG); } private void doSharedCheckin( SessionCookie cookie , boolean removeResource) { ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); ApplicationModule appModule = null; boolean sendPoolMessage = false; SessionCookieInfo cookieInfo = null; ApplicationModuleInfo appModuleInfo = null; synchronized(mLock) { validateSessionCookieInPool(cookie); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); appModule = getApplicationModuleForSession(cookie); // Determine if the pool recognizes this application module // as having been checked out. If not, throw an exception. if (appModule == null) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_CHECKIN , new Object[] {mName}); } appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); disassociateSessionCookie(appModule, cookie, true); cookie.resetStateInternal(); Properties props = new Properties(); props.put(SESSION_COOKIE, cookie); props.put(SVC_MSG_CONTEXT, ctx); super.releaseResource(appModule, props); if (isInstanceAliveInternal(appModule)) { if (removeResource) { removeResource(appModule, ctx); } sendPoolMessage = true; } } // release mLock // only send the pool message if the appModule was detected as alive above. // otherwise, sendPoolMessage will probably just fail. okay for shared // instance since it is already marked as destroyed anyway. if (sendPoolMessage) { sendPoolMessage(ctx, cookie, cookieInfo, appModule, appModuleInfo); } } private void doUnmanagedCheckin( SessionCookie cookie , boolean removeResource) { SessionCookieInfo cookieInfo = null; ApplicationModuleInfo appModuleInfo = null; ApplicationModule appModule = null; synchronized(mLock) { validateSessionCookieInPool(cookie); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); appModule = getApplicationModuleForSession(cookie); // Determine if the pool recognizes this application module // as having been checked out. If not, throw an exception. if ((appModule == null) || isAvailable(appModule)) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_CHECKIN , new Object[] {mName}); } appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); } // release mLock // It's okay to release the pool lock above because // we still have not set the application module as available ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); resetApplicationModule( cookie, cookieInfo, appModule, appModuleInfo , RESET_UNMANAGED_FLAGS | (removeResource ? RESET_REMOVE_FLAG : 0) , ctx); } void resetApplicationModule(SessionCookie cookie, int flags) { ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); resetApplicationModule(cookie, flags, ctx); } ApplicationModule resetApplicationModule( SessionCookie cookie , int flags , ApplicationPoolSvcMsgContext ctx) { ApplicationModule appModule = null; ApplicationModuleInfo appModuleInfo = null; SessionCookieInfo cookieInfo = null; synchronized(mLock) { validateSessionCookieInPool(cookie); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); appModule = getApplicationModuleForSession(cookie); if (appModule == null) { // the session is not referencing an ApplicationModule return null; } appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); // Just in case. Mark the application module as not available setAvailable(appModule, false); } return resetApplicationModule( cookie, cookieInfo, appModule, appModuleInfo, flags, ctx); } ApplicationModule resetApplicationModule( SessionCookie cookie , SessionCookieInfo cookieInfo , ApplicationModule appModule , ApplicationModuleInfo appModuleInfo , int flags , ApplicationPoolSvcMsgContext ctx) { // always inside cookie lock // Internal use only. Unreferences a session cookie from an application // module. Cleans up any transactional state that an application module // may reference. // // The ApplicationModule should be not available before entering // this method. if (appModule == null) { // the session is not referencing an ApplicationModule return null; } // whatever happens in the following block make sure that the pool is // left in a consistent state. boolean removedResource = false; try { // If we are not in release then the application module may have been // disconnected. Reconnect before proceeding. if ((flags & RESET_RECONNECT_FLAG) > 0) { reconnect(appModule, cookie, false, ctx); } // Clean up any old session state that may be associated with this // application module before returning. When a stateless check-in // occurs it is assumed that there is no longer any application module // state associated with the session. if ((flags & RESET_REMOVE_STATE_FLAG) > 0) { int passivationId = cookie.getPassivationId() != SessionCookie.NULL_PASSIVATION_ID ? cookie.getPassivationId() : ((SessionCookieImpl)cookie).mPrevPassivationId; removeState(passivationId, ctx); cookie.setPassivationId(SessionCookie.NULL_PASSIVATION_ID); // remove the reserved passivation id as well. we may not // have ever passivated. int reservedPassivationId = cookie.getReservedPassivationId(); removeState(reservedPassivationId, ctx); cookie.setReservedPassivationId(SessionCookie.NULL_PASSIVATION_ID); } // Rollback the application module first to clean up any potential // database state. This is okay because we have chosen not to retain // application module state. If we do not perform a rollback then // an exception may occur when application module state is activated. // A side effect of rollback should clear the VO and EO caches. // // Moved rollback into resetState on middle tier. Reduce roundtrips. // Reset the non-transactional state of the application module. By // default non-transactional state should be reset. This is necessary // to support bacwards compatibility with 3.2. If a developer has // disabled reset they must be aware that non-transaction application // state like dynamic view objects, where clauses, bind parameters, // order by clauses, view usage instances and application module usage // instances have not been reset. // // A developer may wish to disable reset if they would like to prevent // garbage collection of the application module, view usages, and their // related states. Another benefit of disabling reset may be realized // through re-use of the cached JDBC prepared statements (assumes that // connection pooling has also been disabled). if ((flags & RESET_REMOVE_FLAG) > 0) { // disassociate the SessionCookie before removing the resource // so that removeResource does not initiate a failover synchronized(mLock) { disassociateSessionCookie(appModule, cookie, true); cookie.resetStateInternal(); } // set removedResource before invoking removeResource. // removeResource already knows how to handle a dead instance. removedResource = true; removeResource(appModule, ctx); } else { if (cookie.isConnectionPoolingEnabled()) { // Disconnect. Do not retain the application module state. // This will have the side effect of invoking resetState(true) // // JRS Perform an explicit resetState. The internal resetState // will not make the reset callback anymore (bug 3413849). Okay // resetState should be re-entrant. if ((flags & RESET_MANAGE_SNAPSHOTS_FLAG) > 0) { ctx.resetState(ApplicationModule.RESET_MANAGE_SNAPSHOTS); } else { ctx.resetState(0); } disconnect(appModule, false, cookie, ctx); } else { // handle a no application context exception. it is possible // that resetApplicationModule is invoked after the application // module's old application context has gone out of scope. In // this case the pool should also go out of scope so it is okay // to handle the exception here. It is also a good idea to leave // the pool in a consistent state so that gc, etc. may properly // proceed for the out of scope pool. see bug 2546658. // // JRS Removed the code to handle the exception. Instead // deferring reloading the components until prepareSession // is invoked. if ((flags & RESET_MANAGE_SNAPSHOTS_FLAG) > 0) { ctx.resetState(ApplicationModule.RESET_ROLLBACK_FLAG | ApplicationModule.RESET_MANAGE_SNAPSHOTS); } else { ctx.resetState(ApplicationModule.RESET_ROLLBACK_FLAG); } } } // JRS Must send the pool message now we are about to make the // ApplicationModule available. sendPoolMessage(ctx, cookie, cookieInfo, appModule, appModuleInfo); } finally { if (!removedResource) { synchronized(mLock) { // sendPoolMessage may have resulted in an unexpected removal. if (mInstanceInfo.containsKey(appModule)) { disassociateSessionCookie(appModule, cookie, true); cookie.resetStateInternal(); // test if the application module is still in the pool. // removeResource may have removed it above. if ((flags & RESET_RELEASE_FLAG) > 0) { super.releaseResource(appModule, null); } } } } // release mLock } return appModule; } /** * Check-in the application module as being referenced by the invoking * session thread. *

* This method should be used by pool clients that wish to maintain logical * application module state while still sharing an application module * resource across requests. The method returns a system generated session * id for the check-in that should be used as the unique application module * identifier by the client session.

*

* The application module will be passivated immediately if failover support * has been requested (default). If failover support has been disable, * through the jbo.DoFailover system parameter, the application module will * not be passivated until it is re-used by a session other than the * session checking in the application module.

* * @param appModule the application module that will be checked in *

* @deprecated Replaced by: * {@link oracle.jbo.common.ampool.SessionCookie#releaseApplicationModule(boolean, boolean)}. *

* Application developers should invoke: * SessionCookie.releaseApplicationModule(true, true) *

* instead of this method. A session cookie instance may be acquired by * invoking: * {@link #createSessionCookie(String, String, Properties)}. *

* This change was necessary to support the SessionCookie interface. Please * see: * {@link oracle.jbo.common.ampool.SessionCookie} *

* for more information about using SessionCookies with the application pool. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.SessionCookie#releaseApplicationModule(boolean, boolean) * @see oracle.jbo.common.ampool.SessionCookie#useApplicationModule() */ public String checkinWithSessionState(ApplicationModule appModule) { // DO NOT ACQUIRE A POOL LOCK. // This may cause deadlock to occur between threads that are waiting // for the pool lock and those waiting for the session cookie lock (see // cookie.releaseApplicationModule(boolean, boolean) below. SessionCookie cookie = getReferencingSessionCookie(appModule); // Use the cookie to release the application module instead of invoking // the private doManagedCheckin directly. This is done to ensure that // the session cookie synchronization between multi-threaded requests // which are using the 3.2 API. StringBuffer sb = null; synchronized(cookie.getSyncLock()) { cookie.releaseApplicationModule( SessionCookie.SHARED_MANAGED_RELEASE_MODE); sb = new StringBuffer(cookie.getSessionId()); String value = cookie.getValue(); if (value != null) { sb.append(SessionCookieImpl.COOKIE_SEPARATOR).append(value); } removeSessionCookie(cookie); } return sb.toString(); } private ApplicationModule doCheckout(SessionCookie cookie, SessionCookieInfo cookieInfo, int numOfRetries) { // retry indicates that if recycling/reusing then doCheckout should retry // in event of failure while preparing the AM. ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); // Pass a return structure by reference to access the objects that are // located during findAvailInstance. findAvailInstance is separated // from doCheckout because it contains most of the synchronized work // that is required to locate an application module instance for the // current thread. It is also invoked recursively. SessionCookieInfo referencingCookieInfo = new SessionCookieInfo(); // if the number of retries remaining is zero then force a creation // attempt. ApplicationModuleInfo[] appModuleInfos = new ApplicationModuleInfo[1]; int op = findAvailableInstance( cookie, referencingCookieInfo, ctx, (numOfRetries == 0), appModuleInfos); // Synchronized the operations that must update shared data structures. // The "real" work is performed by the switch statement below. This // is done to prevent the current thread from holding a lock on the pool // while the expensive application module connection/creation logic // is executed below. ApplicationModule reusedAppModule = referencingCookieInfo.mApplicationModule; SessionCookie referencingCookie = referencingCookieInfo.mCookie; ConnectionMetadata oldConnectionMetadata = referencingCookieInfo.mConnectionMetadata; ApplicationModule appModule = null; try { appModule = prepareApplicationModule( reusedAppModule , appModuleInfos[0] , cookie , cookieInfo , op , referencingCookie , oldConnectionMetadata , ctx); } catch (Throwable t) { logPoolEvent(ApplicationPoolListener.CHECKOUT_FAILED); // if we couldn't create a new instance then how do we ever // expect retry to succeed? // // JRS Don't retry checkout if we failed on connect. The connect retry // is handled by another piece of code. if ((op != CREATE_NEW_INSTANCE) && (numOfRetries > 0) && (ctx.getJDBCConnectException() == null) && (ctx.getAuthException() == null)) { if (Diagnostic.isOn()) { Diagnostic.println("An exception occured during checkout."); Diagnostic.println("Retrying checkout."); DebugDiagnostic.printStackTrace(t); } return doCheckout(cookie, cookieInfo, --numOfRetries); } else { ApplicationPoolException apex = new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_CHECKOUT_FAILED , new Object[] {mName} , new Exception[] {new JboException(t)}); throw apex; } } appModule.getSession().getEnvironment().put( Session.JBO_SESSION_COOKIE, cookie); synchronized(mLock) { // synchronized everything below in the pool lock. prevent // monitor thrashing while firing events. if (op != SHARE_INSTANCE) { logPoolEvent(ApplicationPoolListener.INSTANCE_CHECKED_OUT); if (op == RECYCLE_REFERENCED_INSTANCE) { logPoolEvent(ApplicationPoolListener.REFERENCED_INSTANCE_RECYCLED); } else if (op == RECYCLE_UNREFERENCED_INSTANCE) { logPoolEvent(ApplicationPoolListener.UNREFERENCED_INSTANCE_RECYCLED); } else if (op == REUSE_REFERENCED_INSTANCE) { logPoolEvent(ApplicationPoolListener.INSTANCE_REUSED); } } } return appModule; } private ApplicationModule prepareApplicationModule( ApplicationModule appModule , ApplicationModuleInfo appModuleInfo , SessionCookie cookie , SessionCookieInfo cookieInfo , int op , SessionCookie referencingCookie , ConnectionMetadata oldConnectionMetadata , ApplicationPoolSvcMsgContext ctx) { ApplicationModuleInfo localAppModuleInfo = appModuleInfo; switch (op) { case (REUSE_REFERENCED_INSTANCE): { reuseReferencedInstance(cookie, cookieInfo, appModule, ctx); break; } // end REUSE_REFERENCED_INSTANCE case (RECYCLE_REFERENCED_INSTANCE): { try { ctx = recycleReferencedInstance( cookie , cookieInfo , appModule , oldConnectionMetadata , referencingCookie , ctx); } finally { setSessionCookieAvailable(referencingCookie, true); } break; } case (RECYCLE_UNREFERENCED_INSTANCE): { recycleUnreferencedInstance( cookie, cookieInfo, appModule, oldConnectionMetadata, ctx); break; } // end RECYCLE_UNREFERENCED_INSTANCE case (CREATE_NEW_INSTANCE): { try { Properties props = new Properties(); props.put(SESSION_COOKIE, cookie); props.put(SVC_MSG_CONTEXT, ctx); appModule = (ApplicationModule)createResource(props); } catch (java.lang.Exception ex) { if (ex instanceof RuntimeException) { throw (RuntimeException)ex; } else { throw new JboException(ex); } } synchronized(mLock) { associateSessionCookie(appModule, cookie); localAppModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); // Be sure to set the application module as not available within // the synchronized block. This will prevent other threads from // "stealing" the instance from the current thread once it // releases the lock setAvailable(appModule, false); // Immediately add the application module to the referenced // instance list. mReferencedList.addElement((RecentlyUsedLinkedListElement)localAppModuleInfo); } // If the passivation id is not null then the application module // must be activated because the client either: 1) passed a // passivation id in the cookieValue that was specified during // checkout or, 2) specified a session id for which a valid // in-memory cookie existed that referenced a passivation id. // Please note that this branch is not executed if a referenced // instance was re-used. int passivationId = cookie.getPassivationId(); if (passivationId != SessionCookie.NULL_PASSIVATION_ID) { activateState(passivationId, cookie, ctx); } else { prepareSession(appModule, cookie, ctx); } break; } // end CREATE_NEW_INSTANCE } if (((SessionCookieImpl)cookie).mIsPiggybackReservePassivationId) { ctx.reservePassivationId(); } sendPoolMessage(ctx, cookie, cookieInfo, appModule, localAppModuleInfo); return appModule; } private void reuseReferencedInstance( SessionCookie cookie , SessionCookieInfo sessionCookieInfo , ApplicationModule am , ApplicationPoolSvcMsgContext ctx) { Diagnostic.println("Reusing a cached session application module instance"); ConnectionMetadata cookieMetadata = sessionCookieInfo.mConnectionMetadata; reconnect(am, cookie, true, ctx); // If the doActivate flag is marked true on the session cookie // then activate the application module. The application module // must have been passivated because database state existed and the // application module transaction had been disconnected. if (cookie.isActivationRequired(am)) { int passivationId = cookie.getPassivationId(); activateState(passivationId, cookie, ctx); } // JRS Why would this be null? else if (cookieMetadata != null) { EnvInfoProvider prov = cookie.getEnvInfoProvider(); Hashtable env = (Hashtable)cookie.getEnvironment().clone(); if (prov != null) { prov.getInfo(null, env); } // tells the MT to prepare the session if the AM connection MD // has a different signature than the specified cookie MD. ctx.setSessionData(new SessionData(env, cookie.getUserData())); ctx.hintPrepareSession(null, cookieMetadata); } } private ApplicationPoolSvcMsgContext recycleReferencedInstance( SessionCookie cookie , SessionCookieInfo sessionCookieInfo , ApplicationModule am , ConnectionMetadata oldConnectionMetadata , SessionCookie referencingCookie , ApplicationPoolSvcMsgContext origCtx) { Diagnostic.println("Recycling a referenced, available pool instance"); manageReferencingState( referencingCookie , am , true /* reconnect */ , origCtx); ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); // If an available, unreferenced application module was not located above // then grab the first referenced application module off of the FIFO // stack. Use a FIFO stack to ensure that the oldest references are // recycled first. // // If the referencing cookie and the existing cookie connection // metadata are not equal then do a disconnect/connect. // This is necessary because the JDBC credentials may be different // between the two sessions. if (!compareConnectionMetadata( oldConnectionMetadata, sessionCookieInfo.mConnectionMetadata)) { // we only need to specify the reset internal flag here so that // the snapshot stack is reset before rollback clobbers it. ctx.resetState(ApplicationModule.RESET_MANAGE_SNAPSHOTS); disconnect(am, false, referencingCookie, ctx); connect(am, cookie, ctx); } else { // Otherwise, simply reconnect and then rollback the application // module state reconnect(am, cookie, false, ctx); // Rollback. This is necessary to remove any // unposted transaction validation listeners which may still be // registered on a stateful application module's transaction. // // Moved rollback, cache clearing, to resetState on the middle tier. // Reload the AM state for the new session ctx.resetState((ApplicationModule.RESET_RELOAD_FLAG | ApplicationModule.RESET_ROLLBACK_FLAG | ApplicationModule.RESET_MANAGE_SNAPSHOTS)); } // If the passivation id is not null then the application module must // be activated because the client either: 1) passed a passivation id // in the cookieValue that was specified during checkout or, 2) // specified a session id for which a valid in-memory cookie existed // that referenced a passivation id. Please note that this branch is // not executed if a referenced instance was re-used. int passivationId = cookie.getPassivationId(); if (passivationId != SessionCookie.NULL_PASSIVATION_ID) { activateState(passivationId, cookie, ctx); } else { prepareSession(am, cookie, ctx); } return ctx; } private void recycleUnreferencedInstance( SessionCookie cookie , SessionCookieInfo sessionCookieInfo , ApplicationModule am , ConnectionMetadata oldConnectionMetadata , ApplicationPoolSvcMsgContext ctx) { Diagnostic.println("Recycling an unreferenced, available pool instance"); // If the referencing cookie and the existing cookie environment // signatures are not equal then do a disconnect/connect. // This is necessary because the JDBC credentials may be different // between the two sessions. if (!compareConnectionMetadata( oldConnectionMetadata, sessionCookieInfo.mConnectionMetadata)) { disconnect(am, false, cookie, ctx); connect(am, cookie, ctx); } else { reconnect(am, cookie, false, ctx); } // If the passivation id is not null then the application module must // be activated because the client either: 1) passed a passivation id // in the cookieValue that was specified during checkout or, 2) // specified a session id for which a valid in-memory cookie existed // that referenced a passivation id. Please note that this branch is // not executed if a referenced instance was re-used. int passivationId = cookie.getPassivationId(); if (passivationId != SessionCookie.NULL_PASSIVATION_ID) { activateState(passivationId, cookie, ctx); } else { prepareSession(am, cookie, ctx); } } private void removeDeadInstance(ApplicationModule appModule) { Diagnostic.println("A dead application module instance was detected"); Diagnostic.println("The application module instance was removed from the pool"); removeResource(appModule); // watch out for cycle. // ->removeDeadInstance // ->removeResource // ->setState // ->finalizeResource // ->removeDeadInstance // ->removeResource // ->setState // ->removeResourceInternal // } private void doManagedCheckin( SessionCookie cookie , boolean removeResource) { ApplicationModule appModule = null; SessionCookieInfo cookieInfo = null; ApplicationModuleInfo appModuleInfo = null; synchronized(mLock) { validateSessionCookieInPool(cookie); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); appModule = getApplicationModuleForSession(cookie); // Determine if the pool recognizes this application module // as having been checked out. If not, throw an exception. if ((appModule == null) || isAvailable(appModule)) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_CHECKIN , new Object[] {mName}); } appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); } // release mLock // It's okay to release the pool lock above because // we still have not set the application module as available boolean removedResource = false; try { ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); boolean doFailover = cookie.isFailoverEnabled(); // If failover support has been requested then passivate the // application module before setting it as available. if (doFailover || removeResource) { Diagnostic.println("Application Module failover is enabled"); doFailover(appModule, cookie, ctx); } else { doPassivateHint(cookie, ctx); } if (removeResource) { cookie.setActivationRequired(true); removedResource = true; removeResource(appModule, ctx); } else { // Release the application module's JDBC connection to the // connection pool. Retain the application module state so that // the application does not have to be activated if it is requested // by the same session. if (cookie.isConnectionPoolingEnabled()) { disconnect(appModule, true, cookie, ctx); } } sendPoolMessage(ctx, cookie, cookieInfo, appModule, appModuleInfo); } finally { if (!removedResource) { synchronized(mLock) { // sendPoolMessage may have resulted in an unexpected removal if (mInstanceInfo.containsKey(appModule)) { // Refresh the instance's position in the referenced list mReferencedList.touchElement( (RecentlyUsedLinkedListElement)mInstanceInfo.get(appModule)); super.releaseResource(appModule, null); } // release mLock } } } } /** * @deprecated Replaced by * {@link oracle.jbo.common.ampool.ConnectionStrategy#createApplicationModule(SessionCookie, EnvInfoProvider)}. * All extending logic that was implemented here should be implemented in a * custom ConnectionStrategy class that extends: * {@link oracle.jbo.common.ampool.DefaultConnectionStrategy}. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.ConnectionStrategy#createApplicationModule(SessionCookie, EnvInfoProvider) */ public ApplicationModule createNewInstance() throws Exception { // The internal blocks still invoke this deprecated API in order to // account for custom logic. return (ApplicationModule)createResource(null); } public Object instantiateResource(Properties properties) { ApplicationPoolSvcMsgContext ctx = null; SessionCookie cookie = null; ApplicationModule am = null; if (properties != null) { cookie = (SessionCookie)properties.get(SESSION_COOKIE); ctx = (ApplicationPoolSvcMsgContext)properties.get(SVC_MSG_CONTEXT); } if (cookie != null) { am = getConnectionStrategy().createApplicationModule( cookie , cookie.getEnvInfoProvider()); } else { am = getConnectionStrategy().createApplicationModule(getEnvironment()); } ApplicationModuleInfo info = new ApplicationModuleInfo(am); synchronized(mLock) { mInstanceInfo.put(am, info); // Don't set available yet. This may cause a race condition if // this method has been invoked by doCheckout. It is the responsiblity // of the client to set the application module as available when it // is ready to go. Setting availability false just to make sure // that this instance cannot be "grabbed" by another thread after // it has been created but before it is available. // setAvailable(am, false); } // release mLock if ((cookie != null) && (ctx != null)) { connect(am, cookie, ctx); } return am; } /** * Checks out stateless application instance from the pool. If the pool does * not have any available instances, it will create a new instance and return * it to the request thread. *

* @deprecated Replaced by: * {@link oracle.jbo.common.ampool.SessionCookie#useApplicationModule()}. *

* Application developers should invoke: * SessionCookie.useApplicationModule() *

* instead of this method. A session cookie instance may be acquired by * invoking: * {@link #createSessionCookie(String, String, Properties)}. *

* This change was necessary to support the SessionCookie interface. Please * see: * {@link oracle.jbo.common.ampool.SessionCookie} * for more information about using SessionCookies with the application pool. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.SessionCookie#useApplicationModule() * @see oracle.jbo.common.ampool.SessionCookie#releaseApplicationModule(boolean, boolean) */ public ApplicationModule checkout() throws Exception { // DO NOT ACQUIRE A POOL LOCK. // This may cause deadlock to occur between threads that are waiting // for the pool lock and those waiting for the session cookie lock (see // cookie.useApplicationModule(boolean) below. SessionCookie cookie = createSessionCookie(getName(), getNextSessionId(), null); // Use the cookie to release the application module instead of invoking // the private doCheckout directly. This is done to ensure that // the session cookie synchronization between multi-threaded requests // which are using the 3.2 API. Do not down the session cookie mutex // since this was not performed in 3.2. return cookie.useApplicationModule(false /* lock */); } /** * @deprecated Replaced by {@link #removeResources()}. Method may be * confused with releaseResource. *

* @since 9.0.2 * @see #removeResources() */ public void releaseInstances() { removeResources(); } /** * @deprecated * @since 9.0.2 * @see #removeResource(Object) */ protected void releaseInstance(ApplicationModule instance) { removeResource(instance); } private void removeResource( ApplicationModule instance, ApplicationPoolSvcMsgContext ctx) { synchronized(mLock) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(instance); if (info != null) { info.mPendingContext = ctx; } } removeResource(instance); } protected boolean removeResourceInternal(Object resource) { // just cleans up pool state boolean wasRemoved = false; ApplicationPoolSvcMsgContext ctx = null; ApplicationModuleInfo appModuleInfo = null; synchronized(mLock) { appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(resource); if (appModuleInfo != null && (appModuleInfo.mReferencingSessionCookie != null)) { // can't remove an ApplicationModule which is still referenced // return addToRemoveList(resource); return false; } // a remove exception from putting the pool in an invalid state. wasRemoved = super.removeResourceInternal(resource); // Multithreaded apps may cause the same AM to be removed // concurrently. See processRemoveList. if (appModuleInfo != null) { ctx = appModuleInfo.mPendingContext; mRefAvailList.removeElement(appModuleInfo.mRefAvailListElement); mUnrefAvailList.removeElement(appModuleInfo.mUnrefAvailListElement); mInstanceInfo.remove(resource); } } // only invoke remove if we actually removed an instance. if (wasRemoved) { boolean sendMessage = false; if (ctx == null) { ctx = new ApplicationPoolSvcMsgContext(); sendMessage = true; } // indicates that the pool has already cleaned up its own refs // to the AM. If an exception occurs now, there is nothing else // the pool can do with the AM. ctx.setApplicationModuleRemoved(true); ctx.resetState(ApplicationModule.RESET_MANAGE_SNAPSHOTS); ctx.removeApplicationModule(); if (sendMessage) { try { sendPoolMessage( ctx, null, null, (ApplicationModule)resource, appModuleInfo); } catch (Throwable t) { // nothing the pool can do now. } } } return wasRemoved; } public ArrayList removeResources() { ArrayList resources = null; synchronized(mLock) { resources = super.removeResources(); mInstanceInfo.clear(); Iterator iter = mSessionCookieInfo.values().iterator(); while (iter.hasNext()) { ((SessionCookieInfo)iter.next()).mApplicationModule = null; } mReferencedList.reset(); mRefAvailList.reset(); mUnrefAvailList.reset(); } int size = resources.size(); for (int i = 0; i < size; i++) { ApplicationModule instance = (ApplicationModule)resources.get(i); ApplicationPoolSvcMsgContext ctx = new ApplicationPoolSvcMsgContext(); ctx.setApplicationModuleRemoved(true); ctx.resetState(ApplicationModule.RESET_MANAGE_SNAPSHOTS); ctx.removeApplicationModule(); try { sendPoolMessage(ctx, null, instance); } catch (Throwable t) { // nothing the pool can do now. } } return resources; } public int getAvailableInstanceCount() { return getAvailableResourceCount(); } int getReferencedResourceCount() { synchronized(mLock) { return mReferencedList.getSize(); } } int getReferencingSessionCount() { synchronized(mLock) { return mReferencingSessionCount; } } int getSessionCount() { synchronized(mLock) { return mSessionCookieInfo.size(); } } /** * @deprecated Replaced by {@link #getAvailableInstanceCount()} * @since 9.0.2 * @see #getAvailableInstanceCount() */ public int getAvailableNumPools() { return getAvailableResourceCount(); } public int getInstanceCount() { return getResourceCount(); } public ApplicationModule getInstance(int index) { return (ApplicationModule)getResource(index); } /** * Returns an application module for the specified session. The session * id must have been generated by a previous call to: * {@link #checkinWithSessionState(ApplicationModule)} *

* against this application module pool. *

* The session id should be used to re-create an application module state * from a previous request. *

* If an unrecognized session id is specified then the pool should return * a stateless application module instance. *

* @param sessionId a session identifier from a previous request * * @deprecated Replaced by: * {@link oracle.jbo.common.ampool.SessionCookie#useApplicationModule()}. *

* Application developers should invoke: * SessionCookie.useApplicationModule() *

* instead of this method. A session cookie instance may be acquired by * invoking: * {@link #createSessionCookie(String, String, Properties)}. *

* This change was necessary to support the SessionCookie interface. Please * see: * {@link oracle.jbo.common.ampool.SessionCookie} *

* for more information about using SessionCookies with the application pool. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.SessionCookie#useApplicationModule() * @see oracle.jbo.common.ampool.SessionCookie#releaseApplicationModule(boolean, boolean) */ public ApplicationModule checkout(String cookieValue) { // DO NOT ACQUIRE A POOL LOCK. // This may cause deadlock to occur between threads that are waiting // for the pool lock and those waiting for the session cookie lock (see // cookie.useApplicationModule(boolean) below. String sessionId = SessionCookieImpl.parseSessionId(cookieValue); // Check to see if an existing cookie exists first. SessionCookie cookie = getSessionCookieFactory() .createSessionCookie(getName(), sessionId, this, null); synchronized(mLock) { SessionCookieInfo info = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (info != null) { cookie = info.mCookie; } } if (cookie == null) { cookie = createSessionCookie(getName(), sessionId, null); int passivationId = SessionCookieImpl.parsePassivationId(cookieValue); if (passivationId != SessionCookie.NULL_PASSIVATION_ID) { cookie.setPassivationId(passivationId); } } // Use the cookie to release the application module instead of invoking // the private doCheckout directly. This is done to ensure that // the session cookie synchronization between multi-threaded requests // which are using the 3.2 API. Do not down the session cookie mutex // since this was not performed in 3.2. return cookie.useApplicationModule(false /* lock */); } public ApplicationModule useApplicationModule(SessionCookie cookie, boolean checkout) { ApplicationModule rtnAppModule = null; SessionCookieInfo cookieInfo = null; synchronized(mLock) { validateSessionCookieInPool(cookie); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); ApplicationModule appModule = getApplicationModuleForSession(cookie); // If the cookie is referencing an application module instance and // the cookie that is referencing the application module is equal to // the cookie that was passed in and the application module instance // is checked out then the request has originated from a session has // already checked out the application module. It is not // necessary to check out the application module instance again. if (isUseExclusive()) { if (appModule != null && !isAvailable(appModule)) { rtnAppModule = appModule; } } else { rtnAppModule = appModule; } } if ((rtnAppModule == null) && checkout) { // only validate the session cookie availability when an AM is not // already reserved and when we are checking out an AM. In the other // scenarios we either do not care if the cookie is available or // it is not possible for the cookie to be unavailable // // JRS bug 3770068. Moved to findAvailableInstance. This needs // to occur in the pool lock when an AM is being selected for the // cookie. //validateSessionCookieAvailable(cookie); // No need to synchronize in here. The private method is synchronized // properly. rtnAppModule = doCheckout(cookie, cookieInfo, 1); } else if (checkout) { setApplicationModuleReleased(rtnAppModule, false); } cookieInfo.touch(); return rtnAppModule; } /** * @deprecated Replaced by {@link #getName()}. * @since 5.0 * @see #getName() */ public String getPoolName() { return getName(); } public String getName() { // Do not synchronize. mName is not mutable. return mName; } public Hashtable getUserData() { synchronized(mLock) { return mUserData; } } public void setUserData(Hashtable userData) { synchronized(mLock) { mUserData = userData; } } /** * Returns the user name. * * @deprecated This value should be passed to the pool connection strategy as * SessionCookie environment or by implementing an EnvInfoProvider. The * value may be acquired from the ApplicationPool or SessionCookie * environment by using the Configuration.DB_USERNAME_PROPERTY key. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.ConnectionStrategy * @see oracle.jbo.common.ampool.EnvInfoProvider * @see oracle.jbo.common.ampool.SessionCookie */ public String getUserName() { synchronized(mLock) { return mUserName; } } /** * @deprecated This value should be passed to the pool connection strategy as * SessionCookie environment or by implementing an EnvInfoProvider. The * value may be set in the ApplicationPool or SessionCookie * environment by using the Configuration.DB_USERNAME_PROPERTY key. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.ConnectionStrategy * @see oracle.jbo.common.ampool.EnvInfoProvider * @see oracle.jbo.common.ampool.SessionCookie */ public void setUserName(String userName) { synchronized(mLock) { mUserName = userName; } } /** * Returns the password. * * @deprecated This value should be passed to the pool connection strategy as * SessionCookie environment or by implementing an EnvInfoProvider. The * value may be acquired from the ApplicationPool or SessionCookie * environment by using the Configuration.DB_PASSWORD_PROPERTY key. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.ConnectionStrategy * @see oracle.jbo.common.ampool.EnvInfoProvider * @see oracle.jbo.common.ampool.SessionCookie */ public String getPassword() { synchronized(mLock) { return mPassword; } } /** * @deprecated This value should be passed to the pool connection strategy as * SessionCookie environment or by implementing an EnvInfoProvider. The * value may be set in the ApplicationPool or SessionCookie * environment by using the Configuration.DB_PASSWORD_PROPERTY key. *

* @since 9.0.2 * @see oracle.jbo.common.ampool.ConnectionStrategy * @see oracle.jbo.common.ampool.EnvInfoProvider * @see oracle.jbo.common.ampool.SessionCookie */ public void setPassword(String password) { synchronized(mLock) { mPassword = password; } } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { try { in.defaultReadObject(); } catch (java.io.NotActiveException naex) { // Only thrown if defaultReadObject is not invoked from readObject } try { PoolMgr.getInstance().addResourcePool(mName, this); initialize(); } catch (oracle.jbo.JboException jex) { // If a JboException is thrown then the pool must already exist. } } public void commitAndSyncCache(ApplicationModule instance) { synchronized(mLock) { int snapId = instance.getTransaction().commitAndSaveChangeSet(); if (snapId >= 0) { Transaction txn = null; boolean wasConnected = true; ApplicationModule current = null; ApplicationModuleInfo instanceInfo = null; int resourceCount = getResourceCount(); for(int i = 0 ; i < resourceCount; i++) { current = (ApplicationModule)getResource(i); if (current == null) { continue; } instanceInfo = (ApplicationModuleInfo)mInstanceInfo.get(current); if (current != instance) { // Only apply the change set if the application module // references some transactional state. Whether or not the // application module references transactional state is // determined by checking if the application module is // referenced by a session cookie. An application module will // only be referenced by a session cookie if it is currently // in use or if it has been released with state management // enabled. Be sure not to apply the change set if forced // activation is required because this implies that the // transactional state of the application module has already // been cleared. We do not have to deal with unreferenced // application modules b/c those application modules should // already have had their transactional states cleared. SessionCookie refCookie = getReferencingSessionCookie(current); if (refCookie != null && !refCookie.isActivationRequired(current)) { txn = current.getTransaction(); if (!txn.isConnected()) { // If the txn is not connected and the instance is a // member of the referenced list then it must have been // released to the pool with the state management // enabled. Go ahead and reconnect the application // module instance so that we may update the state of // its caches. wasConnected = false; txn.reconnect(); } txn.applyChangeSet(snapId); if (!wasConnected) { txn.disconnect(true); } wasConnected = true; } } } instance.getTransaction().removeChangeSet(snapId); } } } private ApplicationModule getApplicationModuleForSession( SessionCookie cookie) { SessionCookieInfo info = (SessionCookieInfo)mSessionCookieInfo.get(cookie); return info.mApplicationModule; } private SessionCookie getReferencingSessionCookie(ApplicationModule instance) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(instance); return info.mReferencingSessionCookie; } private Iterator getReferencingSessionCookies(ApplicationModule instance) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(instance); return info.mReferencingSessionCookies.keySet().iterator(); } private boolean isReferenced(ApplicationModule instance) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(instance); return (info.mReferencingSessionCookie != null); } private void associateSessionCookie(ApplicationModule appModule, SessionCookie cookie) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(appModule); SessionCookieInfo sessionCookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); info.mReferencingSessionCookies.put(cookie, null); if (info.mReferencingSessionCookie == null) { info.mReferencingSessionCookie = cookie; } sessionCookieInfo.mApplicationModule = appModule; if (!sessionCookieInfo.mIsReferencingState) { sessionCookieInfo.mIsReferencingState = true; mReferencingSessionCount++; } } private void disassociateSessionCookie( ApplicationModule appModule , SessionCookie cookie , boolean hardReset) { // a hard reset occurs when the SessionCookie will no longer reference // the AM and no longer references an AM session/txn state. a soft // reset occurs when the SessionCookie will no longer reference // the AM but will still reference an AM session/txn state. // // hard resets are the result of an unmanaged release. soft // resets are the result of a recycle event or of a gc. ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(appModule); info.mReferencingSessionCookies.remove(cookie); if (cookie.equals(info.mReferencingSessionCookie)) { info.mReferencingSessionCookie = null; // nominate the next cookie identifed by the iterator. I think this // is the best that we can do right now. Iterator refSessionCookies = getReferencingSessionCookies(appModule); if (refSessionCookies.hasNext()) { associateSessionCookie( appModule, (SessionCookie)refSessionCookies.next()); } } mReferencedList.removeElement(info); if (info.mRefAvailListElement.isInList()) { // transition from the refavail list to the unrefavail list mRefAvailList.removeElement(info.mRefAvailListElement); mUnrefAvailList.touchElement(info.mUnrefAvailListElement); } // Just reset the SessionCookie's AM reference. Do not reset the // SessionCookie info state. This is referred to as a soft reset. SessionCookieInfo sessionCookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); sessionCookieInfo.mApplicationModule = null; if (hardReset) { sessionCookieInfo.resetState(); } } private void doFailover( ApplicationModule appModule , SessionCookie cookie , ApplicationPoolSvcMsgContext ctx) { int reservedPassivationId = cookie.getReservedPassivationId(); int prevPassivationId = cookie.getPassivationId() != SessionCookie.NULL_PASSIVATION_ID ? cookie.getPassivationId() : ((SessionCookieImpl)cookie).mPrevPassivationId; // If cookie activation is required then the session state was already // failed over properly. doFailover may be invoked even if the state // has already been failed over if the pool had to failover the session // application module in order to persist some database state // (i.e. the state of some database cursors) at the end of a request when // connection pooling was enabled, but failover was disabled. If the // application module for this session is recycled then there is no // need to failover the application state a second time. if (cookie.isActivationRequired(appModule)) { return; } // the MT may need the JDBC context in order to reconnect, if necessary ((DefaultConnectionStrategy)getConnectionStrategy()) .initMessageJDBCContext(cookie, cookie.getEnvInfoProvider(), ctx); if (reservedPassivationId != SessionCookie.NULL_PASSIVATION_ID) { ctx.passivateState(reservedPassivationId, null, 0); // Reset the reserved passivation id after the application module // has been properly passivated with the reserved passivation id cookie.setReservedPassivationId(SessionCookie.NULL_PASSIVATION_ID); } else { ctx.passivateState(-1, null, 0); } // After passivating the application module attempt to clean up any // records associated with previously passivated state. State may not be // removed if the VM instance has died (pass a previous state to this // method? Add logic to the application registry to handle this?). removeState(prevPassivationId, ctx); } private boolean isInstanceAliveInternal(ApplicationModule instance) { synchronized(mLock) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(instance); return info.mIsInstanceAlive; } } /** * Performs tests to determine if the application module instance is still * alive. This is only invoked for an application module instance when * it is recycled/reused. The default implementation tests the application * module by invoking the remote getSession().getVersion(). *

* This method may be overriden to provide a custom test. */ protected boolean isInstanceAlive(ApplicationModule instance) { boolean isInstanceAlive = true; try { // This is a very inexpensive method that will force a round-trip // to the middle tier. instance.getSession().getVersion(); } catch (Throwable ex) { isInstanceAlive = false; } return isInstanceAlive; } public boolean validateSessionCookieInPool(SessionCookie cookie) { synchronized(mLock) { if (mSessionCookieInfo.get(cookie) == null) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_HANDLE , new Object[] { cookie.getSessionId() , cookie.getApplicationId() , getName()}); } } return validateSessionCookie(cookie); } public boolean validateSessionCookie(SessionCookie cookie) { // Compare the pool signature of the cookie with the signature of the pool // If the two signatures are the same then the cookie must have been // created by an instance of this pool and is consequently a valid // handle to the pool. if (getSignature() != cookie.getPoolSignature()) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_INVALID_HANDLE , new Object[] {cookie.getSessionId(), cookie.getApplicationId(), getName()}); } return true; } private void setApplicationModuleReleased( SessionCookie cookie, boolean isReleased) { synchronized(mLock) { validateSessionCookieInPool(cookie); ApplicationModule appModule = getApplicationModuleForSession(cookie); // Determine if the pool recognizes this application module // as having been checked out. If not, throw an exception. if ((appModule != null) && !isAvailable(appModule)) { setApplicationModuleReleased(appModule, isReleased); appModule.getSession().getEnvironment() .remove(Session.JBO_SESSION_COOKIE); } else { // Do not throw. 9.0.2 applications did not throw if // a reserved release occured multiple times. // throw new ApplicationPoolException( // AMPoolMessageBundle.class // , AMPoolMessageBundle.EXC_AMPOOL_INVALID_CHECKIN // , new Object[] {mName}); } } } boolean isApplicationModuleReleased(SessionCookie cookie) { boolean isReleased = true; synchronized(mLock) { validateSessionCookieInPool(cookie); ApplicationModule appModule = getApplicationModuleForSession(cookie); // Determine if the pool recognizes this application module // as having been checked out. If not, throw an exception. if (appModule != null) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(appModule); isReleased = info.mIsReleased; } } return isReleased; } private void setApplicationModuleReleased(Object appModule, boolean isReleased) { synchronized(mLock) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get(appModule); setApplicationModuleReleased( info, (ApplicationModule)appModule, isReleased); } } private void setApplicationModuleReleased( ApplicationModuleInfo appModuleInfo , ApplicationModule appModule , boolean isReleased) { notifyApplicationModuleReleased(appModule, isReleased); appModuleInfo.mIsReleased = isReleased; } /** * May be customized to perform special release processing. *

* Please note that this notification may be delivered while the * ApplicationPool is still in an inconsistent state. A customizing * pool should *not* attempt to work with the pool when handling this * notification. *

* Customizing pools should also be careful to minimize the amount of work * performed by any notification handler as this notification may be * delivered while the ApplicationPool monitor is held therefore preventing * other threads from using the ApplicationPool. *

* The notification will be delivered before the ApplicationModule is * released -- though, in the case that the pool monitor is held this will * not make a difference since this codepath will be single threaded. */ protected void notifyApplicationModuleReleased( ApplicationModule appModule, boolean isReleased) { } protected boolean setState(Object appModule, byte state, Properties props) { byte oldState = getState(appModule); if ((oldState == state) || (oldState == ResourceInfo.STATE_DESTROYED)) { return super.setState(appModule, state, props); } SessionCookie cookie = null; ApplicationPoolSvcMsgContext ctx = null; if (props != null) { cookie = (SessionCookie)props.get(SESSION_COOKIE); ctx = (ApplicationPoolSvcMsgContext)props.get(SVC_MSG_CONTEXT); } switch(state) { // this is where shared AM initialization/destruction may be performed case(ResourceInfo.STATE_AVAILABLE_REFERENCED): { // reconnect here to make sure that our connection is still // valid if (cookie != null) { // in case the connection has gone bad. Note that this is // a stateless reconnect the shared AM should be stateless // (even though the caches are managed) so this should be okay reconnect((ApplicationModule)appModule, cookie, false, ctx); prepareSession((ApplicationModule)appModule, cookie, ctx); } break; } case(ResourceInfo.STATE_AVAILABLE_UNREFERENCED): { break; } } synchronized(mLock) { ApplicationModuleInfo info = (ApplicationModuleInfo)mInstanceInfo.get( appModule); switch(state) { case(ResourceInfo.STATE_AVAILABLE_REFERENCED): case(ResourceInfo.STATE_AVAILABLE_UNREFERENCED): { // if the ApplicationModule is in the referenced list if (info.isInList()) { mUnrefAvailList.removeElement(info.mUnrefAvailListElement); mRefAvailList.touchElement(info.mRefAvailListElement); } else { mRefAvailList.removeElement(info.mRefAvailListElement); mUnrefAvailList.touchElement(info.mUnrefAvailListElement); } // an available application module is always released setApplicationModuleReleased( info, (ApplicationModule)appModule, true); break; } case(ResourceInfo.STATE_DESTROYED): case(ResourceInfo.STATE_UNAVAILABLE): { mRefAvailList.removeElement(info.mRefAvailListElement); mUnrefAvailList.removeElement(info.mUnrefAvailListElement); // an unavailable application module is always not released setApplicationModuleReleased( info, (ApplicationModule)appModule, false); break; } default: { } } } // super.setState will perform its own synchronization. don't include // this call because it could potentially deadlock (super.setState // will make finalizeResource callback in some cases which will // require a cookie lock which was not already held). // // should be okay since the resource latch is still held none of // the above logic should result in an inconsistent pool state. return super.setState(appModule, state, props); } public void setAvailable(ApplicationModule appModule) { super.setAvailable(appModule); } /** * @deprecated Implementation detail. This method has been made protected. * @since 9.0.2 */ public boolean isAvailable(ApplicationModule appModule) { return super.isAvailable(appModule); } public long getCreationTimeMillis(ApplicationModule appModule) { return super.getCreationTimeMillis(appModule); } public long getTimeToCreateMillis(ApplicationModule appModule) { return super.getTimeToCreateMillis(appModule); } protected int getMinAvailableSize() { return getProperty( PropertyMetadata.ENV_AMPOOL_MIN_AVAIL_SIZE.pName , getEnvironment() , Integer.parseInt(PropertyMetadata.ENV_AMPOOL_MIN_AVAIL_SIZE.pDefault)); } protected int getMaxAvailableSize() { return getProperty( PropertyMetadata.ENV_AMPOOL_MAX_AVAIL_SIZE.pName , getEnvironment() , Integer.parseInt(PropertyMetadata.ENV_AMPOOL_MAX_AVAIL_SIZE.pDefault)); } protected int getMaxInactiveAge() { return getProperty( PropertyMetadata.ENV_AMPOOL_MAX_INACTIVE_AGE.pName , getEnvironment() , Integer.parseInt(PropertyMetadata.ENV_AMPOOL_MAX_INACTIVE_AGE.pDefault)); } protected int getTimeToLive() { return getProperty( PropertyMetadata.ENV_AMPOOL_TIME_TO_LIVE.pName , getEnvironment() , Integer.parseInt(PropertyMetadata.ENV_AMPOOL_TIME_TO_LIVE.pDefault)); } protected int getRecycleThreshold() { return getProperty( PropertyMetadata.ENV_POOL_RECYCLE_THRESHOLD.pName , getEnvironment() , Integer.parseInt(PropertyMetadata.ENV_POOL_RECYCLE_THRESHOLD.pDefault)); } protected ResourcePoolLogger createPoolLogger() { return new ApplicationPoolLogger(this); } public Statistics getStatistics() { return ((ApplicationPoolLogger)mLogger).getStatistics(); } void computeSessionAgeHistogram(AgeHistogram sessionAgeHistogram) { Iterator sessionCookieInfos = null; synchronized(mLock) { sessionCookieInfos = ((HashMap)mSessionCookieInfo.clone()).values().iterator(); } while (sessionCookieInfos.hasNext()) { SessionCookieInfo info = (SessionCookieInfo)sessionCookieInfos.next(); boolean found = false; for (int i = sessionAgeHistogram.mAges.length - 1; i >= 0; --i) { if (info.mLastUpdate.before(sessionAgeHistogram.mAges[i])) { sessionAgeHistogram.mBuckets[i+1]++; found = true; break; } } if (!found) { sessionAgeHistogram.mBuckets[0]++; } } } void computeAppModuleAgeHistograms( AgeHistogram refInstanceAgeHistogram , AgeHistogram unrefInstanceAgeHistogram) { Iterator instanceInfos = null; synchronized(mLock) { instanceInfos = ((HashMap)mInstanceInfo.clone()).values().iterator(); } while (instanceInfos.hasNext()) { ApplicationModuleInfo info = (ApplicationModuleInfo)instanceInfos.next(); if (info.isInList()) { boolean found = false; for (int i = refInstanceAgeHistogram.mAges.length - 1; i >= 0; --i) { if (info.getLastUpdate().before(refInstanceAgeHistogram.mAges[i])) { refInstanceAgeHistogram.mBuckets[i+1]++; found = true; break; } } if (!found) { refInstanceAgeHistogram.mBuckets[0]++; } } else { boolean found = false; for (int i = unrefInstanceAgeHistogram.mAges.length - 1; i >= 0; --i) { if (info.getLastUpdate().before(unrefInstanceAgeHistogram.mAges[i])) { unrefInstanceAgeHistogram.mBuckets[i+1]++; found = true; break; } } if (!found) { unrefInstanceAgeHistogram.mBuckets[0]++; } } } } /** * Determines how long a request should wait for an available application * module instance when the maximum pool size has been reached. Applications * may override this method to configure the wait time. */ protected long getMaxWaitTime() { return MAX_WAIT_TIME; } /** * Invoked by the application pool implementation when an application module * instance is recycled for use by a session different than the session that * had previously used that instance. The oldConnectionMetadata represents * the JDBC connection metadata that was used to establish the JDBC connection * for the session that previously used the application module instance. The * newConnectionMetadata represents the JDBC connection metadata of the session * for which the application module is being recycled. The newConnectionMetadata * will be null if this is the first time that the session has acquired an * application module instance from the pool. *

* If compareConnectionMetadata returns true then the pool assumes that the * oldConnectionMetadata and the newConnectionMetadata are equal and does * not attempt to connect the application module with the new session * connection metadata. If compareConnectionMetadata returns false then the * pool assumes that the oldConnectionMetadata and the newConnectionMetadata * are note equal and will attempt to connect the application module using * the new connection metadata. *

* If it is known at design time that the connection metadata will not change * then applications may set the property, jbo.ampool.dynamicjdbccredentials, * equal to false. This will prevent the potential overhead of performing a * disconnect/connect whenever a new session causes the pool to recycle an * application module instance. *

* * @returns true if the connection metadata are equal * false if the connection metadata are not equal */ protected boolean compareConnectionMetadata(ConnectionMetadata oldConnectionMetadata, ConnectionMetadata newConnectionMetadata) { boolean isEqual = false; if (oldConnectionMetadata != null) { // If the pool has been declared not to require dynamic JDBC credentials // then go ahead and return true. if (!isDynamicJDBCCredentials()) { isEqual = true; } else { isEqual = oldConnectionMetadata.equals(newConnectionMetadata); } } return isEqual; } public Object useResource(Properties properties) { // The application pool overrides ResourcePool.useResource. The // application pool requires an enhanced interface which accepts a session // identifier in order to manage session state between pool requests. The // application pool still uses the resource pool to manage available // resources. However, the application pool acquires and returns these // resources by using internal resource pool APIs. return null; } public void releaseResource(Object resource, Properties properties) { // The application pool overrides ResourcePool.useResource. The // application pool requires an enhanced interface which accepts a session // identifier in order to manage session state between pool requests. The // application pool still uses the resource pool to manage available // resources. However, the application pool acquires and returns these // resources by using internal resource pool APIs. } /** * @deprecated This property is specific to the SessionCookie. Extending * logic should be migrated to a custom extension of: * {@link oracle.jbo.common.ampool.SessionCookieImpl#isFailoverEnabled()} *

* @since 9.0.2 * @see oracle.jbo.common.ampool.SessionCookie#isFailoverEnabled() */ protected boolean isDoFailover() { return true; } public boolean isDynamicJDBCCredentials() { return getProperty( PropertyMetadata.ENV_AMPOOL_DYNAMIC_JDBC_CREDENTIALS.pName , mEnvironment , Boolean.valueOf(PropertyMetadata.ENV_AMPOOL_DYNAMIC_JDBC_CREDENTIALS.pDefault) .booleanValue()); } public boolean isAMPoolingEnabled() { return getProperty( PropertyMetadata.ENV_AMPOOL_DO_AM_POOLING.pName , mEnvironment , Boolean.valueOf(PropertyMetadata.ENV_AMPOOL_DO_AM_POOLING.pDefault) .booleanValue()); } public boolean isUseExclusive() { if (mUseExclusive == -1) { synchronized(mLock) { mUseExclusive = getProperty( PropertyMetadata.ENV_AMPOOL_IS_USE_EXCLUSIVE.pName , mEnvironment , Boolean.valueOf( PropertyMetadata.ENV_AMPOOL_IS_USE_EXCLUSIVE.pDefault) .booleanValue()) ? 0 : 1; } } return mUseExclusive == 0; } /** * @deprecated This property is specific to the SessionCookie. Extending * logic should be migrated to a custom extension of: * {@link oracle.jbo.common.ampool.SessionCookieImpl#isConnectionPoolingEnabled()} *

* @since 9.0.2 * @see oracle.jbo.common.ampool.SessionCookie#isConnectionPoolingEnabled() */ protected boolean isDoConnectionPooling() { return false; } /** * Establish the inital application module JDBC connection. This method is * invoked by the application module pool when new application module * instances are instantiated. Application developers who would like to * plug a custom connection framework into the application module pool may * override this method. * * @deprecated Replaced by {@link oracle.jbo.common.ampool.ConnectionStrategy#connect(ApplicationModule, SessionCookie)}. * All extending logic that was implemented here should be implemented in a * custom ConnectionStrategy class that extends {@link oracle.jbo.common.ampool.DefaultConnectionStrategy}. * @since 5.0 * @see oracle.jbo.ConnectionStrategy#connect(ApplicationModule, SessionCookie) */ protected void connect(ApplicationModule appModule, Hashtable environment) { } private void initializeCookieJDBCProps(SessionCookie cookie) { try { // Check the usernames, password, and connectString attributes of the // pool. If they are not null then set the environment with those // values. This is required for backwards compatibility since 5.0. if ((getUserName() != null) && (cookie.getEnvironment(ConnectionStrategy.DB_USERNAME_PROPERTY) == null)) { cookie.setEnvironment( ConnectionStrategy.DB_USERNAME_PROPERTY , getUserName()); } if ((getPassword() != null) && (cookie.getEnvironment(ConnectionStrategy.DB_PASSWORD_PROPERTY) == null)) { cookie.setEnvironment( ConnectionStrategy.DB_PASSWORD_PROPERTY , getPassword()); } if ((getConnectString() != null) && (cookie.getEnvironment(ConnectionStrategy.DB_CONNECT_STRING_PROPERTY) == null)) { cookie.setEnvironment( ConnectionStrategy.DB_CONNECT_STRING_PROPERTY , getConnectString()); } } catch(ApplicationPoolException apex) { // Ignore the application pool exception. The cookie must already // be active. } } private void connect( ApplicationModule appModule , SessionCookie cookie , ApplicationPoolSvcMsgContext ctx) { initializeCookieJDBCProps(cookie); try { cookie.setUserData( DefaultConnectionStrategy.APPLICATION_POOL_SVC_MSG_CONTEXT_KEY , ctx); getConnectionStrategy().connect( appModule, cookie, cookie.getEnvInfoProvider()); } finally { cookie.removeUserData( DefaultConnectionStrategy.APPLICATION_POOL_SVC_MSG_CONTEXT_KEY); } } /** * Re-establish an application module's JDBC connection. This method is * invoked by the application module pool when an application module instance * is recycled by the pool. Application developers who would like to * plug a custom connection framework into the application module pool may * override this method. * * @deprecated Replaced by {@link oracle.jbo.common.ampool.ConnectionStrategy#reconnect(ApplicationModule, SessionCookie)}. * All extending logic that was implemented here should be implemented in a * custom ConnectionStrategy class that extends {@link oracle.jbo.common.ampool.DefaultConnectionStrategy}. * @since 5.0 * @see oracle.jbo.ConnectionStrategy#reconnect(ApplicationModule, SessionCookie) */ protected void reconnect(ApplicationModule appModule, Hashtable environment) { } private void reconnect( ApplicationModule appModule , SessionCookie cookie , boolean retainState , ApplicationPoolSvcMsgContext ctx) { initializeCookieJDBCProps(cookie); try { cookie.setUserData( DefaultConnectionStrategy.APPLICATION_POOL_SVC_MSG_CONTEXT_KEY , ctx); cookie.setUserData(DefaultConnectionStrategy.RETAIN_STATE_KEY , new Boolean(retainState)); getConnectionStrategy().reconnect( appModule, cookie, cookie.getEnvInfoProvider()); } finally { cookie.removeUserData( DefaultConnectionStrategy.APPLICATION_POOL_SVC_MSG_CONTEXT_KEY); cookie.removeUserData( DefaultConnectionStrategy.RETAIN_STATE_KEY); } reconnect(appModule, cookie.getEnvironment()); } /** * Disconnect an application module from its JDBC connection. This method is * invoked by the application module pool when an application module instance * is checked into the pool. The method checks the application property * jbo.doconnectionpooling before disconnecting the application * module. Application developers who would like to plug a custom connection * framework into the application module pool may override this method. * * @param retainState Indicates whether the state of the application * module's caches should be retained while disconnecting. If true, the * application module's Transaction should have not database state. * * @deprecated Replaced by {@link oracle.jbo.common.ampool.ConnectionStrategy#disconnect(ApplicationModule, boolean, SessionCookie)}. * All extending logic that was implemented here should be implemented in a * custom ConnectionStrategy class that extends {@link oracle.jbo.common.ampool.DefaultConnectionStrategy}. * @since 5.0 * @see oracle.jbo.ConnectionStrategy#disconnect(ApplicationModule, boolean, SessionCookie) */ protected void disconnect(ApplicationModule appModule, boolean retainState, Hashtable environment) { } private void disconnect( ApplicationModule appModule , boolean retainState , SessionCookie cookie , ApplicationPoolSvcMsgContext ctx) { initializeCookieJDBCProps(cookie); try { cookie.setUserData( DefaultConnectionStrategy.APPLICATION_POOL_SVC_MSG_CONTEXT_KEY , ctx); getConnectionStrategy().disconnect(appModule, retainState, cookie); } finally { cookie.removeUserData( DefaultConnectionStrategy.APPLICATION_POOL_SVC_MSG_CONTEXT_KEY); } disconnect(appModule, retainState, cookie.getEnvironment()); } protected String getConnectionStrategyClassName() { return getProperty( PropertyMetadata.ENV_AMPOOL_CONNECTION_STRATEGY_CLASS_NAME.pName , getEnvironment() , PropertyMetadata.ENV_AMPOOL_CONNECTION_STRATEGY_CLASS_NAME.pDefault); } protected String getSessionCookieFactoryClassName() { return getProperty( PropertyMetadata.ENV_AMPOOL_COOKIE_FACTORY_CLASS_NAME.pName , getEnvironment() , PropertyMetadata.ENV_AMPOOL_COOKIE_FACTORY_CLASS_NAME.pDefault); } /* HashMap getInstanceInfos() { synchronized(mLock) { return (HashMap)mInstanceInfo.clone(); } } HashMap getSessionCookieInfos() { synchronized(mLock) { return (HashMap)mSessionCookieInfo.clone(); } } */ private String getProperty(String name, Hashtable environment, String defaultValue) { String rtn = (String)environment.get(name); if (rtn == null) { rtn = JboEnvUtil.getProperty(name, defaultValue); } return rtn; } private boolean getProperty(String name, Hashtable environment, boolean defaultValue) { boolean rtn = true; String value = (String)environment.get(name); if (value != null) { rtn = Boolean.valueOf(value).booleanValue(); } else { rtn = JboEnvUtil.getPropertyAsBoolean(name, defaultValue); } return rtn; } private int getProperty(String name, Hashtable environment, int defaultValue) { return PoolMgr.getProperty(name, environment, defaultValue); } private void removeState( int pId , ApplicationPoolSvcMsgContext ctx) { if (pId != SessionCookie.NULL_PASSIVATION_ID) { ctx.removeState(pId); } } private void activateState( int pId , SessionCookie cookie , ApplicationPoolSvcMsgContext ctx) { if (pId != SessionCookie.NULL_PASSIVATION_ID) { EnvInfoProvider prov = cookie.getEnvInfoProvider(); Hashtable env = (Hashtable)cookie.getEnvironment().clone(); if (prov != null) { prov.getInfo(null, env); } ctx.activateState( pId , new SessionData(env, cookie.getUserData()) , 0); } } private void prepareSession( ApplicationModule appModule , SessionCookie cookie , ApplicationPoolSvcMsgContext ctx) { EnvInfoProvider prov = cookie.getEnvInfoProvider(); Hashtable env = (Hashtable)cookie.getEnvironment().clone(); if (prov != null) { prov.getInfo(null, env); } SessionData sessionData = new SessionData(env, cookie.getUserData()); ctx.setSessionData(sessionData); // JRS Passing the session data as context instead. ctx.prepareSession(null); } private void validateSessionCookieAvailable(SessionCookie cookie) { // used to determine if a session cookie is in a available state. synchronized(mLock) { SessionCookieInfo info = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (info != null && !info.mIsAvailable) { throw new ApplicationPoolException( AMPoolMessageBundle.class , AMPoolMessageBundle.EXC_AMPOOL_UNAVAILABLE_COOKIE , new Object[] { cookie.getSessionId() , cookie.getApplicationId()}); } } } private void setSessionCookieAvailable( SessionCookie cookie, boolean isAvailable) { synchronized(mLock) { if (cookie != null) { SessionCookieInfo info = (SessionCookieInfo)mSessionCookieInfo.get(cookie); if (info != null) { info.mIsAvailable = isAvailable; } } } } private void sendPoolMessage( ApplicationPoolSvcMsgContext ctx , SessionCookie cookie , ApplicationModule appModule) { ApplicationModuleInfo appModuleInfo = null; SessionCookieInfo cookieInfo = null; synchronized(mLock) { appModuleInfo = (ApplicationModuleInfo)mInstanceInfo.get(appModule); cookieInfo = (SessionCookieInfo)mSessionCookieInfo.get(cookie); } sendPoolMessage(ctx, cookie, cookieInfo, appModule, appModuleInfo); } private void sendPoolMessage( ApplicationPoolSvcMsgContext ctx , SessionCookie cookie , SessionCookieInfo cookieInfo , ApplicationModule appModule , ApplicationModuleInfo appModuleInfo) { ApplicationPoolSvcMsgContext rtnCtx = null; // this will force doPoolMessage to clear the response entries // for some events. if (cookie != null) { ctx.setIgnorePiggyback(cookie.getIgnorePiggyback()); ctx.setBoundToWorkingSet(cookie.getEnvironment("__jbo_bound_to_working_set__") != null); } try { rtnCtx = appModule.doPoolMessage(ctx); } catch (RuntimeException r) { // if an exception is thrown then the AM must be dead. everything // on the MT is handled // the first thing to do is break the association between // cookie and AM (if it exists). synchronized(mLock) { if (!ctx.isApplicationModuleRemoved()) { if (cookie != null) { // once this is done the cookie should be in good shape. i.e. // the cookie state should not have mutated before // doPoolMessage. now, take care of the application module: disassociateSessionCookie(appModule, cookie, false); } // info should not be null. see the isApplicationModuleRemoved // check above. appModuleInfo.mIsInstanceAlive = false; } } if (!ctx.isApplicationModuleRemoved()) { removeDeadInstance(appModule); } throw r; } // now process the message. if ((rtnCtx.getStatus() == ApplicationPoolSvcMsgContext.STATUS_CONNECT_ERROR) || (rtnCtx.getStatus() == ApplicationPoolSvcMsgContext.STATUS_AUTH_ERROR)) { EnvInfoProvider prov = cookie.getEnvInfoProvider(); int numOfRetries = -1; if (prov != null) { numOfRetries = prov.getNumOfRetries(); } if (rtnCtx.getJDBCConnectCount() < numOfRetries) { rtnCtx.incrementJDBCConnectCount(); if (rtnCtx.getStatus() == ApplicationPoolSvcMsgContext.STATUS_CONNECT_ERROR) { ((DefaultConnectionStrategy)getConnectionStrategy()) .initMessageJDBCContext( cookie, cookie.getEnvInfoProvider(), rtnCtx); rtnCtx.setJDBCConnectException(null); } else { Hashtable env = (Hashtable)cookie.getEnvironment().clone(); try { env.put( DefaultConnectionStrategy.LAST_EXCEPTION , ctx.getAuthException()); // null prov check is unnecessary. we can't get here if prov // is null. prov.getInfo(null, env); } finally { env.remove(DefaultConnectionStrategy.LAST_EXCEPTION); } rtnCtx.setSessionData(new SessionData(env, cookie.getUserData())); rtnCtx.setAuthException(null); } rtnCtx.setStatus(ApplicationPoolSvcMsgContext.STATUS_INITIALIZED); // recursion. yuk. sendPoolMessage(rtnCtx, cookie, cookieInfo, appModule, appModuleInfo); } else { Throwable t = null; if (rtnCtx.getStatus() == ApplicationPoolSvcMsgContext.STATUS_CONNECT_ERROR) { t = rtnCtx.getJDBCConnectException(); // JRS This is a hack. Synching the JDBCConnectException in the // rtnCtx with the ctx. This is necessary so that doCheckout // can determine if a JDBC connect exception occured during // prepareApplicationModule. ctx.setJDBCConnectException(t); } else { t = rtnCtx.getAuthException(); ctx.setAuthException((JboException)t); } synchronized(mLock) { if (!ctx.isApplicationModuleRemoved() && (cookie != null)) { disassociateSessionCookie(appModule, cookie, false); } } if (t instanceof RuntimeException) { throw (RuntimeException)t; } else { throw new JboException(t); } } } else { if (appModuleInfo != null) { appModuleInfo.mConnectionMetadata = rtnCtx.getRtnConnectionMetadata(); } if (cookie == null) { return; } cookieInfo.mConnectionMetadata = rtnCtx.getRtnConnectionMetadata(); cookie.setMostRecentStackId(rtnCtx.getRtnMostRecentStackId()); // reset cookie activate bit cookie.setActivated(false); java.util.ArrayList eventList = rtnCtx.getRtnEventList(); if (eventList == null) { return; } int size = eventList.size(); for (int i=0; i < size; i++) { int event = ((Integer)eventList.get(i)).intValue(); switch(event) { case ApplicationPoolSvcMsgContext.POOL_EVENT_DISCONNECT_FAILOVER: { // Mark the cookie's doActivate flag to indicate that // activation must occur upon checkout. cookie.setActivationRequired(true); } case ApplicationPoolSvcMsgContext.POOL_EVENT_FAILOVER: { int prevPassivationId = cookie.getPassivationId(); cookie.setPassivationId(rtnCtx.getRtnPassivationId()); // if after failover the passivation id has not been reset // then transition the passivation id to the prev passivation // id and reset it. the passivation id is no longer in sync // with the current state of the cookie. // // hold onto the reserved passivation id though. it may still // be necessary if failover is disabled and a recycle event // occurs. if (prevPassivationId == cookie.getPassivationId()) { ((SessionCookieImpl)cookie).mPrevPassivationId = prevPassivationId; cookie.setPassivationId(SessionCookie.NULL_PASSIVATION_ID); } logPoolEvent(ApplicationPoolListener.STATE_PASSIVATED); break; } case ApplicationPoolSvcMsgContext.POOL_EVENT_CONNECT: { // the application module may not be connected. see // 2374088. if (rtnCtx.isApplicationModuleConnected()) { connect(appModule, cookie.getEnvironment()); } break; } case ApplicationPoolSvcMsgContext.POOL_EVENT_ACTIVATE: { cookie.setActivationRequired(false); cookie.setActivated(true); logPoolEvent(ApplicationPoolListener.STATE_ACTIVATED); break; } case ApplicationPoolSvcMsgContext.POOL_EVENT_RESERVE_PASSIVATION_ID: { cookie.setReservedPassivationId(rtnCtx.getRtnReservedPassivationId()); } } } } } public String getEnvConfigurationName() { return (String)mEnvironment.get(Configuration.CONFIGURATION_NAME); } public long getTimeCreated() { return mTimeCreated; } protected boolean removeFromManager() { // okay called by super in a synchronized block. return super.removeFromManager() && (mSessionCookieInfo.size() == 0); } protected Object getResourcePoolKey() { return getName(); } class SessionCookieInfo { // This class is a simple structure for maintaining internal session // cookie information. The external session cookie class does not // manage this information because it is specific to the application // pool implementation. private SessionCookie mCookie; Date mLastUpdate; private ApplicationModule mApplicationModule = null; private ConnectionMetadata mConnectionMetadata = null; boolean mIsReferencingState = false; private boolean mIsAvailable = true; SessionCookieInfo() { } SessionCookieInfo(SessionCookie cookie) { synchronized(mLock) { mCookie = cookie; mLastUpdate = new Date(System.currentTimeMillis()); if (cookie.getPassivationId() != SessionCookie.NULL_PASSIVATION_ID) { mIsReferencingState = true; mReferencingSessionCount++; } } } void resetState() { synchronized(mLock) { if (mIsReferencingState) { mIsReferencingState = false; --mReferencingSessionCount; } } } void touch() { mLastUpdate.setTime(System.currentTimeMillis()); } } class ApplicationModuleInfo extends RecentlyUsedLinkedListElement { // This class is a simple structure for maintaining internal application // module information. The application module class does not // manage this information because it is specific to the application // pool implementation. ApplicationModuleInfo(ApplicationModule appModule) { super(appModule); mRefAvailListElement = new RecentlyUsedLinkedListElement(appModule); mUnrefAvailListElement = new RecentlyUsedLinkedListElement(appModule); } // Initialize the application module as being checked out. It must // be explicitly after createInstance has been invoked before it can // be used. HashMap mReferencingSessionCookies = new HashMap(); SessionCookie mReferencingSessionCookie = null; ConnectionMetadata mConnectionMetadata = null; boolean mIsReleased = true; boolean mIsInstanceAlive = true; ApplicationPoolSvcMsgContext mPendingContext = null; final RecentlyUsedLinkedListElement mRefAvailListElement; final RecentlyUsedLinkedListElement mUnrefAvailListElement; } }