//----------------------------BEGIN LICENSE----------------------------
/* 
 * Willow : the Open Source WorkFlow Project
 * Distributable under GNU LGPL license by gun.org
 *
 * Copyright (C) 2004-2010  huihoo.org 
 * Copyright (C) 2004-2010  ZosaTapo <dertyang@hotmail.com> 
 * 
 * ====================================================================
 * Project Homepage : http://www.huihoo.org/willow
 * Source Forge     : http://sourceforge.net/projects/huihoo
 * Mailing list     : willow@lists.sourceforge.net 
 */
//----------------------------END  LICENSE-----------------------------
package org.huihoo.willow.core;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;

import javax.naming.directory.DirContext;
import javax.servlet.http.HttpSessionAttributeListener;
import javax.servlet.http.HttpSessionListener;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
import org.huihoo.willow.ContainerListener;
import org.huihoo.willow.Context;
import org.huihoo.willow.ContextDeployer;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleException;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Loader;
import org.huihoo.willow.RouteEvalutor;
import org.huihoo.willow.Wrapper;
import org.huihoo.willow.deploy.ApplicationParameter;
import org.huihoo.willow.loader.WorkflowLoader;
import org.huihoo.willow.naming.resources.BaseDirContext;
import org.huihoo.willow.naming.resources.FileDirContext;
import org.huihoo.willow.naming.resources.ProxyDirContext;
import org.huihoo.willow.naming.resources.WARDirContext;
import org.huihoo.workflow.WorkflowException;
import org.huihoo.workflow.server.WorkflowContext;
import org.huihoo.workflow.server.WorkflowContextAttributeListener;
import org.huihoo.workflow.server.WorkflowContextEvent;
import org.huihoo.workflow.server.WorkflowContextListener;
import org.huihoo.workflow.server.WorkflowXPDL;

public class StandardContext extends ContainerBase implements ContextDeployer, Context
{
	private static transient Log log = LogFactory.getLog(StandardContext.class);

	// ----------------------------------------------------------- Constructors

	/**
	 * Create a new StandardContext component with the default basic Valve.
	 */
	public StandardContext()
	{
		super();
		this.backgroundProcessorDelay = 300;
	}

	// ----------------------------------------------------- Instance Variables

	/**
	 * The set of application listener class names configured for this
	 * application, in the order they were encountered in the web.xml file.
	 */
	private String applicationListeners[] = new String[0];

	/**
	 * The set of instantiated application event listener objects</code>.
	 */
	private transient Object applicationEventListenersObjects[] = new Object[0];

	/**
	 * The set of instantiated application lifecycle listener objects</code>.
	 */
	private transient Object applicationLifecycleListenersObjects[] = new Object[0];

	/**
	 * The set of application parameters defined for this application.
	 */
	private ApplicationParameter applicationParameters[] = new ApplicationParameter[0];

	/**
	 * The set of wrapper defined for this application.
	 */
	private Wrapper childWrappers[] = new Wrapper[0];

	/**
	 * The selector mappings for this workflow application, keyed by
	 * matching pattern.
	 */
	private HashMap selectorMappings = new HashMap();

	/**
	 * The application available flag for this Context.
	 */
	private boolean available = false;

	/**
	 * The path to a file to save this Context information.
	 */
	private String configFile = null;

	/**
	 * The "correctly configured" flag for this Context.
	 */
	private boolean configured = false;

	/**
	 * The WorkflowContext implementation associated with this Context.
	 */
	private transient ApplicationContext workflowContext = null;

	/**
	 * The WorkflowRouteEvalutor implementation associated with this Context.
	 */
	private RouteEvalutor routeEvalutor = null;

	/**
	 * Compiler classpath to use.
	 */
	private String compilerClasspath = null;

	/**
	* The display name of this context.
	*/
	private String displayName = null;

	/** 
	 * Override the default workflow xml location. ContextConfig is not configurable
	 * so the setter is not used.
	 */
	private String defaultWorkflowXml;

	/**
	 * The document root for this context.
	 */
	private String docBase = null;

	/**
	 * The descriptive information string for this implementation.
	 */
	private static final String info = "StandardContext/1.0";

	/**
	 * The <code>ContextDeployer</code> to whom we delegate application
	 * deployment requests.
	 */
	private ContextDeployer deployer = null;

	/**
	 * The context initialization parameters for this context,
	 * keyed by name.
	 */
	private HashMap parameters = new HashMap();

	/**
	 * The reloadable flag for this web application.
	 */
	private boolean reloadable = false;

	/**
	 * Unpack WAR property.
	 */
	private boolean unpackWAR = true;

	/**
	 * The DefaultContext override flag for this web application.
	 */
	private boolean override = false;

	/**
	 * The privileged flag for this web application.
	 */
	private boolean privileged = false;

	/**
	 * The set of classnames of LifecycleListeners that will be added
	 * to each newly created Wrapper by <code>createWrapper()</code>.
	 */
	private String wrapperLifecycles[] = new String[0];

	/**
	 * The set of classnames of ContainerListeners that will be added
	 * to each newly created Wrapper by <code>createWrapper()</code>.
	 */
	private String wrapperListeners[] = new String[0];

	/**
	 * The pathname to the work directory for this context (relative to
	 * the server's home if not absolute).
	 */
	private String workDir = null;

	/**
	 * Java class name of the Wrapper class implementation we use.
	 */
	private String wrapperClass = "org.apache.catalina.core.StandardWrapper";

	//--------------------------------------------------------
	//--------------------------------------------------------

	/**
	 * The "follow standard delegation workflow" flag that will be used to
	 * configure our ClassLoader.
	 */
	private boolean delegate = false;

	/**
		* The auto deploy flag for this Context.
		*/
	private boolean autoDeploy = true;

	/**
	 * Filesystem based flag.
	 */
	private boolean filesystemBased = false;

	/**
	 * Case sensitivity.
	 */
	protected boolean caseSensitive = true;

	/**
	 * Allow linking.
	 */
	protected boolean allowLinking = false;

	/**
	 * Non proxied resources.
	 */
	private transient DirContext appResources = null;

	private ArrayList wrappers = new ArrayList();

	/**
	 * Set flag to true to cause the system.out and system.err to be redirected
	 * to the logger when executing a servlet.
	 */
	private boolean swallowOutput = false;

	/**
	 * The request processing pause flag (while reloading occurs)
	 */
	private boolean paused = false;

	/**
	 * The Java class name of the default JavaInterpretor implementation class for
	 * deployed workflow applications.
	 */
	private String routeEvalutorClass = "org.huihoo.willow.core.StandardRouteEvalutor";
	//WorkflowRouteEvalutor

	// ----------------------------------------------------- context Properties

