/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/SandboxImpl.java |
---|
26,6 → 26,9 |
// $Id$ |
// Related reference: |
// http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ |
import java.io.IOException; |
import java.lang.reflect.Method; |
import java.net.URI; |
50,11 → 53,24 |
private final String boilerPlate; |
private int execution = 0; |
private final PluginProperties properties; |
private transient ScriptEngine currentEngine = null; |
private static final String END_KEY = "e"; |
private static final boolean debug; |
private final PluginEnvironment pE; |
private static final String END_KEY = "e"; |
private static class ExecResult { |
final ScriptEngine rhino; |
final Object evalResult; |
ExecResult(final ScriptEngine rhino_, final Object evalResult_) { |
rhino = rhino_; |
evalResult = evalResult_; |
} |
} |
static { |
nextUniqueVarNum = (int) (1193 * (1+Math.random())); |
debug = System.getProperty("debug", "0").equals("1"); |
} |
{ |
62,16 → 78,19 |
hooks.put(END_KEY, new LinkedList<Runnable>()); |
} |
protected SandboxImpl(final URI plugin, final Bindings bs, final PluginProperties pp) { |
this(plugin, bs, pp, null); |
protected SandboxImpl(final PluginEnvironment pe, final URI plugin, |
final Bindings bs, final PluginProperties pp) { |
this(pe, plugin, bs, pp, null); |
} |
protected SandboxImpl(final URI plugin, final Bindings bs, |
protected SandboxImpl(final PluginEnvironment pe, final URI plugin, |
final Bindings bs, |
final PluginProperties pp, final String boilerPlate) { |
bindings = bs; |
pluginUri = plugin; |
pluginUri = plugin; // if null => sandbox without file |
this.boilerPlate = boilerPlate; |
properties = pp; |
pE = pe; |
} |
public void addEndHook(Runnable r) { |
82,7 → 101,7 |
* Namespace for variables used by the enviroment |
*/ |
private static String namespace() { |
return "$net.outlyer.runtime"; |
return "$net.outlyer.runtime.internal"; |
} |
/** |
101,44 → 120,34 |
return new StringBuilder(namespace()).append(".") |
.append(uniqueVarName()).toString(); |
} |
/** |
* Gets an engine. |
* If the system property "debug" is set to "1" the returned engine |
* will dump extra information to the console |
* @return New engine |
*/ |
private static ScriptEngine getEngine() { |
return new ScriptEngineManager().getEngineByName("rhino"); |
final ScriptEngine se = new ScriptEngineManager().getEngineByName("rhino"); |
if (debug) { |
return new EngineSink(se); |
} |
return se; |
} |
private ScriptEngine execute(final String prependText, |
private ExecResult execute(final String prependText, |
final String appendText, |
final Map<String,Object> extraBindings) |
throws PluginExecutionException { |
final ScriptEngine rhino = getEngine(); |
currentEngine = rhino; |
// First, export in the global namespace the reference to this |
// sandbox. This variable isn't meant to be used directly, it's only |
// set to refer to it from the boilerplate code (AFAIK there's no |
// way to add a binding with a FQ name). |
// TODO: Can a fully qualified name be bound directly? |
final String globalSanboxVarName = uniqueVarName(); |
bindings.put(globalSanboxVarName, this); |
if (null != extraBindings) { |
for (final String key : extraBindings.keySet()) { |
bindings.put(key, extraBindings.get(key)); |
} |
bindings.putAll(extraBindings); |
} |
Object result = null; |
// Will contain some biolerplate code used to provide access |
// to the runtime wrapped-environment |
final StringBuilder runtime = new StringBuilder(); |
// $net.outlyer.runtime.sandbox Contains a reference to this object |
// Implementation note: PluginEnvironment.EXPORTED_SANDBOX_VARIABLE should have the same name |
// TODO: ? $net.outlyer.runtime.pluginEnvironment |
runtime.append("var $net = {\n") |
.append(" outlyer : {\n") |
.append(" runtime : {\n") |
.append(" sandbox : ").append(globalSanboxVarName).append("\n") |
.append(" }\n") |
.append(" }\n") |
.append("};\n"); |
try { |
rhino.setBindings(bindings, ScriptContext.ENGINE_SCOPE); |
147,15 → 156,27 |
if (null != boilerPlate) { |
rhino.eval(boilerPlate); |
} |
// Create the $net.outlyer.runtime pseudo-namespace... |
rhino.eval(runtime.toString()); |
// $net.outlyer.runtime.sandbox Contains a reference to this object |
// Implementation note: PluginEnvironment.EXPORTED_SANDBOX_VARIABLE |
// should have the same name |
final NamespaceContainer.$Net $net = new NamespaceContainer.$Net(pE, this); |
assert this == $net.outlyer.runtime.sandbox; |
rhino.put("$net", $net); |
// ....internal can be used to store random internal data, it's |
// a dynamic object so that fields can be created as needed |
rhino.eval("$net.outlyer.runtime.internal={};"); |
if (null != prependText) { |
rhino.eval(prependText); |
} |
rhino.eval(new PluginReader(pluginUri)); |
if (null != pluginUri) { |
result = rhino.eval(new PluginReader(pluginUri)); |
} |
else { |
result = null; |
} |
if (null != appendText) { |
rhino.eval(appendText); |
172,24 → 193,42 |
catch (final ScriptException e) { |
throw new PluginExecutionException("Script exception: " + e.getMessage(), e); |
} |
finally { |
currentEngine = null; |
} |
return rhino; |
return new ExecResult(rhino, result); |
} |
public void execute() throws PluginExecutionException { |
execute(null, null, null); |
} |
ScriptEngine executeAndGetEngine() throws PluginExecutionException { |
return execute(null, null, null).rhino; |
} |
public <T> T createDelayedImplementation(final Class<T> c, |
final String objectName) |
throws PluginExecutionException { |
final String varImpl = uniqueVarName(); |
if (null == c) { |
throw new IllegalArgumentException("Can't instantiate null"); |
} |
if (!c.isInterface()) { |
throw new IllegalArgumentException("Can only create delayed " + |
"implementations for interfaces"); |
} |
if (null == objectName) { |
throw new IllegalArgumentException("Implementor's name can't be null"); |
} |
final StringBuilder preCode = new StringBuilder(); |
if (debug) { |
preCode.append("// Forward declaration for delayed implementation:\n"); |
} |
preCode.append("var ").append(objectName).append(" = {\n"); |
for (final Method method : c.getMethods()) { |
if (method.getDeclaringClass() != c) { |
if (method.getDeclaringClass() == Object.class) { // Implement everything, including hierarchies |
continue; |
} |
preCode.append(method.getName()).append(": null,\n"); |
196,104 → 235,104 |
} |
preCode.append("};\n"); |
// Could be safely instantiated before actually being assigned IF |
// plugins were required to define their methods with |
// objectName.methodName = function() {} ... |
// but would file with |
// objectName = { methodName: function() {}, ... } |
// Creating the instance as postCode does the job for both |
final StringBuilder postCode = new StringBuilder(); |
postCode.append(varImpl).append(" = new ") |
final ScriptEngine rhino = execute(preCode.toString(), null, null).rhino; |
try { |
final StringBuilder retriever = new StringBuilder(); |
retriever.append("new ") |
.append(c.getCanonicalName()).append("(").append(objectName).append(");"); |
if (debug) { |
retriever.append("// <== Actual delayed implementation creation"); |
} |
final ScriptEngine rhino = execute(preCode.toString(), postCode.toString(), null); |
// Could be safely instantiated before actually being assigned IF |
// plugins were required to define their methods with |
// objectName.methodName = function() {} ... |
// but would file with |
// objectName = { methodName: function() {}, ... } |
// Creating the instance as postCode does the job for both |
assert (c.isInstance(rhino.get(varImpl))); |
return (T) rhino.get(varImpl); |
try { |
currentEngine = rhino; |
// eval() returns the value of the last evaluated line/command |
assert (c.isInstance(rhino.eval(retriever.toString()))); |
return (T) rhino.eval(retriever.toString()); |
} |
finally { |
currentEngine = null; |
} |
} |
catch (final ScriptException e) { |
throw new PluginExecutionException("Failed to create the delayed " + |
"implementation instance: " + e.getMessage(), e); |
} |
} |
public <T> T createDelayedImplementation(final Class<T> c) |
/** |
* {@inheritDoc} |
*/ |
public <T> T createDelayedImplementation(final Class<T> c, |
final Map<Method, String> map) |
throws PluginExecutionException { |
return createDelayedImplementation(c, false); |
} |
public <T> T createDelayedImplementation(final Class<T> c, boolean allowPartial) |
throws PluginExecutionException { |
return createDelayedImplementation(c, null, allowPartial); |
} |
public <T> T createDelayedImplementation(final Class<T> interfaceClass, |
final T fallbackObject) |
throws PluginExecutionException { |
if (null == fallbackObject) { |
throw new IllegalArgumentException("Can't use a null fallback object"); |
if (null == c) { |
throw new IllegalArgumentException("Can't instantiate null"); |
} |
return createDelayedImplementation(interfaceClass, fallbackObject, true); |
} |
if (!c.isInterface()) { |
throw new IllegalArgumentException("Can only create delayed " + |
"implementations for interfaces"); |
} |
if (null == map || map.isEmpty()) { |
throw new IllegalArgumentException("No mappings; creating a delayed " + |
"implementation with no mappings " + |
"is equivalent to creating" + |
"the object from Java"); |
} |
private <T> T createDelayedImplementation(final Class<T> c, |
final T fallback, |
boolean allowPartial) |
throws PluginExecutionException { |
final StringBuilder hack = new StringBuilder(); |
final StringBuffer preCode = new StringBuffer(); |
final String var = uniqueFQVarName(); |
// Apparently only global namespace objects can be retrieved with get() |
// so to simplify retrieval a global variable is used |
final String varImpl = uniqueVarName(); |
hack.append(var).append("={\n"); |
final StringBuilder script = new StringBuilder(); |
final String varFallback = uniqueVarName(); |
for (final Method m : c.getMethods()) { |
if (m.getDeclaringClass() != c) { |
continue; |
} |
final String implFuncName = c.getSimpleName()+"_"+m.getName(); |
if (allowPartial) { |
// This makes undefined functions to be mapped to undefined |
// variables (otherwise the interface instantation would fail) |
// Apparently defining a var with the same name as a function |
// produces no error, while checking for an undefined |
// variable/method does fail. |
// FIXME: Might be cleaner by handling "undefined" |
script.append("var ").append(implFuncName).append(";\n"); |
if (null != fallback) { |
script.append("if (!").append(implFuncName).append(")\n\t") |
.append(implFuncName).append("=") |
.append(varFallback).append(".").append(m.getName()) |
.append("\n"); |
} |
// script.append("if (!").append(implFuncName).append(") ") |
// .append(implFuncName).append("=null;\n"); |
} |
hack.append("\t").append(m.getName()).append(" : ").append(implFuncName).append(",\n"); |
preCode.append(var).append(" = {\n"); |
for (final Method method : map.keySet()) { |
preCode.append(method.getName()).append(": ") |
.append(map.get(method)) |
.append("\n,\n"); |
} |
hack.append("};\nvar ") |
.append(varImpl).append("=new ") |
.append(c.getCanonicalName()).append("(").append(var).append(");"); |
preCode.append("};\n"); |
script.append(hack.toString()); |
final ScriptEngine rhino = execute(preCode.toString(), null, null).rhino; |
final HashMap<String, Object> bn = new HashMap(); |
bn.put(varFallback, fallback); |
try { |
final StringBuilder retriever = new StringBuilder(); |
retriever.append("new ") |
.append(c.getCanonicalName()).append("(").append(var).append(");"); |
if (debug) { |
retriever.append("// <== Actual delayed implementation creation"); |
} |
if (null != fallback) { |
script.append("\n// " + bn.get(varFallback).getClass()); |
// Could be safely instantiated before actually being assigned IF |
// plugins were required to define their methods with |
// objectName.methodName = function() {} ... |
// but would file with |
// objectName = { methodName: function() {}, ... } |
// Creating the instance as postCode does the job for both |
try { |
currentEngine = rhino; |
// eval() returns the value of the last evaluated line/command |
assert (c.isInstance(rhino.eval(retriever.toString()))); |
return (T) rhino.eval(retriever.toString()); |
} |
finally { |
currentEngine = null; |
} |
} |
//System.err.println(script.toString()); |
final ScriptEngine rhino = execute(null, script.toString(), bn); |
assert (c.isInstance(rhino.get(varImpl))); |
return (T) rhino.get(varImpl); |
catch (final ScriptException e) { |
throw new PluginExecutionException("Failed to create the delayed " + |
"implementation instance: " + e.getMessage(), e); |
} |
} |
public BasePluginObject getPluginObject() throws PluginExecutionException { |
323,6 → 362,8 |
public URI loadedFrom() { |
return pluginUri.normalize(); // FIXME: Return a copy |
} |
public ScriptEngine getCurrentEngine() { |
return currentEngine; |
} |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/utils/Output.java |
---|
42,6 → 42,9 |
public Output(final PrintStream ps) { |
this.ps = ps; |
} |
// Note rhino already provides print() and println() in the global |
// namespace |
public void println() { |
ps.println(); |
61,6 → 64,10 |
} |
public void printf(final String s) { |
ps.printf("%s", s); |
print(s); |
} |
public void print(final String s) { |
ps.print(s); |
} |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/utils/LanguageExtensions.java |
---|
0,0 → 1,75 |
package net.outlyer.plugins.utils; |
/* |
* Copyright (c) 2008, Toni Corvera. All rights reserved. |
* |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED "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. |
*/ |
// $Id$ |
import java.io.File; |
import java.io.FileNotFoundException; |
import java.io.FileReader; |
import javax.script.ScriptException; |
import net.outlyer.plugins.SandboxAccessorImpl; |
/** |
* Extra facilities found in other languages or more complete |
* versions of available facilities. |
* Recommended name: lang |
*/ |
public class LanguageExtensions extends SandboxAccessorImpl { |
/** |
* ScriptEngine's eval. |
* The built-in eval() appears to be less-powerful, in general they're |
* equivalent, but if the eval'ed string is obtainer from input |
* <code>eval()</code> appears to ignore it, while |
* <code>LanguageExtensions.eval()</code> does eval it. |
* @param code Code to evaluate |
* @return Eval's result |
*/ |
public Object eval(final String code) { |
try { |
return getSandbox().getCurrentEngine().eval(code); |
} |
catch (final ScriptException e) { |
// FIXME: Handle better |
System.err.println("Exception: " + e.getMessage()); |
} |
return null; |
} |
public Object include(final String fileName) { // TODO: Support jar URIs |
try { |
return getSandbox().getCurrentEngine().eval(new FileReader(new File(fileName))); |
} |
catch (final FileNotFoundException e) { |
// FIXME: Handle better |
System.err.println("Failed to read "+fileName); |
} |
catch (final ScriptException e) { |
// FIXME: Handle better |
System.err.println("Exception: " + e.getMessage()); |
} |
return null; |
} |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/utils/package-info.java |
---|
1,7 → 1,7 |
/** |
* This package provides some common functionality that might often be |
* wanted to be exported as global objects. |
* {@see net.outlyer.plugins.sandboxing.PluginEnvironment#exportGlobalObject}. |
* {@see net.outlyer.plugins.PluginEnvironment#exportObject}. |
*/ |
// $Id$ |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/SandboxAccessor.java |
---|
32,12 → 32,28 |
* it enables exported objects to retrieve the sandbox object in which they're |
* being executed. |
* |
* Implementors must contain a field exactly: |
* Implementors must contain a field like |
* <pre> |
* public Object sandboxGetter = null; |
* </pre> |
* |
* public Callable<Sandbox> _getSandbox = null; |
* |
* sandboxGetter MUST be public, it can be of class Object or |
* Callable<Sandbox>, but shouldn't be used directly, instead, if required, |
* use SandboxAccessorImpl#getSandbox(SandboxAccessor) to retrieve it's |
* value, or inherit from SandboxAccessorImpl to be able to use |
* SandboxAccessorImpl#getSandbox(). |
* <br /> |
* Implementation notes: It will be dynamically initialised, no user |
* initialisation is required. |
* |
* The convenience default implementation, {@link SandboxAccessorImpl} can |
* be used as a base class if that's possible, to hide such requirement. |
*/ |
public interface SandboxAccessor { |
/** |
* Obtains the associated sanbdox. |
* Implementors can use {@link SandboxAccessor#getSandbox(SandboxAccessor)}. |
* @return The sandbox |
*/ |
public Sandbox getSandbox(); |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/PluginEnvironment.java |
---|
60,7 → 60,7 |
private BasePluginObject pluginObject; |
private StringBuilder boilerPlate; |
private List<String> acceptedTypes = null; |
private int maxApi=API.REVISION; |
private int maxApi=API.VERSION; |
// Since these lines are evaluated they'll be executed twice |
// so the lower the number the better to reduce unexpected side effects |
private static int linesToCheckForSupport = 1; |
71,6 → 71,11 |
exportedObjects = new HashMap(); |
pluginObject = null; |
boilerPlate = new StringBuilder(); |
// plugin is guaranteed to be a SandboxAccessor, regardless of which |
// exact object it ends up mapping to, see enableSandboxAccess() |
boilerPlate.append("plugin.sandboxGetter = new java.util.concurrent.Callable(function() {") |
.append(" return $net.outlyer.runtime.sandbox; });\n"); |
} |
//////////////////////////////////////////////////////// |
86,7 → 91,17 |
public Sandbox createSandbox(final URI forUri) throws PluginException { |
final ScriptEngine rhino = new ScriptEngineManager().getEngineByName("rhino"); |
final PluginProperties pp = checkForSupport(forUri); |
PluginProperties pp; |
if (null != forUri) { |
pp = checkForSupport(forUri); |
} |
else { |
pp = new PluginProperties(); |
pp.apiVersion = API.VERSION; |
pp.name = "none"; |
pp.type = ""; |
} |
if (null == pluginObject) { |
pluginObject = new PluginObject(); |
101,9 → 116,9 |
bindings.put(objectName, exportedObjects.get(objectName)); |
} |
bindings.put("plugin", po); |
enableSandboxAccess("plugin", po); |
//enableSandboxAccess("plugin", po); |
return new SandboxImpl(forUri, bindings, pp, boilerPlate.toString()); |
return new SandboxImpl(this, forUri, bindings, pp, boilerPlate.toString()); |
} |
catch (final CloneNotSupportedException e) { |
assert false; |
117,12 → 132,17 |
* the exporting it. |
* @param name Name with which to access the object |
* @param object Object to export |
* @throws IllegalArgumentException If name is <code>null</code> |
* @throws IllegalArgumentException If name is "<code>plugin</code>" (use |
* {@link #setPluginObject()} instead). |
*/ |
public void exportGlobalObject(final String name, final Object object) { |
public void exportObject(final String name, final Object object) { |
if (null == name) { |
throw new IllegalArgumentException("Can't export an object with" + |
"a null name"); |
} |
// Since the plugin object rules' are special (it will be cloned) |
// being nice could (?) lead to confusion |
if (name.equals("plugin")) { |
throw new IllegalArgumentException("Can't export an object named " + |
"plugin, use setPluginObject() instead"); |
133,8 → 153,39 |
enableSandboxAccess(name, (SandboxAccessor)object); |
} |
} |
/** |
* Makes a package's contents available in the global namespace. |
* (<em>Imports</em> it into JavaScript). |
* @param pkg Package to export |
* @see #exportPackage(String) |
*/ |
public void exportPackage(final Package pkg) { |
if (null == pkg) { |
throw new IllegalArgumentException("Can't export a null package"); |
} |
boilerPlate.append("importPackage("+pkg.getName()+")"); |
} |
/** |
* Makes a package's contents available in the global namespace. |
* Like {@see #exportPackage(Package)} but exporting by name. |
* @param packageName |
* @see #exportPackage(Package) |
*/ |
public void exportPackage(final String packageName) { |
if (null == packageName) { |
throw new IllegalArgumentException("Can't export a null-named package"); |
} |
final Package pkg = Package.getPackage(packageName); |
if (null == pkg) { |
throw new IllegalArgumentException("Failed to find package "+packageName); |
} |
exportPackage(pkg); |
} |
/** |
* Defines the object to be exported as <code>plugin</code>. |
* The <code>plugin</code> object is the only one exported by default. |
* If none is set, a default one will be provided. |
169,9 → 220,24 |
// Non-public interface |
//////////////////////////////////////////////////////// |
/** |
* Makes a {@link SanboxAccessor}'s field <code>getSandbox</code> |
* be evaluable as a <code>Callable<Sandbox></code>, i.e., |
* from Java it will be possible to use:<br /> |
* <pre> |
* Sandbox sb = ( (Callable<Sandbox>) object.getSandbox ).call() |
* </pre> |
* to retrieve the sandbox, while from JavaScript it will be enough |
* with: |
* <pre> |
* var sb = object.getSandbox() |
* </pre> |
* @param name Exported object's name |
* @param sa Exported object |
*/ |
private void enableSandboxAccess(final String name, final SandboxAccessor sa) { |
try { |
Field f = sa.getClass().getField("_getSandbox"); |
Field f = sa.getClass().getField("sandboxGetter"); |
} |
catch (final NoSuchFieldException e) { |
throw new IllegalArgumentException("object " + name + " must comply " + |
178,12 → 244,9 |
"with the contract of SandboxAccessor"); |
} |
final String callback = name + "._getSandbox"; |
boilerPlate.append(callback).append("=function() {") |
.append(" return ") |
.append(EXPORTED_SANDBOX_VARIABLE).append(";\n};"); |
boilerPlate.append(name).append(".sandboxGetter=new java.util.concurrent.Callable(function() {") |
.append(" return ") |
.append(EXPORTED_SANDBOX_VARIABLE).append("; });\n"); |
} |
PluginProperties checkForSupport(final URI uri) throws PluginException { |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/package-info.java |
---|
0,0 → 1,32 |
/** |
* This package contains the classes used to load JavaScript-based plugins. |
* See {@link http://p.outlyer.net./pluggablejs/} |
*/ |
// $Id$ |
package net.outlyer.plugins; |
/* |
* Copyright (c) 2008, Toni Corvera. All rights reserved. |
* |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED "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. |
*/ |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/PluginObject.java |
---|
26,8 → 26,8 |
// $Id$ |
import java.util.LinkedList; |
import java.util.List; |
import java.util.HashSet; |
import java.util.Set; |
/** |
* Default plugin object, adds a "features" field intended to describe |
35,22 → 35,33 |
* the script has been executed. |
*/ |
public class PluginObject extends BasePluginObject { |
public List features; |
/** |
* Plugins can describe which features they provide by adding |
* elements to this field. Possible values are application-specific. |
* Default: empty |
* @see #properties |
*/ |
public Set<String> features; |
{ |
features = new LinkedList(); |
features = new HashSet<String>(); |
} |
/** |
* Obtains the number of the current execution |
* @return Current execution number |
* @see Sandbox#getExecution() |
*/ |
public int execution() { |
return getSandbox().getExecution(); |
} |
/** |
* {@inheritDoc} |
*/ |
@Override public Object clone() throws CloneNotSupportedException { |
PluginObject po = (PluginObject) super.clone(); |
po.features = new LinkedList(); |
po.features.addAll(this.features); |
final PluginObject po = (PluginObject) super.clone(); |
po.features = new HashSet<String>(this.features); |
return po; |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/RuntimeHooks.java |
---|
27,9 → 27,9 |
// $Id$ |
/** |
* Provides hooks to the execution |
* Provides hooks to some points in the execution process. |
*/ |
public interface RuntimeHooks { |
interface RuntimeHooks { |
/** |
* Adds a callback to be executed after the script is finished |
*/ |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/EngineSink.java |
---|
0,0 → 1,118 |
package net.outlyer.plugins; |
/* |
* Copyright (c) 2008, Toni Corvera. All rights reserved. |
* |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED "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. |
*/ |
// $Id$ |
import java.io.PrintStream; |
import java.io.Reader; |
import javax.script.Bindings; |
import javax.script.ScriptContext; |
import javax.script.ScriptEngine; |
import javax.script.ScriptEngineFactory; |
import javax.script.ScriptException; |
/** |
* Intercepts ScriptEngine's methods and dumps their information to console. |
* @see SandboxImpl#getEngine |
*/ |
class EngineSink implements ScriptEngine { |
private final ScriptEngine e; |
private final PrintStream s; |
{ |
s = System.err; |
} |
public EngineSink(final ScriptEngine actualEngine) { |
e = actualEngine; |
} |
public Object eval(String script, ScriptContext context) throws ScriptException { |
return e.eval(script, context); |
} |
public Object eval(Reader reader, ScriptContext context) throws ScriptException { |
return e.eval(reader, context); |
} |
public Object eval(String script) throws ScriptException { |
s.printf("//[D] eval(String):\n%s//[EOS]\n", script); |
return e.eval(script); |
} |
public Object eval(Reader reader) throws ScriptException { |
return e.eval(reader); |
} |
public Object eval(String script, Bindings n) throws ScriptException { |
return e.eval(script, n); |
} |
public Object eval(Reader reader, Bindings n) throws ScriptException { |
return e.eval(reader, n); |
} |
public void put(String key, Object value) { |
s.printf("//[D] put('%s', %s)\n", key, value); |
e.put(key, value); |
} |
public Object get(String key) { |
return e.get(key); |
} |
public Bindings getBindings(int scope) { |
return e.getBindings(scope); |
} |
public void setBindings(Bindings bindings, int scope) { |
s.printf("//[D] setBindings() scope=%s:\n", |
(scope==ScriptContext.ENGINE_SCOPE) ? "ENGINE_SCOPE" : "GLOBAL_SCOPE"); |
for (final String key : bindings.keySet()) { |
s.printf(" %s=%s\n", key, bindings.get(key)); |
} |
s.println("//[EOL]"); |
e.setBindings(bindings, scope); |
} |
public Bindings createBindings() { |
return e.createBindings(); |
} |
public ScriptContext getContext() { |
return e.getContext(); |
} |
public void setContext(ScriptContext context) { |
e.setContext(context); |
} |
public ScriptEngineFactory getFactory() { |
throw new UnsupportedOperationException("Not supported yet."); |
} |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/Sandbox.java |
---|
26,59 → 26,69 |
// $Id$ |
import java.lang.reflect.Method; |
import java.util.Map; |
import javax.script.ScriptEngine; |
/** |
* Public methods provided by the sandbox. |
* Note that all methods in this interface imply a call to {@link #execute}. |
*/ |
// Note that all methods defined directly in this interface imply a call to {@link #execute}. |
public interface Sandbox extends RuntimeHooks, SandboxProperties { |
/** |
* @see #createDelayedImplementation(Class, boolean) |
* @deprecated (TEMPORARILY) Until the API stabilizes, |
* {@link #createDelayedImplementation(Class, String)} is the only |
* secure variant. |
*/ |
<T> T createDelayedImplementation(final Class<T> interfaceClass) throws PluginExecutionException; |
/** |
* Creates an implementation of an interface from JS code |
* @param interfaceClass Class of the interface to implement |
* @param allowPartialImplementation If true, the implementation would be used |
* even if incomplete (note that calling an unimplemented method will produce |
* an exception) |
* @return The implementation |
* @throws net.outlyer.plugins.sandboxing.PluginExecutionException |
* @deprecated (TEMPORARILY) Until the API stabilizes, |
* {@link #createDelayedImplementation(Class, String)} is the only |
* secure variant. |
*/ |
<T> T createDelayedImplementation(final Class<T> interfaceClass, |
boolean allowPartialImplementation) |
throws PluginExecutionException; |
/** |
* @see #createDelayedImplementation(Class, boolean) |
* @param fallbackObject If not null incomplete implementations are allowed and |
* calls to undefined methods will be passed to this object |
* @deprecated (TEMPORARILY) Until the API stabilizes, |
* {@link #createDelayedImplementation(Class, String)} is the only |
* secure variant. |
*/ |
<T> T createDelayedImplementation(final Class<T> interfaceClass, |
final T fallbackObject) |
throws PluginExecutionException; |
/** |
* Creates an implementation of an interface from a JS object |
* containing the interface's methods. |
* <br /> |
* <b>Note</b>: Implies a call to {@link #execute()}. |
* @param interfaceClass Interface to implement |
* @param objectName Name of the JS object implementing the interface |
* @return The implementation |
* @throws net.outlyer.plugins.PluginExecutionException |
* @see #createDelayedImplementation(Class,Map) |
* @see #execute() |
*/ |
<T> T createDelayedImplementation(final Class<T> interfaceClass, final String objectName) throws PluginExecutionException; |
/** |
* Creates an implementation of an interface from inline JS code. |
* This variant allows more flexibility (e.g. the implementation might |
* be split among objects, map to global functions or fallback to other |
* object if unimplemented). |
* Note the mapped code should be evaluable as a JS function AND |
* it shouldn't end in a semicolon, e.g.: |
* <br /> |
* Valid: |
* <pre> |
* function() { return SomeValue; } |
* </pre> |
* <pre> |
* // myFunc() is a function defined somewhere in the code |
* myFunc |
* </pre> |
* <br /> |
* Not valid: |
* <pre> |
* function() { return SomeValue; }; |
* </pre> |
* <pre> |
* SomeValue |
* </pre> |
* <br /> |
* Where possible {@link #createDelayedImplementation(Class,String)} is |
* a much cleaner option. |
* <br /> |
* <b>Note</b>: Implies a call to {@link #execute()}. |
* @param interfaceClass |
* @param methodToCodeMap |
* @return |
* @throws net.outlyer.plugins.PluginExecutionException |
* @see #createDelayedImplementation(Class,String) |
*/ |
<T> T createDelayedImplementation(final Class<T> interfaceClass, |
final Map<Method, String> methodToCodeMap) |
throws PluginExecutionException; |
/** |
* Run the script |
*/ |
void execute() throws PluginExecutionException; |
85,8 → 95,18 |
/** |
* Obtains object containing the plugin object |
* <br /> |
* <b>Note</b>: Implies a call to {@link #execute()}. |
* @see #getPluginName |
* @return Plugin object associated to this sandbox |
*/ |
BasePluginObject getPluginObject() throws PluginExecutionException; |
/** |
* Obtains a reference to the engine currently in use, if any. |
* If called during an execution it will return the current engine, from calling |
* outside will return null. |
* @return Current ScriptEngine |
*/ |
ScriptEngine getCurrentEngine(); |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/NamespaceContainer.java |
---|
0,0 → 1,59 |
package net.outlyer.plugins; |
/* |
* Copyright (c) 2008, Toni Corvera. All rights reserved. |
* |
* 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. |
* |
* THIS SOFTWARE IS PROVIDED "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. |
*/ |
// $Id$ |
/** |
* Provides the hierarchy exported to JavaScript as $net.outlyer.runtime. |
*/ |
class NamespaceContainer { |
public static class Runtime { |
public final Sandbox sandbox; |
public final PluginEnvironment pluginEnvironment; |
public Object internal = null; |
private Runtime(final PluginEnvironment pe, final Sandbox sandbox_) { |
sandbox = sandbox_; |
pluginEnvironment = pe; |
} |
} |
public static class Outlyer { |
public final Runtime runtime; |
private Outlyer(final PluginEnvironment pe, final Sandbox sandbox) { |
runtime = new Runtime(pe, sandbox); |
} |
} |
public static class $Net { |
public final Outlyer outlyer; |
$Net(final PluginEnvironment pe, final Sandbox sandbox) { |
outlyer = new Outlyer(pe, sandbox); |
} |
} |
} |
Property changes: |
Added: svn:keywords |
+Rev Id Date |
\ No newline at end of property |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/SandboxAccessorImpl.java |
---|
26,24 → 26,45 |
// $Id$ |
import java.lang.reflect.Field; |
import java.util.concurrent.Callable; |
/** |
* {@see SandboxAccessor} |
* Default implementation of a {@see SandboxAccessor}. |
*/ |
public abstract class SandboxAccessorImpl implements SandboxAccessor { |
public Callable<Sandbox> _getSandbox; |
protected final Sandbox getSandbox() { |
if (null == _getSandbox) { |
public Object sandboxGetter; |
public static Sandbox getSandbox(final SandboxAccessor sa) { |
try { |
// Retrieve the field... |
final Field gS = sa.getClass().getField("sandboxGetter"); |
// Evaluate as a callable |
return ((Callable<Sandbox>)gS.get(sa)).call(); |
} |
catch (final NoSuchFieldException e) { |
throw new IllegalStateException(sa.getClass()+" hasn't been correctly initialised", e); |
} |
catch (final Exception e) { |
throw new IllegalStateException("getSandbox produced an exception ("+ |
e.getClass().getSimpleName()+"),"+ |
"for object "+sa+" which is not expected!: " + |
e.getMessage(), e); |
} |
} |
public final Sandbox getSandbox() { |
if (null == sandboxGetter) { |
throw new IllegalStateException(getClass()+" hasn't been correctly initialised"); |
} |
try { |
return _getSandbox.call(); |
return ((Callable<Sandbox>)sandboxGetter).call(); |
} |
catch (final Exception e) { |
throw new IllegalStateException("getSandbox produced an exception, which is not expected!"); |
throw new IllegalStateException("sandboxGetter produced an " + |
"exception, which is not expected!"); |
} |
} |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/Shell.java |
---|
27,34 → 27,95 |
// $Id$ |
import java.io.File; |
import javax.script.ScriptEngine; |
import javax.script.ScriptException; |
import net.outlyer.plugins.utils.*; |
/** |
* Sample shell for tests, files passed as arguments will be loaded and |
* executed. |
* Sample shell for tests, or scripts. |
* Files passed as arguments will be loaded and executed. |
*/ |
public final class Shell { |
public static void interactive(final PluginEnvironment pe) { |
try { |
final Sandbox sandbox = pe.createSandbox(null); |
final ScriptEngine rhino = ((SandboxImpl) sandbox).executeAndGetEngine(); |
final Input in = new Input(); |
final Output out = new Output(); |
System.err.printf("pluggablejsShell v%d.%d.%d\n",API.VERSION,API.MIN_SUPPORTED_VERSION,API.REVISION); |
final boolean isWin = System.getProperty("os.name").toLowerCase().startsWith("windows"); |
// apparently windows doesn't get EOF until enter is pressed |
System.err.printf(" use EOF (%s) or break (CTRL+C) to exit\n", (isWin)?"CTRL+Z,Enter":"CTRL+D"); |
try { |
// undocumented feature: when |
// $net.outlyer.runtime.internal.shell is true an extra println() is |
// issued after each eval |
rhino.eval("$net.outlyer.runtime.internal.shell = { autoEOL: false, };"); |
} |
catch (final ScriptException e) { |
e.printStackTrace(); |
} |
while (true) { |
out.print("pjsh% : "); |
final String line = in.readline(); |
if (null == line) { |
break; |
} |
try { |
rhino.eval(line); |
final boolean eol = (Boolean) rhino.eval("$net.outlyer.runtime.internal.shell.autoEOL"); |
if (eol) { |
out.println(); |
} |
} |
catch (final ScriptException e) { |
System.err.println("//[E] Error: " + e.getMessage()); |
} |
} |
// EOF |
System.exit(0); |
} |
catch (final PluginException e) { |
System.err.printf("Exception while running on-the-fly " + |
"interpreted script", e.getLocalizedMessage()); |
System.exit(5); |
} |
} |
public static void main(String...args) { |
if (args.length == 0) { |
System.err.printf("Usage: java [...] %s [file1.js [file2.js [...]]]\n", |
System.err.printf("Usage: \n" + |
" java [...] %s <file1.js [file2.js [...]]>\n"+ |
" or\n"+ |
" java [...] %1$s <-i|--interactive>\n", |
Shell.class.getName()); |
System.exit(1); |
} |
final PluginEnvironment pe = new PluginEnvironment(); |
pe.exportGlobalObject("input", new Input()); |
pe.exportGlobalObject("out", new Output()); |
pe.exportGlobalObject("err", new Output(System.err)); |
pe.exportGlobalObject("hooks", new Hooks()); |
pe.exportObject("input", new Input()); |
pe.exportObject("out", new Output()); |
pe.exportObject("err", new Output(System.err)); |
pe.exportObject("hooks", new Hooks()); |
// Generally either UI or GUI should be used but not both |
// since GUI falls-back to UI if appropriate (so the ui object |
// will use the "best" implementation) |
pe.exportGlobalObject("ui", new GUI()); |
pe.exportGlobalObject("gui", new GUI()); |
pe.exportGlobalObject("cui", new UI()); // cui stands for Console UI |
pe.exportObject("ui", new GUI()); |
pe.exportObject("gui", new GUI()); |
pe.exportObject("cui", new UI()); // cui stands for Console UI |
pe.exportObject("lang", new LanguageExtensions()); |
final PluginObject po = new PluginObject(); |
pe.setPluginObject(po); |
if (args.length == 1 && (args[0].equals("-i") || args[0].equals("--interactive"))) { |
interactive(pe); |
assert false; |
} |
// Non-interactive mode |
for (final String path : args) { |
try { |
final Sandbox sandbox = pe.createSandbox(new File(path).toURI()); |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/API.java |
---|
27,34 → 27,51 |
// $Id$ |
/** |
* API versioning |
* API versioning. |
* |
* Versioning is inspired by libtool's rules |
* {@link http://www.gnu.org/software/libtool/manual.html#Versioning}, i.e. |
* <pre> |
* API version = "current" |
* API revvision = "revision" |
* API min_supported_revision = "current" - "age" |
* </pre> |
* |
* Release numbers are meaningful and composed like: |
* <code>Version.MinSupported.Revision.build#</code>, |
* so, e.g. release 2.3.0 won't valid since MinSupported can't be |
* greater than version. |
*/ |
public final class API { |
// Release will be REVISION.(IMPLEMENTATION-1) i.e. |
// for REVISION=1 and IMPLEMENTATION=1, release would be 1.0 |
/** |
* API Revision. |
* Newer revisions might break backwards compatibility (see |
* {@link #MIN_SUPPORTED_REVISION}). |
* API Version. |
* Newer versions might break backwards compatibility. |
*/ |
public static final int REVISION = 1; |
public static final int VERSION = 1; |
/** |
* Implementation of the API revision. |
* Different implementations of an API are guaranteed to maintain |
* the API unchanged. |
* @see #REVISION |
* Revision of the API Version. |
* A newer revision might add features but not change the meaning of |
* existing ones neither remove any of them. |
* @see #VERSION |
*/ |
public static final int IMPLEMENTATION = 1; |
public static final int REVISION = 0; |
/** |
* Minimum API revision supported by this implementation. |
* Number of previous Versions supported. |
* For future use. |
* {@see #VERSION} |
* {@see #MIN_SUPPORTED_VERSION} |
*/ |
public static final int AGE = 0; |
/** |
* Minimum API version supported by this implementation. |
* For future use. |
* In principle a new API should provide support for previous revisions, |
* in case it doesn't, this constant will hold the lower supported API. |
* @see #REVISION |
* @pre MIN_SUPPORTED_REVISION <= REVISION |
* @see #VERSION |
* @pre MIN_SUPPORTED_VERSION <= VERSION |
*/ |
public static final int MIN_SUPPORTED_REVISION = 1; |
public static final int MIN_SUPPORTED_VERSION = VERSION - AGE; |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/PluginProperties.java |
---|
30,9 → 30,32 |
* Common properties of the plugin object. All of them |
* take sane defaults. |
*/ |
public class PluginProperties extends SandboxAccessorImpl { |
class PluginProperties extends SandboxAccessorImpl { |
/** |
* pluggablejs Api Version against which this plugin was written. |
* Default: 1 |
* @see API#VERSION |
*/ |
public int apiVersion = 1; |
/** |
* Type of this plugin, application-specific. |
* Default: "" (no type) |
* @see PluginLocator#getAll() |
* @see PluginLocator#getFirst() |
*/ |
public String type; |
/** |
* Name of this plugin. |
* Default: same as filename (including extension but not path) |
* @see PluginLocator#getAll() |
* @see PluginLocator#getFirst() |
*/ |
public String name; |
/** |
* @deprecated The use of this field is yet to be defined |
*/ |
public Object properties; // FIXME: To be defined |
} |
/pluggablejs/branches/1.1.0build33/src/net/outlyer/plugins/SandboxProperties.java |
---|
29,10 → 29,10 |
import java.net.URI; |
/** |
* Provides access to some properties of a sandbox |
* Provides access to properties of a sandbox |
* @see Sandbox |
*/ |
public interface SandboxProperties { |
interface SandboxProperties { |
/** |
* Obtains the number of this execution (each time the script is executed |