/*
 * @(#)PoolMgr.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 java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import oracle.jbo.JboException;
import oracle.jbo.client.Configuration;
import oracle.jbo.common.JBOClass;
import oracle.jbo.common.JboEnvUtil;
import oracle.jbo.common.PropertyConstants;
import oracle.jbo.common.PropertyMetadata;
import oracle.jbo.pool.ResourcePoolManager;

/**
 **  This class manages the Application Pool. It is a singleton instance.
 ** <BR>
 ** <a HREF="PoolMgr.txt">View implementation of PoolMgr</a>
 ** <P>
 **/
public class PoolMgr extends ResourcePoolManager
{
   private static final PoolMgr mInstance;

   static
   {
      if (JboEnvUtil.inOC4J())
      {
         mInstance = new ContextPoolManager();
      }
      else
      {
         mInstance = new PoolMgr();
      }
   }
   /**
    *  Retrieves the singleton instance of the Pool Manager
    */
   static public PoolMgr getInstance()
   {
      return mInstance;
   }

   /**
    *  Returns true if the pool has already been created.
    */
   public boolean isPoolCreated(String sName)
   {
      return (getResourcePool(sName) != null);
   }

   /**
    *  Returns the ApplicationPool interface for the named pool.
    * 
    * @deprecated
    * @since 5.0
    * @see oracle.jbo.pool.ResourcePoolManager.getResourcePool()
    */
   public ApplicationPool getPool(String sName)
   {
      return (ApplicationPool)getResourcePool(sName);
   }

   /**
    * Adds the named pool to the pool manager collection.
    * @param name the name of the pool to add.
    *
    * @deprecated
    * @since 5.0
    * @see oracle.jbo.pool.ResourcePoolManager.addResourcePool(String, Object)
    */
   public void addPool(ApplicationPool pool)
   {
      addResourcePool(pool.getName(), pool);
   }

   /**
    *  Removes the named pool and calls remove() method on all ApplicationModule
    *  instances that are being managed by the pool.
    * @param the name of the pool to remove.
    *
    * @deprecated
    * @since 5.0
    * @see oracle.jbo.pool.ResourcePoolManager.removeResourcePool(String)
    */
   public void removePool(String sName)
   {
      removeResourcePool(sName);
   }

   /**
    *  Returns the Enumeration interface that allows you to enumerate through
    *  all the Application Pools that are registered with the Pool Manager.
    *
    * @deprecated
    * @since 5.0
    * @see oracle.jbo.pool.ResourcePoolManager.getResourcePools()
    */
   public Enumeration getPools()
   {
      return getResourcePools();
   }

   /**
    * @deprecated
    * @since 5.0
    * @see oracle.jbo.pool.ResourcePoolManager.getResourcePoolKeys()
    */
   public Enumeration getPoolNames()
   {
      return getResourcePoolKeys();
   }

   /**
    * Create a new Application Module pool, throws an exception if the pool
    * is already registered.  The connectInfo parameter provides the necessary
    * settings required for creating instances of ApplicationModules that are
    * part of the pool. You can also specify the name of the class that
    * implements the ApplicationPool interface. This is an important setting
    * for users that want to provide a custom implementation of the
    * ApplicationPool interface. The default ApplicationPool implementation is
    * in the oracle.jbo.client.ampool.ApplicationPoolImpl class.
    */
   public ApplicationPool createPool(
      String sName 
      , String sClass
      , String sApplicationModule
      , String sConnectString
      , Hashtable env) throws Exception
   {
      synchronized(mLock)
      {
         if (isPoolCreated(sName))
         {
            throw new ApplicationPoolException(
               AMPoolMessageBundle.class
               , AMPoolMessageBundle.EXC_AMPOOL_MGR_ALREADY_CREATED
               , new Object[] { sName });
         }

         Class  poolClass = JBOClass.forName(sClass);

         ApplicationPool pool = (ApplicationPool)poolClass.newInstance();

         pool.initialize(sName, sApplicationModule, sConnectString, env);

         addPool(pool);
         return pool;
      }
   }