	/**
	 * Set case sensitivity.
	 */
	public void setCaseSensitive(boolean caseSensitive)
	{
		this.caseSensitive = caseSensitive;
	}

	/**
	 * Is case sensitive ?
	 */
	public boolean isCaseSensitive()
	{
		return caseSensitive;
	}

	/**
	 * Set allow linking.
	 */
	public void setAllowLinking(boolean allowLinking)
	{
		this.allowLinking = allowLinking;
	}

	/**
	 * Is linking allowed.
	 */
	public boolean isAllowLinking()
	{
		return allowLinking;
	}

	/**
	 * Return the "follow standard delegation workflow" flag used to configure
	 * our ClassLoader.
	 */
	public boolean getDelegate()
	{

		return (this.delegate);

	}

	/**
	 * Set the "follow standard delegation workflow" flag used to configure
	 * our ClassLoader.
	 *
	 * @param delegate The new flag
	 */
	public void setDelegate(boolean delegate)
	{

		boolean oldDelegate = this.delegate;
		this.delegate = delegate;
		support.firePropertyChange("delegate", new Boolean(oldDelegate), new Boolean(this.delegate));

	}

	/**
	 * Returns true if the resources associated with this context are
	 * filesystem based.
	 */
	public boolean isFilesystemBased()
	{

		return (filesystemBased);

	}

	/**
	 * Return the set of initialized application event listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @exception IllegalStateException if this method is called before
	 *  this application has started, or after it has been stopped
	 */
	public Object[] getApplicationEventListeners()
	{
		return (applicationEventListenersObjects);
	}

	/**
	 * Store the set of initialized application event listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @param listeners The set of instantiated listener objects.
	 */
	public void setApplicationEventListeners(Object listeners[])
	{
		applicationEventListenersObjects = listeners;
	}

	/**
	 * Return the set of initialized application lifecycle listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @exception IllegalStateException if this method is called before
	 *  this application has started, or after it has been stopped
	 */
	public Object[] getApplicationLifecycleListeners()
	{
		return (applicationLifecycleListenersObjects);
	}

	/**
	 * Store the set of initialized application lifecycle listener objects,
	 * in the order they were specified in the web application deployment
	 * descriptor, for this application.
	 *
	 * @param listeners The set of instantiated listener objects.
	 */
	public void setApplicationLifecycleListeners(Object listeners[])
	{
		applicationLifecycleListenersObjects = listeners;
	}

	/**
	 * Return the display name of this context.
	 */
	public String getDisplayName()
	{

		return (this.displayName);

	}

	/**
	 * Return the compiler classpath.
	 */
	public String getCompilerClasspath()
	{
		return compilerClasspath;
	}

	/**
	 * Set the compiler classpath.
	 */
	public void setCompilerClasspath(String compilerClasspath)
	{
		this.compilerClasspath = compilerClasspath;
	}

	/**
	 * Set the display name of this context.
	 *
	 * @param displayName The new display name
	 */
	public void setDisplayName(String displayName)
	{

		String oldDisplayName = this.displayName;
		this.displayName = displayName;
		support.firePropertyChange("displayName", oldDisplayName, this.displayName);
	}

	/**
	 * Return the document root for this Context.  This can be an absolute
	 * pathname, a relative pathname
	 */
	public String getDocBase()
	{
		return (this.docBase);
	}

	/**
	 * Set the document root for this org.huihoo.workflow context.  This can be an absolute
	 * pathname, a relative pathname
	 *
	 * @param docBase The new document root
	 */
	public void setDocBase(String appBase)
	{

		this.docBase = appBase;
	}

	/**
	 * Return descriptive information about this Container implementation and
	 * the corresponding version number, in the format
	 * <code>&lt;description&gt;/&lt;version&gt;</code>.
	 */
	public String getInfo()
	{

		return (info);

	}

	/**
	 * Set the Loader with which this org.huihoo.workflow context is associated.
	 *
	 * @param loader The newly associated loader
	 */
	public synchronized void setLoader(Loader loader)
	{

		super.setLoader(loader);

	}

	/**
	 * Return the DefaultContext override flag for this web application.
	 */
	public boolean getOverride()
	{

		return (this.override);

	}

	/**
	 * Return the reloadable flag for this context.
	 */
	public boolean getReloadable()
	{

		return (this.reloadable);

	}

	/**
	 * Set the DefaultContext override flag for this web application.
	 *
	 * @param override The new override flag
	 */
	public void setOverride(boolean override)
	{

		boolean oldOverride = this.override;
		this.override = override;
		support.firePropertyChange("override", new Boolean(oldOverride), new Boolean(this.override));

	}

	/**
	 * Set the reloadable flag for this context.
	 *
	 * @param reloadable The new reloadable flag
	 */
	public void setReloadable(boolean reloadable)
	{

		boolean oldReloadable = this.reloadable;
		this.reloadable = reloadable;
		support.firePropertyChange("reloadable", new Boolean(oldReloadable), new Boolean(this.reloadable));

	}

	/**
	 * Return the value of the auto deploy flag.  If true, it indicates that 
	 * this xpdl will be dynamically deployed.
	 */
	public boolean getAutoDeploy()
	{

		return (this.autoDeploy);

	}

	/**
	 * Set the auto deploy flag value for this Context.
	 * 
	 * @param autoDeploy The new auto deploy flag
	 */
	public void setAutoDeploy(boolean autoDeploy)
	{

		boolean oldAutoDeploy = this.autoDeploy;
		this.autoDeploy = autoDeploy;
		support.firePropertyChange("autoDeploy", oldAutoDeploy, this.autoDeploy);

	}

	/**
	 * Return the Java class name of the JavaInterpretor class
	 * for new workflow applications.
	 */
	public String getRouteEvalutorClass()
	{

		return (this.routeEvalutorClass);

	}

	/**
	 * Set the Java class name of the JavaInterpretor class
	 * for new workflow applications.
	 *
	 * @param configClass The new JavaInterpretor class
	 */
	public void setRouteEvalutorClass(String workflowRouteEvalutorClass)
	{

		String oldJavaInterpretorClass = this.routeEvalutorClass;
		this.routeEvalutorClass = workflowRouteEvalutorClass;
		support.firePropertyChange("workflowRouteEvalutorClass", oldJavaInterpretorClass, this.routeEvalutorClass);

	}

	/**
	 * Add a child Container, only if the proposed child is an implementation
	 * of WorkflowXPDL.
	 *
	 * @param child Child container to be added
	 *
	 * @exception IllegalArgumentException if the proposed container is
	 *  not an implementation of WorkflowXPDL
	 */
	public void addChild(Container child)
	{

		if (!(child instanceof WorkflowXPDL))
		{
			throw new IllegalArgumentException(sm.getString("standardContext.notXPDL"));
		}

		super.addChild(child);

	}

