package org.fenixedu.bennu.core.domain;
import java.util.Objects;
import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
/**
* A {@link NashornStrategy} is a special value-type that allows using JavaScript to store interface implementations.
*
* The implementation is provided by Nashorn. For a reference of Nashorn, see <a
* href="http://docs.oracle.com/javase/8/docs/technotes/guides/scripting/nashorn/">Oracle's
* Nashorn Guide</a>.
*
*
* @author João Carvalho (joao.pedro.carvalho@tecnico.ulisboa.pt)
*
*/
public final class NashornStrategy<T> {
private final String code;
private final Class<?> type;
private final T strategy;
/**
* Create a new {@link NashornStrategy} of the given type, implemented by the given code.
*
* @param type
* The interface type to implement.
* @param code
* The code that implements the interface.
*
* @throws IllegalArgumentException
* If {@code type} is not an interface, or if the given JavaScript code is invalid, or doesn't implement the given
* interface.
* @throws NullPointerException
* If either argument is null.
*/
public NashornStrategy(Class<?> type, String code) {
this.type = Objects.requireNonNull(type, "type");
this.code = Objects.requireNonNull(code, "code");
if (!type.isInterface()) {
throw new IllegalArgumentException("Type " + type.getName() + " is not an interface!");
}
this.strategy = getImplementation(code, type);
if (this.strategy == null) {
throw new IllegalArgumentException("The given code does not implement the interface!");
}
}
@SuppressWarnings("unchecked")
private T getImplementation(String code, Class<?> type) {
try {
ScriptEngineManager engineManager = new ScriptEngineManager();
ScriptEngine engine = engineManager.getEngineByName("nashorn");
if (engine == null) {
throw new Error("A javascript engine could not be found!");
}
Invocable invocable = (Invocable) engine;
engine.eval(code);
return (T) invocable.getInterface(type);
} catch (ScriptException e) {
throw new IllegalArgumentException("Could not compile Javascript code!", e);
}
}
/**
* Returns the code that implements the interface.
*
* @return
* The JavaScript code that implements the interface
*/
public String getCode() {
return code;
}
/**
* Returns the implemented interface type.
*
* @return
* The implemented interface type
*/
public Class<?> getType() {
return type;
}
/**
* Returns the instance of the interface type, implemented by the
* given JavaScript code.
*
* @return
* The instance implemented by the given code
*/
public T getStrategy() {
return strategy;
}
}