/** Copyright (c) Oracle Corporation 1998.  All Rights Reserved. */
package oracle.jbo.html.jsp;

import java.io.PrintWriter;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.InputStream;

import java.util.Vector;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Properties;
import java.util.Hashtable;

import oracle.jbo.ApplicationModule;

import oracle.jbo.domain.TypeFactory;

import oracle.jdeveloper.html.WebBeanImpl;

import oracle.jdeveloper.cm.ConnectionManager;
import oracle.jdeveloper.cm.ConnectionDescriptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpSession;

import javax.servlet.jsp.PageContext;

import oracle.jbo.common.ampool.PoolMgr;
import oracle.jbo.common.ampool.ApplicationPool;
import oracle.jbo.common.ampool.ApplicationPoolException;
import oracle.jbo.common.ampool.AMPoolMessageBundle;

import oracle.jbo.common.Diagnostic;
import oracle.jbo.common.PropertyMetadata;
import oracle.jbo.common.PropertyConstants;

import oracle.jbo.http.HttpContainer;
import oracle.jbo.http.ApplicationBindingListener;

import oracle.jbo.html.jsp.datatags.ReleasePageResourcesTag;


/**
 **  This class provides the main interface for DataWebBeans to use the Application Module Pool.
 **
 ** <a HREF="JSPApplicationRegistry.txt">View implementation of JSPApplicationRegistry</a>
 **
 ** @author  Juan Oropeza
 ** @version PUBLIC
 **
 **/
public class JSPApplicationRegistry extends WebBeanImpl
{
   static JSPApplicationRegistry mInstance =  new JSPApplicationRegistry();

   static PoolMgr mPoolManager = PoolMgr.getInstance();

   private static final String TIMEOUT_HANDLER_SUFFIX = "_TimeoutHandler";

   public static final String RESERVED                = PropertyConstants.AM_RELEASE_RESERVED;
   public static final String STATEFUL                = PropertyConstants.AM_RELEASE_STATEFUL;
   public static final String STATELESS               = PropertyConstants.AM_RELEASE_STATELESS;

   private static final String SESSION_INITIALIZED = "SessionInitialized";

   /**
   **  Constructor, this should not be called directly
   **/
   public JSPApplicationRegistry()
   {
   }

   /**
   **  Returns the singleton instance of the registry class.
   **/
   static public  JSPApplicationRegistry getInstance()
   {
      return mInstance;
   }

   /**
   **  Returns the user data associated with the named pool. The user data is a convenient palce for storing
   **  and retrieving application-specific information shared by all application instances that are part of the
   **  named pool.
   **/
   static synchronized public Hashtable getAppSettings(String appName)
   {
      ApplicationPool pool = mPoolManager.getPool(appName);

      return (Hashtable)pool.getUserData();
   }

   /**
   ** This is a convenience method for returning an Application Module back to
   ** the pool. This method will find the ApplicationInstance associated with
   ** the Application Module and check it in to the pool.
   **
   ** @deprecated As of BC4J 3.2, replaced by {@link #releaseAppModuleInstance(String, HttpSession, HttpServletResponse, String)}
   **/
   static synchronized public void returnAppModuleInstance(
      String appName, ApplicationModule appModule)
   {
      getInstance().releaseAppModuleInstance(
         appName
         , getInstance().session
         , getInstance().response
         , getInstance().getReleaseMode(appName));
   }

