Subversion Repositories pub

Compare Revisions

No changes between revisions

Ignore whitespace Rev 61 → Rev 59

/pluggablejs/trunk/ChangeLog
File deleted
/pluggablejs/trunk/build.num
1,3 → 1,3
#Build Number for ANT. Do not edit!
#Mon Jun 30 20:53:31 CEST 2008
build.number=33
#Sun Jun 29 04:30:52 CEST 2008
build.number=29
/pluggablejs/trunk/debian/changelog
1,9 → 1,3
pluggablejs (1.1.0build33-upstream.0) experimental; urgency=low
 
* New release.
 
-- Toni Corvera <outlyer@gmail.com> Tue, 01 Jul 2008 02:20:32 +0200
 
pluggablejs (1.0build29-upstream.0) experimental; urgency=low
 
* Initial public release.
/pluggablejs/trunk/debian/rules
67,7 → 67,7
binary-arch: build install
dh_testdir
dh_testroot
dh_installchangelogs ChangeLog
dh_installchangelogs
dh_installdocs
dh_installexamples
# dh_install
/pluggablejs/trunk/debian/docs
File deleted
/pluggablejs/trunk/debian/README.Debian
1,7 → 1,6
libpluggablejs-java for Debian
------------------------------
 
This is debian package created from the upstream source, it might
not follow the Debian policy correctly.
<possible notes regarding this package - if none, delete this file>
 
-- Toni Corvera <outlyer@gmail.com> Thu, 26 Jun 2008 02:54:11 +0200
/pluggablejs/trunk/src/net/outlyer/plugins/EngineSink.java
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/pluggablejs/trunk/src/net/outlyer/plugins/package-info.java
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/pluggablejs/trunk/src/net/outlyer/plugins/NamespaceContainer.java
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/pluggablejs/trunk/src/net/outlyer/plugins/Shell.java
27,95 → 27,34
// $Id$
 
import java.io.File;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import net.outlyer.plugins.utils.*;
 
/**
* Sample shell for tests, or scripts.
* Files passed as arguments will be loaded and executed.
* Sample shell for tests, 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: \n" +
" java [...] %s <file1.js [file2.js [...]]>\n"+
" or\n"+
" java [...] %1$s <-i|--interactive>\n",
System.err.printf("Usage: java [...] %s [file1.js [file2.js [...]]]\n",
Shell.class.getName());
System.exit(1);
}
 
final PluginEnvironment pe = new PluginEnvironment();
pe.exportObject("input", new Input());
pe.exportObject("out", new Output());
pe.exportObject("err", new Output(System.err));
pe.exportObject("hooks", new Hooks());
pe.exportGlobalObject("input", new Input());
pe.exportGlobalObject("out", new Output());
pe.exportGlobalObject("err", new Output(System.err));
pe.exportGlobalObject("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.exportObject("ui", new GUI());
pe.exportObject("gui", new GUI());
pe.exportObject("cui", new UI()); // cui stands for Console UI
pe.exportObject("lang", new LanguageExtensions());
pe.exportGlobalObject("ui", new GUI());
pe.exportGlobalObject("gui", new GUI());
pe.exportGlobalObject("cui", new UI()); // cui stands for Console UI
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/trunk/src/net/outlyer/plugins/SandboxImpl.java
26,9 → 26,6
 
// $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;
53,24 → 50,11
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 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");
}
{
78,19 → 62,16
hooks.put(END_KEY, new LinkedList<Runnable>());
}
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, final PluginProperties pp) {
this(plugin, bs, pp, null);
}
protected SandboxImpl(final PluginEnvironment pe, final URI plugin,
final Bindings bs,
protected SandboxImpl(final URI plugin, final Bindings bs,
final PluginProperties pp, final String boilerPlate) {
bindings = bs;
pluginUri = plugin; // if null => sandbox without file
pluginUri = plugin;
this.boilerPlate = boilerPlate;
properties = pp;
pE = pe;
}
public void addEndHook(Runnable r) {
101,7 → 82,7
* Namespace for variables used by the enviroment
*/
private static String namespace() {
return "$net.outlyer.runtime.internal";
return "$net.outlyer.runtime";
}
 
