//----------------------------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.workflow.rules.compiler;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.huihoo.workflow.rules.Constants;
import org.huihoo.workflow.rules.ScriptContext;
import org.huihoo.workflow.rules.config.Options;
import org.huihoo.workflow.rules.config.ScriptCompilationContext;
import org.huihoo.workflow.rules.interpretor.ScriptWrapper;
import org.huihoo.workflow.rules.util.StringManager;
import org.huihoo.workflow.xpdl.WorkflowCondition;
import org.huihoo.workflow.xpdl.WorkflowTransition;
import org.huihoo.workflow.xpdl.condition.ScriptCondition;
import com.zosatapo.commons.util.log.SystemLogHandler;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public class ScriptRuntimeContext implements Runnable
{
// Logger
private static Log log = LogFactory.getLog(ScriptRuntimeContext.class);
protected static StringManager sm = StringManager.getManager(Constants.RESOURCE_BUNDLE_BASE);
// ----------------------------------------------------------- Constructors
/**
* Create a ScriptRuntimeContext for a org.huihoo.workflow application servletContext.
*
* Loads in any previously generated dependencies from file.
*
* @param ScriptContext for org.huihoo.workflow application
*/
public ScriptRuntimeContext(ScriptContext servletContext, Options options)
{
System.setErr(new SystemLogHandler(System.err));
System.setOut(new SystemLogHandler(System.out));
this.servletContext = servletContext;
this.options = options;
// Get the parent class loader
parentClassLoader = (URLClassLoader) servletContext.getAttribute(Constants.SCRIPT_CLASS_LOADER);
if (parentClassLoader == null)
{
parentClassLoader = (URLClassLoader) this.getClass().getClassLoader();
}
if (parentClassLoader != null)
{
log.debug(sm.getString("script.message.parent_class_loader_is", parentClassLoader.toString()));
}
else
{
log.debug(sm.getString("script.message.parent_class_loader_is", "<none>"));
}
initClassPath();
// If workflow application servletContext is running from a
// directory, start the background compilation thread
String appBase = servletContext.getRealPath("/");
if (appBase != null && options.getReloading())
{
if (appBase.endsWith(File.separator))
{
appBase = appBase.substring(0, appBase.length() - 1);
}
String directory = appBase.substring(appBase.lastIndexOf(File.separator));
threadName = threadName + "[" + directory + "]";
threadStart();
}
}
// ----------------------------------------------------- Instance Variables
/**
* This org.huihoo.workflow applications ScriptContext
*/
private ScriptContext servletContext;
private Options options;
private URLClassLoader parentClassLoader;
private String classpath;
/**
* Maps WFS pages to their ScriptBase's
*/
private Map wfses = Collections.synchronizedMap(new HashMap());
/**
* The background thread.
*/
private Thread thread = null;
/**
* The background thread completion semaphore.
*/
private boolean threadDone = false;
/**
* Name to register for the background thread.
*/
private String threadName = "ScriptRuntimeContext";
// ------------------------------------------------------ Protected Methods
/**
* Add a new ScriptWrapper.
*
* @param String uri of WFS
* @param Script className for WFS
*/
public void addScriptWrapper(
WorkflowTransition transition,
ScriptWrapper scriptWrapper)
{
wfses.remove(genScriptUUID(transition));
wfses.put(genScriptUUID(transition), scriptWrapper);
}
/**
* Get an already existing ScriptWrapper.
*
*/
public ScriptWrapper getScriptWrapper(WorkflowTransition transition)
{
return (ScriptWrapper) wfses.get(genScriptUUID(transition));
}
/**
* Remove a ScriptWrapper.
*
*/
public void removeScriptWrapper(WorkflowTransition transition)
{
wfses.remove(genScriptUUID(transition));
}
/**
* Get the parent URLClassLoader.
*
* @return URLClassLoader parent
*/
public URLClassLoader getParentClassLoader()
{
return parentClassLoader;
}
/**
* Process a "destory" event for this org.huihoo.workflow application servletContext.
*/
public void destroy()
{
threadStop();
if (System.err instanceof SystemLogHandler)
{
System.setErr(((SystemLogHandler) System.err).getWrapped());
}
Iterator servlets = wfses.values().iterator();
while (servlets.hasNext())
{
((ScriptWrapper) servlets.next()).destroy();
}
}
/**
* The classpath that is passed off to the Java compiler.
*/
public String getClassPath()
{
return classpath;
}
/**
* Method used to initialize classpath for compiles.
*/
private void initClassPath()
{
URL[] urls = parentClassLoader.getURLs();
StringBuffer cpath = new StringBuffer();
String sep = System.getProperty("path.separator");
for (int i = 0; i < urls.length; i++)
{
// Willow can use URL's other than file URL's,
// a protocol other than file: will generate a
// bad file system path, so only add file:
// protocol URL's to the classpath.
if (urls[i].getProtocol().equals("file"))
{
cpath.append((String) urls[i].getFile() + sep);
}
}
cpath.append(options.getScratchDir() + sep);
classpath = cpath.toString();
String cp = (String) servletContext.getAttribute(Constants.SCRIPT_CLASS_PATH);
if (cp == null || cp.equals(""))
{
cp = options.getClassPath();
}
if(cp!=null)
{
classpath +=cp;
}
}
private String genScriptUUID(WorkflowTransition workflowTransition)
{
WorkflowCondition condition=workflowTransition.getCondition();
if(condition!=null && condition instanceof ScriptCondition)
{
return workflowTransition.getUUID()+"."+condition.getContent();
}
return workflowTransition.getUUID();
}
// -------------------------------------------------------- Thread Support
/**
* Start the background thread that will periodically check for
* changes to compile time script files in a WFS.
*
* @exception IllegalStateException if we should not be starting
* a background thread now
*/
protected void threadStart()
{
// Has the background thread already been started?
if (thread != null)
{
return;
}
// Start the background thread
threadDone = false;
thread = new Thread(this, threadName);
thread.setDaemon(true);
thread.start();
}
/**
* Stop the background thread that is periodically checking for
* changes to compile time included files in a RULE.
*/
protected void threadStop()
{
if (thread == null)
{
return;
}
threadDone = true;
thread.interrupt();
thread = null;
}
/**
* Sleep for the duration specified by the <code>checkInterval</code>
* property.
*/
protected void threadSleep()
{
try
{
Thread.sleep(options.getCheckInterval() * 1000L);
}
catch (InterruptedException e)
{
;
}
}
// ------------------------------------------------------ Background Thread
/**
* Method used by background thread to check the RULE dependencies
* registered with this class for RULE's.
*/
private void checkCompile()
{
Object[] scriptWrappers = wfses.values().toArray();
for (int i = 0; i < scriptWrappers.length; i++)
{
ScriptWrapper scriptWrapper = (ScriptWrapper) scriptWrappers[i];
ScriptCompilationContext ctxt = scriptWrapper.getServletEngineContext();
// scriptWrapper also synchronizes on this when
// it detects it has to do a reload
synchronized (scriptWrapper)
{
try
{
ctxt.compile();
}
catch (FileNotFoundException ex)
{
ctxt.incrementRemoved();
}
catch (Throwable t)
{
scriptWrapper.getServletContext().log("Background compile failed", t);
}
}
}
}
/**
* The background thread that checks for changes to files
* included by a RULE and flags that a recompile is required.
*/
public void run()
{
// Loop until the termination semaphore is set
while (!threadDone)
{
// Wait for our check interval
threadSleep();
// Check for script files which are newer than the
// WFS which uses them.
try
{
checkCompile();
}
catch (Throwable t)
{
t.printStackTrace();
}
}
}
}