Subversion Repositories pub

Compare Revisions

No changes between revisions

Ignore whitespace Rev 58 → Rev 59

/pluggablejs/trunk/build.num
1,3 → 1,3
#Build Number for ANT. Do not edit!
#Fri Jun 27 22:16:02 CEST 2008
build.number=28
#Sun Jun 29 04:30:52 CEST 2008
build.number=29
/pluggablejs/trunk/pjsh.sh
1,6 → 1,7
#!/bin/sh
 
prefix=/usr/local
JAR="$prefix/pluggablejs.jar"
installpath="$prefix/share/java"
JAR="$installpath/pluggablejs.jar"
 
java -cp "$JAR" net.outlyer.plugins.Shell $@
/pluggablejs/trunk/debian/changelog
1,6 → 1,6
pluggablejs (0.0build28-upstream.0) experimental; urgency=low
pluggablejs (1.0build29-upstream.0) experimental; urgency=low
 
* Initial public release.
 
-- Toni Corvera <outlyer@gmail.com> Sun, 29 Jun 2008 02:00:42 +0200
-- Toni Corvera <outlyer@gmail.com> Sun, 29 Jun 2008 04:31:28 +0200
 
/pluggablejs/trunk/src/net/outlyer/plugins/PluginEnvironmentImpl.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
41,7 → 41,7
System.exit(1);
}
final PluginEnvironment pe = PluginEnvironment.create();
final PluginEnvironment pe = new PluginEnvironment();
pe.exportGlobalObject("input", new Input());
pe.exportGlobalObject("out", new Output());
pe.exportGlobalObject("err", new Output(System.err));
/pluggablejs/trunk/src/net/outlyer/plugins/SandboxImpl.java
26,8 → 26,6
 
// $Id$
 
import net.outlyer.plugins.Sandbox;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
40,8 → 38,6
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import net.outlyer.plugins.BasePluginObject;
import net.outlyer.plugins.PluginProperties;
 