	/**
	 * Add a child Container, only if the proposed child is an implementation
	 * of WorkflowXPDL.
	 *
	 * @param child Child container to be added
	 *
	 * @exception IllegalArgumentException if the proposed container is
	 *  not an implementation of WorkflowXPDL
	 */
	public void removeChild(Container child)
	{
		if (!(child instanceof WorkflowXPDL))
		{
			throw new IllegalArgumentException(sm.getString("standardContext.notXPDL"));
		}

		super.removeChild(child);
	}

	// ------------------------------------------------------ Public Properties

	/** Get the absolute path to the work dir.
	 *  To avoid duplication.
	 * 
	 * @return
	 */
	public String getWorkPath()
	{
		File workDir = new File(getWorkDir());
		if (!workDir.isAbsolute())
		{
			File willowHome = new File(System.getProperty(Globals.PROPS_WILLOW_HOME));
			String willowHomePath = null;
			try
			{
				willowHomePath = willowHome.getCanonicalPath();
				workDir = new File(willowHomePath, getWorkDir());
			}
			catch (IOException e)
			{
			}
		}
		return workDir.getAbsolutePath();
	}

	/**
	 * Return the work directory for this org.huihoo.workflow context.
	 */
	public String getWorkDir()
	{

		return (this.workDir);

	}

	/**
	 * Set the work directory for this org.huihoo.workflow context.
	 *
	 * @param workDir The new work directory
	 */
	public void setWorkDir(String workDir)
	{

		this.workDir = workDir;

		if (started)
		{
			postWorkDirectory();
		}

	}

	/**
	 * Return the privileged flag for this web application.
	 */
	public boolean getPrivileged()
	{

		return (this.privileged);

	}

	/**
	 * Set the privileged flag for this web application.
	 *
	 * @param privileged The new privileged flag
	 */
	public void setPrivileged(boolean privileged)
	{

		boolean oldPrivileged = this.privileged;
		this.privileged = privileged;
		support.firePropertyChange("privileged", new Boolean(oldPrivileged), new Boolean(this.privileged));

	}

	/**
	 * Return the application available flag for this org.huihoo.workflow context.
	 */
	public boolean getAvailable()
	{

		return (this.available);

	}

	/**
	 * Set the application available flag for this org.huihoo.workflow context.
	 *
	 * @param available The new application available flag
	 */
	public void setAvailable(boolean available)
	{

		boolean oldAvailable = this.available;
		this.available = available;
		support.firePropertyChange("available", new Boolean(oldAvailable), new Boolean(this.available));

	}

	/**
	 * Return the "correctly configured" flag for this Context.
	 */
	public boolean getConfigured()
	{

		return (this.configured);

	}

	/**
	 * Set the "correctly configured" flag for this Context.  This can be
	 * set to false by startup listeners that detect a fatal configuration
	 * error to avoid the application from being made available.
	 *
	 * @param configured The new correctly configured flag
	 */
	public void setConfigured(boolean configured)
	{

		boolean oldConfigured = this.configured;
		this.configured = configured;
		support.firePropertyChange("configured", new Boolean(oldConfigured), new Boolean(this.configured));

	}

	public String getDefaultWorkflowXml()
	{
		return defaultWorkflowXml;
	}

	/** Set the location of the default workflow xml that will be used.
	 * If not absolute, it'll be made relative to the service's base dir
	 * ( which defaults to willow.home system property ).
	 *
	 * XXX If a file is not found - we can attempt a getResource()
	 *
	 * @param defaultWebXml
	 */
	public void setDefaultWebXml(String defaultWorkflowXml)
	{
		this.defaultWorkflowXml = defaultWorkflowXml;
	}

	public RouteEvalutor getRouteEvalutor()
	{
		return routeEvalutor;
	}

	public void setRouteEvalutor(RouteEvalutor workflowTransitionSelector)
	{
		this.routeEvalutor = workflowTransitionSelector;
	}

	/**
	 * Add a new Listener class name to the set of Listeners
	 * configured for this application.
	 *
	 * @param listener Java class name of a listener class
	 */
	public void addApplicationListener(String listener)
	{

		synchronized (applicationListeners)
		{
			String results[] = new String[applicationListeners.length + 1];
			for (int i = 0; i < applicationListeners.length; i++)
			{
				if (listener.equals(applicationListeners[i]))
					return;
				results[i] = applicationListeners[i];
			}
			results[applicationListeners.length] = listener;
			applicationListeners = results;
		}
		fireContainerEvent("addApplicationListener", listener);

		// FIXME - add instance if already started?

	}

	/**
	 * Add a new application parameter for this application.
	 *
	 * @param parameter The new application parameter
	 */
	public void addApplicationParameter(ApplicationParameter parameter)
	{

		synchronized (applicationParameters)
		{
			String newName = parameter.getName();
			for (int i = 0; i < applicationParameters.length; i++)
			{
				if (name.equals(applicationParameters[i].getName()) && !applicationParameters[i].getOverride())
					return;
			}
			ApplicationParameter results[] = new ApplicationParameter[applicationParameters.length + 1];
			System.arraycopy(applicationParameters, 0, results, 0, applicationParameters.length);
			results[applicationParameters.length] = parameter;
			applicationParameters = results;
		}
		fireContainerEvent("addApplicationParameter", parameter);

	}

	public void addWrapper(Wrapper wrapper)
	{
		if (wrapper instanceof Container)
		{
			((Container) wrapper).setParent(this);
		}

		synchronized (childWrappers)
		{
			String newName = wrapper.getName();
			for (int i = 0; i < childWrappers.length; i++)
			{
				if (name.equals(childWrappers[i].getName()))
					return;
			}
			Wrapper results[] = new Wrapper[childWrappers.length + 1];
			System.arraycopy(applicationParameters, 0, results, 0, childWrappers.length);
			results[childWrappers.length] = wrapper;
			childWrappers = results;
		}
		fireContainerEvent("addWrapper", wrapper);

	}

	/**
	 * Add the given URL pattern as a script-property-group.  This maps
	 * resources that match the given pattern so they will be passed
	 * to the Script container.  Though there are other elements in the
	 * property group, we only care about the URL pattern here.  The
	 * Script container will parse the rest.
	 *
	 * @param pattern URL pattern to be mapped
	 */
	public void addSelectorMapping(String pattern)
	{
		String scriptName = findSelectorMapping("*.ws");
		if (scriptName == null)
		{
			scriptName = "script";
		}

		if (findWrapper(scriptName) != null)
		{
			addSelectorMapping(pattern, scriptName);
		}
		else
		{
			log.debug("Skiping " + pattern + " , no servlet " + scriptName);
		}
	}

