/*
|
* JBoss, Home of Professional Open Source.
|
* Copyright 2006, Red Hat Middleware LLC, and individual contributors
|
* as indicated by the @author tags. See the copyright.txt file in the
|
* distribution for a full listing of individual contributors.
|
*
|
* This is free software; you can redistribute it and/or modify it
|
* under the terms of the GNU Lesser General Public License as
|
* published by the Free Software Foundation; either version 2.1 of
|
* the License, or (at your option) any later version.
|
*
|
* This software is distributed in the hope that it will be useful,
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
* Lesser General Public License for more details.
|
*
|
* You should have received a copy of the GNU Lesser General Public
|
* License along with this software; if not, write to the Free
|
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
*/
|
package org.jbpm.pvm.internal.script;
|
|
import de.odysseus.el.util.SimpleResolver;
|
|
import javax.el.*;
|
import javax.script.*;
|
import java.io.IOException;
|
import java.io.Reader;
|
import java.lang.reflect.Method;
|
import java.lang.reflect.Modifier;
|
|
class JuelScriptEngine extends AbstractScriptEngine
|
implements Compilable
|
{
|
private ScriptEngineFactory factory;
|
private ExpressionFactory exprFactory;
|
|
public JuelScriptEngine(ScriptEngineFactory factory)
|
{
|
this.factory = factory;
|
this.exprFactory = new de.odysseus.el.ExpressionFactoryImpl();
|
}
|
|
public CompiledScript compile(String script)
|
throws ScriptException
|
{
|
ValueExpression expr = parse(script, this.context);
|
|
return new JuelCompiledScript(this, expr);
|
}
|
|
public CompiledScript compile(Reader reader)
|
throws ScriptException
|
{
|
return compile(readFully(reader));
|
}
|
|
public Object eval(String script, ScriptContext ctx)
|
throws ScriptException
|
{
|
ValueExpression expr = parse(script, ctx);
|
|
return evalExpr(expr, ctx);
|
}
|
|
public Object eval(Reader reader, ScriptContext ctx)
|
throws ScriptException
|
{
|
return eval(readFully(reader), ctx);
|
}
|
|
public ScriptEngineFactory getFactory()
|
{
|
synchronized (this)
|
{
|
if (this.factory == null)
|
{
|
this.factory = new JuelScriptEngineFactory();
|
}
|
|
}
|
|
return this.factory;
|
}
|
|
public Bindings createBindings()
|
{
|
return new SimpleBindings();
|
}
|
|
private ELContext toELContext(final ScriptContext ctx)
|
{
|
Object tmp = ctx.getAttribute("elcontext");
|
|
if (tmp instanceof ELContext)
|
{
|
return ((ELContext)tmp);
|
}
|
|
ctx.setAttribute("context", ctx, 100);
|
|
ctx.setAttribute("out:print", getPrintMethod(), 100);
|
|
SecurityManager manager = System.getSecurityManager();
|
|
if (manager == null)
|
{
|
ctx.setAttribute("lang:import", getImportMethod(), 100);
|
}
|
|
ELContext elContext = new ELContext()
|
{
|
ELResolver resolver = makeResolver();
|
VariableMapper varMapper = new ScriptContextVariableMapper(ctx);
|
FunctionMapper funcMapper = new ScriptContextFunctionMapper(ctx);
|
|
public ELResolver getELResolver()
|
{
|
return this.resolver;
|
}
|
|
public VariableMapper getVariableMapper()
|
{
|
return this.varMapper;
|
}
|
|
public FunctionMapper getFunctionMapper()
|
{
|
return this.funcMapper;
|
}
|
|
};
|
|
ctx.setAttribute("elcontext", elContext, 100);
|
|
return elContext;
|
}
|
|
private ELResolver makeResolver()
|
{
|
CompositeELResolver chain = new CompositeELResolver();
|
|
chain.add(new ArrayELResolver());
|
|
chain.add(new ListELResolver());
|
|
chain.add(new MapELResolver());
|
|
chain.add(new ResourceBundleELResolver());
|
|
chain.add(new BeanELResolver());
|
|
return new SimpleResolver(chain);
|
}
|
|
private ValueExpression parse(String script, ScriptContext ctx)
|
throws ScriptException
|
{
|
try
|
{
|
return this.exprFactory.createValueExpression(
|
toELContext(ctx), script, Object.class);
|
}
|
catch (ELException elexp)
|
{
|
throw new ScriptException(elexp);
|
}
|
}
|
|
private Object evalExpr(ValueExpression expr, ScriptContext ctx)
|
throws ScriptException
|
{
|
try
|
{
|
return expr.getValue(toELContext(ctx));
|
}
|
catch (ELException elexp)
|
{
|
throw new ScriptException(elexp);
|
}
|
}
|
|
private String readFully(Reader reader)
|
throws ScriptException
|
{
|
int numChars;
|
char[] arr = new char[8192];
|
|
StringBuilder text = new StringBuilder();
|
try
|
{
|
while ((numChars = reader.read(arr, 0, arr.length)) > 0)
|
{
|
text.append(arr, 0, numChars);
|
}
|
|
}
|
catch (IOException exp)
|
{
|
throw new ScriptException(exp);
|
}
|
|
return text.toString();
|
}
|
|
private static Method getPrintMethod()
|
{
|
Class myClass;
|
try
|
{
|
myClass = JuelScriptEngine.class;
|
|
Method method = myClass.getMethod(
|
"print", new Class[] { Object.class });
|
|
return method;
|
}
|
catch (Exception exp)
|
{
|
}
|
|
return null;
|
}
|
|
public static void print(Object obj)
|
{
|
System.out.print(obj);
|
}
|
|
private static Method getImportMethod()
|
{
|
Class myClass;
|
try
|
{
|
myClass = JuelScriptEngine.class;
|
|
Method method = myClass.getMethod(
|
"importFunctions",
|
new Class[] { ScriptContext.class,
|
String.class,
|
Object.class });
|
|
return method;
|
}
|
catch (Exception exp)
|
{
|
}
|
|
return null;
|
}
|
|
public static void importFunctions(ScriptContext ctx, String namespace, Object obj)
|
{
|
Class clazz = null;
|
|
if (obj instanceof Class)
|
{
|
clazz = (Class)obj;
|
} else {
|
if (obj instanceof String)
|
{
|
try
|
{
|
clazz = Class.forName((String)obj);
|
}
|
catch (ClassNotFoundException cnfe)
|
{
|
throw new ELException(cnfe);
|
}
|
|
}
|
|
throw new ELException("Class or class name is missing");
|
}
|
|
Method[] methods = clazz.getMethods();
|
|
for (Method m : methods)
|
{
|
int mod = m.getModifiers();
|
|
if ((Modifier.isStatic(mod)) &&
|
(Modifier.isPublic(mod)))
|
{
|
String name = namespace + ":" + m.getName();
|
|
ctx.setAttribute(name, m,
|
100);
|
}
|
}
|
}
|
|
private class JuelCompiledScript extends CompiledScript
|
{
|
private ValueExpression expr;
|
private ScriptEngine engine;
|
|
JuelCompiledScript(ScriptEngine engine, ValueExpression expr)
|
{
|
this.engine = engine;
|
this.expr = expr;
|
}
|
|
public ScriptEngine getEngine()
|
{
|
return engine;
|
}
|
|
public Object eval(ScriptContext ctx)
|
throws ScriptException
|
{
|
return engine.eval(this.expr.getExpressionString(), ctx);
|
}
|
}
|
|
private class ScriptContextFunctionMapper extends FunctionMapper
|
{
|
private ScriptContext ctx;
|
|
ScriptContextFunctionMapper(ScriptContext ctx)
|
{
|
this.ctx = ctx;
|
}
|
|
private String getFullName(String prefix, String localName)
|
{
|
return prefix + ":" + localName;
|
}
|
|
public Method resolveFunction(String prefix, String localName)
|
{
|
String fullName = getFullName(prefix, localName);
|
|
int scope = this.ctx.getAttributesScope(fullName);
|
|
if (scope != -1)
|
{
|
Object tmp = this.ctx.getAttribute(fullName);
|
|
return ((tmp instanceof Method) ? (Method)tmp : null);
|
}
|
|
return null;
|
}
|
}
|
|
private class ScriptContextVariableMapper extends VariableMapper
|
{
|
private ScriptContext ctx;
|
|
ScriptContextVariableMapper(ScriptContext ctx)
|
{
|
this.ctx = ctx;
|
}
|
|
public ValueExpression resolveVariable(String variable)
|
{
|
int scope = this.ctx.getAttributesScope(variable);
|
|
if (scope != -1)
|
{
|
Object value = this.ctx.getAttribute(variable, scope);
|
|
if (value instanceof ValueExpression)
|
{
|
return ((ValueExpression)value);
|
}
|
|
return exprFactory.createValueExpression(
|
value, Object.class);
|
}
|
|
return null;
|
}
|
|
|
public ValueExpression setVariable(String variable, ValueExpression value)
|
{
|
ValueExpression oldValue = resolveVariable(variable);
|
|
this.ctx.setAttribute(variable, value, 100);
|
|
return oldValue;
|
}
|
}
|
}
|