/*
* Copyright (c) 2005, 2006, Oracle and/or its affiliates. All rights reserved.
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*
*/
package javax.script;
import java.util.*;
import java.net.URL;
import java.io.*;
import java.security.*;
import sun.misc.Service;
import sun.misc.ServiceConfigurationError;
/**
* The ScriptEngineManager
implements a discovery and instantiation
* mechanism for ScriptEngine
classes and also maintains a
* collection of key/value pairs storing state shared by all engines created
* by the Manager. This class uses the service provider mechanism to enumerate all the
* implementations of ScriptEngineFactory
.
* The ScriptEngineManager
provides a method to return a list of all these factories
* as well as utility methods which look up factories on the basis of language name, file extension
* and mime type.
*
* The Bindings
of key/value pairs, referred to as the "Global Scope" maintained
* by the manager is available to all instances of ScriptEngine
created
* by the ScriptEngineManager
. The values in the Bindings
are
* generally exposed in all scripts.
*
* @author Mike Grogan
* @author A. Sundararajan
* @since 1.6
*/
public class ScriptEngineManager {
private static final boolean DEBUG = false;
/**
* If the thread context ClassLoader can be accessed by the caller,
* then the effect of calling this constructor is the same as calling
* ScriptEngineManager(Thread.currentThread().getContextClassLoader())
.
* Otherwise, the effect is the same as calling ScriptEngineManager(null)
.
*
* @see java.lang.Thread#getContextClassLoader
*/
public ScriptEngineManager() {
ClassLoader ctxtLoader = Thread.currentThread().getContextClassLoader();
init(ctxtLoader);
}
/**
* This constructor loads the implementations of
* ScriptEngineFactory
visible to the given
* ClassLoader
using the service provider mechanism.
* If loader is null
, the script engine factories that are
* bundled with the platform and that are in the usual extension
* directories (installed extensions) are loaded.
*
* @param loader ClassLoader used to discover script engine factories.
*/
public ScriptEngineManager(ClassLoader loader) {
init(loader);
}
private void init(final ClassLoader loader) {
globalScope = new SimpleBindings();
engineSpis = new HashSet();
nameAssociations = new HashMap();
extensionAssociations = new HashMap();
mimeTypeAssociations = new HashMap();
AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
initEngines(loader);
return null;
}
});
}
private void initEngines(final ClassLoader loader) {
Iterator itr = null;
try {
if (loader != null) {
itr = Service.providers(ScriptEngineFactory.class, loader);
} else {
itr = Service.installedProviders(ScriptEngineFactory.class);
}
} catch (ServiceConfigurationError err) {
System.err.println("Can't find ScriptEngineFactory providers: " +
err.getMessage());
if (DEBUG) {
err.printStackTrace();
}
// do not throw any exception here. user may want to
// manage his/her own factories using this manager
// by explicit registratation (by registerXXX) methods.
return;
}
try {
while (itr.hasNext()) {
try {
ScriptEngineFactory fact = (ScriptEngineFactory) itr.next();
engineSpis.add(fact);
} catch (ServiceConfigurationError err) {
System.err.println("ScriptEngineManager providers.next(): "
+ err.getMessage());
if (DEBUG) {
err.printStackTrace();
}
// one factory failed, but check other factories...
continue;
}
}
} catch (ServiceConfigurationError err) {
System.err.println("ScriptEngineManager providers.hasNext(): "
+ err.getMessage());
if (DEBUG) {
err.printStackTrace();
}
// do not throw any exception here. user may want to
// manage his/her own factories using this manager
// by explicit registratation (by registerXXX) methods.
return;
}
}
/**
* setBindings
stores the specified Bindings
* in the globalScope
field. ScriptEngineManager sets this
* Bindings
as global bindings for ScriptEngine
* objects created by it.
*
* @param bindings The specified Bindings
* @throws IllegalArgumentException if bindings is null.
*/
public void setBindings(Bindings bindings) {
if (bindings == null) {
throw new IllegalArgumentException("Global scope cannot be null.");
}
globalScope = bindings;
}
/**
* getBindings
returns the value of the globalScope
field.
* ScriptEngineManager sets this Bindings
as global bindings for
* ScriptEngine
objects created by it.
*
* @return The globalScope field.
*/
public Bindings getBindings() {
return globalScope;
}
/**
* Sets the specified key/value pair in the Global Scope.
* @param key Key to set
* @param value Value to set.
* @throws NullPointerException if key is null.
* @throws IllegalArgumentException if key is empty string.
*/
public void put(String key, Object value) {
globalScope.put(key, value);
}
/**
* Gets the value for the specified key in the Global Scope
* @param key The key whose value is to be returned.
* @return The value for the specified key.
*/
public Object get(String key) {
return globalScope.get(key);
}
/**
* Looks up and creates a ScriptEngine
for a given name.
* The algorithm first searches for a ScriptEngineFactory
that has been
* registered as a handler for the specified name using the registerEngineName
* method.
* If one is not found, it searches the set of ScriptEngineFactory
instances
* stored by the constructor for one with the specified name. If a ScriptEngineFactory
* is found by either method, it is used to create instance of ScriptEngine
.
* @param shortName The short name of the ScriptEngine
implementation.
* returned by the getNames
method of its ScriptEngineFactory
.
* @return A ScriptEngine
created by the factory located in the search. Returns null
* if no such factory was found. The ScriptEngineManager
sets its own globalScope
* Bindings
as the GLOBAL_SCOPE
Bindings
of the newly
* created ScriptEngine
.
* @throws NullPointerException if shortName is null.
*/
public ScriptEngine getEngineByName(String shortName) {
if (shortName == null) throw new NullPointerException();
//look for registered name first
Object obj;
if (null != (obj = nameAssociations.get(shortName))) {
ScriptEngineFactory spi = (ScriptEngineFactory)obj;
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
}
for (ScriptEngineFactory spi : engineSpis) {
List names = null;
try {
names = spi.getNames();
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
if (names != null) {
for (String name : names) {
if (shortName.equals(name)) {
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
}
}
}
}
return null;
}
/**
* Look up and create a ScriptEngine
for a given extension. The algorithm
* used by getEngineByName
is used except that the search starts
* by looking for a ScriptEngineFactory
registered to handle the
* given extension using registerEngineExtension
.
* @param extension The given extension
* @return The engine to handle scripts with this extension. Returns null
* if not found.
* @throws NullPointerException if extension is null.
*/
public ScriptEngine getEngineByExtension(String extension) {
if (extension == null) throw new NullPointerException();
//look for registered extension first
Object obj;
if (null != (obj = extensionAssociations.get(extension))) {
ScriptEngineFactory spi = (ScriptEngineFactory)obj;
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
}
for (ScriptEngineFactory spi : engineSpis) {
List exts = null;
try {
exts = spi.getExtensions();
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
if (exts == null) continue;
for (String ext : exts) {
if (extension.equals(ext)) {
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
}
}
}
return null;
}
/**
* Look up and create a ScriptEngine
for a given mime type. The algorithm
* used by getEngineByName
is used except that the search starts
* by looking for a ScriptEngineFactory
registered to handle the
* given mime type using registerEngineMimeType
.
* @param mimeType The given mime type
* @return The engine to handle scripts with this mime type. Returns null
* if not found.
* @throws NullPointerException if mimeType is null.
*/
public ScriptEngine getEngineByMimeType(String mimeType) {
if (mimeType == null) throw new NullPointerException();
//look for registered types first
Object obj;
if (null != (obj = mimeTypeAssociations.get(mimeType))) {
ScriptEngineFactory spi = (ScriptEngineFactory)obj;
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
}
for (ScriptEngineFactory spi : engineSpis) {
List types = null;
try {
types = spi.getMimeTypes();
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
if (types == null) continue;
for (String type : types) {
if (mimeType.equals(type)) {
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
if (DEBUG) exp.printStackTrace();
}
}
}
}
return null;
}
/**
* Returns a list whose elements are instances of all the ScriptEngineFactory
classes
* found by the discovery mechanism.
* @return List of all discovered ScriptEngineFactory
s.
*/
public List getEngineFactories() {
List res = new ArrayList(engineSpis.size());
for (ScriptEngineFactory spi : engineSpis) {
res.add(spi);
}
return Collections.unmodifiableList(res);
}
/**
* Registers a ScriptEngineFactory
to handle a language
* name. Overrides any such association found using the Discovery mechanism.
* @param name The name to be associated with the ScriptEngineFactory
.
* @param factory The class to associate with the given name.
* @throws NullPointerException if any of the parameters is null.
*/
public void registerEngineName(String name, ScriptEngineFactory factory) {
if (name == null || factory == null) throw new NullPointerException();
nameAssociations.put(name, factory);
}
/**
* Registers a ScriptEngineFactory
to handle a mime type.
* Overrides any such association found using the Discovery mechanism.
*
* @param type The mime type to be associated with the
* ScriptEngineFactory
.
*
* @param factory The class to associate with the given mime type.
* @throws NullPointerException if any of the parameters is null.
*/
public void registerEngineMimeType(String type, ScriptEngineFactory factory) {
if (type == null || factory == null) throw new NullPointerException();
mimeTypeAssociations.put(type, factory);
}
/**
* Registers a ScriptEngineFactory
to handle an extension.
* Overrides any such association found using the Discovery mechanism.
*
* @param extension The extension type to be associated with the
* ScriptEngineFactory
.
* @param factory The class to associate with the given extension.
* @throws NullPointerException if any of the parameters is null.
*/
public void registerEngineExtension(String extension, ScriptEngineFactory factory) {
if (extension == null || factory == null) throw new NullPointerException();
extensionAssociations.put(extension, factory);
}
/** Set of script engine factories discovered. */
private HashSet engineSpis;
/** Map of engine name to script engine factory. */
private HashMap nameAssociations;
/** Map of script file extension to script engine factory. */
private HashMap extensionAssociations;
/** Map of script script MIME type to script engine factory. */
private HashMap mimeTypeAssociations;
/** Global bindings associated with script engines created by this manager. */
private Bindings globalScope;
}