Subversion Repositories pub

Compare Revisions

Ignore whitespace Rev 53 → Rev 49

/pluggablejs/trunk/src/net/outlyer/plugins/PluginEnvironment.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/PluginEnvironmentImpl.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/RuntimeHooks.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/PluginExecutionException.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/Sandbox.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/SandboxAccessor.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/PluginReader.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/SandboxImpl.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/SandboxAccessorImpl.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/SandboxProperties.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/Shell.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/BasePluginObject.java
1,28 → 1,6
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.
*/
import net.outlyer.plugins.sandboxing.SandboxAccessor;
 
/**
* Simplest object to be exported as "plugin"
29,6 → 7,7
* @see PluginObject
*/
public class BasePluginObject extends PluginProperties implements SandboxAccessor, Cloneable {
public String name;
public int version = 0;
 
@Override public Object clone() throws CloneNotSupportedException {
/pluggablejs/trunk/src/net/outlyer/plugins/utils/GUI.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/utils/UI.java
File deleted
/pluggablejs/trunk/src/net/outlyer/plugins/utils/Input.java
1,33 → 1,7
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.
*/
 
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
 
/**
*
35,22 → 9,26
public class Input {
 
private final InputStream in;
 
public Input() {
this(System.in);
}
public Input(final InputStream is) {
in = is;
{
in = System.in;
}
public String readline() {
final BufferedReader br = new BufferedReader(new InputStreamReader(in));
final StringBuilder sb = new StringBuilder();
 
int c;
try {
return br.readLine();
do {
c = in.read();
sb.append((char) c);
} while (0 != c && '\n' != c);
}
catch (final IOException e) {
return null;
}
return sb.toString();
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/utils/Output.java
1,29 → 1,5
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.
*/
 
import java.io.PrintStream;
 
/**
33,10 → 9,6
 
private final PrintStream ps;
public Output() {
this(System.out);
}
public Output(final PrintStream ps) {
this.ps = ps;
}
/pluggablejs/trunk/src/net/outlyer/plugins/utils/Hooks.java
1,31 → 1,7
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.
*/
import net.outlyer.plugins.sandboxing.SandboxAccessorImpl;
 
import net.outlyer.plugins.SandboxAccessorImpl;
 
/**
*
*/
/pluggablejs/trunk/src/net/outlyer/plugins/utils/package-info.java
1,3 → 1,4
 
/**
* This package provides some common functionality that might often be
* wanted to be exported as global objects.
6,26 → 7,4
 
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.
*/
 
/pluggablejs/trunk/src/net/outlyer/plugins/PluginLocator.java
1,29 → 1,5
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.
*/
 
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
38,10 → 14,11
import java.util.List;
import java.util.Set;
import java.util.jar.JarEntry;
import net.outlyer.plugins.sandboxing.PluginEnvironment;
import net.outlyer.plugins.sandboxing.Sandbox;
 
/**
* Simplifies seeking plugins and abstracts their location (whether they're
* in a directory or inside a jar file).
*
*/
public class PluginLocator {
 
49,7 → 26,6
private final Library library;
private static final FileFilter jsFileFilter;
private boolean scanned = false;
private final PluginEnvironment environment;
static {
jsFileFilter = new FileFilter() {
61,18 → 37,14
}
private static class Library {
// FIXME: Do something more elegant...
private final HashMap<String, List<Sandbox>> byType;
private final HashMap<String, Sandbox> byFilename; // FIXME: Not really unique
private final HashMap<String, Sandbox> byFilename;
private final HashMap<String, Sandbox> byName;
private final HashSet<Sandbox> unique;
{
byType = new HashMap<String, List<Sandbox>>();
byFilename = new HashMap<String, Sandbox>();
byName = new HashMap<String, Sandbox>();
unique = new HashSet<Sandbox>();
}
void add(final String fileName, final BasePluginObject bpo, final Sandbox sbox) {
83,7 → 55,6
byType.put(bpo.type, new LinkedList<Sandbox>());
}
byType.get(bpo.type).add(sbox);
unique.add(sbox);
}
void drop() {
90,7 → 61,6
byType.clear();
byFilename.clear();
byName.clear();
unique.clear();
}
}
99,29 → 69,19
library = new Library();
}
public PluginLocator(final PluginEnvironment pe, final URI...searchPaths) {
if (null == searchPaths) {
throw new IllegalArgumentException("Can't initialise PluginLocator with no search path");
public PluginLocator(final URI defaultPath) {
if (null == defaultPath) {
throw new IllegalArgumentException("Null path in list of paths");
}
for (final URI path : searchPaths) {
addSearchPath(path);
}
environment = pe;
}
public void addSearchPath(final URI path) {
if (null == path) {
throw new IllegalArgumentException("Can't have a null path in the search paths");
}
 
if (path.getScheme().equals("file")) {
final File file = new File(path.normalize()).getAbsoluteFile();
if (file.exists() && file.isDirectory() && file.canRead()) {
paths.add(file.toURI().normalize());
if (defaultPath.getScheme().equals("file")) {
final File path = new File(defaultPath.normalize()).getAbsoluteFile();
if (path.exists() && path.isDirectory() && path.canRead()) {
paths.add(path.toURI().normalize());
}
}
else if (path.getScheme().equals("jar")) {
final URI normalized = path.normalize();
else if (defaultPath.getScheme().equals("jar")) {
final URI normalized = defaultPath.normalize();
if (!normalized.getSchemeSpecificPart().endsWith("/")) {
throw new IllegalArgumentException("Jar file entries must" +
" represent directories");
240,24 → 200,20
return Collections.unmodifiableSet(set);
}
public void scan() {
public void scan(final PluginEnvironment pe) {
if (scanned) {
return;
}
if (paths.size() == 0) { // Nothing to scan
scanned = true;
return;
}
 
for (final URI uri : paths) {
if (uri.getScheme().equals("file")) {
final File dir = new File(uri);
checkDirConsistency(dir);
scan(dir);
scan(pe, dir);
}
else {
assert uri.getScheme().equals("jar");
scan(uri);
scan(pe, uri);
}
}
 
268,7 → 224,7
* Scan over a directory
* @param dir Directory to scan
*/
private void scan(final File dir) {
private void scan(final PluginEnvironment pe, final File dir) {
assert dir.isDirectory() && dir.canRead();
for (final File candidateFile : dir.listFiles(jsFileFilter)) {
275,7 → 231,7
final URI fileURI = candidateFile.toURI();
try {
final Sandbox sb = environment.createSandbox(fileURI);
final Sandbox sb = pe.createSandbox(fileURI);
final BasePluginObject bpo = sb.getPluginObject();
295,7 → 251,7
* @param jarUri Directory to scan
* @return LIst of plugins in directory <code>jarUri</code>
*/
private void scan(final URI jarUri) {
private void scan(final PluginEnvironment pe, final URI jarUri) {
assert jarUri.getScheme().equals("jar");
 
try {
317,11 → 273,11
// Accepted URI
 
try {
final URI pluginUri = new URI("jar:"+jarFile+"!"+url);
final Sandbox sb = pe.createSandbox(jarUri);
 
final Sandbox sb = environment.createSandbox(pluginUri);
final BasePluginObject bpo = sb.getPluginObject();
final String fileName = new File(jarURIElements[1]).getName();
 
final String fileName = new File(jarUri.getPath()).getName();
if (bpo.name == null) {
bpo.name = fileName;
}
354,13 → 310,10
}
public Collection<Sandbox> list() {
scan();
return Collections.unmodifiableCollection(library.unique);
if (!scanned) {
throw new IllegalStateException("Can't list before scanning");
}
return Collections.unmodifiableCollection(library.byFilename.values());
}
public int foundPlugins() {
scan();
return library.unique.size();
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/PluginObject.java
1,29 → 1,5
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.
*/
 
import java.util.LinkedList;
import java.util.List;
 
/pluggablejs/trunk/src/net/outlyer/plugins/API.java
1,58 → 1,9
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.
*/
 
/**
* 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 Revision.
* Newer revisions might break backwards compatibility (see
* {@link #MIN_SUPPORTED_REVISION}).
*/
public static final int REVISION = 1;
 
/**
* Implementation of the API revision.
* Different implementations of an API are guaranteed to maintain
* the API unchanged.
* @see #REVISION
*/
public static final int IMPLEMENTATION = 1;
 
/**
* Minimum API revision 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 &lt;= REVISION
*/
public static final int MIN_SUPPORTED_REVISION = 1;
}
/pluggablejs/trunk/src/net/outlyer/plugins/PluginProperties.java
1,31 → 1,7
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.
*/
import net.outlyer.plugins.sandboxing.SandboxAccessorImpl;
 
import net.outlyer.plugins.SandboxAccessorImpl;
 
/**
*
*/
32,6 → 8,4
public class PluginProperties extends SandboxAccessorImpl {
public int apiVersion = 1;
public String type;
public String name;
public Object properties; // FIXME: To be defined
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/PluginEnvironment.java
0,0 → 1,200
package net.outlyer.plugins.sandboxing;
 
import net.outlyer.plugins.PluginException;
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;
import net.outlyer.plugins.API;
import net.outlyer.plugins.BasePluginObject;
import net.outlyer.plugins.PluginObject;
import net.outlyer.plugins.PluginProperties;
 
/**
* Provides the environment for {@link net.outlyer.plugins.sandboxing.Sandbox}es.
*/
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;
int linesToCheckForSupport = 5;
static final String EXPORTED_SANDBOX_VARIABLE = "$net.outlyer.runtime.sandbox";
{
exportedObjects = new HashMap();
pluginObject = null;
boilerPlate = new StringBuilder();
}
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};");
 
}
public void exportGlobalObject(String name, Object object) {
exportedObjects.put(name, object);
if (object instanceof SandboxAccessor) {
enableSandboxAccess(name, (SandboxAccessor)object);
}
}
public void setPluginObject(BasePluginObject pObject) {
pluginObject = pObject;
}
public PluginProperties checkForSupport(final URI uri) throws PluginException {
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;
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 != (line = script.readLine())) &&
script.getLineNumber() < linesToCheckForSupport) {
try {
rhino.eval(line);
if (null == type) {
rhino.eval("$_1_$ = plugin.type;");
type = rhino.get("$_1_$");
}
if (null == apiVersion) {
rhino.eval("$_1_$ = plugin.apiVersion;");
apiVersion = rhino.get("$_1_$");
}
}
catch (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) {
throw new PluginExecutionException("Failed while reading plugin: " + e.getMessage(), e);
}
 
if (null == type) {
type = "";
}
if (null == apiVersion) {
apiVersion = 0;
}
final PluginProperties pp = new PluginProperties();
 
try {
pp.apiVersion = Double.valueOf(apiVersion.toString()).intValue();
pp.type = type.toString();
}
catch (final NumberFormatException e) {
throw new PluginException("Incorrect value for apiVersion provided, must be integer");
}
if (null != acceptedTypes) {
if (!acceptedTypes.contains(pp.type)) {
throw new UnsupportedTypeException(pp.type);
}
}
if (pp.apiVersion > maxApi) {
throw new UnsupportedAPIException(pp.apiVersion);
}
return pp;
}
 
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, boilerPlate.toString());
}
catch (final CloneNotSupportedException e) {
assert false;
throw new IllegalStateException("Incorrect implementation");
}
}
 
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);
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/SandboxAccessorImpl.java
0,0 → 1,23
package net.outlyer.plugins.sandboxing;
 
import java.util.concurrent.Callable;
 
/**
* {@see SandboxAccessor}
*/
public abstract class SandboxAccessorImpl implements SandboxAccessor {
public Callable<Sandbox> _getSandbox;
protected final Sandbox getSandbox() {
if (null == _getSandbox) {
throw new IllegalStateException(getClass()+" hasn't been correctly initialised");
}
try {
return _getSandbox.call();
}
catch (final Exception e) {
throw new IllegalStateException("getSandbox produced an exception, which is not expected!");
}
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/RuntimeHooks.java
0,0 → 1,11
package net.outlyer.plugins.sandboxing;
 
/**
* Provides hooks to the execution
*/
public interface RuntimeHooks {
/**
* Adds a callback to be executed after the script is finished
*/
void addEndHook(final Runnable r);
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/SandboxProperties.java
0,0 → 1,9
package net.outlyer.plugins.sandboxing;
 
/**
* Provides access to some properties of a sandbox
* @see Sandbox
*/
public interface SandboxProperties {
public int getExecution();
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/Sandbox.java
0,0 → 1,46
package net.outlyer.plugins.sandboxing;
 
import net.outlyer.plugins.BasePluginObject;
 
/**
* Public methods provided by the sandbox.
* Note that all methods in this interface imply a call to {@link #execute}.
*/
public interface Sandbox extends RuntimeHooks, SandboxProperties {
 
/**
* @see #createDelayedImplementation(Class, boolean)
*/
<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
*/
<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
*/
<T> T createDelayedImplementation(final Class<T> interfaceClass, final T fallbackObject) throws PluginExecutionException;
<T> T createDelayedImplementation(final Class<T> interfaceClass, final String objectName) throws PluginExecutionException;
 
/**
* Run the script
*/
void execute() throws PluginExecutionException;
 
/**
* Obtains object contauning the plugin object
* @return
*/
BasePluginObject getPluginObject() throws PluginExecutionException;
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/SandboxImpl.java
0,0 → 1,253
package net.outlyer.plugins.sandboxing;
 
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import javax.script.Bindings;
import javax.script.ScriptContext;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import net.outlyer.plugins.BasePluginObject;
 
/**
* Implementation of the Sandbox interface
*/
class SandboxImpl implements Sandbox {
private final Bindings bindings;
private final URI pluginUri;
private static int nextUniqueVarNum;
private Map<String, Collection<Runnable>> hooks;
private final String boilerPlate;
private int execution = 0;
private static final String END_KEY = "e";
 
static {
nextUniqueVarNum = (int) (1193 * (1+Math.random()));
}
{
hooks = new HashMap<String, Collection<Runnable>>();
hooks.put(END_KEY, new LinkedList<Runnable>());
}
protected SandboxImpl(final URI plugin, final Bindings bs) {
this(plugin, bs, null);
}
protected SandboxImpl(final URI plugin, final Bindings bs, final String boilerPlate) {
bindings = bs;
pluginUri = plugin;
this.boilerPlate = boilerPlate;
}
public void addEndHook(Runnable r) {
hooks.get(END_KEY).add(r);
}
 
/**
* Namespace for variables used by the enviroment
*/
private static String namespace() {
return "$net.outlyer.runtime";
}
 
/**
* Creates a unique variable name.
*/
private static String uniqueVarName() {
return new StringBuilder("$_").append(nextUniqueVarNum+=1193).append("_$").toString();
}
 
/**
* Creates a unique variable name including namespace
* @see #namespace
* @see #uniqueVarName
*/
private static String uniqueFQVarName() {
return new StringBuilder(namespace()).append(".")
.append(uniqueVarName()).toString();
}
private static ScriptEngine getEngine() {
return new ScriptEngineManager().getEngineByName("rhino");
}
private ScriptEngine execute(boolean appendText, String text, final Map<String,Object> extraBindings)
throws PluginExecutionException {
final ScriptEngine rhino = getEngine();
// 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));
}
}
// 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);
 
execution++;
if (null != boilerPlate) {
rhino.eval(boilerPlate);
}
// Create the $net.outlyer.runtime pseudo-namespace...
rhino.eval(runtime.toString());
 
rhino.eval(new PluginReader(pluginUri));
if (appendText) {
rhino.eval(text);
}
// Execute any end hooks
for (final Runnable hook : hooks.get(END_KEY)) {
hook.run();
}
}
catch (final IOException e) {
throw new PluginExecutionException("I/O Error: " + e.getMessage(), e);
}
catch (final ScriptException e) {
throw new PluginExecutionException("Script exception: " + e.getMessage(), e);
}
return rhino;
}
public void execute() throws PluginExecutionException {
execute(false, null, null);
}
 
public <T> T createDelayedImplementation(Class<T> c, String objectName)
throws PluginExecutionException {
final String varImpl = uniqueVarName();
final StringBuilder code = new StringBuilder();
code.append(varImpl).append(" = new ")
.append(c.getCanonicalName()).append("(").append(objectName).append(");");
//System.err.println(code.toString());
final ScriptEngine rhino = execute(true, code.toString(), null);
assert (c.isInstance(rhino.get(varImpl)));
return (T) rhino.get(varImpl);
}
 
public <T> T createDelayedImplementation(Class<T> c) throws PluginExecutionException {
return createDelayedImplementation(c, false);
}
public <T> T createDelayedImplementation(Class<T> c, boolean allowPartial) throws PluginExecutionException {
return createDelayedImplementation(c, null, allowPartial);
}
 
public <T> T createDelayedImplementation(Class<T> interfaceClass, final T fallbackObject) throws PluginExecutionException {
if (null == fallbackObject) {
throw new IllegalArgumentException("Can't use a null fallback object");
}
return createDelayedImplementation(interfaceClass, fallbackObject, true);
}
private <T> T createDelayedImplementation(Class<T> c, T fallback, boolean allowPartial) throws PluginExecutionException {
final StringBuilder hack = new StringBuilder();
 
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.
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");
}
hack.append("};\nvar ")
.append(varImpl).append("=new ")
.append(c.getCanonicalName()).append("(").append(var).append(");");
script.append(hack.toString());
final HashMap<String, Object> bn = new HashMap();
bn.put(varFallback, fallback);
 
if (null != fallback) {
script.append("\n// " + bn.get(varFallback).getClass());
}
//System.err.println(script.toString());
 
final ScriptEngine rhino = execute(true, script.toString(), bn);
 
assert (c.isInstance(rhino.get(varImpl)));
return (T) rhino.get(varImpl);
}
 
public BasePluginObject getPluginObject() throws PluginExecutionException {
execute();
assert bindings.get("plugin") instanceof BasePluginObject;
return (BasePluginObject) bindings.get("plugin");
}
public int getExecution() {
return execution;
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/PluginExecutionException.java
0,0 → 1,25
package net.outlyer.plugins.sandboxing;
 
import net.outlyer.plugins.PluginException;
 
/**
*
*/
public class PluginExecutionException extends PluginException {
 
public PluginExecutionException(Throwable cause) {
super(cause);
}
 
public PluginExecutionException(String message, Throwable cause) {
super(message, cause);
}
 
public PluginExecutionException(String message) {
super(message);
}
 
public PluginExecutionException() {
}
 
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/PluginReader.java
0,0 → 1,51
package net.outlyer.plugins.sandboxing;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.net.URI;
 
/**
* java.io.Reader for plugins, tries to abstract the plugin location (file or jar)
*/
class PluginReader extends Reader {
 
private final Reader readerImpl;
 
PluginReader(final URI uri) throws IOException {
super();
assert null != uri;
try {
if (uri.getScheme().equals("file")) {
readerImpl = new FileReader(new File(uri));
}
else {
final String path = uri.getSchemeSpecificPart().split("!")[1];
final InputStream is = getClass().getResourceAsStream(path);
if (null == is) {
throw new IOException("Failed to get resource for " + uri);
}
readerImpl = new InputStreamReader(is);
}
}
catch (final FileNotFoundException e) {
throw new IOException("Can\'t read input " + uri);
}
}
 
@Override
public int read(char[] cbuf, int off, int len) throws IOException {
return readerImpl.read(cbuf, off, len);
}
 
@Override
public void close() throws IOException {
readerImpl.close();
}
}
/pluggablejs/trunk/src/net/outlyer/plugins/sandboxing/SandboxAccessor.java
0,0 → 1,17
package net.outlyer.plugins.sandboxing;
 
/**
* Optional interface for exported objects (see
* {@link net.outlyer.plugins.sandboxing.PluginEnvironment#exportGlobalObject}),
* it enables exported objects to retrieve the sandbox object in which they're
* being executed.
*
* Implementors must contain a field exactly:
*
* 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 {
}
/pluggablejs/trunk/src/net/outlyer/plugins/PluginException.java
1,32 → 1,8
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.
*/
 
/**
* Base exception
*/
public class PluginException extends Exception {
 
public PluginException(Throwable cause) {