	/**
	 * Add a new selector mapping, replacing any existing mapping for
	 * the specified pattern.
	 *
	 * @param pattern URL pattern to be mapped
	 * @param name Name of the corresponding servlet to execute
	 *
	 * @exception IllegalArgumentException if the specified servlet name
	 *  is not known to this Context
	 */
	public void addSelectorMapping(String pattern, String name)
	{

		// Validate the proposed mapping
		if (findWrapper(name) == null)
			throw new IllegalArgumentException(sm.getString("standardContext.servletMap.name", name));

		// Add this mapping to our registered set
		synchronized (selectorMappings)
		{
			String name2 = (String) selectorMappings.get(pattern);
			if (name2 != null)
			{
				// Don't allow more than one servlet on the same pattern
				Wrapper wrapper = (Wrapper) findWrapper(name2);
				wrapper.removeMapping(pattern);
			}
			selectorMappings.put(pattern, name);
		}
		Wrapper wrapper = (Wrapper) findWrapper(name);
		wrapper.addMapping(pattern);

		fireContainerEvent("addSelectorMapping", pattern);

	}

	/**
	 * Add a new context initialization parameter.
	 *
	 * @param name Name of the new parameter
	 * @param value Value of the new  parameter
	 *
	 * @exception IllegalArgumentException if the name or value is missing,
	 *  or if this context initialization parameter has already been
	 *  registered
	 */
	public void addInitParameter(String name, String value)
	{
		// Validate the proposed context initialization parameter
		if ((name == null) || (value == null))
			throw new IllegalArgumentException(sm.getString("standardContext.parameter.required"));
		if (parameters.get(name) != null)
			throw new IllegalArgumentException(sm.getString("standardContext.parameter.duplicate", name));

		// Add this parameter to our defined set
		synchronized (parameters)
		{
			parameters.put(name, value);
		}
		fireContainerEvent("addInitParameter", name);
	}

	/**
	 * Add the classname of a LifecycleListener to be added to each
	 * Wrapper appended to this Context.
	 *
	 * @param listener Java class name of a LifecycleListener class
	 */
	public void addWrapperLifecycle(String listener)
	{

		synchronized (wrapperLifecycles)
		{
			String results[] = new String[wrapperLifecycles.length + 1];
			for (int i = 0; i < wrapperLifecycles.length; i++)
				results[i] = wrapperLifecycles[i];
			results[wrapperLifecycles.length] = listener;
			wrapperLifecycles = results;
		}
		fireContainerEvent("addWrapperLifecycle", listener);

	}

	/**
	 * Add the classname of a ContainerListener to be added to each
	 * Wrapper appended to this Context.
	 *
	 * @param listener Java class name of a ContainerListener class
	 */
	public void addWrapperListener(String listener)
	{

		synchronized (wrapperListeners)
		{
			String results[] = new String[wrapperListeners.length + 1];
			for (int i = 0; i < wrapperListeners.length; i++)
				results[i] = wrapperListeners[i];
			results[wrapperListeners.length] = listener;
			wrapperListeners = results;
		}
		fireContainerEvent("addWrapperListener", listener);

	}

