8264326: Modernize javax.script.ScriptEngineManager and related classes' implementation

Reviewed-by: sundar
This commit is contained in:
Attila Szegedi 2021-03-30 18:13:39 +00:00
parent b08d6383b9
commit 2bd80f94a1
5 changed files with 87 additions and 187 deletions

View File

@ -25,8 +25,6 @@
package javax.script;
import java.io.Reader;
import java.util.Map;
import java.util.Iterator;
/**
* Provides a standard implementation for several of the variants of the <code>eval</code>
@ -141,9 +139,9 @@ public abstract class AbstractScriptEngine implements ScriptEngine {
public void setBindings(Bindings bindings, int scope) {
if (scope == ScriptContext.GLOBAL_SCOPE) {
context.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);;
context.setBindings(bindings, ScriptContext.GLOBAL_SCOPE);
} else if (scope == ScriptContext.ENGINE_SCOPE) {
context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);;
context.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
} else {
throw new IllegalArgumentException("Invalid scope value.");
}

View File

@ -28,6 +28,8 @@ import java.util.*;
import java.security.*;
import java.util.ServiceLoader;
import java.util.ServiceConfigurationError;
import java.util.function.Function;
import java.util.stream.Stream;
/**
* The <code>ScriptEngineManager</code> implements a discovery and instantiation
@ -58,8 +60,7 @@ public class ScriptEngineManager {
* @see java.lang.Thread#getContextClassLoader
*/
public ScriptEngineManager() {
ClassLoader ctxtLoader = Thread.currentThread().getContextClassLoader();
init(ctxtLoader);
this(Thread.currentThread().getContextClassLoader());
}
/**
@ -72,18 +73,6 @@ public class ScriptEngineManager {
* @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 TreeSet<ScriptEngineFactory>(Comparator.comparing(
ScriptEngineFactory::getEngineName,
Comparator.nullsLast(Comparator.naturalOrder()))
);
nameAssociations = new HashMap<String, ScriptEngineFactory>();
extensionAssociations = new HashMap<String, ScriptEngineFactory>();
mimeTypeAssociations = new HashMap<String, ScriptEngineFactory>();
initEngines(loader);
}
@ -96,23 +85,13 @@ public class ScriptEngineManager {
}
private void initEngines(final ClassLoader loader) {
Iterator<ScriptEngineFactory> itr = null;
Iterator<ScriptEngineFactory> itr;
try {
ServiceLoader<ScriptEngineFactory> sl = AccessController.doPrivileged(
new PrivilegedAction<ServiceLoader<ScriptEngineFactory>>() {
@Override
public ServiceLoader<ScriptEngineFactory> run() {
return getServiceLoader(loader);
}
});
var sl = AccessController.doPrivileged(
(PrivilegedAction<ServiceLoader<ScriptEngineFactory>>)() -> getServiceLoader(loader));
itr = sl.iterator();
} catch (ServiceConfigurationError err) {
System.err.println("Can't find ScriptEngineFactory providers: " +
err.getMessage());
if (DEBUG) {
err.printStackTrace();
}
reportException("Can't find ScriptEngineFactory providers: ", err);
// do not throw any exception here. user may want to
// manage his/her own factories using this manager
// by explicit registratation (by registerXXX) methods.
@ -125,25 +104,15 @@ public class ScriptEngineManager {
ScriptEngineFactory fact = itr.next();
engineSpis.add(fact);
} catch (ServiceConfigurationError err) {
System.err.println("ScriptEngineManager providers.next(): "
+ err.getMessage());
if (DEBUG) {
err.printStackTrace();
}
reportException("ScriptEngineManager providers.next(): ", err);
// one factory failed, but check other factories...
continue;
}
}
} catch (ServiceConfigurationError err) {
System.err.println("ScriptEngineManager providers.hasNext(): "
+ err.getMessage());
if (DEBUG) {
err.printStackTrace();
}
reportException("ScriptEngineManager providers.hasNext(): ", err);
// 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;
}
}
@ -212,44 +181,7 @@ public class ScriptEngineManager {
* @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<String> 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;
return getEngineBy(shortName, nameAssociations, ScriptEngineFactory::getNames);
}
/**
@ -263,41 +195,7 @@ public class ScriptEngineManager {
* @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<String> 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;
return getEngineBy(extension, extensionAssociations, ScriptEngineFactory::getExtensions);
}
/**
@ -311,41 +209,52 @@ public class ScriptEngineManager {
* @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();
}
}
return getEngineBy(mimeType, mimeTypeAssociations, ScriptEngineFactory::getMimeTypes);
}
for (ScriptEngineFactory spi : engineSpis) {
List<String> 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();
}
private ScriptEngine getEngineBy(String selector, Map<String, ScriptEngineFactory> associations,
Function<ScriptEngineFactory, List<String>> valuesFn)
{
Objects.requireNonNull(selector);
Stream<ScriptEngineFactory> spis = Stream.concat(
//look for registered types first
Stream.ofNullable(associations.get(selector)),
engineSpis.stream().filter(spi -> {
try {
List<String> matches = valuesFn.apply(spi);
return matches != null && matches.contains(selector);
} catch (Exception exp) {
debugPrint(exp);
return false;
}
}
})
);
return spis
.map(spi -> {
try {
ScriptEngine engine = spi.getScriptEngine();
engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);
return engine;
} catch (Exception exp) {
debugPrint(exp);
return null;
}
})
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
private static void reportException(String msg, Throwable exp) {
System.err.println(msg + exp.getMessage());
debugPrint(exp);
}
private static void debugPrint(Throwable exp) {
if (DEBUG) {
exp.printStackTrace();
}
return null;
}
/**
@ -354,11 +263,7 @@ public class ScriptEngineManager {
* @return List of all discovered <code>ScriptEngineFactory</code>s.
*/
public List<ScriptEngineFactory> getEngineFactories() {
List<ScriptEngineFactory> res = new ArrayList<ScriptEngineFactory>(engineSpis.size());
for (ScriptEngineFactory spi : engineSpis) {
res.add(spi);
}
return Collections.unmodifiableList(res);
return List.copyOf(engineSpis);
}
/**
@ -369,8 +274,7 @@ public class ScriptEngineManager {
* @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);
associateFactory(nameAssociations, name, factory);
}
/**
@ -384,8 +288,7 @@ public class ScriptEngineManager {
* @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);
associateFactory(mimeTypeAssociations, type, factory);
}
/**
@ -398,22 +301,33 @@ public class ScriptEngineManager {
* @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);
associateFactory(extensionAssociations, extension, factory);
}
private static void associateFactory(Map<String, ScriptEngineFactory> associations, String association,
ScriptEngineFactory factory)
{
if (association == null || factory == null) throw new NullPointerException();
associations.put(association, factory);
}
private static final Comparator<ScriptEngineFactory> COMPARATOR = Comparator.comparing(
ScriptEngineFactory::getEngineName,
Comparator.nullsLast(Comparator.naturalOrder())
);
/** Set of script engine factories discovered. */
private TreeSet<ScriptEngineFactory> engineSpis;
private final TreeSet<ScriptEngineFactory> engineSpis = new TreeSet<>(COMPARATOR);
/** Map of engine name to script engine factory. */
private HashMap<String, ScriptEngineFactory> nameAssociations;
private final HashMap<String, ScriptEngineFactory> nameAssociations = new HashMap<>();
/** Map of script file extension to script engine factory. */
private HashMap<String, ScriptEngineFactory> extensionAssociations;
private final HashMap<String, ScriptEngineFactory> extensionAssociations = new HashMap<>();
/** Map of script MIME type to script engine factory. */
private HashMap<String, ScriptEngineFactory> mimeTypeAssociations;
private final HashMap<String, ScriptEngineFactory> mimeTypeAssociations = new HashMap<>();
/** Global bindings associated with script engines created by this manager. */
private Bindings globalScope;
private Bindings globalScope = new SimpleBindings();
}

