//----------------------------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.config;
import java.io.File;
import java.io.FileNotFoundException;
import java.net.URL;
import java.net.URLClassLoader;
import org.huihoo.workflow.rules.Constants;
import org.huihoo.workflow.rules.ScriptContext;
import org.huihoo.workflow.rules.ScriptException;
import org.huihoo.workflow.rules.compiler.Compiler;
import org.huihoo.workflow.rules.compiler.JavaCompiler;
import org.huihoo.workflow.rules.compiler.ScriptCompiler;
import org.huihoo.workflow.rules.compiler.ScriptReader;
import org.huihoo.workflow.rules.compiler.ScriptRuntimeContext;
import org.huihoo.workflow.rules.compiler.ScriptWriter;
import org.huihoo.workflow.rules.compiler.SunJavaCompiler;
import org.huihoo.workflow.rules.interpretor.ScriptWrapper;
import org.huihoo.workflow.rules.loader.ScriptClassLoader;
import org.huihoo.workflow.rules.util.StringManager;
import org.huihoo.workflow.xpdl.WorkflowCondition;
import org.huihoo.workflow.xpdl.WorkflowPackage;
import org.huihoo.workflow.xpdl.WorkflowProcess;
import org.huihoo.workflow.xpdl.WorkflowTransition;
/**
* @author reic
*
* To change the template for this generated type comment go to
* Window - Preferences - Java - Code Generation - Code and Comments
*/
public class ScriptCompilationContext
{
protected static StringManager sm = StringManager.getManager(Constants.RESOURCE_BUNDLE_BASE);
private String className;
private String basePackageName;
private String derivedPackageName;
private String scriptJavaFileName;
private String classFileName;
private ScriptWriter writer;
private ScriptReader reader;
private Options options;
private ScriptWrapper ssw;
private Compiler scriptCompiler;
private String classPath;
private String outputDir;
private ScriptContext scriptContext;
private URLClassLoader loader;
private ScriptRuntimeContext rctxt;
private int removed = 0;
private URLClassLoader scriptClassLoader;
private URL baseUrl;
private Class scriptClass;
private WorkflowTransition workflowTransition;
public ScriptCompilationContext(
WorkflowTransition workflowTransition,
Options options,
ScriptContext scriptContext,
ScriptWrapper ssw,
ScriptRuntimeContext rctxt)
{
this.workflowTransition = workflowTransition;
this.options = options;
this.scriptContext = scriptContext;
this.ssw=ssw;
this.rctxt = rctxt;
this.basePackageName = Constants.SCRIPT_PACKAGE_NAME;
}
/**
* The classpath that is passed off to the Java compiler.
*/
public String getClassPath()
{
if (classPath != null)
{
return classPath;
}
return rctxt.getClassPath();
}
/**
* The classpath that is passed off to the Java compiler.
*/
public void setClassPath(String classPath)
{
this.classPath = classPath;
}
/**
* What class loader to use for loading classes while compiling
* this script?
*/
public ClassLoader getClassLoader()
{
if (loader != null)
{
return loader;
}
return rctxt.getParentClassLoader();
}
public void setClassLoader(URLClassLoader loader)
{
this.loader = loader;
}
/** ---------- Input/Output ---------- */
/**
* The output directory to generate code into. The output directory
* is make up of the scratch directory, which is provide in Options,
* plus the directory derived from the package name.
*/
public String getOutputDir()
{
if (outputDir == null)
{
createOutputDir();
}
return outputDir;
}
/**
* Create a "Compiler" object based on some init param data. This
* is not done yet. Right now we're just hardcoding the actual
* compilers that are created.
*/
public Compiler createCompiler() throws ScriptException
{
if (scriptCompiler != null)
{
return scriptCompiler;
}
JavaCompiler javac=new SunJavaCompiler();
scriptCompiler = new ScriptCompiler(this);
scriptCompiler.setJavaCompiler(javac);
return scriptCompiler;
}
public Compiler getCompiler()
{
return scriptCompiler;
}
/** ---------- Access resources in the workflowapp ---------- */
/**
* Gets a resource as a stream, relative to the meanings of this
* scriptContext's implementation.
* @return a null if the resource cannot be found or represented
* as an InputStream.
*/
public java.io.InputStream getResourceAsStream(String res)
{
return scriptContext.getResourceAsStream(canonicalURI(res));
}
/**
* Gets the actual path of a URI relative to the scriptContext of
* the compilation.
*/
public String getRealPath(String path)
{
if (scriptContext != null)
{
return scriptContext.getRealPath(path);
}
return path;
}
/* ==================== Common implementation ==================== */
/**
* Just the class name (does not include package name) of the
* generated class.
*/
public String getScriptClassName()
{
if (className != null)
{
return className;
}
className = formatJavaID("_script_" + workflowTransition.getUUID());
return className;
}
public void setScriptClassName(String className)
{
this.className = formatJavaID(className);
}
public WorkflowCondition getCondition()
{
return workflowTransition.getCondition();
}
/**
* Package name for the generated class is make up of the base package
* name, which is user settable, and the derived package name. The
* derived package name directly mirrors the file heirachy of the script page.
*/
public String getScriptPackageName()
{
String dPackageName = getDerivedPackageName();
if (dPackageName.length() == 0)
{
return basePackageName;
}
return basePackageName + '.' + getDerivedPackageName();
}
private String getDerivedPackageName()
{
if (derivedPackageName != null)
{
return derivedPackageName;
}
WorkflowProcess workflowProcess = workflowTransition.getWorkflowProcess();
WorkflowPackage workflowPackage = workflowProcess.getWorkflowPackage();
derivedPackageName = formatJavaID("_" + workflowPackage.getUUID() + "._" + workflowProcess.getUUID());
return derivedPackageName;
}
/**
* The package name into which the script class is generated.
*/
public void setScriptPackageName(String scriptPackageName)
{
this.basePackageName = formatJavaID(scriptPackageName);
}
/**
* Full path name of the Java file into which the script is being
* generated.
*/
public String getScriptJavaFileName()
{
if (scriptJavaFileName == null)
{
scriptJavaFileName = getOutputDir() + File.separator+ getScriptClassName() + ".java";
}
else
{
// Make sure output dir exists
makeOutputDir();
}
return scriptJavaFileName;
}
public void setScriptJavaFileName(String scriptJavaFileName)
{
this.scriptJavaFileName = formatJavaID(scriptJavaFileName);
}
/**
* Get hold of the Options object for this scriptContext.
*/
public Options getOptions()
{
return options;
}
public ScriptContext getScriptContext()
{
return scriptContext;
}
public ScriptRuntimeContext getRuntimeContext()
{
return rctxt;
}
public String getScriptClassFileName()
{
if (classFileName == null)
{
classFileName = getOutputDir() + File.separator+getScriptClassName() + ".class";
}
else
{
// Make sure output dir exists
makeOutputDir();
}
return classFileName;
}
/**
* Where is the script being generated?
*/
public ScriptWriter getWriter()
{
return writer;
}
public void setWriter(ScriptWriter writer)
{
this.writer = writer;
}
/**
* Get the input reader for the SCRIPT text.
*/
public ScriptReader getReader()
{
return this.reader;
}
public void setReader(ScriptReader reader)
{
this.reader=reader;
}
/**
* Are we keeping generated code around?
*/
public boolean keepGenerated()
{
return getOptions().getKeepGenerated();
}
// ==================== Removal ====================
public void incrementRemoved()
{
if (removed > 1)
{
scriptCompiler.removeGeneratedFiles();
if (rctxt != null)
{
rctxt.removeScriptWrapper(workflowTransition);
}
}
removed++;
}
public boolean isRemoved()
{
if (removed > 1)
{
return true;
}
return false;
}
// ==================== Compile and reload ====================
public void compile() throws FileNotFoundException, ScriptException
{
createCompiler();
if (scriptCompiler.isOutDated())
{
try
{
scriptCompiler.compile();
ssw.setReload(true);
}
catch (ScriptException ex)
{
throw ex;
}
catch (Exception ex)
{
throw new ScriptException(sm.getString("script.error.unable.compile"), ex);
}
}
}
// ==================== Manipulating the class ====================
public Class load() throws ScriptException,FileNotFoundException
{
try
{
URL baseUrl = options.getScratchDir().toURL();
scriptClassLoader = new ScriptClassLoader(new URL[] { baseUrl }, rctxt.getParentClassLoader());
String name = getScriptPackageName() + "." + getScriptClassName();
scriptClass = scriptClassLoader.loadClass(name);
}
catch (Exception ex)
{
if(ex instanceof ScriptException)
{
throw (ScriptException)ex;
}
throw new ScriptException(sm.getString("script.error.unable.load"), ex);
}
removed = 0;
return scriptClass;
}
// ==================== Private methods ====================
static Object outputDirLock = new Object();
private void makeOutputDir()
{
synchronized (outputDirLock)
{
File outDirFile = new File(outputDir);
outDirFile.mkdirs();
}
}
private void createOutputDir()
{
String path = getScriptPackageName().replace('.', '/');
try
{
// Append script or tag handler path to scratch dir
baseUrl = options.getScratchDir().toURL();
String outUrlString = baseUrl.toString() + '/' + path;
URL outUrl = new URL(outUrlString);
outputDir = outUrl.getFile() + File.separator;
makeOutputDir();
}
catch (Exception e)
{
throw new IllegalStateException("No output directory: " + e.getMessage());
}
}
private static final boolean isPathSeparator(char c)
{
return (c == '/' || c == '\\');
}
private static final String formatJavaID(String s)
{
if (s == null)
{
return null;
}
return s.replace('-','_');
}
private static final String canonicalURI(String s)
{
if (s == null)
return null;
StringBuffer result = new StringBuffer();
final int len = s.length();
int pos = 0;
while (pos < len)
{
char c = s.charAt(pos);
if (isPathSeparator(c))
{
/*
* multiple path separators.
* 'foo///bar' -> 'foo/bar'
*/
while (pos + 1 < len && isPathSeparator(s.charAt(pos + 1)))
{
++pos;
}
if (pos + 1 < len && s.charAt(pos + 1) == '.')
{
/*
* a single dot at the end of the path - we are done.
*/
if (pos + 2 >= len)
break;
switch (s.charAt(pos + 2))
{
/*
* self directory in path
* foo/./bar -> foo/bar
*/
case '/' :
case '\\' :
pos += 2;
continue;
/*
* two dots in a path: go back one hierarchy.
* foo/bar/../baz -> foo/baz
*/
case '.' :
// only if we have exactly _two_ dots.
if (pos + 3 < len && isPathSeparator(s.charAt(pos + 3)))
{
pos += 3;
int separatorPos = result.length() - 1;
while (separatorPos >= 0 && !isPathSeparator(result.charAt(separatorPos)))
{
--separatorPos;
}
if (separatorPos >= 0)
result.setLength(separatorPos);
continue;
}
}
}
}
result.append(c);
++pos;
}
return result.toString();
}
}