/* Modified from https://scripting.dev.java.net/" (JSR 223 Java Scripting) */
/*
* Copyright (C) 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met: Redistributions of source code
* must retain the above copyright notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution. Neither the name of the Sun Microsystems nor the names of
* is contributors may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
* OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
/*
* JavaScriptEngine.java
* @author A. Sundararajan
*/
package com.cloudera.cdk.morphline.scriptengine.java;
import java.io.Reader;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Iterator;
import java.util.Map;
import javax.script.AbstractScriptEngine;
import javax.script.Bindings;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptException;
import javax.script.SimpleBindings;
/**
* This is a fast script engine for Java programming language - I modified JSR
* 223 Java Scripting a little for some 100x less overhead for invoking "static" methods.
*/
public class FastJavaScriptEngine extends AbstractScriptEngine implements Compilable {
// Java compiler
private JavaCompiler compiler;
public FastJavaScriptEngine() {
compiler = new JavaCompiler();
}
// my implementation for CompiledScript
public static final class JavaCompiledScript extends CompiledScript {
private final Method targetMethod; // TODO: use java.lang.invoke.MethodHandle on Java7 for better perf; e.g. see http://vanillajava.blogspot.com/2011/08/methodhandle-performance-in-java-7.html
JavaCompiledScript (Class clazz, String methodName, Class[] parameterTypes) throws ScriptException {
try {
this.targetMethod = clazz.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
throw new ScriptException(e);
}
int modifiers = this.targetMethod.getModifiers();
if (!Modifier.isPublic(modifiers) || !Modifier.isStatic(modifiers)) {
throw new ScriptException(
"Cannot find public static method: " + methodName); }
}
public Object eval(Object... params) throws ScriptException {
try {
return targetMethod.invoke(null, params);
} catch (Exception e) {
throw new ScriptException(e);
}
}
public ScriptEngine getEngine() {
throw new UnsupportedOperationException();
}
public Object eval(ScriptContext ctx) throws ScriptException {
throw new UnsupportedOperationException();
}
}
public CompiledScript compile(String script, String methodName, Class[] parameterTypes) throws ScriptException {
Class clazz = parse(script, context);
return new JavaCompiledScript(clazz, methodName, parameterTypes);
}
public CompiledScript compile(String script) throws ScriptException {
throw new UnsupportedOperationException();
}
public CompiledScript compile(Reader reader) throws ScriptException {
throw new UnsupportedOperationException();
}
public Object eval(String str, ScriptContext ctx) throws ScriptException {
throw new UnsupportedOperationException();
}
public Object eval(Reader reader, ScriptContext ctx) throws ScriptException {
throw new UnsupportedOperationException();
}
public ScriptEngineFactory getFactory() {
throw new UnsupportedOperationException();
}
public Bindings createBindings() {
return new SimpleBindings();
}
// Internals only below this point
private Class parse(String str, ScriptContext ctx) throws ScriptException {
String fileName = getFileName(ctx);
String sourcePath = getSourcePath(ctx);
String classPath = getClassPath(ctx);
Map<String, byte[]> classBytes = compiler.compile(fileName, str,
ctx.getErrorWriter(), sourcePath, classPath);
if (classBytes == null) {
throw new ScriptException("compilation failed");
}
// create a ClassLoader to load classes from MemoryJavaFileManager
MemoryClassLoader loader = new MemoryClassLoader(classBytes, classPath,
getParentLoader(ctx));
String mainClassName = getMainClassName(ctx);
if (mainClassName != null) {
try {
Class clazz = loader.load(mainClassName);
Method mainMethod = findMainMethod(clazz);
if (mainMethod == null) {
throw new ScriptException("no main method in " + mainClassName);
}
return clazz;
} catch (ClassNotFoundException cnfe) {
throw new ScriptException(cnfe);
}
}
// no main class configured - load all compiled classes
Iterable<Class> classes;
try {
classes = loader.loadAll();
} catch (ClassNotFoundException exp) {
throw new ScriptException(exp);
}
// search for class with main method
Class c = findMainClass(classes);
if (c != null) {
return c;
} else {
// if class with "main" method, then
// return first class
Iterator<Class> itr = classes.iterator();
if (itr.hasNext()) {
return itr.next();
} else {
return null;
}
}
}
private static Class findMainClass(Iterable<Class> classes) {
// find a public class with public static main method
for (Class clazz : classes) {
int modifiers = clazz.getModifiers();
if (Modifier.isPublic(modifiers)) {
Method mainMethod = findMainMethod(clazz);
if (mainMethod != null) {
return clazz;
}
}
}
// okay, try to find package private class that
// has public static main method
for (Class clazz : classes) {
Method mainMethod = findMainMethod(clazz);
if (mainMethod != null) {
return clazz;
}
}
// no main class found!
return null;
}
// find public static void main(String[]) method, if any
private static Method findMainMethod(Class clazz) {
try {
Method mainMethod = clazz.getMethod("main", new Class[] { String[].class });
int modifiers = mainMethod.getModifiers();
if (Modifier.isPublic(modifiers) &&
Modifier.isStatic(modifiers)) {
return mainMethod;
}
} catch (NoSuchMethodException nsme) {
}
return null;
}
private static String getFileName(ScriptContext ctx) {
int scope = ctx.getAttributesScope(ScriptEngine.FILENAME);
if (scope != -1) {
return ctx.getAttribute(ScriptEngine.FILENAME, scope).toString();
} else {
return "$unnamed.java";
}
}
// for certain variables, we look for System properties. This is
// the prefix used for such System properties
private static final String SYSPROP_PREFIX = "com.sun.script.java.";
private static final String SOURCEPATH = "sourcepath";
private static String getSourcePath(ScriptContext ctx) {
int scope = ctx.getAttributesScope(SOURCEPATH);
if (scope != -1) {
return ctx.getAttribute(SOURCEPATH).toString();
} else {
// look for "com.sun.script.java.sourcepath"
return System.getProperty(SYSPROP_PREFIX + SOURCEPATH);
}
}
private static final String CLASSPATH = "classpath";
private static String getClassPath(ScriptContext ctx) {
int scope = ctx.getAttributesScope(CLASSPATH);
if (scope != -1) {
return ctx.getAttribute(CLASSPATH).toString();
} else {
// look for "com.sun.script.java.classpath"
String res = System.getProperty(SYSPROP_PREFIX + CLASSPATH);
if (res == null) {
res = System.getProperty("java.class.path");
}
return res;
}
}
private static final String MAINCLASS = "mainClass";
private static String getMainClassName(ScriptContext ctx) {
int scope = ctx.getAttributesScope(MAINCLASS);
if (scope != -1) {
return ctx.getAttribute(MAINCLASS).toString();
} else {
// look for "com.sun.script.java.mainClass"
return System.getProperty(SYSPROP_PREFIX + MAINCLASS);
}
}
private static final String PARENTLOADER = "parentLoader";
private static ClassLoader getParentLoader(ScriptContext ctx) {
int scope = ctx.getAttributesScope(PARENTLOADER);
if (scope != -1) {
Object loader = ctx.getAttribute(PARENTLOADER);
if (loader instanceof ClassLoader) {
return (ClassLoader) loader;
} // else fall through..
}
return null;
}
}