/**
120,34 → 101,44
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() {
final ScriptEngine se = new ScriptEngineManager().getEngineByName("rhino");
if (debug) {
return new EngineSink(se);
}
return se;
return new ScriptEngineManager().getEngineByName("rhino");
}
 
private ExecResult execute(final String prependText,
private ScriptEngine 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) {
bindings.putAll(extraBindings);
for (final String key : extraBindings.keySet()) {
bindings.put(key, extraBindings.get(key));
}
}
// 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");
 
Object result = null;
try {
rhino.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
 
156,27 → 147,15
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);
}
if (null != pluginUri) {
result = rhino.eval(new PluginReader(pluginUri));
}
else {
result = null;
}
rhino.eval(new PluginReader(pluginUri));
if (null != appendText) {
rhino.eval(appendText);
193,42 → 172,24
catch (final ScriptException e) {
throw new PluginExecutionException("Script exception: " + e.getMessage(), e);
}
finally {
currentEngine = null;
}
return new ExecResult(rhino, result);
return rhino;
}
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 {
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 String varImpl = uniqueVarName();
 
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() == Object.class) { // Implement everything, including hierarchies
if (method.getDeclaringClass() != c) {
continue;
}
preCode.append(method.getName()).append(": null,\n");
235,104 → 196,104
}
preCode.append("};\n");
 
final ScriptEngine rhino = execute(preCode.toString(), null, null).rhino;
try {
final StringBuilder retriever = new StringBuilder();
retriever.append("new ")
// 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 ")
.append(c.getCanonicalName()).append("(").append(objectName).append(");");
if (debug) {
retriever.append("// <== Actual delayed implementation creation");
}
 
// 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 ScriptEngine rhino = execute(preCode.toString(), postCode.toString(), null);
 
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);
}
assert (c.isInstance(rhino.get(varImpl)));
return (T) rhino.get(varImpl);
}
 
/**
* {@inheritDoc}
*/
public <T> T createDelayedImplementation(final Class<T> c,
final Map<Method, String> map)
public <T> T createDelayedImplementation(final Class<T> c)
throws PluginExecutionException {
if (null == c) {
throw new IllegalArgumentException("Can't instantiate null");
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 (!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");
}
return createDelayedImplementation(interfaceClass, fallbackObject, true);
}
 
final StringBuffer preCode = new StringBuffer();
private <T> T createDelayedImplementation(final Class<T> c,
final T fallback,
boolean allowPartial)
throws PluginExecutionException {
final StringBuilder hack = new StringBuilder();
 
final String var = uniqueFQVarName();
preCode.append(var).append(" = {\n");
for (final Method method : map.keySet()) {
preCode.append(method.getName()).append(": ")
.append(map.get(method))
.append("\n,\n");
// 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("};\n");
hack.append("};\nvar ")
.append(varImpl).append("=new ")
.append(c.getCanonicalName()).append("(").append(var).append(");");
final ScriptEngine rhino = execute(preCode.toString(), null, null).rhino;
script.append(hack.toString());
try {
final StringBuilder retriever = new StringBuilder();
retriever.append("new ")
.append(c.getCanonicalName()).append("(").append(var).append(");");
if (debug) {
retriever.append("// <== Actual delayed implementation creation");
}
final HashMap<String, Object> bn = new HashMap();
bn.put(varFallback, fallback);
 
// 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
if (null != fallback) {
script.append("\n// " + bn.get(varFallback).getClass());
}
//System.err.println(script.toString());
 
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);
}
final ScriptEngine rhino = execute(null, script.toString(), bn);
assert (c.isInstance(rhino.get(varImpl)));
return (T) rhino.get(varImpl);
}
 
public BasePluginObject getPluginObject() throws PluginExecutionException {
362,8 → 323,6
public URI loadedFrom() {
return pluginUri.normalize(); // FIXME: Return a copy
}
 
public ScriptEngine getCurrentEngine() {
return currentEngine;
}
}
/pluggablejs/trunk/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.VERSION;
private int maxApi=API.REVISION;
// 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,11 → 71,6
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");
}
 