   /**
    * Release the specified application module instance.  This method is
    * intended for use by servlet clients in order to release an application
    * module resource to the application module pool.</p>
    * <p>
    * The specified release mode will determine how the application module is
    * returned to the pool.  The following release modes are currently
    * supported:</p>
    * <p>
    * <b>RESERVED:  </b>Do not allow the application module instance to be
    *    shared with other client requests.  The reserved lock will be released
    *    when the application module is checked in by a latter request in the
    *    application life cycle or when the http session times out.</p>
    * <p>
    * <b>STATEFUL:  </b>Only available if a HttpServletResponse has been
    *    specified. The implementation will check the application module
    *    instance into the pool in a stateful manner which will allow the pool
    *    to maintain the application module state between session requests.
    *    This may involve passivating the session's application module state so
    *    that the application may be reused by other session requests.  Using
    *    this option will cause a servlet cookie to be generated and written
    *    to the HttpServletResponse.</p>
    * <p>
    * <b>STATELESS:  </b>Check the application module instance into the pool
    *    without retaining application module state.</p>
    *
    * @see oracle.jbo.common.ampool.ApplicationPool ApplicationPool
    */
   public void releaseAppModuleInstance(
      ApplicationModule appModule
      , ApplicationPool pool
      , HttpSession session
      , HttpServletResponse response
      , String releaseMode)
   {
      // Only proceed if the application is not available.  Otherwise,
      // the application module has been successfully checked in already.
      // This check is necessary to prevent redundant check-ins to the pool.
      // The check is implemented here because the JSPApplicationRegistry
      // may potentially register two application module references in the
      // HTTP contexts (page and session).
      if (!pool.isAvailable(appModule))
      {
         // If the application release mode has been specified as reserved
         // then promote the application module to the session level context.
         // The application module will be checked in if it is used in a page
         // again with a release mode other than reserved or if the session
         // times out.
         if (JSPApplicationRegistry.RESERVED.equals(releaseMode))
         {
            Diagnostic.println("ApplicationModule release mode is:  Reserved");

            // Add the application module to the BC4J session context container.
            addAppModuleToSession(appModule, pool, session);
         }
         // If a stateful release mode has been specified then passivate
         // the application module and return it to the application pool.  This
         // mode is not supported if a response object has not been specified.
         else if ((response != null)
            && (JSPApplicationRegistry.STATEFUL.equals(releaseMode)))
         {

            Diagnostic.println("ApplicationModule release mode is:  Stateful");

            String sessionId = pool.checkinWithSessionState(appModule);

            writeSessionId(response, pool.getPoolName(), sessionId);

            HttpContainer container = (HttpContainer)session
               .getValue(HttpContainer.SESSION_CONTEXT_CONTAINER_NAME);

            // Remove the application module from the JBO session context
            // container
            try
            {
               if (container != null)
               {
                  container.removeValueInternal(
                     HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX
                     + pool.getPoolName());

                  container.removeValueInternal(
                     HttpContainer.APPLICATION_PREFIX + pool.getPoolName());
               }
            }
            catch (IllegalStateException iex)
            {
               // If an illegal state exception is thrown then the session
               // has already been cleaned up.  Eat the exception and continue.
            }
         }
         // If the release mode is stateless then simply return the application
         // module to the pool.
         else
         {
            Diagnostic.println("ApplicationModule release mode is:  Stateless");

            pool.checkin(appModule);

            try
            {
               HttpContainer container = (HttpContainer)session
                  .getValue(HttpContainer.SESSION_CONTEXT_CONTAINER_NAME);

               // Remove the application module from the JBO session context
               // container
               if (container != null)
               {
                  container.removeValueInternal(
                     HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX
                     + pool.getPoolName());

                  container.removeValueInternal(
                     HttpContainer.APPLICATION_PREFIX + pool.getPoolName());
                  }
            }
            catch (IllegalStateException iex)
            {
               // If an illegal state exception is thrown then the session
               // has already been cleaned up.  Eat the exception and continue.
            }
         }
      }
   }

   /**
    * Release the specified application module instance.  This method is
    * intended for use by JSP clients in order to release an application
    * module resource to the application module pool.</p>
    * <p>
    * The specified release mode will determine how the application module is
    * returned to the pool.  The following release modes are currently
    * supported:</p>
    * <p>
    * <b>RESERVED:  </b>Do not allow the application module instance to be
    *    shared with other client requests.  The reserved lock will be released
    *    when the application module is checked in by a latter request in the
    *    application life cycle or when the http session times out.</p>
    * <p>
    * <b>STATEFUL:  </b>Only available if a HttpServletResponse has been
    *    specified. The implementation will check the application module
    *    instance into the pool in a stateful manner which will allow the pool
    *    to maintain the application module state between session requests.
    *    This may involve passivating the session's application module state so
    *    that the application may be reused by other session requests.  Using
    *    this option will cause a servlet cookie to be generated and written
    *    to the HttpServletResponse.</p>
    * <p>
    * <b>STATELESS:  </b>Check the application module instance into the pool
    *    without retaining application module state.</p>
    *
    * @see oracle.jbo.common.ampool.ApplicationPool ApplicationPool
    */
   public void releaseAppModuleInstance(
      ApplicationModule appModule
      , ApplicationPool pool
      , PageContext pageContext
      , String releaseMode)
   {
      HttpSession session = pageContext.getSession();

      HttpServletResponse response =
         (HttpServletResponse)pageContext.getResponse();

      releaseAppModuleInstance(
         appModule
         , pool
         , session
         , response
         , releaseMode);


      HttpContainer container = (HttpContainer)pageContext
         .getAttribute(HttpContainer.PAGE_CONTEXT_CONTAINER_NAME);

      // Remove the application module from JBO page context container.
      if (container != null)
      {
         container.removeValueInternal(
            HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX
               + pool.getPoolName());

         container.removeValueInternal(
               HttpContainer.APPLICATION_PREFIX + pool.getPoolName());
      }

   }

