//$Header: /Development/AEDevelopment/projects/org.activebpel.rt.bpel.server/src/org/activebpel/rt/bpel/server/engine/AeEngineFactory.java,v 1.93 2008/02/17 21:38:49 mford Exp $
/////////////////////////////////////////////////////////////////////////////
//PROPRIETARY RIGHTS STATEMENT
//The contents of this file represent confidential information that is the
//proprietary property of Active Endpoints, Inc.  Viewing or use of
//this information is prohibited without the express written consent of
//Active Endpoints, Inc. Removal of this PROPRIETARY RIGHTS STATEMENT
//is strictly forbidden. Copyright (c) 2002-2006 All rights reserved.
/////////////////////////////////////////////////////////////////////////////
package org.activebpel.rt.bpel.server.engine;

import commonj.timers.TimerManager;
import commonj.work.Work;
import commonj.work.WorkException;
import commonj.work.WorkManager;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.logging.Handler;

import javax.naming.InitialContext;
import javax.xml.soap.MessageFactory;

import org.activebpel.rt.AeException;
import org.activebpel.rt.bpel.AeBusinessProcessException;
import org.activebpel.rt.bpel.IAeEngineListener;
import org.activebpel.rt.bpel.IAeExpressionLanguageFactory;
import org.activebpel.rt.bpel.config.AeDefaultEngineConfiguration;
import org.activebpel.rt.bpel.config.IAeEngineConfiguration;
import org.activebpel.rt.bpel.coord.IAeCoordinationManager;
import org.activebpel.rt.bpel.def.IAeBPELConstants;
import org.activebpel.rt.bpel.def.io.registry.AeEngineConfigExtensionRegistry;
import org.activebpel.rt.bpel.def.visitors.AeDefVisitorFactory;
import org.activebpel.rt.bpel.impl.AeSOAPMessageFactory;
import org.activebpel.rt.bpel.impl.IAeAttachmentManager;
import org.activebpel.rt.bpel.impl.IAeBusinessProcessEngineInternal;
import org.activebpel.rt.bpel.impl.IAeCoordinationManagerInternal;
import org.activebpel.rt.bpel.impl.IAeLockManager;
import org.activebpel.rt.bpel.impl.IAeManager;
import org.activebpel.rt.bpel.impl.IAeProcessManager;
import org.activebpel.rt.bpel.impl.IAeQueueManager;
import org.activebpel.rt.bpel.impl.reply.IAeTransmissionTracker;
import org.activebpel.rt.bpel.server.AeMessages;
import org.activebpel.rt.bpel.server.IAeDeploymentProvider;
import org.activebpel.rt.bpel.server.addressing.AePartnerAddressing;
import org.activebpel.rt.bpel.server.addressing.IAePartnerAddressing;
import org.activebpel.rt.bpel.server.addressing.pdef.AePartnerAddressingFactory;
import org.activebpel.rt.bpel.server.addressing.pdef.IAePartnerAddressingFactory;
import org.activebpel.rt.bpel.server.addressing.pdef.IAePartnerAddressingProvider;
import org.activebpel.rt.bpel.server.admin.IAeEngineAdministration;
import org.activebpel.rt.bpel.server.admin.rdebug.server.AeRemoteDebugImpl;
import org.activebpel.rt.bpel.server.admin.rdebug.server.IAeBpelAdmin;
import org.activebpel.rt.bpel.server.catalog.IAeCatalog;
import org.activebpel.rt.bpel.server.deploy.IAeDeploymentHandlerFactory;
import org.activebpel.rt.bpel.server.deploy.IAePolicyMapper;
import org.activebpel.rt.bpel.server.engine.storage.AePersistentStoreFactory;
import org.activebpel.rt.bpel.server.engine.storage.AeStorageException;
import org.activebpel.rt.bpel.server.engine.storage.sql.AeDataSource;
import org.activebpel.rt.bpel.server.engine.transaction.IAeTransactionManagerFactory;
import org.activebpel.rt.bpel.server.logging.IAeDeploymentLoggerFactory;
import org.activebpel.rt.bpel.server.security.AeSecurityProvider;
import org.activebpel.rt.bpel.server.security.IAeSecurityProvider;
import org.activebpel.rt.bpel.urn.IAeURNResolver;
import org.activebpel.rt.config.AeConfigurationUtil;
import org.activebpel.rt.expr.validation.functions.IAeFunctionValidatorFactory;
import org.activebpel.rt.util.AeUtil;
import org.activebpel.timer.AeTimerManager;
import org.activebpel.timer.IAeStoppableTimerManager;
import org.activebpel.work.AeExceptionReportingWorkManager;
import org.activebpel.work.IAeProcessWorkManager;
import org.activebpel.work.IAeStoppableWorkManager;
import org.activebpel.work.child.AeConfigAwareChildWorkManager;
import org.activebpel.work.factory.AeDefaultWorkManagerFactory;
import org.activebpel.work.factory.IAeWorkManagerFactory;
import org.activebpel.work.input.IAeInputMessageWork;
import org.activebpel.work.input.IAeInputMessageWorkManager;

/**
 * Maintains a singleton instance of the engine.
 */