////////////////////////////////////////////////////////
91,17 → 86,7
public Sandbox createSandbox(final URI forUri) throws PluginException {
final ScriptEngine rhino = new ScriptEngineManager().getEngineByName("rhino");
PluginProperties pp;
if (null != forUri) {
pp = checkForSupport(forUri);
}
else {
pp = new PluginProperties();
pp.apiVersion = API.VERSION;
pp.name = "none";
pp.type = "";
}
final PluginProperties pp = checkForSupport(forUri);
 
if (null == pluginObject) {
pluginObject = new PluginObject();
116,9 → 101,9
bindings.put(objectName, exportedObjects.get(objectName));
}
bindings.put("plugin", po);
//enableSandboxAccess("plugin", po);
enableSandboxAccess("plugin", po);
 
return new SandboxImpl(this, forUri, bindings, pp, boilerPlate.toString());
return new SandboxImpl(forUri, bindings, pp, boilerPlate.toString());
}
catch (final CloneNotSupportedException e) {
assert false;
132,17 → 117,12
* 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 exportObject(final String name, final Object object) {
public void exportGlobalObject(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");
153,39 → 133,8
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.
220,24 → 169,9
// Non-public interface
////////////////////////////////////////////////////////
 
/**
* Makes a {@link SanboxAccessor}'s field <code>getSandbox</code>
* be evaluable as a <code>Callable&lt;Sandbox&gt;</code>, i.e.,
* from Java it will be possible to use:<br />
* <pre>
* Sandbox sb = ( (Callable&lt;Sandbox&gt;) 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("sandboxGetter");
Field f = sa.getClass().getField("_getSandbox");
}
catch (final NoSuchFieldException e) {
throw new IllegalArgumentException("object " + name + " must comply " +
244,9 → 178,12
"with the contract of SandboxAccessor");
}
 
boilerPlate.append(name).append(".sandboxGetter=new java.util.concurrent.Callable(function() {")
.append(" return ")
.append(EXPORTED_SANDBOX_VARIABLE).append("; });\n");
final String callback = name + "._getSandbox";
 
boilerPlate.append(callback).append("=function() {")
.append(" return ")
.append(EXPORTED_SANDBOX_VARIABLE).append(";\n};");
 
}
PluginProperties checkForSupport(final URI uri) throws PluginException {
/pluggablejs/trunk/src/net/outlyer/plugins/PluginObject.java
26,8 → 26,8
 
// $Id$
 
import java.util.HashSet;
import java.util.Set;
import java.util.LinkedList;
import java.util.List;
 
/**
* Default plugin object, adds a "features" field intended to describe
35,33 → 35,22
* the script has been executed.
*/
public class PluginObject extends BasePluginObject {
/**
* 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;
public List features;
 
{
features = new HashSet<String>();
features = new LinkedList();
}
 
/**
* 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 {
final PluginObject po = (PluginObject) super.clone();
po.features = new HashSet<String>(this.features);
PluginObject po = (PluginObject) super.clone();
po.features = new LinkedList();
po.features.addAll(this.features);
return po;
}
/pluggablejs/trunk/src/net/outlyer/plugins/RuntimeHooks.java
27,9 → 27,9
// $Id$
 
/**
* Provides hooks to some points in the execution process.
* Provides hooks to the execution
*/
interface RuntimeHooks {
public interface RuntimeHooks {
/**
* Adds a callback to be executed after the script is finished
*/
/pluggablejs/trunk/src/net/outlyer/plugins/Sandbox.java
26,69 → 26,59
 
// $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;
95,18 → 85,8
 
/**
* 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/trunk/src/net/outlyer/plugins/SandboxAccessorImpl.java
26,45 → 26,24
 
// $Id$
 
import java.lang.reflect.Field;
import java.util.concurrent.Callable;
 
/**
* Default implementation of a {@see SandboxAccessor}.
* {@see SandboxAccessor}
*/
public abstract class SandboxAccessorImpl implements SandboxAccessor {
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) {
public Callable<Sandbox> _getSandbox;
protected final Sandbox getSandbox() {
if (null == _getSandbox) {
throw new IllegalStateException(getClass()+" hasn't been correctly initialised");
}
try {
return ((Callable<Sandbox>)sandboxGetter).call();
return _getSandbox.call();
}
catch (final Exception e) {
throw new IllegalStateException("sandboxGetter produced an " +
"exception, which is not expected!");
throw new IllegalStateException("getSandbox produced an exception, which is not expected!");
}
 
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/API.java
27,51 → 27,34
// $Id$
 
/**
* 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.
* API versioning
*/
public final class API {
// Release will be REVISION.(IMPLEMENTATION-1) i.e.
// for REVISION=1 and IMPLEMENTATION=1, release would be 1.0
 
/**
* API Version.
* Newer versions might break backwards compatibility.
* API Revision.
* Newer revisions might break backwards compatibility (see
* {@link #MIN_SUPPORTED_REVISION}).
*/
public static final int VERSION = 1;
public static final int REVISION = 1;
 
/**
* 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
* Implementation of the API revision.
* Different implementations of an API are guaranteed to maintain
* the API unchanged.
* @see #REVISION
*/
public static final int REVISION = 0;
public static final int IMPLEMENTATION = 1;
 
/**
* Number of previous Versions supported.
* Minimum API revision supported by this implementation.
* 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 #VERSION
* @pre MIN_SUPPORTED_VERSION &lt;= VERSION
* @see #REVISION
* @pre MIN_SUPPORTED_REVISION &lt;= REVISION
*/
public static final int MIN_SUPPORTED_VERSION = VERSION - AGE;
public static final int MIN_SUPPORTED_REVISION = 1;
}
/pluggablejs/trunk/src/net/outlyer/plugins/PluginProperties.java
30,32 → 30,9
* Common properties of the plugin object. All of them
* take sane defaults.
*/
class PluginProperties extends SandboxAccessorImpl {
/**
* pluggablejs Api Version against which this plugin was written.
* Default: 1
* @see API#VERSION
*/
public class PluginProperties extends SandboxAccessorImpl {
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/trunk/src/net/outlyer/plugins/SandboxProperties.java
29,10 → 29,10
import java.net.URI;
 
/**
* Provides access to properties of a sandbox
* Provides access to some properties of a sandbox
* @see Sandbox
*/
interface SandboxProperties {
public interface SandboxProperties {
/**
* Obtains the number of this execution (each time the script is executed
/pluggablejs/trunk/src/net/outlyer/plugins/utils/LanguageExtensions.java
File deleted
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/pluggablejs/trunk/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.PluginEnvironment#exportObject}.
* {@see net.outlyer.plugins.sandboxing.PluginEnvironment#exportGlobalObject}.
*/
 
// $Id$
/pluggablejs/trunk/src/net/outlyer/plugins/utils/Output.java
42,9 → 42,6
public Output(final PrintStream ps) {
this.ps = ps;
}
 
// Note rhino already provides print() and println() in the global
// namespace
public void println() {
ps.println();
64,10 → 61,6
}
public void printf(final String s) {
print(s);
ps.printf("%s", s);
}
 
public void print(final String s) {
ps.print(s);
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/SandboxAccessor.java
32,28 → 32,12
* it enables exported objects to retrieve the sandbox object in which they're
* being executed.
*
* Implementors must contain a field like
* <pre>
* public Object sandboxGetter = null;
* </pre>
* Implementors must contain a field exactly:
*
* sandboxGetter MUST be public, it can be of class Object or
* Callable&lt;Sandbox&gt;, 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.
*
* public Callable<Sandbox> _getSandbox = null;
*
* 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/trunk/Makefile
1,32 → 1,26
# $Id$
 
prefix:=/usr/local
DESTDIR:=
JAR=dist/pluggablejs.jar
 
buildnum=$(shell grep 'build.number' build.num | cut -d'=' -f2)
version=$(shell grep '<property name="version"' build.xml | sed -r 's/.*value="([^"]*)".*/\1/')
jarbasename=pluggablejs-$(version)-build$(buildnum).jar
 
bindir=$(DESTDIR)/$(prefix)/bin
 
all:
ant compile.downstream
 
dist/$(jarbasename):
$(JAR):
ant dist.downstream
 
clean:
ant clean
 
install: dist/$(jarbasename)
test 0 -eq `id -u` # Must be root (or use fakeroot)
install: $(JAR)
mkdir -p $(DESTDIR)/$(prefix)/share/java $(bindir)/
install -m644 -o0 -g0 dist/$(jarbasename) $(DESTDIR)/$(prefix)/share/java/
cd $(DESTDIR)/$(prefix)/share/java && ln -s $(jarbasename) pluggablejs.jar
install -m644 -o0 -g0 $(JAR) $(DESTDIR)/$(prefix)/share/java/
cat pjsh.sh | sed 's\^prefix=.*$$\prefix=$(prefix)\g' > $(bindir)/pjsh
chmod 755 $(bindir)/pjsh
-chown root.root $(bindir)/pjsh
 
uninstall:
$(RM) $(DESTDIR)/$(prefix)/share/java/$(jarbasename)
$(RM) $(DESTDIR)/$(prefix)/share/java/`basename "$(JAR)"`
-rmdir -p $(DESTDIR)/$(prefix)/share/java
Property changes:
Deleted: svn:keywords
-Rev Id Date
\ No newline at end of property
/pluggablejs/trunk/samples/interactive-call.js
File deleted
/pluggablejs/trunk/samples/langext-included.js
File deleted
/pluggablejs/trunk/samples/langext.js
File deleted
/pluggablejs/trunk/build.xml
9,8 → 9,7
<property name="release" location="release"/>
<property name="depends" location="lib"/>
 
<property name="version" value="1.1.0"/>
<property name="jarbasename" value="pluggablejs-${version}-build" /><!-- followed by build.number, can't resolve yet -->
<property name="version" value="1.0"/>
 
<target name="init">
<!-- Create timestamp, sets TODAY (full, human), DSTAMP (yyyymmdd) and
42,7 → 41,7
<target name="dist.real" description="Generate the distribution">
<mkdir dir="${dist}/"/>
 
<jar jarfile="${dist}/${jarbasename}${build.number}.jar" basedir="${build}">
<jar jarfile="${dist}/pluggablejs.jar" basedir="${build}">
<!-- <classpath refid="${third.party.libs}" /> -->
<manifest>
<attribute name="X-COMMENT" value="Standard build" />
55,7 → 54,7
-->
</manifest>
</jar>
<jar destfile="${dist}/${jarbasename}${build.number}-src.jar" basedir="${src}"/>
<jar destfile="${dist}/pluggablejs-src.jar" basedir="${src}"/>
</target>
 
<target name="dist.downstream" depends="compile.downstream, dist.real" />