   /**
    * Create a new Application Module pool, throws an exception if the pool
    * is already registered.  The connectInfo parameter provides the necessary
    * settings required for creating instances of ApplicationModules that are
    * part of the pool. You can also specify the name of the class that
    * implements the ApplicationPool interface. This is an important setting
    * for users that want to provide a custom implementation of the
    * ApplicationPool interface. The default ApplicationPool implementation is
    * in the oracle.jbo.client.ampool.ApplicationPoolImpl class.
    */
   public ApplicationPool createPool(
      String sName
      , String sApplicationModule
      , String sConnectString
      , Hashtable env) throws Exception
   { 
      return createPool(
         sName
         , "oracle.jbo.common.ampool.ApplicationPoolImpl"
         , sApplicationModule
         , sConnectString
         , env);
   }

   private ApplicationPool createPool(
      String name
      , Configuration configuration
      , String configName)
   {
      synchronized(mLock)
      {
         Hashtable config = configuration.getConfiguration(configName);
         String applicationModule
            = (String)config.get(Configuration.APPLICATION_NAME_PROPERTY);
         String connectString
            = (String)config.get(Configuration.DB_CONNECTION_PROPERTY);

         if (applicationModule == null)
            throw new RuntimeException("Configuration does not define one or more properties ");


         // Check for a pool class name property in the config.  If it exists
         // use the pool class name to create the pool.  Otherwise use the
         // default pool implementation.
         String poolClassName
            = (String)config.get(PropertyConstants.ENV_POOL_CLASS_NAME);
            
         ApplicationPool pool = null;

         try
         {
            if (poolClassName != null)
            {
               pool = createPool(
                  name
                  , poolClassName
                  , applicationModule
                  , connectString
                  , config);
            }
            else
            {
               pool = createPool(
                  name
                  , applicationModule
                  , connectString
                  , config);
            }
         }
         catch (Exception e)
         {
            throw new JboException(e);
         }
         
         pool.setUserData(config);

         return pool;
      }   
   }
   
   /**
    *  Creates a new Application Module pool, throws an exception if the pool is
    *  already registered.
    * <p>
    *  This method creates an Application Module pool, based on the named
    *  AppModule Configuaraion
    *
    * <p>
    *  The application module configuration provides the necessary settings
    *  required for creating instances of Application Modules that are part of
    *  the pool. You can also specify the name of a custom application pool
    *  implementation (implements oracle.jbo.common.ApplicationPool interface).
    *  The default ApplicationPool implementation is provided in
    *  oracle.jbo.common.ampool.ApplicationPoolImpl.
    *
    *  @param sName name of the pool
    *  @param sPackageName package name of the AppModule., package10, for package10.Package10Module
    *  @param sConfigName  named Configuration to use
    *  @param props collection of additional properties to be passed to the application pool
    */
   public ApplicationPool createPool(
      String sName
      , String sPackageName
      , String sConfigName
      , Properties props) throws Exception
   {
      synchronized(mLock)
      {
         Configuration configuration = new Configuration();
         configuration.loadFromClassPath(Configuration.buildConfigurationFileNameFromClassPath(sPackageName));
         Hashtable config = configuration.getConfiguration(sConfigName);
         String sApplicationModule = null;
         sApplicationModule = (String)config.get(Configuration.APPLICATION_NAME_PROPERTY);
         String sConnectString  = (String)config.get(Configuration.DB_CONNECTION_PROPERTY);

         if (sApplicationModule == null)
            throw new Exception("Configuration does not define one or more properties ");


         // If properties were passed to createPool then merge them with the
         // config properties
         if (props != null)
         {
            Enumeration propKeys = props.keys();
            Object key = null;
            String keyStr = null;
            boolean doMapProperty = true;

            while (propKeys.hasMoreElements())
            {
               // By default merge the specified properties into the
               // configuration properties
               doMapProperty = true;

               key = propKeys.nextElement();

               // Check for special cases
               if (key instanceof String)
               {
                  keyStr = (String)key;

                  if (keyStr.equals(PropertyConstants.IS_STATELESS_RUNTIME))
                  {
                     // Check if a release mode has already been mapped.
                     // If so, ignore the property and use the configuration value
                     // instead.
                     if (config.containsKey(PropertyConstants.AM_RELEASE_MODE))
                     {
                        doMapProperty = false;
                     }
                  }
               }

               if (doMapProperty)
               {
                  config.put(key, props.get(key));
               }
            }
         }

         // Check for a pool class name property in the config.  If it exists use
         // the pool class name to create the pool.  Otherwise use the default
         // pool implementation.
         String poolClassName = (String)config.get(
            PropertyConstants.ENV_POOL_CLASS_NAME);

         ApplicationPool pool = null;
         if (poolClassName != null)
         {
            pool = createPool(
               sName
               , poolClassName
               , sApplicationModule
               , sConnectString
               , config);
         }
         else
         {
            pool = createPool(
               sName
               , sApplicationModule
               , sConnectString
               , config);
         }
         
         pool.setUserData(config);

         return pool;
      }
   }