public class AeEngineFactory
{
   /** The deployment provider which manages all process deployments */
   private static IAeDeploymentProvider sDeploymentProvider;

   /** The singleton engine instance */
   private static AeBpelEngine sEngine;

   /** The singleton admin instance */
   private static IAeEngineAdministration sAdmin;

   /** The logger for creating process log files */
   private static IAeProcessLogger sProcessLogger;

   /** The partner addressing layer */
   private static IAePartnerAddressing sPartnerAddressing;

   /** WorkManager impl for asynchronous work */
   private static WorkManager sWorkManager;

   /** Timer Manager impl for scheduling alarms */
   private static TimerManager sTimerManager;

   /** The current configuration settings */
   private static IAeEngineConfiguration sConfig;

   /** Provides mappings between principals and partners */
   private static IAePartnerAddressingProvider sAddressProvider;

   /** Deployment handler factory. */
   private static IAeDeploymentHandlerFactory sDeploymentHandlerFactory;
  

   /** Global catalog. */
   private static IAeCatalog sCatalog;

   /** Deployment logger factory. */
   private static IAeDeploymentLoggerFactory sDeploymentLoggerFactory;

   /** Flag indicating the persistent store is available in the configuration. */
   private static boolean sPersistentStoreConfiguration;

   /** Flag indicating the persistent store is ready for use. */
   private static boolean sPersistentStoreReadyForUse;

   /** String indicating the error message from the persistent store if it is not ready for use. */
   private static String sPersistentStoreError;

   /** The singleton remote debug engine instance */
   private static IAeBpelAdmin sRemoteDebugImpl;

   /** Coordination manager factory. */
   private static IAeCoordinationManagerInternal sCoordinationManager;

   /** Policy Mapper to create handler chains from policy assertions. */
   private static IAePolicyMapper sPolicyMapper;

   /** Work manager for per-process work requests */
   private static IAeProcessWorkManager sProcessWorkManager;

   /** Flag indicating if using internal WorkManager version */
   private static boolean sInternalWorkManager;

   /** Transmission and receive/reply manager. */
   private static IAeTransmissionTracker sTransmissionTracker;

   /** Transaction manager factory. */
   private static IAeTransactionManagerFactory sTransactionManagerFactory;
   
   /** Security Provider */
   private static IAeSecurityProvider sSecurityProvider;

   /** Child work managers indexed by name. */
   private static Map sChildWorkManagers = new HashMap();

   /** Input message work manager. */
   private static IAeInputMessageWorkManager sInputMessageWorkManager;

   /**
    * Pre-initialize the engine to set up storage work, policy mappers and timer managers.
    * @param aConfig
    * @throws AeException
    */
   public static void preInit(IAeEngineConfiguration aConfig) throws AeException
   {
      // Load the logging handler if one was specified
      Map logMap = aConfig.getMapEntry(IAeEngineConfiguration.LOG_HANDLER_ENTRY);
      if (logMap != null)
      {
         for (Iterator iter = logMap.keySet().iterator(); iter.hasNext();)
         {
            String handlerClass = (String)logMap.get(iter.next());
            try
            {
               Class handlerClazz = Class.forName(handlerClass);
               Handler logHandler = (Handler)handlerClazz.newInstance();
               AeException.getLogger().addHandler(logHandler);
            }
            catch(Throwable t)
            {
               t.printStackTrace();
            }
         }
      }

      setEngineConfig(aConfig);

      // create tx manager factory first since the storage layer is dependent on it.
      sTransactionManagerFactory = createTransactionManagerFactory();


      // Initialize storage component.
      initializeStorage(aConfig);

      // Initialize the work manager
      initializeWorkManager();

      // Initialize the timer manager
      initializeTimerManager();

      // Initialize the policy mapper
      initializePolicyMapper();
   }