   /**
    * Release the application module instance that was generated by the
    * specified pool from the BC4J session context.
    */
   public void releaseAppModuleInstance(String poolName
      , HttpSession session
      , HttpServletResponse response
      , String releaseMode)
   {
      HttpContainer container = (HttpContainer)session
         .getValue(HttpContainer.SESSION_CONTEXT_CONTAINER_NAME);

      if (container != null)
      {
         Properties props = new Properties();
         props.put(ApplicationBindingListener.RELEASE_MODE, releaseMode);
         props.put(ApplicationBindingListener.HTTP_RESPONSE, response);
         container.removeValue(
            HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX
               + poolName
            , props);
      }
   }

   /**
    * Get an application module instance for the specified application pool.
    * This method is intended for servlet clients that require an application
    * module.
    * <p>
    * The implementation will attempt to locate an application
    * module in the session context before asking the
    * application pool for an application module.</p>
    * <p>
    * If an application module is not found in the session or page contexts
    * the implementation will check the http request for a session cookie id.
    * If a session cookie id exists the implementation will invoke,
    * {@link oracle.jbo.common.ampool.ApplicationPool#getInstanceWithSessionId getInstanceWithSessionId},
    * in order to return an application module with the same state as the
    * application module that had been checked in by the session.</p>
    * <p>
    * If a session cookie id does not exist the implementation will invoke,
    * {@link oracle.jbo.common.ampool.ApplicationPool#checkout() checkout} in order
    * to return a fresh application module (no existing state) to the invokee.</p>
    * <p>
    * The returned application module is always checked out of the specified
    * application pool.</p>
    *
    * @see oracle.jdeveloper.html.DataWebBeanImpl#internalInitialize()
    */
   public ApplicationModule getAppModuleInstance(
      String appName
      , HttpServletRequest request
      , HttpSession session)
   {

      ApplicationPool pool = mPoolManager.getPool(appName);

      ApplicationModule	appModule = getAppModuleFromContexts(
         appName
         , session
         , null);

      // If the application module was not located in the http contexts
		if (appModule == null)
		{
         appModule = internalGetAppModuleInstance(appName, pool, request);
      }

      addAppModuleToSession(appModule, pool, session);

      return appModule;
   }

   /**
    * Get an application module instance for the specified application pool.
    * This method is intended for JSP clients that require an application module.
    * <p>
    * The implementation will attempt to locate an application
    * module in the page and session contexts before asking the
    * application pool for an application module.</p>
    * <p>
    * If an application module is not found in the session or page contexts
    * the implementation will check the http request for a session cookie id.
    * If a session cookie id exists the implementation will invoke,
    * {@link oracle.jbo.common.ampool.ApplicationPool#getInstanceWithSessionId() getInstanceWithSessionId},
    * in order to return an application module with the same state as the
    * application module that had been checked in by the session.</p>
    * <p>
    * If a session cookie id does not exist the implementation will invoke,
    * {@link oracle.jbo.common.ampool.ApplicationPool#checkout() checkout} in order
    * to return a fresh application module (no existing state) to the invokee.</p>
    * <p>
    * The returned application module is always checked out of the specified
    * application pool.</p>
    *
    * @see oracle.jbo.html.jsp.datatags.ApplicationModuleTag#doEndTag
    */
   public ApplicationModule getAppModuleInstance(
      String appName
      , PageContext pageContext)
   {
      ApplicationPool pool = mPoolManager.getPool(appName);

      HttpSession session = pageContext.getSession();
      HttpServletRequest request = (HttpServletRequest)pageContext.getRequest();

      ApplicationModule	appModule = getAppModuleFromContexts(
         appName
         , session
         , pageContext);

      // If the application module was not located in the http contexts
		if (appModule == null)
		{
         appModule = internalGetAppModuleInstance(appName, pool, request);
      }

      addAppModuleToPage(appModule, pool, pageContext, session);

      // It is necessary to place the appmodule in the session context
      // container so that it may be reused by the data web beans.
      addAppModuleToSession(appModule, pool, session);

      return appModule;
   }