   /**
    * Searches for a pool with the specified name.  Creates a new pool if
    * one is not located.
    */
   public ApplicationPool findPool(
      String poolName
      , String poolClassName
      , String applicationModuleName
      , String connectString
      , Hashtable environment)
   {
      synchronized(mLock)
      {
         ApplicationPool pool = getPool(poolName);
         if (pool == null)
         {
            try
            {
               pool = createPool(
                  poolName
                  , poolClassName
                  , applicationModuleName
                  , connectString
                  , environment);
            }
            catch (java.lang.Exception ex)
            {
               throw new JboException(ex);
            }
         }
         return pool;
      }
   }


   /**
    * Searches for a pool with the specified name.  Creates a new pool if
    * one is not located.
    */
   public ApplicationPool findPool(
      String poolName
      , String applicationModuleName
      , String connectString
      , Hashtable environment)
   {
      synchronized(mLock)
      {
         ApplicationPool pool = getPool(poolName);
         if (pool == null)
         {
            try
            {
               pool = createPool(
                  poolName
                  , "oracle.jbo.common.ampool.ApplicationPoolImpl"
                  , applicationModuleName
                  , connectString
                  , environment);
            }
            catch (java.lang.Exception ex)
            {
               throw new JboException(ex);
            }
         }
         return pool;
      }
   }

   public ApplicationPool findPool(
      String poolName
      , Configuration configuration
      , String configName)
   {
      synchronized(mLock)
      {
         ApplicationPool pool = getPool(poolName);

         if (pool == null)
         {
            pool = createPool(poolName, configuration, configName);
         }

         return pool;
      }
   }
   
   /**
    * Searches for a pool with the specified name.  Creates a new pool if
    * one is not located.
    */
   public ApplicationPool findPool(
      String poolName
      , String packageName
      , String configName
      , Properties props)
   {
      synchronized(mLock)
      {
         ApplicationPool pool = getPool(poolName);
         if (pool == null)
         {
            try
            {
               pool = createPool(
                  poolName
                  , packageName
                  , configName
                  , props);

               if (props != null)
               {
                  String password = (String)props.get("Password");
                  if(password != null && password.length() > 0)
                  {
                     pool.setPassword(password);
                  }

                  String username = (String)props.get("UserName");
                  if(username != null && username.length() > 0)
                  {
                     pool.setUserName(username);
                  }
               }
                  
            }
            catch (java.lang.Exception ex)
            {
               throw new JboException(ex);
            }
         }
         return pool;
      }
   }

   protected int getMonitorSleepInterval()
   {
      return JboEnvUtil.getPropertyAsInt(
         PropertyMetadata.ENV_AMPOOL_MONITOR_SLEEP_INTERVAL.pName
         , Integer.valueOf(PropertyMetadata.ENV_AMPOOL_MONITOR_SLEEP_INTERVAL.pDefault)
            .intValue());
   }
}