   /**
    * Initialize the BPEL engine.
    *
    * @throws AeException
    */
   public static void init() throws AeException
   {
      // create the managers
      IAeProcessManager processManager       = createProcessManager();
      IAeQueueManager queueManager           = createQueueManager();
      IAeLockManager lockManager             = createLockManager();
      IAeAttachmentManager attachmentManager = createAttachmentManager();

      // create the engine admin
      sAdmin = createEngineAdmin();

      // Create the remote debug engine implementation instance.
      sRemoteDebugImpl = createRemoteDebugImpl( getEngineConfig().getMapEntry(IAeEngineConfiguration.REMOTE_DEBUG_ENTRY) );

      // Use the managers to create the bpel engine.
      // The class name for the bpel engine can be supplied dynamically, but this
      // factory assumes that it's derived from AeBpelEngine
      sEngine = createNewEngine(queueManager, processManager, lockManager, attachmentManager);

      IAeEngineListener engineListener = createEngineListener();
      if (engineListener != null)
         sEngine.addEngineListener(engineListener);

      // create coordination manager
      sCoordinationManager = createCoordinationManager();

      // set the coordination manager before calling the engine's create() (and hence initialization of all managers)
      sEngine.setCoordinationManager( sCoordinationManager);

      // create transmission/receive id tracker.
      sTransmissionTracker = createTransmissionTracker();
      sEngine.setTransmissionTracker(sTransmissionTracker);
      
      // create any custom managers that are defined in the config
      createCustomManagers();

      // create engine and init its managers.
      sEngine.create();

      // Create the partner addressing layer
      AePartnerAddressing addressLayer = new AePartnerAddressing();
      IAePartnerAddressingFactory factory = AePartnerAddressingFactory.newInstance();
      sAddressProvider = factory.getProvider();
      addressLayer.setProvider(sAddressProvider);
      sPartnerAddressing = addressLayer;

      // Create the deployment plan manager
      IAeDeploymentProvider provider = (IAeDeploymentProvider) createConfigObject( IAeEngineConfiguration.DEPLOYMENT_PROVIDER );
      sDeploymentProvider = provider;
      sEngine.setPlanManager(sDeploymentProvider);
      processManager.setPlanManager(sDeploymentProvider);

      // Create the process logger
      sProcessLogger = createProcessLogger();
      sProcessLogger.setEngine(getEngine());

      sCatalog = (IAeCatalog)createConfigObject( IAeEngineConfiguration.CATALOG_ENTRY );
      sDeploymentLoggerFactory = (IAeDeploymentLoggerFactory)createConfigObject( IAeEngineConfiguration.DEPLOYMENT_LOG_ENTRY );
      sDeploymentHandlerFactory = (IAeDeploymentHandlerFactory)createConfigObject( IAeEngineConfiguration.DEPLOYMENT_HANDLER_ENTRY );

      // Create the work manager for per-process work requests.
      sProcessWorkManager = createProcessWorkManager();

      // create urn resolver
      IAeURNResolver urnResolver = createURNResolver();
      sEngine.setURNResolver(urnResolver);

      // create SOAP Message factory
      AeSOAPMessageFactory.setSOAPMessageFactory(createSOAPMessageFactory());
      
      // create the Security provider
      sSecurityProvider = createSecurityProvider();
      
      // install extension registry
      try
      {
         AeEngineConfigExtensionRegistry registry = new AeEngineConfigExtensionRegistry((AeDefaultEngineConfiguration) AeEngineFactory.getEngineConfig());
         AeDefVisitorFactory.setExtensionRegistry(IAeBPELConstants.WSBPEL_2_0_NAMESPACE_URI, registry);
      }
      catch (ClassNotFoundException ex)
      {
         // DO Nothing if can not find or install an extension registry
      }
   }

   /**
    * Publicly accessible method to initialize storage component of the engine
    * from an engine configuration object.
    *
    * @throws AeStorageException
    */
   public static void initializeStorage(IAeEngineConfiguration aConfig) throws AeStorageException
   {
      setEngineConfig(aConfig);

      // Initialize the persistent store factory
      initializePersistentStoreFactory();
   }

   /**
    * Create an object instance from config params.
    * @param aKey Key to top level entry with sub-entry that specifies the class.
    * @throws AeException
    */
   protected static Object createConfigObject( String aKey ) throws AeException
   {
      Map entryParams = getEngineConfig().getMapEntry(aKey);
      if (AeUtil.isNullOrEmpty(entryParams))
      {
         throw new AeException(AeMessages.getString("AeEngineFactory.ERROR_0") + aKey ); //$NON-NLS-1$
      }
      return createConfigSpecificClass( entryParams );
   }

   /**
    * Start the BPEL engine.  Should not be called until all of
    * the expected deployments have been completed (as previously persisted
    * processes will assume their resources are available as soon as they
    * start up again).
    * @throws AeBusinessProcessException
    */
   public static void start() throws AeBusinessProcessException
   {
      if (isEngineStorageReadyRetest())
      {
         // Start only this engine - startAll() is not necessary on startup - each node will
         // call their own start method on startup.
         sEngine.start();
      }
      else
      {
         throw new AeBusinessProcessException(AeMessages.getString("AeEngineFactory.ERROR_1")); //$NON-NLS-1$
      }
   }

