//----------------------------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.startup;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import org.apache.commons.digester.Digester;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.willow.Container;
import org.huihoo.willow.ContextDeployer;
import org.huihoo.willow.Globals;
import org.huihoo.willow.Lifecycle;
import org.huihoo.willow.LifecycleEvent;
import org.huihoo.willow.LifecycleListener;
import org.huihoo.willow.Logger;
import org.huihoo.willow.core.StandardContext;
import org.huihoo.willow.core.StandardEngine;
import org.huihoo.willow.util.StringManager;
import org.huihoo.workflow.factory.DatabaseFactory;
import org.huihoo.workflow.rules.ScriptContext;
import org.huihoo.workflow.rules.interpretor.JavaInterpretor;
import org.huihoo.workflow.rules.interpretor.ScriptInterpretor;
import org.huihoo.workflow.store.CaseDatabase;
import org.huihoo.workflow.store.SchemaContext;
import org.huihoo.workflow.store.UserDatabase;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import com.zosatapo.commons.store.Store;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public final class ContextConfig implements LifecycleListener
{
private static Log log = LogFactory.getLog(ContextConfig.class);
// ----------------------------------------------------- Instance Variables
/**
* The Context we are associated with.
*/
private StandardContext context = null;
/**
* The debugging detail level for this component.
*/
private int debug = 0;
/**
* Track any fatal errors during startup configuration processing.
*/
private boolean ok = false;
/**
* The names of applications that we have auto-deployed (to avoid
* double deployment attempts).
*/
protected ArrayList deployed = new ArrayList();
/**
* Last modified dates of the XPDL's xml files of the XPDLs, keyed by
* xpdl file name.
*/
private HashMap xpdlXmlLastModified = new HashMap();
/**
* The string resources for this package.
*/
private static final StringManager sm = StringManager.getManager(Constants.PACKAGE);
/**
* The <code>Digester</code> we will use to process org.huihoo.workflow application
* deployment descriptor files.
*/
private Digester workflowDigester = null;
/**
* The <code>Rule</code> used to parse the org.huihoo.workflow.xml
*/
private WorkflowRuleSet workflowRuleSet = new WorkflowRuleSet();
/**
* Attribute value used to turn on/off XML validation
*/
private boolean xmlValidation = false;
/**
* Attribute value used to turn on/off XML namespace awarenes.
*/
private boolean xmlNamespaceAware = false;
// ------------------------------------------------------------- Properties
/**
* Return the debugging detail level for this component.
*/
public int getDebug()
{
return (this.debug);
}
/**
* Set the debugging detail level for this component.
*
* @param debug The new debugging detail level
*/
public void setDebug(int debug)
{
this.debug = debug;
}
// --------------------------------------------------------- Public Methods
/**
* Process the START event for an associated Context.
*
* @param event The lifecycle event that has occurred
*/
public void lifecycleEvent(LifecycleEvent event)
{
if (event.getType().equals("check"))
{
check();
}
// Identify the context we are associated with
try
{
context = (StandardContext) event.getLifecycle();
if (context instanceof StandardContext)
{
int contextDebug = ((StandardContext) context).getDebug();
if (contextDebug > this.debug)
{
this.debug = contextDebug;
}
}
}
catch (ClassCastException e)
{
log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);
return;
}
// Called from ContainerBase.addChild() -> StandardContext.start()
// Process the event that has occurred
if (event.getType().equals(Lifecycle.START_EVENT))
{
start();
}
else
if (event.getType().equals(Lifecycle.STOP_EVENT))
{
stop();
}
}
// -------------------------------------------------------- Private Methods
/**
* Process the application configuration file, if it exists.
*/
private void applicationConfig()
{
// Open the application workflow.xml file, if it exists
ScriptContext servletContext = context.getScriptContext();
InputStream stream = null;
InputStream stream_config = null;
if (servletContext != null)
{
stream = servletContext.getResourceAsStream(Constants.ApplicationWorkflowXml);
stream_config = servletContext.getResourceAsStream(Constants.ApplicationWorkflowXml);
if (stream == null)
{
log.info(sm.getString("contextConfig.applicationMissing") + " " + context);
}
}
if (workflowDigester == null)
{
workflowDigester = createWorkflowDigester();
}
// Process the application org.huihoo.workflow.xml file
synchronized (workflowDigester)
{
try
{
URL workflowURL = servletContext.getResource(Constants.ApplicationWorkflowXml);
if (workflowURL != null)
{
//--------------------entire workflow application-----------------
InputSource is = new InputSource(workflowURL.toExternalForm());
is.setByteStream(stream);
workflowDigester.clear();
workflowDigester.setDebug(getDebug());
workflowDigester.setUseContextClassLoader(false);
workflowDigester.push(context);
workflowDigester.parse(is);
//--------------------script config of workflow application -----------------
Digester configDigester = createConfigDigester();
is = new InputSource(workflowURL.toExternalForm());
is.setByteStream(stream_config);
configDigester.setDebug(getDebug());
configDigester.setUseContextClassLoader(false);
configDigester.addCallMethod("workflow-app/script-config/init-param", "addInitParameter", 2);
configDigester.addCallParam("workflow-app/script-config/init-param/param-name", 0);
configDigester.addCallParam("workflow-app/script-config/init-param/param-value", 1);
configDigester.push(servletContext.getScriptConfig());
configDigester.parse(is);
}
//--------------------create database
UserDatabase userDatabase = null;
CaseDatabase caseDatabase = null;
String schemaConextBase = context.findInitParameter(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_SCHEMA_CONTEXT);
if (schemaConextBase == null)
{
schemaConextBase = WorkflowProperties.getSchemaContextFile();
}
Hashtable env = new Hashtable();
String storeFactoryClass = context.findFactoryClass(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_FACTORY_STORE);
if (storeFactoryClass != null)
{
env.put(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_FACTORY_STORE, storeFactoryClass);
}
DatabaseFactory dbFactory = DatabaseFactory.newInstance(env);
env.clear();
Store userStore = context.findStore(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_STORE_USER);
if (userStore == null)
{
userStore = WorkflowProperties.getUserStore();
}
env.put(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_STORE_USER, userStore);
userDatabase = dbFactory.newUserDatabase(env);
env.clear();
userDatabase.setWorkflowService(context);
Store caseStore = context.findStore(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_STORE_CASE);
if (caseStore == null)
{
caseStore = WorkflowProperties.getCaseStore();
}
env.put(org.huihoo.workflow.xpdl.parser.Constants.WORKFLOW_STORE_CASE, caseStore);
caseDatabase = dbFactory.newCaseDatabase(env);
caseDatabase.setWorkflowService(context);
SchemaContext schemaContext = new SchemaContext(schemaConextBase);
if (userDatabase.getSchemaContext() == null)
{
userDatabase.setSchemaContext(schemaContext);
}
if (caseDatabase.getSchemaContext() == null)
{
caseDatabase.setSchemaContext(schemaContext);
}
ScriptInterpretor scriptInterpretor = new JavaInterpretor();
scriptInterpretor.initialize(context.getScriptContext());
context.setCaseDatabase(caseDatabase);
context.setUserDatabase(userDatabase);
context.setScriptInterpretor(scriptInterpretor);
if (context.getEngine().getService().getNamingServer() != null)
{
context.getEngine().getService().getNamingServer().addWorkflowService(context);
}
}
catch (SAXParseException e)
{
log(sm.getString("contextConfig.applicationParse"), e);
log(sm.getString("contextConfig.applicationPosition", "" + e.getLineNumber(), "" + e.getColumnNumber()));
ok = false;
}
catch (Exception e)
{
log(sm.getString("contextConfig.applicationParse"), e);
ok = false;
}
finally
{
try
{
if (stream != null)
{
stream.close();
}
if (stream_config != null)
{
stream_config.close();
}
}
catch (IOException e)
{
log.error(sm.getString("contextConfig.applicationClose"), e);
}
workflowDigester.push(null);
}
}
}
/**
* Create (if necessary) and return a Digester configured to process the
* org.huihoo.workflow application deployment descriptor (org.huihoo.workflow.xml).
*/
private Digester createWorkflowDigester()
{
Digester workflowDigester = DigesterFactory.newDigester(xmlValidation, xmlNamespaceAware, workflowRuleSet);
return workflowDigester;
}
/**
* Create (if necessary) and return a Digester configured to process the
* org.huihoo.workflow application deployment descriptor (org.huihoo.workflow.xml).
*/
private Digester createConfigDigester()
{
Digester workflowDigester = DigesterFactory.newDigester(xmlValidation, xmlNamespaceAware, null);
return workflowDigester;
}
private String getEngineBase()
{
Container engineC = context.getParent();
if (engineC instanceof StandardEngine)
{
return ((StandardEngine) engineC).getEngineBase();
}
return System.getProperty(Globals.PROPS_WILLOW_HOME);
}
/**
* Log a message on the Logger associated with our ServletContext (if any)
*
* @param message Message to be logged
*/
private void log(String message)
{
Logger logger = null;
if (context != null)
{
logger = context.getLogger();
}
if (logger != null)
{
logger.log("ContextConfig[" + context.getName() + "]: " + message);
}
else
{
log.info(message);
}
}
/**
* Log a message on the Logger associated with our ServletContext (if any)
*
* @param message Message to be logged
* @param throwable Associated exception
*/
private void log(String message, Throwable throwable)
{
Logger logger = null;
if (context != null)
{
logger = context.getLogger();
}
if (logger != null)
{
logger.log("ContextConfig[" + context.getName() + "] " + message, throwable);
}
else
{
log.error(message, throwable);
}
}
private void startNew()
{
try
{
if (context.getUserDatabase() != null)
{
context.getUserDatabase().start();
}
if (context.getCaseDatabase() != null)
{
context.getCaseDatabase().start();
}
if (context.getScriptInterpretor() != null)
{
context.getScriptInterpretor().start();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
private void stopNew()
{
try
{
if (context.getUserDatabase() != null)
{
context.getUserDatabase().stop();
}
if (context.getCaseDatabase() != null)
{
context.getCaseDatabase().stop();
}
if (context.getScriptInterpretor() != null)
{
context.getScriptInterpretor().stop();
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
/**
* Process a "start" event for this ServletContext - in background
*/
private synchronized void start()
{
// Called from StandardContext.start()
log.debug(sm.getString("contextConfig.start"));
// Process the application org.huihoo.workflow.xml files
applicationConfig();
deployApps();
startNew();
ok = true;
}
/**
* Process a "stop" event for this Context.
*/
private synchronized void stop()
{
log.debug(sm.getString("contextConfig.stop"));
try
{
if (context.getEngine().getService().getNamingServer() != null)
{
context.getEngine().getService().getNamingServer().removeWorkflowService(context);
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
int i;
//Removing parameters
String[] parameters = context.findInitParameters();
for (i = 0; i < parameters.length; i++)
{
context.removeInitParameter(parameters[i]);
}
//Removing Store
String[] stores = context.findStores();
for (i = 0; i < stores.length; i++)
{
context.removeStore(stores[i]);
}
//Removing Factory
String[] factories = context.findFactoryClasses();
for (i = 0; i < factories.length; i++)
{
context.removeFactoryClass(factories[i]);
}
stopNew();
undeployApps();
}
/**
* Undeploy all deployed applications.
*/
protected void undeployApps()
{
if (!(context instanceof ContextDeployer))
{
return;
}
log.debug(sm.getString("contextConfig.undeploying"));
ContextDeployer deployer = (ContextDeployer) context;
String xpdlFiles[] = deployer.findDeployedPackages();
for (int i = 0; i < xpdlFiles.length; i++)
{
log.debug(sm.getString("contextConfig.undeploy", xpdlFiles[i]));
try
{
((ContextDeployer) context).remove(xpdlFiles[i]);
}
catch (Throwable t)
{
log.error(sm.getString("contextConfig.undeploy.error", xpdlFiles[i]), t);
}
}
xpdlXmlLastModified.clear();
deployed.clear();
}
/**
* Deploy applications for any directories or WAR files that are found
* in our "application root" directory.
*/
protected void deployApps()
{
if (!(context instanceof ContextDeployer))
{
return;
}
File contextBase = context.getAbsoluteAppBase();
if (!contextBase.exists() || !contextBase.isDirectory())
{
return;
}
File xpdlBase = new File(contextBase, "WEB-INF/xpdl");
String files[] = xpdlBase.list();
deployXpdls(xpdlBase, files);
}
/**
* Deploy directories.
*/
protected void deployXpdls(File xpdlBase, String[] files)
{
ContextDeployer deployer = (ContextDeployer) context;
for (int i = 0; i < files.length; i++)
{
if (deployed.contains(files[i]))
{
continue;
}
File xpdl = new File(xpdlBase, files[i]);
if (xpdl.isFile() && xpdl.getName().endsWith(".xpdl"))
{
deployed.add(files[i]);
// Deploy the application in this directory
log.debug(sm.getString("contextConfig.deployXPDL", files[i]));
long t1 = System.currentTimeMillis();
try
{
URL url = new URL("file", null, xpdl.getCanonicalPath());
deployer.install(xpdl.getName(), url);
}
catch (Throwable t)
{
log(sm.getString("contextConfig.deployXPDL.error", files[i]), t);
}
long t2 = System.currentTimeMillis();
if ((t2 - t1) > 200)
{
log.debug("Deployed " + files[i] + " " + (t2 - t1));
}
}
}
}
/**
* Check deployment descriptors last modified date.
*/
protected void checkXpdlLastModified()
{
if (!(context instanceof ContextDeployer))
{
return;
}
File contextBase = context.getAbsoluteAppBase();
if (!contextBase.exists() || !contextBase.isDirectory())
{
return;
}
ContextDeployer deployer = (ContextDeployer) context;
String xpdlFiles[] = deployer.findDeployedPackages();
for (int i = 0; i < xpdlFiles.length; i++)
{
String xpdlFileName = xpdlFiles[i];
File xpdlBase = new File(contextBase, "WEB-INF/xpdl");
File xpdlFile = new File(xpdlBase, xpdlFileName);
long newLastModified = xpdlFile.lastModified();
Long lastModified = (Long) xpdlXmlLastModified.get(xpdlFileName);
if (lastModified == null)
{
xpdlXmlLastModified.put(xpdlFileName, new Long(newLastModified));
}
else
{
if (lastModified.longValue() != newLastModified)
{
if (newLastModified > (lastModified.longValue() + 5000))
{
xpdlXmlLastModified.remove(xpdlFileName);
try
{
URL url = new URL("file", null, xpdlFile.getCanonicalPath());
deployer.remove(xpdlFileName);
deployer.install(xpdlFileName, url);
}
catch (Throwable t)
{
log.error(sm.getString("contextConfig.deployXPDL.error", xpdlFile.getName()), t);
}
}
else
{
xpdlXmlLastModified.put(xpdlFileName, new Long(newLastModified));
}
}
}
}
}
/**
* Deploy workflows.
*/
protected void check()
{
if (context.getAutoDeploy())
{
deployApps();
checkXpdlLastModified();
}
}
}