   private ApplicationModule internalGetAppModuleInstance(
      String appName
      , ApplicationPool pool
      , HttpServletRequest request)
   {
      ApplicationModule appModule = null;

      String sessionId = readSessionId(request, appName);
      
      if (sessionId != null)
      {
         appModule = pool.checkout(sessionId);
      }
      else
      {
         try
         {
            appModule = pool.checkout();
         }
         catch (Exception ex)
         {
            Diagnostic.printStackTrace(ex);
            ApplicationPoolException aex = new ApplicationPoolException(
               AMPoolMessageBundle.class
               , AMPoolMessageBundle.EXC_AMPOOL_CHECKOUT_FAILED
               , new Object[] {appName});

            aex.addToDetails(ex);
            throw aex;
         }
      }

      return appModule;
   }

   /**
    * Check the relevant http contexts for the named application module
    * instance.
    * <p>
    * If the page context is not null then first check the page context
    * for a Bc4jPageContext container.  If one is found then check for
    * the specified application module instance.  Otherwise check if the
    * specified application module instance has been placed in the session
    * context.</p>
    *
    * @param appModuleName the name of the appModule instance that should be
    *    retrieved
    * @param session the HttpSession context that should be checked for the
    *    named application module instance
    * @param pageContext if a JSP client the name of the pageContext that
    *    should be checked for the named application module instance.  Null
    *    otherwise.
    */
   public ApplicationModule getAppModuleFromContexts(
      String appName
      , HttpSession session
      , PageContext pageContext)
   {
      ApplicationModule appModule = null;
      HttpContainer container = null;

      if (pageContext != null)
      {
         container = (HttpContainer)pageContext
            .getAttribute(HttpContainer.PAGE_CONTEXT_CONTAINER_NAME);

         if (container != null)
         {
            appModule = (ApplicationModule)container
               .getValue(HttpContainer.APPLICATION_PREFIX + appName);
         }
      }

      // If the appModule was not located in the page context
      if (appModule == null)
      {
         synchronized(session)
         {
            container = (HttpContainer)session
               .getValue(HttpContainer.SESSION_CONTEXT_CONTAINER_NAME);

            if (container != null)
            {
               appModule = (ApplicationModule)container
                  .getValue(HttpContainer.APPLICATION_PREFIX + appName);
            }
         }
      }

      return appModule;
   }

   /**
   **  Locates the application pool and check out a new application module. You can use the
   **  {@link #releaseAppModuleInstance(String, HttpSession, HttpServletResponse, String)} method
   **  to return the Application Module back to the pool.
   **
   ** @deprecated As of BC4J 3.2, replaced by {@link #getAppModuleInstance(String, HttpServletRequest, HttpSession)}
   **/
   static synchronized public ApplicationModule getApplication(String appName) throws Exception
   {
      return getInstance().getAppModuleInstance(
         appName
         , getInstance().request
         , getInstance().session);
   }

    /**
    **  Convenience method for defining a new application pool from a property file. This also transfers some system
    **  specific variables to the JSP Session object.
    **/
   static synchronized public void registerApplicationFromPropertyFile(HttpSession session, String sPropFileName)
   {
      if(!mPoolManager.isPoolCreated(sPropFileName))
      {
         registerApplicationFromPropertyFile(sPropFileName);
      }

      if (!PropertyConstants.TRUE.equals((String)session.getValue(SESSION_INITIALIZED)))
      {
         Hashtable   settings = getAppSettings(sPropFileName);
         int         nTimeOut = 300;

         if (settings != null)
         {
            // see if we have a setting for the session timeout
            String sTimeOut;


            if(settings.get("HttpSessionTimeOut") != null)
            {
              sTimeOut = (String)settings.get("HttpSessionTimeOut");

              if(sTimeOut != null)
              {
                nTimeOut = Integer.parseInt(sTimeOut);
              }
            }

            if(settings.get("ImageBase") != null)
            {
               session.putValue("ImageBase", settings.get("ImageBase"));
            }
            else
            {
               settings.put("ImageBase", "/webapp/images");
               session.putValue("ImageBase", "/webapp/images");
            }

            if(settings.get("CSSURL") != null)
            {
               session.putValue("CSSURL",settings.get("CSSURL"));
            }
            else
            {
               settings.put("CSSURL", "/webapp/css/oracle.css");
               session.putValue("CSSURL", "/webapp/css/oracle.css");
            }
         }

         // place default renderers into session, these will not be
         // exposed via config file
         session.putValue("oracle_ord_im_OrdImageDomain_Renderer", "oracle.ord.html.OrdBuildURL");
         session.putValue("oracle_ord_im_OrdAudioDomain_Renderer","oracle.ord.html.OrdBuildURL");
         session.putValue("oracle_ord_im_OrdVideoDomain_Renderer","oracle.ord.html.OrdBuildURL");
         session.putValue("oracle_ord_im_OrdVirDomain_Renderer", "oracle.ord.html.OrdBuildURL");

         session.putValue("oracle_ord_im_OrdImageDomain_EditRenderer",  "oracle.ord.html.FileUploadField");
         session.putValue("oracle_ord_im_OrdAudioDomain_EditRenderer",  "oracle.ord.html.FileUploadField");
         session.putValue("oracle_ord_im_OrdVideoDomain_EditRenderer",  "oracle.ord.html.FileUploadField");
         session.putValue("oracle_ord_im_OrdVirDomain_EditRenderer",    "oracle.ord.html.FileUploadField");

         session.putValue(SESSION_INITIALIZED, PropertyConstants.TRUE);
      }
   }

