org.cometd.javascript.JavaScript Maven / Gradle / Ivy
/*
* Copyright (c) 2008-2018 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.cometd.javascript;
import java.net.URL;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import jdk.nashorn.api.scripting.URLReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class JavaScript implements Runnable {
private static final ScriptEngine engine = new ScriptEngineManager().getEngineByName("nashorn");
private final Logger logger = LoggerFactory.getLogger(getClass());
private final BlockingQueue> queue = new LinkedBlockingQueue<>();
private final Thread thread = new Thread(this, "javascript");
private final Bindings bindings;
private volatile boolean running;
public JavaScript() {
bindings = engine.createBindings();
bindings.put("javaScript", this);
}
public void init() throws Exception {
queue.clear();
running = true;
thread.start();
}
public void destroy() throws Exception {
running = false;
for (FutureTask> task : queue) {
task.cancel(false);
}
thread.interrupt();
thread.join();
bindings.clear();
}
@Override
public void run() {
try {
while (running) {
FutureTask> task = queue.take();
task.run();
}
} catch (InterruptedException x) {
// We've been destroyed, just exit
}
}
public T invoke(boolean sync, ScriptObjectMirror thiz, Object function, Object... arguments) {
FutureTask task = new FutureTask<>(() -> {
try {
if (function instanceof ScriptObjectMirror) {
return (T)((ScriptObjectMirror)function).call(thiz, arguments);
} else {
String name = function.toString();
if (thiz.hasMember(name)) {
Object member = thiz.getMember(name);
if (member instanceof ScriptObjectMirror) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking function {}", name);
}
return (T)((ScriptObjectMirror)member).call(thiz, arguments);
} else {
logger.info("Function {} not defined in {}", name, thiz);
return null;
}
} else {
logger.info("Function {} not a member of {}", name, thiz);
return null;
}
}
} catch (Throwable x) {
logger.info("Exception while trying to invoke " + function, x);
throw x;
}
});
submit(task);
if (!sync) {
return null;
}
return result(task);
}
public T evaluate(URL url) {
FutureTask task = new FutureTask<>(() -> {
try(URLReader r = new URLReader(url)) {
return (T)engine.eval(r, bindings);
} catch (ScriptException x) {
throw new JavaScriptException(x);
}
});
return submitTask(task);
}
public T evaluate(String name, String code) {
String script = name == null ? code : String.format("//# sourceURL=%s%n%s", name, code);
FutureTask task = new FutureTask<>(() -> {
try {
bindings.put(ScriptEngine.FILENAME, name);
return (T)engine.eval(script, bindings);
} catch (ScriptException x) {
throw new JavaScriptException(x);
}
});
return submitTask(task);
}
private T submitTask(FutureTask task) {
submit(task);
return result(task);
}
public T get(String key) {
FutureTask task = new FutureTask<>(() -> getAsync(key));
submit(task);
return result(task);
}
public T getAsync(String key) {
return (T)bindings.get(key);
}
public void putAsync(String key, Object value) {
bindings.put(key, value);
}
private void submit(FutureTask> task) {
if (Thread.currentThread() == thread) {
task.run();
} else {
if (running) {
queue.offer(task);
} else {
throw new RejectedExecutionException();
}
}
}
private T result(FutureTask task) {
try {
return task.get();
} catch (InterruptedException x) {
return null;
} catch (ExecutionException x) {
Throwable xx = x.getCause();
if (xx instanceof Error) {
throw (Error)xx;
}
throw (RuntimeException)xx;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy