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); |
} |
} |