/**
* Implementation of the Sandbox interface
110,7 → 106,9
return new ScriptEngineManager().getEngineByName("rhino");
}
private ScriptEngine execute(boolean appendText, String text, final Map<String,Object> extraBindings)
private ScriptEngine execute(final String prependText,
final String appendText,
final Map<String,Object> extraBindings)
throws PluginExecutionException {
final ScriptEngine rhino = getEngine();
153,10 → 151,14
// Create the $net.outlyer.runtime pseudo-namespace...
rhino.eval(runtime.toString());
 
if (null != prependText) {
rhino.eval(prependText);
}
rhino.eval(new PluginReader(pluginUri));
if (appendText) {
rhino.eval(text);
if (null != appendText) {
rhino.eval(appendText);
}
// Execute any end hooks
175,31 → 177,36
}
public void execute() throws PluginExecutionException {
execute(false, null, null);
execute(null, null, null);
}
 
public <T> T createDelayedImplementation(final Class<T> c,
final String objectName)
throws PluginExecutionException {
System.err.println("createDelayedImple");
final String varImpl = uniqueVarName();
 
final StringBuilder code = new StringBuilder();
final StringBuilder preCode = new StringBuilder();
 
code.append("var ").append(objectName).append(" = {\n");
preCode.append("var ").append(objectName).append(" = {\n");
for (final Method method : c.getMethods()) {
if (method.getDeclaringClass() != c) {
continue;
}
code.append(method.getName()).append(": null,");
preCode.append(method.getName()).append(": null,\n");
}
code.append("};\n");
preCode.append("};\n");
 
code.append(varImpl).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(");");
System.err.println(code.toString());
 
final ScriptEngine rhino = execute(true, code.toString(), null);
final ScriptEngine rhino = execute(preCode.toString(), postCode.toString(), null);
 
assert (c.isInstance(rhino.get(varImpl)));
return (T) rhino.get(varImpl);
284,7 → 291,7
}
//System.err.println(script.toString());
 
final ScriptEngine rhino = execute(true, script.toString(), bn);
final ScriptEngine rhino = execute(null, script.toString(), bn);
assert (c.isInstance(rhino.get(varImpl)));
return (T) rhino.get(varImpl);
}
304,7 → 311,8
*/
public String getPluginName() {
if (null == properties.name) {
return new File(loadedFrom().getPath()).getName(); // FIXME: <= Is this safe?
final String[] elements = loadedFrom().toString().split("/");
return elements[elements.length-1];
}
return properties.name;
}
/pluggablejs/trunk/src/net/outlyer/plugins/PluginLocator.java
31,6 → 31,7
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
77,14 → 78,14
unique = new HashSet<Sandbox>();
}
void add(final String fileName, final BasePluginObject bpo, final Sandbox sbox) {
byName.put(bpo.name, sbox);
void add(final String fileName, final PluginProperties pp, final Sandbox sbox) {
byName.put(pp.name, sbox);
byFilename.put(fileName, sbox);
if (byType.get(bpo.type) == null) {
byType.put(bpo.type, new LinkedList<Sandbox>());
if (byType.get(pp.type) == null) {
byType.put(pp.type, new LinkedList<Sandbox>());
}
byType.get(bpo.type).add(sbox);
byType.get(pp.type).add(sbox);
unique.add(sbox);
}
275,18 → 276,21
for (final File candidateFile : dir.listFiles(jsFileFilter)) {
final URI fileURI = candidateFile.toURI();
 
try {
final Sandbox sb = environment.createSandbox(fileURI);
final BasePluginObject bpo = sb.getPluginObject();
final PluginProperties pp = PluginEnvironment.fetchPluginProperties(fileURI);
if (null == pp) {
continue;
}
if (bpo.name == null) {
bpo.name = candidateFile.getName();
if (null == pp.name) {
pp.name = candidateFile.getName();
}
library.add(candidateFile.getName(), bpo, sb);
library.add(candidateFile.getName(), pp, sb);
}
catch (final Exception e) {
catch (final PluginException e) {
continue;
}
}
322,16 → 326,22
final URI pluginUri = new URI("jar:"+jarFile+"!"+url);
 
final Sandbox sb = environment.createSandbox(pluginUri);
final BasePluginObject bpo = sb.getPluginObject();
final String fileName = new File(jarURIElements[1]).getName();
if (bpo.name == null) {
bpo.name = fileName;
final PluginProperties pp = PluginEnvironment.fetchPluginProperties(jarUri);
// JarEntry's getName() returns the full path
final String fileName = new File(je.getName()).getName();
if (pp.name == null) {
pp.name = fileName;
}
library.add(fileName, bpo, sb);
library.add(fileName, pp, sb);
}
catch (final Exception e) {
catch (final PluginException e) {
continue;
}
catch (final URISyntaxException e) {
assert false;
throw new IllegalStateException("Unexpected malformed " +
"jar URI, implementation error (?)");
}
}
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/PluginEnvironment.java
26,12 → 26,57
 
// $Id$
 
import java.io.IOException;
import java.io.LineNumberReader;
import java.lang.reflect.Field;
import java.net.URI;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
 
/**
* Provides the environment for {@link net.outlyer.plugins.Sandbox}es.
*/
public abstract class PluginEnvironment {
public class PluginEnvironment {
static class UnsupportedAPIException extends PluginException {
public UnsupportedAPIException(int providedApi) {
super("Plugin has unsupported API ("+providedApi+")");
}
}
static class UnsupportedTypeException extends PluginException {
public UnsupportedTypeException(String pluginType) {
super("Plugin has unsupported type ("+pluginType+")");
}
}
private final Map<String, Object> exportedObjects;
private BasePluginObject pluginObject;
private StringBuilder boilerPlate;
private List<String> acceptedTypes = null;
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;
 
static final String EXPORTED_SANDBOX_VARIABLE = "$net.outlyer.runtime.sandbox";
{
exportedObjects = new HashMap();
pluginObject = null;
boilerPlate = new StringBuilder();
}
 
////////////////////////////////////////////////////////
// Public interface
////////////////////////////////////////////////////////
 
/**
* Creates a sandbox associated to the script found in uri
* @param uri Plugin uri
38,8 → 83,34
* @return Sandbox associated to uri
* @throws net.outlyer.plugins.PluginException
*/
abstract public Sandbox createSandbox(final URI uri) throws PluginException;
public Sandbox createSandbox(final URI forUri) throws PluginException {
final ScriptEngine rhino = new ScriptEngineManager().getEngineByName("rhino");
final PluginProperties pp = checkForSupport(forUri);
 
if (null == pluginObject) {
pluginObject = new PluginObject();
}
 
try {
final PluginObject po = (PluginObject) pluginObject.clone();
 
final Bindings bindings = rhino.createBindings();
 
for (final String objectName : exportedObjects.keySet()) {
bindings.put(objectName, exportedObjects.get(objectName));
}
bindings.put("plugin", po);
enableSandboxAccess("plugin", po);
 
return new SandboxImpl(forUri, bindings, pp, boilerPlate.toString());
}
catch (final CloneNotSupportedException e) {
assert false;
throw new IllegalStateException("Incorrect implementation");
}
}
 
/**
* Makes an object accessible from the plugin.
* Note the object will be shared amongst any sandboxes created after
47,17 → 118,23
* @param name Name with which to access the object
* @param object Object to export
*/
abstract public void exportGlobalObject(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");
}
if (name.equals("plugin")) {
throw new IllegalArgumentException("Can't export an object named " +
"plugin, use setPluginObject() instead");
}
exportedObjects.put(name, object);
if (object instanceof SandboxAccessor) {
enableSandboxAccess(name, (SandboxAccessor)object);
}
}
/**
* Restricts the set of supported plugins to the ones with the given plugin
* type (by default no restriction is applied).
* @see PluginProperties#type
* @param pluginTypes Set of types to allow; use null to allow all types
*/
abstract public void setAcceptedTypes(final String... pluginTypes);
 
/**
* 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.
66,9 → 143,147
* shared amongst sandboxes, a clone of the one provided will be used.
* @param pluginObject Object to export, set to null to use a default one.
*/
abstract public void setPluginObject(final BasePluginObject pluginObject);
public void setPluginObject(BasePluginObject pObject) {
pluginObject = pObject;
}
 
public static PluginEnvironment create() {
return new PluginEnvironmentImpl();
}
/**
* Restricts the set of supported plugins to the ones with the given plugin
* type (by default no restriction is applied).
* @see PluginProperties#type
* @param pluginTypes Set of types to allow; use null to allow all types
*/
public void setAcceptedTypes(final String ... pluginTypes) {
if (null != pluginTypes) {
for (final String type : pluginTypes) {
if (null == type) {
throw new IllegalArgumentException("The set of plugin types" +
" must be either null, or a set of non-null values");
}
}
}
acceptedTypes = Arrays.asList(pluginTypes);
}
 
////////////////////////////////////////////////////////
// Non-public interface
////////////////////////////////////////////////////////
 
private void enableSandboxAccess(final String name, final SandboxAccessor sa) {
try {
Field f = sa.getClass().getField("_getSandbox");
}
catch (final NoSuchFieldException e) {
throw new IllegalArgumentException("object " + name + " must comply " +
"with the contract of SandboxAccessor");
}
 
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 {
assert null != uri;
 
final PluginProperties pp = fetchPluginProperties(uri);
if (null == pp) {
throw new PluginExecutionException("Failed while inspecting plugin "+uri);
}
 
if (null != acceptedTypes) {
if (!acceptedTypes.contains(pp.type)) {
throw new UnsupportedTypeException(pp.type);
}
}
if (pp.apiVersion > maxApi) {
throw new UnsupportedAPIException(pp.apiVersion);
}
return pp;
}
 
// TODO: ? Possibly cache this, so that the first line is only eval'ed
// once more than the # of executions
static PluginProperties fetchPluginProperties(final URI uri) {
assert null != uri;
 
final ScriptEngine rhino = new ScriptEngineManager().getEngineByName("rhino");
 
try {
// Define a Plugin object with a field named type
rhino.eval("var plugin={ type: null, apiVersion: null };");
}
catch (ScriptException e) {
assert false;
throw new IllegalStateException("Unknown error encountered");
}
// First of all check the script for support
Object type = null, name = null;
Object apiVersion = null;
// Try to eval each line, since it executes in a sandbox this is
// *relatively* safe
String line;
try {
final LineNumberReader script = new LineNumberReader(new PluginReader(uri));
try {
while ((null == type || null == apiVersion || null == name) &&
(null != (line = script.readLine())) &&
script.getLineNumber() <= linesToCheckForSupport) {
try {
rhino.eval(line);
// Note that undefined doesn't convert to null!
if (null == type) {
rhino.eval("$_1_$ = (undefined===plugin.type)?null:plugin.type;");
type = rhino.get("$_1_$");
}
if (null == apiVersion) {
rhino.eval("$_1_$ = (undefined===plugin.apiVersion)?null:plugin.apiVersion;");
apiVersion = rhino.get("$_1_$");
}
if (null == name) {
rhino.eval("$_1_$ = (undefined===plugin.name)?null:plugin.name;");
name = rhino.get("$_1_$");
}
}
catch (final ScriptException e) {
// Exceptions are to be expected since none of the guaranteed
// plugin tools are provided in this context they'll try to
// access unexisting objects
}
}
}
finally {
script.close();
}
}
catch (final IOException e) {
// XXX: Evil silent failure
return null;
}
 
if (null == type) {
type = "";
}
if (null == apiVersion) {
apiVersion = 0;
}
// name can be null
final PluginProperties pp = new PluginProperties();
try {
pp.apiVersion = Double.valueOf(apiVersion.toString()).intValue();
pp.type = type.toString();
pp.name = (null != name) ? name.toString(): null;
}
catch (final NumberFormatException e) {
// XXX: Evil silent failure
return null;
}
return pp;
}
}
/pluggablejs/trunk
Property changes:
Modified: svn:mergeinfo
Merged /pluggablejs/branches/1.0build29:r58