	/**
	 * Factory method to create and return a new Wrapper instance, of
	 * the Java implementation class appropriate for this Context
	 * implementation.  The constructor of the instantiated Wrapper
	 * will have been called, but no properties will have been set.
	 */
	public Wrapper createWrapper()
	{
		//log.info( "Create wrapper" );
		Wrapper wrapper = new StandardWrapper();
		wrappers.add(wrapper);

		synchronized (wrapperLifecycles)
		{
			for (int i = 0; i < wrapperLifecycles.length; i++)
			{
				try
				{
					Class clazz = Class.forName(wrapperLifecycles[i]);
					LifecycleListener listener = (LifecycleListener) clazz.newInstance();
					if (wrapper instanceof Lifecycle)
						 ((Lifecycle) wrapper).addLifecycleListener(listener);
				}
				catch (Throwable t)
				{
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		synchronized (wrapperListeners)
		{
			for (int i = 0; i < wrapperListeners.length; i++)
			{
				try
				{
					Class clazz = Class.forName(wrapperListeners[i]);
					ContainerListener listener = (ContainerListener) clazz.newInstance();
					wrapper.addContainerListener(listener);
				}
				catch (Throwable t)
				{
					log.error("createWrapper", t);
					return (null);
				}
			}
		}

		return (wrapper);

	}

	/**
	 * Return the set of application listener class names configured
	 * for this application.
	 */
	public String[] findApplicationListeners()
	{

		return (applicationListeners);

	}

	/**
	 * Return the set of application parameters for this application.
	 */
	public ApplicationParameter[] findApplicationParameters()
	{

		return (applicationParameters);

	}

	public Wrapper findWrapper(String name)
	{
		for (int i = 0; i < childWrappers.length; i++)
		{
			if (name.equals(childWrappers[i].getName()))
			{
				return childWrappers[i];
			}
		}

		return null;
	}

	/**
	 * Return the set of wrapper for this application.
	 */
	public Wrapper[] findWrappers()
	{

		return (childWrappers);

	}

	/**
	 * Return the Selector name mapped by the specified pattern (if any);
	 * otherwise return <code>null</code>.
	 *
	 * @param pattern Pattern for which a mapping is requested
	 */
	public String findSelectorMapping(String pattern)
	{

		synchronized (selectorMappings)
		{
			return ((String) selectorMappings.get(pattern));
		}

	}

	/**
	 * Return the patterns of all defined servlet mappings for this
	 * Context.  If no mappings are defined, a zero-length array is returned.
	 */
	public String[] findSelectorMappings()
	{

		synchronized (selectorMappings)
		{
			String results[] = new String[selectorMappings.size()];
			return ((String[]) selectorMappings.keySet().toArray(results));
		}

	}

	/**
	 * Return the set of LifecycleListener classes that will be added to
	 * newly created Wrappers automatically.
	 */
	public String[] findWrapperLifecycles()
	{

		return (wrapperLifecycles);

	}

	/**
	 * Return the set of ContainerListener classes that will be added to
	 * newly created Wrappers automatically.
	 */
	public String[] findWrapperListeners()
	{

		return (wrapperListeners);

	}
	/**
	 * Return the names of all defined context initialization parameters
	 * for this Context.  If no parameters are defined, a zero-length
	 * array is returned.
	 */
	public String[] findInitParameters()
	{

		synchronized (parameters)
		{
			String results[] = new String[parameters.size()];
			return ((String[]) parameters.keySet().toArray(results));
		}

	}

	/**
	 * Return the value for the specified context initialization
	 * parameter name, if any; otherwise return <code>null</code>.
	 *
	 * @param name Name of the parameter to return
	 */
	public String findInitParameter(String name)
	{

		synchronized (parameters)
		{
			return ((String) parameters.get(name));
		}

	}

	/**
	 * Remove the specified application listener class from the set of
	 * listeners for this application.
	 *
	 * @param listener Java class name of the listener to be removed
	 */
	public void removeApplicationListener(String listener)
	{

		synchronized (applicationListeners)
		{

			// Make sure this welcome file is currently present
			int n = -1;
			for (int i = 0; i < applicationListeners.length; i++)
			{
				if (applicationListeners[i].equals(listener))
				{
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified constraint
			int j = 0;
			String results[] = new String[applicationListeners.length - 1];
			for (int i = 0; i < applicationListeners.length; i++)
			{
				if (i != n)
					results[j++] = applicationListeners[i];
			}
			applicationListeners = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeApplicationListener", listener);

		// FIXME - behavior if already started?

	}

	/**
	 * Remove the application parameter with the specified name from
	 * the set for this application.
	 *
	 * @param name Name of the application parameter to remove
	 */
	public void removeApplicationParameter(String name)
	{

		synchronized (applicationParameters)
		{

			// Make sure this parameter is currently present
			int n = -1;
			for (int i = 0; i < applicationParameters.length; i++)
			{
				if (name.equals(applicationParameters[i].getName()))
				{
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified parameter
			int j = 0;
			ApplicationParameter results[] = new ApplicationParameter[applicationParameters.length - 1];
			for (int i = 0; i < applicationParameters.length; i++)
			{
				if (i != n)
					results[j++] = applicationParameters[i];
			}
			applicationParameters = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeApplicationParameter", name);

	}

	/**
	 * Remove the wrapper with the specified name from
	 * the set for this application.
	 *
	 * @param name Name of the wrapper to remove
	 */
	public void removeWrapper(String name)
	{

		synchronized (childWrappers)
		{

			// Make sure this parameter is currently present
			int n = -1;
			for (int i = 0; i < childWrappers.length; i++)
			{
				if (name.equals(childWrappers[i].getName()))
				{
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified parameter
			int j = 0;
			Wrapper results[] = new Wrapper[childWrappers.length - 1];
			for (int i = 0; i < childWrappers.length; i++)
			{
				if (i != n)
					results[j++] = childWrappers[i];
			}
			childWrappers = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeWrapper", name);

	}

	public void removeSelectorMapping(String pattern)
	{

		String name = null;
		synchronized (selectorMappings)
		{
			name = (String) selectorMappings.remove(pattern);
		}
		Wrapper wrapper = (Wrapper) findWrapper(name);
		if (wrapper != null)
		{
			wrapper.removeMapping(pattern);
		}

		fireContainerEvent("removeSelectorMapping", pattern);

	}

	/**
	 * Remove the context initialization parameter with the specified
	 * name, if it exists; otherwise, no action is taken.
	 *
	 * @param name Name of the parameter to remove
	 */
	public void removeInitParameter(String name)
	{

		synchronized (parameters)
		{
			parameters.remove(name);
		}
		fireContainerEvent("removeInitParameter", name);
	}

	/**
	 * Remove a class name from the set of LifecycleListener classes that
	 * will be added to newly created Wrappers.
	 *
	 * @param listener Class name of a LifecycleListener class to be removed
	 */
	public void removeWrapperLifecycle(String listener)
	{

		synchronized (wrapperLifecycles)
		{

			// Make sure this welcome file is currently present
			int n = -1;
			for (int i = 0; i < wrapperLifecycles.length; i++)
			{
				if (wrapperLifecycles[i].equals(listener))
				{
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified constraint
			int j = 0;
			String results[] = new String[wrapperLifecycles.length - 1];
			for (int i = 0; i < wrapperLifecycles.length; i++)
			{
				if (i != n)
					results[j++] = wrapperLifecycles[i];
			}
			wrapperLifecycles = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeWrapperLifecycle", listener);

	}

	/**
	 * Remove a class name from the set of ContainerListener classes that
	 * will be added to newly created Wrappers.
	 *
	 * @param listener Class name of a ContainerListener class to be removed
	 */
	public void removeWrapperListener(String listener)
	{

		synchronized (wrapperListeners)
		{

			// Make sure this welcome file is currently present
			int n = -1;
			for (int i = 0; i < wrapperListeners.length; i++)
			{
				if (wrapperListeners[i].equals(listener))
				{
					n = i;
					break;
				}
			}
			if (n < 0)
				return;

			// Remove the specified constraint
			int j = 0;
			String results[] = new String[wrapperListeners.length - 1];
			for (int i = 0; i < wrapperListeners.length; i++)
			{
				if (i != n)
					results[j++] = wrapperListeners[i];
			}
			wrapperListeners = results;

		}

		// Inform interested listeners
		fireContainerEvent("removeWrapperListener", listener);

	}

	/**
	 * Reload this context, if reloading is supported.
	 * <p>
	 * <b>IMPLEMENTATION NOTE</b>:  This method is designed to deal with
	 * reloads required by changes to classes in the underlying repositories
	 * of our class loader.  It does not handle changes to the context
	 * deployment descriptor.  If that has occurred, you should stop this
	 * workflow context and create (and start) a new workflow context instance instead.
	 *
	 * @exception IllegalStateException if the <code>reloadable</code>
	 *  property is set to <code>false</code>.
	 */
	public synchronized void reload()
	{

		// Validate our current component state
		if (!started)
		{
			throw new IllegalStateException(sm.getString("containerBase.notStarted", logName()));
		}

		// Make sure reloading is enabled
		if (!reloadable)
		{
			throw new IllegalStateException(sm.getString("standardContext.notReloadable"));
		}

		log.info(sm.getString("standardContext.reloadingStarted"));

		// Stop accepting requests temporarily
		setPaused(true);

		try
		{
			stop();
		}
		catch (LifecycleException e)
		{
			log.error(sm.getString("standardContext.stoppingContext"), e);
		}

		try
		{
			start();
		}
		catch (LifecycleException e)
		{
			log.error(sm.getString("standardContext.startingContext"), e);
		}

		setPaused(false);

	}

	// --------------------------------------------------------- Workflow Support

	public WorkflowContext getWorkflowContext()
	{
		if (workflowContext == null)
		{
			workflowContext = new ApplicationContext(getBasePath(), this);
		}
		return (workflowContext);
	}

	/**
	 * Return the value of the swallowOutput flag.
	 */
	public boolean getSwallowOutput()
	{

		return (this.swallowOutput);

	}

	/**
	 * Set the value of the swallowOutput flag. If set to true, the system.out
	 * and system.err will be redirected to the logger during a servlet
	 * execution.
	 *
	 * @param swallowOuptut The new value
	 */
	public void setSwallowOutput(boolean swallowOutput)
	{

		boolean oldSwallowOutput = this.swallowOutput;
		this.swallowOutput = swallowOutput;
		support.firePropertyChange("swallowOutput", new Boolean(oldSwallowOutput), new Boolean(this.swallowOutput));

	}

	/**
	 * Return the Java class name of the Wrapper implementation used
	 * for servlets registered in this Context.
	 */
	public String getWrapperClass()
	{

		return (this.wrapperClass);

	}

	/**
	 * Set the Java class name of the Wrapper implementation used
	 * for servlets registered in this Context.
	 *
	 * @param wrapperClass The new wrapper class
	 */
	public void setWrapperClass(String wrapperClass)
	{

		this.wrapperClass = wrapperClass;

	}

	/**
	 * Set the resources DirContext object with which this Container is
	 * associated.
	 *
	 * @param resources The newly associated DirContext
	 */
	public synchronized void setResources(DirContext resources)
	{

		if (started)
		{
			throw new IllegalStateException(sm.getString("standardContext.resources.started"));
		}

		DirContext oldResources = this.appResources;
		if (oldResources == resources)
		{
			return;
		}

		if (resources instanceof FileDirContext)
		{
			filesystemBased = true;

		}
		this.appResources = resources;

		// The proxied resources will be refreshed on start
		this.resources = null;

		support.firePropertyChange("resources", oldResources, this.appResources);

	}

	//--------------------------------------------------------- Public Methods

	/**
	 * Configure the set of instantiated application event listeners
	 * for this Context.  Return <code>true</code> if all listeners wre
	 * initialized successfully, or <code>false</code> otherwise.
	 */
	public boolean listenerStart()
	{

		if (log.isDebugEnabled())
			log.debug("Configuring application event listeners");

		// Instantiate the required listeners
		ClassLoader loader = getLoader().getClassLoader();
		String listeners[] = findApplicationListeners();
		Object results[] = new Object[listeners.length];
		boolean ok = true;
		for (int i = 0; i < results.length; i++)
		{
			if (log.isDebugEnabled())
				log.debug(" Configuring event listener class '" + listeners[i] + "'");
			try
			{
				Class clazz = loader.loadClass(listeners[i]);
				results[i] = clazz.newInstance();
			}
			catch (Throwable t)
			{
				getWorkflowContext().log(sm.getString("standardContext.applicationListener", listeners[i]), t);
				ok = false;
			}
		}
		if (!ok)
		{
			log.error(sm.getString("standardContext.applicationSkipped"));
			return (false);
		}

		// Sort listeners in two arrays
		ArrayList eventListeners = new ArrayList();
		ArrayList lifecycleListeners = new ArrayList();
		for (int i = 0; i < results.length; i++)
		{
			if ((results[i] instanceof WorkflowContextAttributeListener)
				|| (results[i] instanceof HttpSessionAttributeListener))
			{
				eventListeners.add(results[i]);
			}
			if ((results[i] instanceof WorkflowContextListener) || (results[i] instanceof HttpSessionListener))
			{
				lifecycleListeners.add(results[i]);
			}
		}

		setApplicationEventListeners(eventListeners.toArray());
		setApplicationLifecycleListeners(lifecycleListeners.toArray());

		// Send application start events

		if (log.isDebugEnabled())
			log.debug("Sending application start events");

		Object instances[] = getApplicationLifecycleListeners();
		if (instances == null)
			return (ok);
		WorkflowContextEvent event = new WorkflowContextEvent(getWorkflowContext());
		for (int i = 0; i < instances.length; i++)
		{
			if (instances[i] == null)
				continue;
			if (!(instances[i] instanceof WorkflowContextListener))
				continue;
			WorkflowContextListener listener = (WorkflowContextListener) instances[i];
			try
			{
				fireContainerEvent("beforeContextInitialized", listener);
				listener.contextInitialized(event);
				fireContainerEvent("afterContextInitialized", listener);
			}
			catch (Throwable t)
			{
				fireContainerEvent("afterContextInitialized", listener);
				getWorkflowContext().log(sm.getString("standardContext.listenerStart", instances[i].getClass().getName()), t);
				ok = false;
			}
		}
		return (ok);

	}

	/**
	 * Send an application stop event to all interested listeners.
	 * Return <code>true</code> if all events were sent successfully,
	 * or <code>false</code> otherwise.
	 */
	public boolean listenerStop()
	{

		if (log.isDebugEnabled())
			log.debug("Sending application stop events");

		boolean ok = true;
		Object listeners[] = getApplicationLifecycleListeners();
		if (listeners == null)
			return (ok);
		WorkflowContextEvent event = new WorkflowContextEvent(getWorkflowContext());
		for (int i = 0; i < listeners.length; i++)
		{
			int j = (listeners.length - 1) - i;
			if (listeners[j] == null)
				continue;
			if (!(listeners[j] instanceof WorkflowContextListener))
				continue;
			WorkflowContextListener listener = (WorkflowContextListener) listeners[j];
			try
			{
				fireContainerEvent("beforeContextDestroyed", listener);
				listener.contextDestroyed(event);
				fireContainerEvent("beforeContextDestroyed", listener);
			}
			catch (Throwable t)
			{
				fireContainerEvent("beforeContextDestroyed", listener);
				getWorkflowContext().log(sm.getString("standardContext.listenerStop", listeners[j].getClass().getName()), t);
				ok = false;
			}
		}
		setApplicationEventListeners(null);
		setApplicationLifecycleListeners(null);
		return (ok);

	}

	/**
	 * Allocate resources, including proxy.
	 * Return <code>true</code> if initialization was successfull,
	 * or <code>false</code> otherwise.
	 */
	public boolean resourcesStart()
	{

		boolean ok = true;

		Hashtable env = new Hashtable();
		if (getParent() != null)
		{
			env.put(ProxyDirContext.SERVICE, getParent().getName());
		}
		env.put(ProxyDirContext.CONTEXT, getName());

		try
		{
			ProxyDirContext proxyDirContext = new ProxyDirContext(env, appResources);
			if (appResources instanceof BaseDirContext)
			{
				((BaseDirContext) appResources).setDocBase(getBasePath());
				((BaseDirContext) appResources).allocate();
			}

			this.resources = proxyDirContext;
		}
		catch (Throwable t)
		{
			log.error(sm.getString("standardContext.resourcesStart"), t);
			ok = false;
		}

		return (ok);

	}

	/**
	 * Deallocate resources and destroy proxy.
	 */
	public boolean resourcesStop()
	{

		boolean ok = true;

		try
		{
			if (resources != null)
			{
				if (resources instanceof Lifecycle)
				{
					((Lifecycle) resources).stop();
				}
				if (appResources instanceof BaseDirContext)
				{
					((BaseDirContext) appResources).release();
				}
			}
		}
		catch (Throwable t)
		{
			log.error(sm.getString("standardContext.resourcesStop"), t);
			ok = false;
		}

		this.resources = null;

		return (ok);

	}

	// --------------------------------------------------------- Lifecycle Support
	/**
	 * Start this context component.
	 *
	 * @exception LifecycleException if a startup error occurs
	 */
	public synchronized void start() throws LifecycleException
	{
		if (started)
		{
			log.info(sm.getString("containerBase.alreadyStarted", logName()));
			return;
		}

		log.debug("Starting " + logName());

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

		setAvailable(false);
		setConfigured(false);
		boolean ok = true;

		// Install DefaultContext configuration
		if (!getOverride())
		{
			Container service = getParent();
			if (service instanceof StandardService)
			{
				((StandardService) service).installDefaultContext(this);
			}
		}

		// Add missing components as necessary
		if (appResources == null)
		{ // (1) Required by Loader
			log.debug("Configuring default Resources");
			try
			{
				if ((docBase != null) && (docBase.endsWith(".war")))
				{
					setResources(new WARDirContext());
				}
				else
				{
					setResources(new FileDirContext());
				}
			}
			catch (IllegalArgumentException e)
			{
				log.error("Error initializing resources: " + e.getMessage());
				ok = false;
			}
		}
		if (ok)
		{
			if (!resourcesStart())
			{
				log.error("Error in resourceStart()");
				ok = false;
			}
		}

		if (getLoader() == null)
		{
			ClassLoader parent = null;
			log.debug("Configuring non-privileged default Loader");
			parent = getParentClassLoader();
			WorkflowLoader wfappLoader = new WorkflowLoader(parent);
			wfappLoader.setDelegate(getDelegate());
			setLoader(wfappLoader);
		}

		// Post work directory
		postWorkDirectory();

		// Standard container startup
		log.debug("Processing standard container startup");

		if (ok)
		{
			started = true;

			// Start our subordinate components, if any
			if ((loader != null) && (loader instanceof Lifecycle))
			{
				((Lifecycle) loader).start();
			}
			if ((logger != null) && (logger instanceof Lifecycle))
			{
				((Lifecycle) logger).start();
			}

			if ((realm != null) && (realm instanceof Lifecycle))
			{
				((Lifecycle) realm).start();
			}

			if ((resources != null) && (resources instanceof Lifecycle))
			{
				((Lifecycle) resources).start();
			}

			// Start our child wrapper, if any
			Wrapper childrenWrapper[] = this.findWrappers();
			for (int i = 0; i < childrenWrapper.length; i++)
			{
				if (childrenWrapper[i] instanceof Lifecycle)
				{
					((Lifecycle) childrenWrapper[i]).start();
				}
			}

			// Start our child containers, if any
			Container children[] = findChildren();
			for (int i = 0; i < children.length; i++)
			{
				if (children[i] instanceof Lifecycle)
				{
					((Lifecycle) children[i]).start();
				}
			}

			// Notify our interested LifecycleListeners
			lifecycle.fireLifecycleEvent(START_EVENT, null);

			// Start ContainerBackgroundProcessor thread
			threadStart();

		}

		if (!getConfigured())
		{
			log.error("Error getConfigured");
			ok = false;
		}

		// We put the resources into the workflow context
		if (ok)
		{
			getWorkflowContext().setAttribute(Globals.ATTR_RESOURCES, getResources());
		}

		//	Configure and call application event listeners and filters
		if (ok)
		{
			if (!listenerStart())
			{
				log.error("Error listenerStart");
				ok = false;
			}
		}

		// Set available status depending upon startup success
		if (ok)
		{
			log.debug("Starting completed");
			setAvailable(true);
		}
		else
		{
			log.error(sm.getString("standardContext.startFailed"));
			try
			{
				stop();
			}
			catch (Throwable t)
			{
				log.error(sm.getString("standardContext.startCleanup"), t);
			}
			setAvailable(false);
		}

		if (ok)
		{
			// Notify our interested LifecycleListeners
			lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);
		}

		// Close all JARs right away to avoid always opening a peak number 
		// of files on startup
		if (getLoader() instanceof WorkflowLoader)
		{
			((WorkflowLoader) getLoader()).closeJARs(true);
		}

		// Reinitializing if something went wrong
		if (!ok && started)
		{
			stop();
		}

	}

	/**
	 * Stop this org.huihoo.workflow context component.
	 *
	 * @exception LifecycleException if a shutdown error occurs
	 */
	public synchronized void stop() throws LifecycleException
	{
		// Validate and update our current component state
		if (!started)
		{
			throw new LifecycleException(sm.getString("containerBase.notStarted", logName()));
		}

		log.debug("Stopping");

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);

		// Mark this application as unavailable while we shut down
		setAvailable(false);

		// Stop our application listeners
		listenerStop();

		// Stop ContainerBackgroundProcessor thread
		super.threadStop();

		// Normal container shutdown processing
		if (log.isDebugEnabled())
			log.debug("Processing standard container shutdown");
		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(STOP_EVENT, null);
		started = false;

		// Stop our child containers, if any
		Container[] children = findChildren();
		for (int i = 0; i < children.length; i++)
		{
			if (children[i] instanceof Lifecycle)
			{
				((Lifecycle) children[i]).stop();
			}
		}

		// Start our child wrapper, if any
		Wrapper childrenWrapper[] = this.findWrappers();
		for (int i = 0; i < childrenWrapper.length; i++)
		{
			if (childrenWrapper[i] instanceof Lifecycle)
			{
				((Lifecycle) childrenWrapper[i]).stop();
			}
		}

		// Stop resources
		resourcesStop();

		if ((realm != null) && (realm instanceof Lifecycle))
		{
			((Lifecycle) realm).stop();
		}

		if ((logger != null) && (logger instanceof Lifecycle))
		{
			((Lifecycle) logger).stop();
		}
		if ((loader != null) && (loader instanceof Lifecycle))
		{
			((Lifecycle) loader).stop();
		}

		// Reset application context
		this.workflowContext = null;

		// Notify our interested LifecycleListeners
		lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);

		log.debug("Stopping complete");
	}

	/**
	 * Return a String representation of this component.
	 */
	public String toString()
	{

		StringBuffer sb = new StringBuffer();
		if (getParent() != null)
		{
			sb.append(getParent().toString());
			sb.append(".");
		}
		sb.append("StandardContext[");
		sb.append(getName());
		sb.append("]");
		return (sb.toString());

	}

	/**
	 * Execute a periodic task, such as reloading, etc. This method will be
	 * invoked inside the classloading context of this container. Unexpected
	 * throwables will be caught and logged.
	 */
	public void backgroundProcess()
	{

		if (!started)
		{
			return;
		}

		if (getLoader() != null)
		{
			if (reloadable && (getLoader().modified()))
			{
				try
				{
					Thread.currentThread().setContextClassLoader(StandardContext.class.getClassLoader());
					reload();
				}
				finally
				{
					if (getLoader() != null)
					{
						Thread.currentThread().setContextClassLoader(getLoader().getClassLoader());
					}
				}
			}
			if (getLoader() instanceof WorkflowLoader)
			{
				((WorkflowLoader) getLoader()).closeJARs(false);
			}
		}

		lifecycle.fireLifecycleEvent("check", null);
	}

	// ------------------------------------------------------ Protected Methods

	/**
	 * Return a File object representing the base directory for the
	 * entire servlet container (i.e. the Service container if present).
	 */
	protected File serviceBaseFile()
	{
		StandardService eng = (StandardService) this.getParent();
		String serviceBase = eng.getAppBase();

		File baseFile = new File(serviceBase);

		if (!baseFile.isAbsolute())
		{
			baseFile = new File(System.getProperty(Globals.PROPS_WILLOW_HOME), serviceBase);
		}

		return (baseFile);
	}

	/**
	 * Return the request processing paused flag for this org.huihoo.workflow context.
	 */
	public boolean getPaused()
	{

		return (this.paused);

	}

	/**
	 * Set the appropriate context attribute for our work directory.
	 */
	private void postWorkDirectory()
	{

		// Acquire (or calculate) the work directory path
		String workDir = getWorkDir();
		if (workDir == null)
		{
			// Retrieve our parent (normally a service) name
			String serviceName = null;
			String serviceWorkDir = null;

			Container parentService = getParent();
			if (parentService != null)
			{
				serviceName = parentService.getName();
				serviceWorkDir = ((StandardService) parentService).getWorkDir();
			}

			if ((serviceName == null) || (serviceName.length() < 1))
			{
				serviceName = "_";
			}

			String temp = getName();
			if (temp.startsWith("/"))
			{
				temp = temp.substring(1);
			}

			temp = temp.replace('/', '_');
			temp = temp.replace('\\', '_');
			if (temp.length() < 1)
			{
				temp = "_";
			}

			if (serviceWorkDir != null)
			{
				workDir = serviceWorkDir + File.separator + temp;
			}
			else
			{
				workDir = org.huihoo.willow.Globals.WORK_DIRECTORY + File.separator + serviceName + File.separator + temp;
			}
			setWorkDir(workDir);
		}

		// Create this directory if necessary
		File dir = new File(workDir);
		if (!dir.isAbsolute())
		{
			File willowHome = new File(System.getProperty(Globals.PROPS_WILLOW_HOME));
			String willowHomePath = null;
			try
			{
				willowHomePath = willowHome.getCanonicalPath();
				dir = new File(willowHomePath, workDir);
			}
			catch (IOException e)
			{
			}
		}
		dir.mkdirs();

		getWorkflowContext().setAttribute(Globals.ATTR_WORK_DIR, dir);
		if (getWorkflowContext() instanceof ApplicationContext)
		{
			((ApplicationContext) getWorkflowContext()).setAttributeReadOnly(Globals.ATTR_WORK_DIR);
		}
	}

	/**
	 * Set the request processing paused flag for this org.huihoo.workflow context.
	 *
	 * @param paused The new request processing paused flag
	 */
	private void setPaused(boolean paused)
	{

		this.paused = paused;

	}

	/**
	 * Get base path (full path name).
	 */
	private String getBasePath()
	{
		String appBase = null;

		File file = new File(getDocBase());
		if (!file.isAbsolute())
		{
			appBase = new File(serviceBaseFile(), getDocBase()).getPath();
		}
		else
		{
			appBase = file.getPath();
		}
		return appBase;
	}

	// ------------------------------------------------------- ContextDeployer Methods

	public void install(String xpdlFileName, URL xpdlFileURL) throws WorkflowException
	{
		getDeployer().install(xpdlFileName, xpdlFileURL);
	}

	public WorkflowXPDL findWorkflowXPDL(String procName)
	{

		return (getDeployer().findWorkflowXPDL(procName));

	}

	public String[] findWorkflowXPDLs()
	{

		return (getDeployer().findWorkflowXPDLs());

	}

	public void remove(String procName) throws WorkflowException
	{
		getDeployer().remove(procName);
	}

	// ------------------------------------------------------ Protected Methods

	static String STANDARD_CONTEXT_DEPLOYER = "org.huihoo.willow.core.StandardContextDeployer";

	public ContextDeployer getDeployer()
	{
		if (deployer != null)
		{
			return deployer;
		}

		log.info("Create Context deployer for direct deployment ");

		try
		{
			Class c = Class.forName(STANDARD_CONTEXT_DEPLOYER);
			deployer = (ContextDeployer) c.newInstance();
			Method m = c.getMethod("setContext", new Class[] { Context.class });
			m.invoke(deployer, new Object[] { this });
		}
		catch (Throwable t)
		{
			log.error("Error creating deployer ", t);
		}
		return deployer;
	}

	public void setDeployer(ContextDeployer d)
	{
		this.deployer = d;
	}

	protected void threadStart()
	{
		if (thread == null && backgroundProcessorDelay > 0)
		{
			threadDone = false;
			String threadName = "ContextBackgroundProcessor[" + toString() + "]";
			thread = new Thread(new ContextBackgroundProcessor(), threadName);
			thread.setDaemon(true);
			thread.start();
		}
	}

	protected void threadStop()
	{
		if (thread != null)
		{
			threadDone = true;
			thread.interrupt();
			thread = null;
		}
	}
	// -------------------------------------- ContextBackgroundProcessor Inner Class
	/**
	 * Private thread class to invoke the backgroundProcess method 
	 * of this container and its children after a fixed delay.
	 */
	protected class ContextBackgroundProcessor implements Runnable
	{
		public void run()
		{
			while (!threadDone)
			{
				try
				{
					Thread.sleep(backgroundProcessorDelay * 1000L);
				}
				catch (InterruptedException e)
				{
					;
				}
				if (!threadDone)
				{
					Container container = StandardContext.this;
					ClassLoader cl = Thread.currentThread().getContextClassLoader();
					if (parent.getLoader() != null)
					{
						cl = container.getLoader().getClassLoader();
					}
					try
					{
						if (container.getLoader() != null)
						{
							Thread.currentThread().setContextClassLoader(container.getLoader().getClassLoader());
						}
						container.backgroundProcess();
					}
					catch (Throwable t)
					{
						log.error("Exception invoking periodic operation: ", t);
					}
					finally
					{
						Thread.currentThread().setContextClassLoader(cl);
					}
				}
			}
		}
	}
}