   /**
   **  Creates a new application pool from the contents of a property file. Please look at the ConnectionInfo class
   **  for documentation of what should be contained in the property file.
   **/
   static synchronized public void registerApplicationFromPropertyFile(String sPropFileName)
   {
      try
      {
         String sConnectUrl;
         String sAppModuleClass;

         if(!mPoolManager.isPoolCreated(sPropFileName))
         {
            // open up the property file
            String sConfigPath = sPropFileName + ".properties";

            // find the property file in the classpath
            System.out.println("Loading from CLASSPATH " + sConfigPath);

            InputStream  in = ClassLoader.getSystemResourceAsStream(sConfigPath);

            if(in == null)
            {
               throw new RuntimeException("JSP Registry could not locate runtime property file:" + sConfigPath);
            }

            // load up the properties file
            Properties props	= new Properties();

            props.load(in);

            in.close();

            // check if we are using a configuration (this started in 3.2)
            if(props.get("ConfigName") != null)
            {
               String sConfigName = props.get("ConfigName").toString();
               String sConfigPackage = sConfigName.substring(0, sConfigName.lastIndexOf('.'));
               String sConfigSection = sConfigName.substring(sConfigName.lastIndexOf('.') + 1);

               // strip out the am class
               sConfigPackage = sConfigPackage.substring(0, sConfigPackage.lastIndexOf('.'));

               ApplicationPool pool = mPoolManager.createPool(sPropFileName, sConfigPackage, sConfigSection, props);

               if(props.get("Password") != null)
                  pool.setPassword(props.get("Password").toString());
               if(props.get("UserName") != null)
                  pool.setUserName(props.get("UserName").toString());
            }
            else
            {
               // this handles backward compatibility
               ConnectionInfo connectInfo = new ConnectionInfo(props);
               
               ApplicationPool pool  = mPoolManager.createPool(
                              sPropFileName
                              , connectInfo.getPoolClass()
                              , connectInfo.getAppModuleClass()
                              , connectInfo.getConnectionUrl()
                              , connectInfo.getConnectionSettings());

               pool.setUserName(connectInfo.sUserName);
               pool.setPassword(connectInfo.sPassword);
               pool.setUserData(props);
            }
         }
      }
      catch (Exception ex)
      {
         ex.printStackTrace();
         throw new RuntimeException(ex.toString());
      }
   }

   /**
    * Helper method to obtain the specified application's release mode.
    */
   public String getReleaseMode(String appName)
   {
      Hashtable appProps = getAppSettings(appName);

      String releaseMode =
         ((appProps != null)
            && (appProps.get(PropertyMetadata.AM_RELEASE_MODE.pName) != null))
         ? (String)appProps.get(PropertyMetadata.AM_RELEASE_MODE.pName)
         : null;

      // If the stateless runtime application property value is true then set
      // the release mode to be stateful.  Otherwise set the release mode to be
      // reserved.
      // Required for backwards compatibility with 3.1
      if ((releaseMode == null)
         && (appProps != null)
         && (appProps.get(PropertyConstants.IS_STATELESS_RUNTIME) != null))
      {
         releaseMode =
            ((String)appProps.get(PropertyConstants.IS_STATELESS_RUNTIME))
               .equals(PropertyConstants.TRUE)
            ? STATEFUL
            : RESERVED;
      }

      // Finally, if the release mode is still null set it to be reserved.
      if (releaseMode == null)
      {
         releaseMode = RESERVED;
      }

      return releaseMode;
   }