View File

@ -38,9 +38,9 @@ public class ScriptException extends Exception {
private static final long serialVersionUID = 8265071037049225001L;
private String fileName;
private int lineNumber;
private int columnNumber;
private final String fileName;
private final int lineNumber;
private final int columnNumber;
/**
* Creates a <code>ScriptException</code> with a String to be used in its message.

View File

@ -28,6 +28,7 @@ package javax.script;
import java.util.Map;
import java.util.HashMap;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
/**
@ -42,7 +43,7 @@ public class SimpleBindings implements Bindings {
/**
* The {@code Map} field stores the attributes.
*/
private Map<String,Object> map;
private final Map<String,Object> map;
/**
* Constructor uses an existing {@code Map} to store the values.
@ -50,17 +51,14 @@ public class SimpleBindings implements Bindings {
* @throws NullPointerException if m is null
*/
public SimpleBindings(Map<String,Object> m) {
if (m == null) {
throw new NullPointerException();
}
this.map = m;
this.map = Objects.requireNonNull(m);
}
/**
* Default constructor uses a {@code HashMap}.
*/
public SimpleBindings() {
this(new HashMap<String,Object>());
this(new HashMap<>());
}
/**
@ -91,9 +89,7 @@ public class SimpleBindings implements Bindings {
* if some key in the map is an empty String.
*/
public void putAll(Map<? extends String, ? extends Object> toMerge) {
if (toMerge == null) {
throw new NullPointerException("toMerge map is null");
}
Objects.requireNonNull(toMerge, "toMerge map is null");
for (Map.Entry<? extends String, ? extends Object> entry : toMerge.entrySet()) {
String key = entry.getKey();
checkKey(key);
@ -211,9 +207,7 @@ public class SimpleBindings implements Bindings {
}
private void checkKey(Object key) {
if (key == null) {
throw new NullPointerException("key can not be null");
}
Objects.requireNonNull(key, "key can not be null");
if (!(key instanceof String)) {
throw new ClassCastException("key should be a String");
}

View File

@ -338,11 +338,5 @@ public class SimpleScriptContext implements ScriptContext {
}
}
private static List<Integer> scopes;
static {
scopes = new ArrayList<Integer>(2);
scopes.add(ENGINE_SCOPE);
scopes.add(GLOBAL_SCOPE);
scopes = Collections.unmodifiableList(scopes);
}
private static final List<Integer> scopes = List.of(ENGINE_SCOPE, GLOBAL_SCOPE);
}