aboutsummaryrefslogtreecommitdiffstats
path: root/plugins/plugins-executor/plugins-executor-javascript/src/main
diff options
context:
space:
mode:
authorliamfallon <liam.fallon@est.tech>2020-03-23 17:49:50 +0000
committerliamfallon <liam.fallon@est.tech>2020-03-24 17:40:27 +0000
commit2f75e9d08d1e47e2b9b39ec21653bc3b4d65d00a (patch)
treebe5374421d8b3f026175ab51d197289ea9dbbe0d /plugins/plugins-executor/plugins-executor-javascript/src/main
parentf7746d758149bc68584c01dc0fe15130c7a866b1 (diff)
Launch separate threads for Javascript task execution
When a policy is loaded, a separate thread is spawned for each Javascript script executor. This allows us to precompile the Javascript scripts and also to have a larger stack available for script execution. Issue-ID: POLICY-2106 Change-Id: I97323aafb623ba537ac1889b3c9504b345b4f67e Signed-off-by: liamfallon <liam.fallon@est.tech>
Diffstat (limited to 'plugins/plugins-executor/plugins-executor-javascript/src/main')
-rw-r--r--plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptExecutor.java192
-rw-r--r--plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutor.java13
-rw-r--r--plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutor.java17
-rw-r--r--plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutor.java16
4 files changed, 195 insertions, 43 deletions
diff --git a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptExecutor.java b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptExecutor.java
index c80f58fbe..2394b83d3 100644
--- a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptExecutor.java
+++ b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptExecutor.java
@@ -20,19 +20,28 @@
package org.onap.policy.apex.plugins.executor.javascript;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.atomic.AtomicReference;
+
import org.apache.commons.lang3.StringUtils;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;
import org.mozilla.javascript.Scriptable;
import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
/**
* The Class JavascriptExecutor is the executor for task logic written in Javascript.
*
* @author Liam Fallon (liam.fallon@ericsson.com)
*/
-public class JavascriptExecutor {
+public class JavascriptExecutor implements Runnable {
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(JavascriptExecutor.class);
+
public static final int DEFAULT_OPTIMIZATION_LEVEL = 9;
// Recurring string constants
@@ -41,16 +50,28 @@ public class JavascriptExecutor {
// The key of the subject that wants to execute Javascript code
final AxKey subjectKey;
+ private String javascriptCode;
private Context javascriptContext;
private Script script;
+ private final BlockingQueue<Object> executionQueue = new LinkedBlockingQueue<>();
+ private final BlockingQueue<Boolean> resultQueue = new LinkedBlockingQueue<>();
+
+ private final Thread executorThread;
+ private CountDownLatch intializationLatch = new CountDownLatch(1);
+ private CountDownLatch shutdownLatch = new CountDownLatch(1);
+ private AtomicReference<StateMachineException> executorException = new AtomicReference<>(null);
+
/**
- * Initializes the Javascripe executor.
+ * Initializes the Javascript executor.
*
* @param subjectKey the key of the subject that is requesting Javascript execution
*/
public JavascriptExecutor(final AxKey subjectKey) {
this.subjectKey = subjectKey;
+
+ executorThread = new Thread(this);
+ executorThread.setName(this.getClass().getSimpleName() + ":" + subjectKey.getId());
}
/**
@@ -60,10 +81,143 @@ public class JavascriptExecutor {
* @throws StateMachineException thrown when instantiation of the executor fails
*/
public void init(final String javascriptCode) throws StateMachineException {
+ LOGGER.debug("JavascriptExecutor {} starting ... ", subjectKey.getId());
+
+ if (executorThread.isAlive()) {
+ throw new StateMachineException(
+ "initiation failed, executor " + subjectKey.getId() + " is already running");
+ }
+
if (StringUtils.isEmpty(javascriptCode)) {
throw new StateMachineException("no logic specified for " + subjectKey.getId());
}
+ this.javascriptCode = javascriptCode;
+
+ try {
+ executorThread.start();
+ } catch (Exception e) {
+ throw new StateMachineException("initiation failed, executor " + subjectKey.getId() + " failed to start",
+ e);
+ }
+
+ try {
+ intializationLatch.await();
+ } catch (InterruptedException e) {
+ LOGGER.debug("JavascriptExecutor {} interrupted on execution thread startup", subjectKey.getId(), e);
+ Thread.currentThread().interrupt();
+ }
+
+ if (executorException.get() != null) {
+ clearAndThrowExecutorException();
+ }
+
+ LOGGER.debug("JavascriptExecutor {} started ... ", subjectKey.getId());
+ }
+
+ /**
+ * Execute a Javascript script.
+ *
+ * @param executionContext the execution context to use for script execution
+ * @return true if execution was successful, false otherwise
+ * @throws StateMachineException on execution errors
+ */
+ public boolean execute(final Object executionContext) throws StateMachineException {
+ if (!executorThread.isAlive()) {
+ throw new StateMachineException("execution failed, executor " + subjectKey.getId() + " is not running");
+ }
+
+ executionQueue.add(executionContext);
+
+ boolean result = false;
+
+ try {
+ result = resultQueue.take();
+ } catch (final InterruptedException e) {
+ LOGGER.debug("JavascriptExecutor {} interrupted on execution result wait", subjectKey.getId(), e);
+ Thread.currentThread().interrupt();
+ }
+
+ if (executorException.get() != null) {
+ clearAndThrowExecutorException();
+ }
+
+ return result;
+ }
+
+ /**
+ * Cleans up the executor after processing.
+ *
+ * @throws StateMachineException thrown when cleanup of the executor fails
+ */
+ public void cleanUp() throws StateMachineException {
+ if (!executorThread.isAlive()) {
+ throw new StateMachineException("cleanup failed, executor " + subjectKey.getId() + " is not running");
+ }
+
+ executorThread.interrupt();
+
+ try {
+ shutdownLatch.await();
+ } catch (InterruptedException e) {
+ LOGGER.debug("JavascriptExecutor {} interrupted on execution clkeanup wait", subjectKey.getId(), e);
+ Thread.currentThread().interrupt();
+ }
+
+ if (executorException.get() != null) {
+ clearAndThrowExecutorException();
+ }
+ }
+
+ @Override
+ public void run() {
+ LOGGER.debug("JavascriptExecutor {} initializing ... ", subjectKey.getId());
+
+ try {
+ initExecutor();
+ } catch (StateMachineException sme) {
+ LOGGER.warn("JavascriptExecutor {} initialization failed", sme);
+ executorException.set(sme);
+ intializationLatch.countDown();
+ return;
+ }
+
+ intializationLatch.countDown();
+
+ LOGGER.debug("JavascriptExecutor {} executing ... ", subjectKey.getId());
+
+ // Take jobs from the execution queue of the worker and execute them
+ while (!executorThread.isInterrupted()) {
+ try {
+ Object contextObject = executionQueue.take();
+ if (contextObject == null) {
+ break;
+ }
+
+ resultQueue.add(executeScript(contextObject));
+ } catch (final InterruptedException e) {
+ LOGGER.debug("execution was interruped for " + subjectKey.getId() + WITH_MESSAGE + e.getMessage(), e);
+ resultQueue.add(false);
+ Thread.currentThread().interrupt();
+ } catch (StateMachineException sme) {
+ executorException.set(sme);
+ resultQueue.add(false);
+ }
+ }
+
+ try {
+ Context.exit();
+ } catch (final Exception e) {
+ executorException.set(new StateMachineException(
+ "executor close failed to close for " + subjectKey.getId() + WITH_MESSAGE + e.getMessage(), e));
+ }
+
+ shutdownLatch.countDown();
+
+ LOGGER.debug("JavascriptExecutor {} completed processing", subjectKey.getId());
+ }
+
+ private void initExecutor() throws StateMachineException {
try {
// Create a Javascript context for this thread
javascriptContext = Context.enter();
@@ -76,18 +230,11 @@ public class JavascriptExecutor {
} catch (Exception e) {
Context.exit();
throw new StateMachineException(
- "logic failed to compile for " + subjectKey.getId() + WITH_MESSAGE + e.getMessage(), e);
+ "logic failed to compile for " + subjectKey.getId() + WITH_MESSAGE + e.getMessage(), e);
}
}
- /**
- * Executes the the Javascript code.
- *
- * @param executionContext the execution context of the subject to be passed to the Javascript context
- * @return true if the Javascript executed properly
- * @throws StateMachineException thrown when Javascript execution fails
- */
- public boolean execute(final Object executionContext) throws StateMachineException {
+ private boolean executeScript(final Object executionContext) throws StateMachineException {
Object returnObject = null;
try {
@@ -99,28 +246,25 @@ public class JavascriptExecutor {
returnObject = script.exec(javascriptContext, javascriptScope);
} catch (final Exception e) {
throw new StateMachineException(
- "logic failed to run for " + subjectKey.getId() + WITH_MESSAGE + e.getMessage(), e);
+ "logic failed to run for " + subjectKey.getId() + WITH_MESSAGE + e.getMessage(), e);
}
if (!(returnObject instanceof Boolean)) {
throw new StateMachineException(
- "execute: logic for " + subjectKey.getId() + " returned a non-boolean value " + returnObject);
+ "execute: logic for " + subjectKey.getId() + " returned a non-boolean value " + returnObject);
}
return (boolean) returnObject;
}
- /**
- * Cleans up the executor after processing.
- *
- * @throws StateMachineException thrown when cleanup of the executor fails
- */
- public void cleanUp() throws StateMachineException {
- try {
- Context.exit();
- } catch (final Exception e) {
- throw new StateMachineException("cleanUp: executor cleanup failed to close for " + subjectKey.getId()
- + WITH_MESSAGE + e.getMessage(), e);
+ private void clearAndThrowExecutorException() throws StateMachineException {
+ StateMachineException exceptionToThrow = executorException.getAndSet(null);
+ if (exceptionToThrow != null) {
+ throw exceptionToThrow;
}
}
+
+ protected Thread getExecutorThread() {
+ return executorThread;
+ }
}
diff --git a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutor.java b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutor.java
index 27e649fd3..63e4948dd 100644
--- a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutor.java
+++ b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutor.java
@@ -53,11 +53,12 @@ public class JavascriptStateFinalizerExecutor extends StateFinalizerExecutor {
super.prepare();
// Create the executor
- javascriptExecutor = new JavascriptExecutor(getSubject().getKey());
+ if (javascriptExecutor == null) {
+ javascriptExecutor = new JavascriptExecutor(getSubject().getKey());
+ }
// Initialize and cleanup the executor to check the Javascript code
javascriptExecutor.init(getSubject().getLogic());
- javascriptExecutor.cleanUp();
}
/**
@@ -72,14 +73,12 @@ public class JavascriptStateFinalizerExecutor extends StateFinalizerExecutor {
*/
@Override
public String execute(final long executionId, final Properties executionProperties,
- final Map<String, Object> incomingFields) throws StateMachineException, ContextException {
+ final Map<String, Object> incomingFields) throws StateMachineException, ContextException {
// Do execution pre work
executePre(executionId, executionProperties, incomingFields);
// Execute the Javascript executor
- javascriptExecutor.init(getSubject().getLogic());
boolean result = javascriptExecutor.execute(getExecutionContext());
- javascriptExecutor.cleanUp();
// Execute the Javascript
executePost(result);
@@ -95,6 +94,8 @@ public class JavascriptStateFinalizerExecutor extends StateFinalizerExecutor {
@Override
public void cleanUp() throws StateMachineException {
LOGGER.debug("cleanUp:" + getSubject().getKey().getId() + "," + getSubject().getLogicFlavour() + ","
- + getSubject().getLogic());
+ + getSubject().getLogic());
+
+ javascriptExecutor.cleanUp();
}
}
diff --git a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutor.java b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutor.java
index bec5670c5..a9dba27f2 100644
--- a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutor.java
+++ b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutor.java
@@ -53,11 +53,12 @@ public class JavascriptTaskExecutor extends TaskExecutor {
super.prepare();
// Create the executor
- javascriptExecutor = new JavascriptExecutor(getSubject().getKey());
+ if (javascriptExecutor == null) {
+ javascriptExecutor = new JavascriptExecutor(getSubject().getKey());
+ }
// Initialize and cleanup the executor to check the Javascript code
javascriptExecutor.init(getSubject().getTaskLogic().getLogic());
- javascriptExecutor.cleanUp();
}
/**
@@ -72,15 +73,13 @@ public class JavascriptTaskExecutor extends TaskExecutor {
*/
@Override
public Map<String, Object> execute(final long executionId, final Properties executionProperties,
- final Map<String, Object> incomingFields) throws StateMachineException, ContextException {
+ final Map<String, Object> incomingFields) throws StateMachineException, ContextException {
// Do execution pre work
executePre(executionId, executionProperties, incomingFields);
// Execute the Javascript executor
- javascriptExecutor.init(getSubject().getTaskLogic().getLogic());
boolean result = javascriptExecutor.execute(getExecutionContext());
- javascriptExecutor.cleanUp();
// Execute the Javascript
executePost(result);
@@ -96,6 +95,12 @@ public class JavascriptTaskExecutor extends TaskExecutor {
@Override
public void cleanUp() throws StateMachineException {
LOGGER.debug("cleanUp:" + getSubject().getKey().getId() + "," + getSubject().getTaskLogic().getLogicFlavour()
- + "," + getSubject().getTaskLogic().getLogic());
+ + "," + getSubject().getTaskLogic().getLogic());
+
+ if (javascriptExecutor != null) {
+ javascriptExecutor.cleanUp();
+ }
+
+ javascriptExecutor = null;
}
}
diff --git a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutor.java b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutor.java
index c32b70991..93384c129 100644
--- a/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutor.java
+++ b/plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutor.java
@@ -54,7 +54,9 @@ public class JavascriptTaskSelectExecutor extends TaskSelectExecutor {
super.prepare();
// Create the executor
- javascriptExecutor = new JavascriptExecutor(getSubject().getKey());
+ if (javascriptExecutor == null) {
+ javascriptExecutor = new JavascriptExecutor(getSubject().getKey());
+ }
// Initialize and cleanup the executor to check the Javascript code
javascriptExecutor.init(getSubject().getTaskSelectionLogic().getLogic());
@@ -72,14 +74,12 @@ public class JavascriptTaskSelectExecutor extends TaskSelectExecutor {
*/
@Override
public AxArtifactKey execute(final long executionId, final Properties executionProperties,
- final EnEvent incomingEvent) throws StateMachineException, ContextException {
+ final EnEvent incomingEvent) throws StateMachineException, ContextException {
// Do execution pre work
executePre(executionId, executionProperties, incomingEvent);
// Execute the Javascript executor
- javascriptExecutor.init(getSubject().getTaskSelectionLogic().getLogic());
boolean result = javascriptExecutor.execute(getExecutionContext());
- javascriptExecutor.cleanUp();
// Execute the Javascript
executePost(result);
@@ -94,8 +94,10 @@ public class JavascriptTaskSelectExecutor extends TaskSelectExecutor {
*/
@Override
public void cleanUp() throws StateMachineException {
- LOGGER.debug("cleanUp:" + getSubject().getKey().getId() + ","
- + getSubject().getTaskSelectionLogic().getLogicFlavour() + ","
- + getSubject().getTaskSelectionLogic().getLogic());
+ LOGGER.debug(
+ "cleanUp:" + getSubject().getKey().getId() + "," + getSubject().getTaskSelectionLogic().getLogicFlavour()
+ + "," + getSubject().getTaskSelectionLogic().getLogic());
+
+ javascriptExecutor.cleanUp();
}
}