   /**
    * Read the session id from the HttpServletRequest.  By default this method
    * uses Http cookies to store the unique session identifier for a
    * stateful application module.
    *
    * @param applicationName the name of the application that has session state
    */
   protected String readSessionId(
      HttpServletRequest request
      , String applicationName)
   {
      Cookie[] cookies = request.getCookies();
      Cookie cookie = null;

      if (cookies != null)
      {
         for (int i=0; i < cookies.length; i++)
         {
            if (cookies[i].getName().equals(
               HttpContainer.APPLICATION_COOKIE_PREFIX + applicationName))
            {
               cookie = cookies[i];
               break;
            }
         }
      }

      return (cookie != null) ? cookie.getValue() : null;
   }

   /**
    * Write the session id to the HttpServletResponse.  By default this method
    * uses Http cookies to store a unique session identifier for a
    * stateful application module.
    *
    * @param applicationName the name of the application that has session state
    */
   protected void writeSessionId(
      HttpServletResponse response
      , String applicationName
      , String sessionId)
   {
      Hashtable appSettings = getAppSettings(applicationName);

      String maxAgeStr = (String)appSettings.get(PropertyConstants.ENV_MAX_POOL_COOKIE_AGE);
      int maxAge = (maxAgeStr != null) ? Integer.parseInt(maxAgeStr) : -1;

      Cookie cookie = new Cookie(
         HttpContainer.APPLICATION_COOKIE_PREFIX + applicationName
         , sessionId);

      if (maxAge >= 0)
      {
         cookie.setMaxAge(maxAge);
      }

      response.addCookie(cookie);
   }

   private void addAppModuleToPage(
      ApplicationModule appModule
      , ApplicationPool pool
      , PageContext pageContext
      , HttpSession session)
   {
      HttpContainer container = (HttpContainer)pageContext
         .getAttribute(HttpContainer.PAGE_CONTEXT_CONTAINER_NAME);

      if (container == null)
      {
         container = new HttpContainer(pageContext);

         pageContext.setAttribute(
            HttpContainer.PAGE_CONTEXT_CONTAINER_NAME
            , container);

         // Add the new pageContext to the session context, just in
         // case the page context is orphaned.  This will ensure that
         // any page resources will get cleaned up when
         // the session times out.
         synchronized(session)
         {
            // Add the page context container to the session only if it has not
            // already been added
            if (session.getValue(
               HttpContainer.PAGE_CONTEXT_CONTAINER_NAME) == null)
            {
               session.putValue(
                  HttpContainer.PAGE_CONTEXT_CONTAINER_NAME
                  , container);
            }
         }
      }

      // Add the application module reference to the page context only if it
      // has not been added already
      if (container.getValue(
         HttpContainer.APPLICATION_PREFIX + pool.getPoolName()) == null)
      {
         container.putValue(
            HttpContainer.APPLICATION_PREFIX + pool.getPoolName()
            , appModule
            , null);

      }

      if (container.getValue(
            HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX
            + pool.getPoolName()) == null)
      {
         container.putValue(
            HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX + pool.getPoolName()
            , new ApplicationBindingListener(appModule, pool)
            , null
         );
      }
   }

   private void addAppModuleToSession(
      ApplicationModule appModule
      , ApplicationPool pool
      , HttpSession session)
   {
      // Add the application module to the BC4J session context container.
      // Check out a lock on the session context to prevent
      // multiple threads from simultaneously attempting to create
      // JBO contexts.
      synchronized(session)
      {
         HttpContainer container = getHttpContainer(session);

         // Add the application module reference to the session only if it
         // has not been added already
         if (container.getValue(
            HttpContainer.APPLICATION_PREFIX + pool.getPoolName()) == null)
         {

            container.putValue(
               HttpContainer.APPLICATION_PREFIX + pool.getPoolName()
               , appModule
               , null);

         }

         if (container.getValue(
               HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX
               + pool.getPoolName()) == null)
         {
            container.putValue(
               HttpContainer.APPLICATION_BINDING_LISTENER_PREFIX + pool.getPoolName()
               , new ApplicationBindingListener(appModule, pool)
               , null);
         }
      }
   }

   static synchronized public HttpContainer getHttpContainer(HttpSession session)
   {
       HttpContainer container = (HttpContainer)session.getValue(HttpContainer.SESSION_CONTEXT_CONTAINER_NAME);

       if (container == null)
       {
           container = new HttpContainer(session);

           session.putValue(HttpContainer.SESSION_CONTEXT_CONTAINER_NAME , container);
       }

       return container;
   }

}