   /**
    * Creates the class that listens for process events and logs them.
    */
   protected static IAeProcessLogger createProcessLogger() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.PROCESS_LOGGER_ENTRY);
      return (IAeProcessLogger) createConfigSpecificClass(configMap);
   }

   /**
    * Creates the new engine admin instance.
    * @return A new engine admin instance.
    * @throws AeException
    */
   protected static IAeEngineAdministration createEngineAdmin() throws AeException
   {
      String engineAdminClass = getEngineConfig().getEntry(
            IAeEngineConfiguration.ENGINE_ADMIN_IMPL_ENTRY,
            AeEngineAdministration.class.getName());

      try
      {
         Class clazz = Class.forName(engineAdminClass);
         return (IAeEngineAdministration) clazz.newInstance();
      }
      catch (Exception e)
      {
         throw new AeException(AeMessages.getString("AeEngineFactory.ERROR_2"), e); //$NON-NLS-1$
      }

   }

   /**
    * Creates the new remote debug engine instance.
    * @param aMap
    * @return IAeBPelAdmin
    * @throws AeException
    */
   protected static IAeBpelAdmin createRemoteDebugImpl(Map aMap) throws AeException
   {
      try
      {
         String debugClassName;
         String eventLocatorClass;

         // Defaults if values not configured in the given map.
         String defaultRDebugClass = AeRemoteDebugImpl.class.getName();
         String defaultEventLocatorClass = "org.activebpel.rt.axis.bpel.rdebug.client.AeEventHandlerLocator"; //$NON-NLS-1$

         if ( aMap == null || aMap.isEmpty() )
         {
            debugClassName = defaultRDebugClass;
            eventLocatorClass = defaultEventLocatorClass;
         }
         else
         {
            // Get the remote debug impl class name.
            debugClassName = (String) aMap.get(IAeEngineConfiguration.REMOTE_DEBUG_IMPL_ENTRY);
            if ( AeUtil.isNullOrEmpty(debugClassName) )
               debugClassName = defaultRDebugClass;

            // Get the handler locator class names.
            eventLocatorClass = (String) aMap.get(IAeEngineConfiguration.EVENT_HANDLER_LOCATOR_ENTRY);
            if ( AeUtil.isNullOrEmpty(eventLocatorClass) )
               eventLocatorClass = defaultEventLocatorClass;
         }

         Class c = Class.forName(debugClassName);
         Constructor constructor = c.getConstructor( new Class[] { String.class } );
         return (IAeBpelAdmin) constructor.newInstance( new Object[] {eventLocatorClass} );
      }
      catch (Exception e)
      {
         throw new AeException(AeMessages.getString("AeEngineFactory.ERROR_5"), e); //$NON-NLS-1$
      }
   }

   /**
    * Creates the coordination manager instance.
    *
    * @throws AeException
    */
   protected static IAeCoordinationManagerInternal createCoordinationManager() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.COORDINATION_MANAGER_ENTRY);
      return (IAeCoordinationManagerInternal) createConfigSpecificClass(configMap);
   }

   /**
    * Creates the durable transmit/receive manager.
    *
    * @throws AeException
    */
   protected static IAeTransmissionTracker createTransmissionTracker() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.TRANSMISSION_TRACKER_ENTRY);
      return (IAeTransmissionTracker) createConfigSpecificClass(configMap);
   }

   /**
    * Creates managers for each of the managers list in the custom
    * "Managers" section of the engine config.
    * 
    * @throws AeException
    */
   protected static void createCustomManagers() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.CUSTOM_MANAGERS_ENTRY);
      if (AeUtil.notNullOrEmpty(configMap))
      {
         for (Iterator iter = configMap.keySet().iterator(); iter.hasNext(); )
         {
            String managerName = (String) iter.next();
            Map managerMap = (Map) configMap.get(managerName);
            IAeManager manager = (IAeManager) createConfigSpecificClass(managerMap);
            sEngine.addCustomManager(managerName, manager);
         }
      }
   }

   /**
    * Creates the engine listener specified in the config file
    * @throws AeException
    */
   protected static IAeEngineListener createEngineListener() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.ENGINE_LISTENER);
      return configMap != null? (IAeEngineListener) createConfigSpecificClass(configMap) : null;
   }

   /**
    * Creates the new engine instance. This provides a means of changing the underlying
    * engine class based on the config file which is something that we do in order
    * to easily swap in our clustered version of the engine.
    * @param aQueueManager
    * @param aProcessManager
    * @param aLockManager
    * @param aAttachmentManager
    */
   protected static AeBpelEngine createNewEngine(
         IAeQueueManager aQueueManager,
         IAeProcessManager aProcessManager,
         IAeLockManager aLockManager,
         IAeAttachmentManager aAttachmentManager)
   throws AeException
   {
      String engineClass = getEngineConfig().getEntry(
            IAeEngineConfiguration.ENGINE_IMPL_ENTRY, AeBpelEngine.class.getName());

      try
      {
         Class clazz = Class.forName(engineClass);
         Constructor cons =
            clazz.getConstructor(
                  new Class[] {
                        IAeEngineConfiguration.class,
                        IAeQueueManager.class,
                        IAeProcessManager.class,
                        IAeLockManager.class,
                        IAeAttachmentManager.class});
         return (AeBpelEngine) cons.newInstance(new Object[] { getEngineConfig(), aQueueManager, aProcessManager, aLockManager, aAttachmentManager});
      }
      catch (Exception e)
      {
         throw new AeException(AeMessages.getString("AeEngineFactory.ERROR_6"), e); //$NON-NLS-1$
      }

   }

   /**
    * This method initializes the persistent store factory.
    */
   public static void initializePersistentStoreFactory() throws AeStorageException
   {
      // Clear the datasource global
      AeDataSource.MAIN = null;

      Map storeConfigMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.PERSISTENT_STORE_ENTRY);
      if (!AeUtil.isNullOrEmpty(storeConfigMap))
      {
         sPersistentStoreConfiguration = true;

         // Initialize the persistent store factory with the current config.
         try
         {
            AePersistentStoreFactory.init(storeConfigMap);
            sPersistentStoreReadyForUse = true;
         }
         catch(AeStorageException ex)
         {
            AeException.logWarning(""); //$NON-NLS-1$
            ex.logError();
            AeException.logWarning(""); //$NON-NLS-1$
            setPersistentStoreError(ex.getLocalizedMessage());
            sPersistentStoreReadyForUse = false;
         }
      }
   }

   /**
    * Creates the transaction manager factory.
    * @throws AeStorageException
    */
   protected static IAeTransactionManagerFactory createTransactionManagerFactory() throws AeStorageException
   {
      try
      {
         Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.TRANSACTION_MANAGER_FACTORY_ENTRY);
         return  (IAeTransactionManagerFactory) createConfigSpecificClass(configMap);
      } catch(Throwable t)
      {
         throw new AeStorageException(t);
      }
   }

   /**
    * This method constructs the work manager used by the engine through the
    * configured work manager factory.
    */
   protected static void initializeWorkManager() throws AeException
   {
      IAeWorkManagerFactory factory = null;

      // Try to construct the work manager factory specified in the work manager
      // configuration.
      Map workManagerMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.WORK_MANAGER_ENTRY);
      if (!AeUtil.isNullOrEmpty(workManagerMap))
      {
         Map factoryMap = (Map) workManagerMap.get(IAeEngineConfiguration.FACTORY_ENTRY);
         if (!AeUtil.isNullOrEmpty(factoryMap))
         {
            String className = (String) factoryMap.get(IAeEngineConfiguration.CLASS_ENTRY);
            if (!AeUtil.isNullOrEmpty(className))
            {
               try
               {
                  Class clazz = Class.forName(className);
                  factory = (IAeWorkManagerFactory) clazz.newInstance();
               }
               catch (Exception e)
               {
                  AeException.logError(e, AeMessages.format("AeEngineFactory.ERROR_WorkManagerFactoryClass", className)); //$NON-NLS-1$
               }
            }
         }
      }

      // If the work manager factory was not specified or was invalid, then use
      // the default factory.
      if (factory == null)
      {
         factory = new AeDefaultWorkManagerFactory();
      }

      factory.init(workManagerMap);

      sWorkManager = factory.getWorkManager();
      sInternalWorkManager = factory.isInternalWorkManager();
      sInputMessageWorkManager = factory.getInputMessageWorkManager();

      // Wrap the chosen work manager with one that reports unhandled exceptions.
      sWorkManager = new AeExceptionReportingWorkManager(sWorkManager);

      // Initialize child work managers from WorkManager config map.
      initializeChildWorkManagers(workManagerMap);
   }

   /**
    * Initializes child work managers.
    *
    * @param aWorkManagerConfig
    */
   protected static void initializeChildWorkManagers(Map aWorkManagerConfig)
   {
      // Names of child work managers.
      Set names = new HashSet();

      // Make sure the set includes the Alarm child work manager.
      names.add(IAeEngineConfiguration.ALARM_CHILD_WORK_MANAGER_ENTRY);

      // Scan entries for child work managers.
      if (!AeUtil.isNullOrEmpty(aWorkManagerConfig))
      {
         Map childWorkManagersConfig = (Map) aWorkManagerConfig.get(IAeEngineConfiguration.CHILD_WORK_MANAGERS_ENTRY);
         if (!AeUtil.isNullOrEmpty(childWorkManagersConfig))
         {
            names.addAll(childWorkManagersConfig.keySet());
         }
      }

      // Each instance of AeConfigAwareChildWorkManager will pick up its max
      // work count from the engine configuration. However, if the child work
      // manager is not mentioned in the engine configuration (like, possibly,
      // the Alarm child work manager), then use the default max work count.
      int childMaxWorkCount = IAeEngineConfiguration.DEFAULT_CHILD_MAX_WORK_COUNT;

      // Create each defined child work manager.
      for (Iterator i = names.iterator(); i.hasNext(); )
      {
         try
         {
            String name = (String) i.next();
            
            sChildWorkManagers.put(name, new AeConfigAwareChildWorkManager(name, childMaxWorkCount, getWorkManager()));
         }
         catch (Throwable t)
         {
            AeException.logError(t);
         }
      }
   }

   /**
    * This method initializes the timer manager used by the engine. We will first attempt
    * to lookup the timer manager from the JNDI location specified in the engine config
    * file. If not specified or unable to load, then we will use default timer manager.
    */
   protected static void initializeTimerManager()
   {
      // Make sure we initialize to null, or may not behave properly during servlet hot deploy
      sTimerManager = null;

      Map timerMgrConfigMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.TIMER_MANAGER_ENTRY);
      if (! AeUtil.isNullOrEmpty(timerMgrConfigMap))
      {
         String timerMgrLocation = (String)timerMgrConfigMap.get(IAeEngineConfiguration.TM_JNDI_NAME_ENTRY);
         if (! AeUtil.isNullOrEmpty(timerMgrLocation))
         {
            try
            {
               // Lookup the timer manager from the JNDI location specified in engine config.
               InitialContext ic = new InitialContext();
               sTimerManager = (TimerManager)ic.lookup(timerMgrLocation);
               AeException.info(AeMessages.getString("AeEngineFactory.ERROR_16") + timerMgrLocation); //$NON-NLS-1$
            }
            catch (Exception e)
            {
               AeException.info(AeMessages.getString("AeEngineFactory.ERROR_17") + timerMgrLocation); //$NON-NLS-1$
            }
         }
      }

      // The JNDI location was missing or invalid, so try to construct a timer
      // manager from an explicitly specified class name.
      if ((sTimerManager == null) && !AeUtil.isNullOrEmpty(timerMgrConfigMap))
      {
         String className = (String) timerMgrConfigMap.get(IAeEngineConfiguration.CLASS_ENTRY);
         if (!AeUtil.isNullOrEmpty(className))
         {
            try
            {
               Class clazz = Class.forName(className);
               sTimerManager = (TimerManager) clazz.newInstance();
            }
            catch (Exception e)
            {
               AeException.logError(e, AeMessages.format("AeEngineFactory.ERROR_Instantiation", className)); //$NON-NLS-1$
            }
         }
      }

      // Timer manager not specified or invalid JNDI location given, use default
      if (sTimerManager == null)
      {
         AeException.info(AeMessages.getString("AeEngineFactory.18")); //$NON-NLS-1$
         sTimerManager = new AeTimerManager();
      }
   }

   /**
    * This method initializes the policy mapper used by the engine.
    */
   protected static void initializePolicyMapper() throws AeException
   {
      // Make sure we initialize to null, or may not behave properly during servlet hot deploy
      sPolicyMapper = null;
      // get the main policy mapper
      IAeEngineConfiguration config = getEngineConfig();
      sPolicyMapper = (IAePolicyMapper) createConfigSpecificClass(config.getMapEntry(IAeEngineConfiguration.POLICY_MAPPER));
   }

   /**
    * Factory method for creating the queue manager for the engine.  The type
    * of manager to use will be determined based on information found in the
    * engine configuration.
    *
    * @return A queue manager.
    */
   protected static IAeQueueManager createQueueManager() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.QUEUE_MANAGER_ENTRY);
      return (IAeQueueManager) createConfigSpecificClass(configMap);
   }

   /**
    * Factory method for creating the process manager for the engine.  The type
    * of manager to use will be determined based on information found in the
    * engine configuration.
    *
    * @return A process manager.
    */
   protected static IAeProcessManager createProcessManager() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.PROCESS_MANAGER_ENTRY);
      return (IAeProcessManager) createConfigSpecificClass(configMap);
   }

   /**
    * Factory method for creating the lock manager for the engine.  The type
    * of manager to use will be determined based on information found in the
    * engine configuration.
    *
    * @return A lock manager.
    */
   private static IAeLockManager createLockManager() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.LOCK_MANAGER_ENTRY);
      return (IAeLockManager) createConfigSpecificClass(configMap);
   }
   
   /**
    * Factory method for creating the attachment manager for the engine.  The type
    * of manager to use will be determined based on information found in the
    * engine configuration.
    *
    * @return A lock manager.
    */
   private static IAeAttachmentManager createAttachmentManager() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.ATTACHMENT_MANAGER_ENTRY);
      return (IAeAttachmentManager) createConfigSpecificClass(configMap);
   }

   /**
    * Factory method for creating the urn resolver.
    */
   private static IAeURNResolver createURNResolver() throws AeException
   {
      Map configMap = getEngineConfig().getMapEntry(IAeEngineConfiguration.URN_RESOLVER_ENTRY);
      return (IAeURNResolver) createConfigSpecificClass(configMap);
   }

   /**
    * This method takes a configuration map for a manager and instantiates that
    * manager.  This involves some simple java reflection to find the proper
    * constructor and then calling that constructor.
    *
    * @param aConfig The engine configuration map for the manager.
    * @return An engine manager (alert, queue, etc...).
    */
   public static Object createConfigSpecificClass(Map aConfig) throws AeException
   {
      if (AeUtil.isNullOrEmpty(aConfig))
      {
         throw new AeException(AeMessages.getString("AeEngineFactory.ERROR_10")); //$NON-NLS-1$
      }
      return AeConfigurationUtil.createConfigSpecificClass(aConfig);
   }

   /**
    * Gets the process logger.
    */
   public static IAeProcessLogger getLogger()
   {
      return sProcessLogger;
   }

   /**
    * Gets the partner addressing
    */
   public static IAePartnerAddressing getPartnerAddressing()
   {
      return sPartnerAddressing;
   }

   /**
    * Gets the policy mapper
    */
   public static IAePolicyMapper getPolicyMapper()
   {
      return sPolicyMapper;
   }

   /**
    * Gets a ref to the administration API
    */
   public static IAeEngineAdministration getEngineAdministration()
   {
      return sAdmin;
   }

   /**
    * Getter for the deployment descriptor.
    */
   public static IAeDeploymentProvider getDeploymentProvider()
   {
      return sDeploymentProvider;
   }

   /**
    * Getter for the engine.
    */
   public static IAeBusinessProcessEngineInternal getEngine()
   {
      return sEngine;
   }

   /**
    * Gets the installed work manager.
    */
   public static WorkManager getWorkManager()
   {
      return sWorkManager;
   }

   /**
    * Gets the installed timer manager.
    */
   public static TimerManager getTimerManager()
   {
      return sTimerManager;
   }

   /**
    * Convenience method that schedules work to be done and translates any work exceptions
    * into our standard business process exception.
    * @param aWork
    */
   public static void schedule(Work aWork) throws AeBusinessProcessException
   {
      try
      {
         getWorkManager().schedule(aWork);
      }
      catch (WorkException e)
      {
         throw new AeBusinessProcessException(AeMessages.getString("AeEngineFactory.ERROR_15"), e); //$NON-NLS-1$
      }
   }

   /**
    * Convenience method for stopping the work manager.
    */
   public static void shutDownWorkManager()
   {
      // Stop is only available in our default implementation of the work manager
      if (getWorkManager() instanceof IAeStoppableWorkManager)
         ((IAeStoppableWorkManager)getWorkManager()).stop();

      // Notify the input message work manager that we're shutting down.
      getInputMessageWorkManager().stop();
   }

   /**
    * Convenience method for stopping the timer manager.
    */
   public static void shutDownTimerManager()
   {
      // Stop is only available in our default implementation of the timer manager
      if (getTimerManager() instanceof IAeStoppableTimerManager)
         ((IAeStoppableTimerManager) getTimerManager()).stop();
   }

   /**
    * Set the engine configuration settings.
    */
   protected static void setEngineConfig(IAeEngineConfiguration aConfig)
   {
      sConfig = aConfig;
   }

   /**
    * Accessor for engine configuration settings.
    */
   public static IAeEngineConfiguration getEngineConfig()
   {
      return sConfig;
   }

   /**
    * Convenience method for getting the expression language factory.
    */
   public static IAeExpressionLanguageFactory getExpressionLanguageFactory() throws AeException
   {
      return getEngineConfig().getExpressionLanguageFactory();
   }
   
   /**
    * Convenience method for getting the function validator factory.
    * 
    *
    * @throws AeException
    */
   public static IAeFunctionValidatorFactory getFunctionValidatorFactory() throws AeException
   {
      return getEngineConfig().getFunctionValidatorFactory();
   }

   /**
    * Accessor for the partner addressing provider.
    */
   public static IAePartnerAddressingProvider getPartnerAddressProvider()
   {
      return sAddressProvider;
   }

   /**
    * Accessor for the global catalog.
    */
   public static IAeCatalog getCatalog()
   {
      return sCatalog;
   }

   /**
    * Accessor for deployment logger factory.
    */
   public static IAeDeploymentLoggerFactory getDeploymentLoggerFactory()
   {
      return sDeploymentLoggerFactory;
   }

   /**
    * Access the deployment handler factory.
    */
   public static IAeDeploymentHandlerFactory getDeploymentHandlerFactory()
   {
      return sDeploymentHandlerFactory;
   }

   /**
    * Returns the coordination manager instance.
    */
   public static IAeCoordinationManager getCoordinationManager()
   {
      return sCoordinationManager;
   }

   /**
    * Returns the transmission-receive id tracker instance.
    */
   public static IAeTransmissionTracker getTransmissionTracker()
   {
      return sTransmissionTracker;
   }

   /**
    * @return Storage transaction manager factory.
    */
   public static IAeTransactionManagerFactory getTransactionManagerFactory()
   {
      return sTransactionManagerFactory;
   }

   /**
    * Returns true if the current configuration contains a persistent store.
    */
   public static boolean isPersistentStoreConfiguration()
   {
      return sPersistentStoreConfiguration;
   }

   /**
    * Returns true if the persistent storage is ready for use.
    */
   public static boolean isPersistentStoreReadyForUse()
   {
      return sPersistentStoreReadyForUse;
   }

   /**
    * If engine storage is not already in ready state then we will
    * check it again before returning status.
    */
   public static boolean isEngineStorageReadyRetest()
   {
      try
      {
         if(! isEngineStorageReady())
            initializePersistentStoreFactory();
         return isEngineStorageReady();
      }
      catch (AeStorageException ex)
      {
         AeException.logWarning(""); //$NON-NLS-1$
         ex.logError();
         AeException.logWarning(""); //$NON-NLS-1$
         return false;
      }
   }

   /**
    * Returns true if the engine storage system is ready, either not persistent
    * or the persistent and the storage is ready.
    */
   public static boolean isEngineStorageReady()
   {
      if(! isPersistentStoreConfiguration())
         return true;
      return isPersistentStoreReadyForUse();
   }

   /**
    * Returns the error message if the persistent store is not ready for use.
    */
   public static String getPersistentStoreError()
   {
      return sPersistentStoreError;
   }

   /**
    * Sets the error message if the persistent store is not ready for use.
    */
   public static void setPersistentStoreError(String aString)
   {
      sPersistentStoreError = aString;
   }

   /**
    * Gets the remote debug engine instance.
    * @return IAeBpelAdmin
    */
   public static IAeBpelAdmin getRemoteDebugImpl()
   {
      return sRemoteDebugImpl;
   }

   /**
    * Gets the URN manager that is used to resolve URN to URL
    */
   public static IAeURNResolver getURNResolver()
   {
      return getEngine().getURNResolver();
   }

   /**
    * Constructs the work manager for per-process work requests.
    */
   protected static IAeProcessWorkManager createProcessWorkManager() throws AeException
   {
      Map config = getEngineConfig().getMapEntry(IAeEngineConfiguration.PROCESS_WORK_MANAGER_ENTRY);
      return (IAeProcessWorkManager) createConfigSpecificClass(config);
   }

   /**
    * Returns the work manager for per-process work requests.
    */
   public static IAeProcessWorkManager getProcessWorkManager()
   {
      return sProcessWorkManager;
   }

   /**
    * Schedules per-process work for the given process.
    *
    * @param aProcessId
    * @param aWork
    */
   public static void schedule(long aProcessId, Work aWork) throws AeBusinessProcessException
   {
      getProcessWorkManager().schedule(aProcessId, aWork);
   }

   /**
    * Returns True if using internal WorkManager implementation or False if using server version.
    */
   public static boolean isInternalWorkManager()
   {
      return sInternalWorkManager;
   }

   /**
    * @return configured instance of SOAP MessageFactory.
    * If no entry defined in AeEngineConfig or if there is an exception creating the instance,
    * the default for the server is used.
    * @throws AeBusinessProcessException
    */
   private static MessageFactory createSOAPMessageFactory() throws AeBusinessProcessException
   {
      Map config = getEngineConfig().getMapEntry(IAeEngineConfiguration.SOAP_MESSAGE_FACTORY);

      if (!AeUtil.isNullOrEmpty(config))
      {
         String factoryName = (String) config.get(IAeEngineConfiguration.CLASS_ENTRY);
         if (!AeUtil.isNullOrEmpty(factoryName))
         {
            try
            {
               return (MessageFactory) Class.forName(factoryName).newInstance();
            }
            catch (Throwable ae)
            {
               // if we can't create the class, fall through to the default
               AeException.logWarning(AeMessages.format("AeEngineFactory.8", factoryName)); //$NON-NLS-1$
            }
         }
      }

      try
      {
         // default for app server
         return MessageFactory.newInstance();
      }
      catch (Throwable ae)
      {
         throw new AeBusinessProcessException(ae.getMessage(), ae);
      }
   }

   /**
    * Returns the SOAP Message factory used by the engine
    */
   public static MessageFactory getSOAPMessageFactory()
   {
      return AeSOAPMessageFactory.getSOAPMessageFactory();
   }
   
   /**
    * Constructs the security manager. 
    */
   protected static IAeSecurityProvider createSecurityProvider() throws AeException
   {
      Map config = getEngineConfig().getMapEntry(IAeEngineConfiguration.SECURITY_PROVIDER_ENTRY);
      if (!AeUtil.isNullOrEmpty(config))
      {
         String mgrName = (String) config.get(IAeEngineConfiguration.CLASS_ENTRY);
         if (!AeUtil.isNullOrEmpty(mgrName))
         return (IAeSecurityProvider) createConfigSpecificClass(config);
      }
      
      return new AeSecurityProvider(null);
   }

   /**
    * Returns the security provider.
    */
   public static IAeSecurityProvider getSecurityProvider()
   {
      return sSecurityProvider;
   }

   /**
    * Schedules work on the alarm child work manager.
    *
    * @param aWork
    */
   public static void scheduleAlarmWork(Work aWork) throws AeBusinessProcessException
   {
      try
      {
         scheduleChildWork(IAeEngineConfiguration.ALARM_CHILD_WORK_MANAGER_ENTRY, aWork);
      }
      catch (AeBusinessProcessException e)
      {
         AeException.logError(e);

         // This should never happen, but let's guarantee that we always
         // schedule alarm work.
         schedule(aWork);
      }
   }

   /**
    * Schedules work on a child work manager.
    *
    * @param aName
    * @param aWork
    */
   public static void scheduleChildWork(String aName, Work aWork) throws AeBusinessProcessException
   {
      WorkManager childWorkManager = getChildWorkManager(aName);
      if (childWorkManager == null)
      {
         throw new AeBusinessProcessException(AeMessages.format("AeEngineFactory.ERROR_NoChildWorkManager", aName)); //$NON-NLS-1$
      }

      try
      {
         childWorkManager.schedule(aWork);
      }
      catch (Exception e)
      {
         throw new AeBusinessProcessException(AeMessages.getString("AeEngineFactory.ERROR_15"), e); //$NON-NLS-1$
      }
   }

   /**
    * @return the child work manager with the given name
    */
   public static WorkManager getChildWorkManager(String aName)
   {
      return (WorkManager) sChildWorkManagers.get(aName);
   }

   /**
    * Schedules input message work on the configured input message work manager.
    *
    * @param aProcessId
    * @param aInputMessageWork
    */
   public static void scheduleInputMessageWork(long aProcessId, IAeInputMessageWork aInputMessageWork) throws AeBusinessProcessException
   {
      getInputMessageWorkManager().schedule(aProcessId, aInputMessageWork);
   }
   
   /**
    * @return the input message work manager configured for this engine
    */
   public static IAeInputMessageWorkManager getInputMessageWorkManager()
   {
      return sInputMessageWorkManager;
   }
}
