Launch separate threads for Javascript task execution 30/104230/4
authorliamfallon <liam.fallon@est.tech>
Mon, 23 Mar 2020 17:49:50 +0000 (17:49 +0000)
committerliamfallon <liam.fallon@est.tech>
Tue, 24 Mar 2020 17:40:27 +0000 (17:40 +0000)
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>
examples/examples-decisionmaker/src/main/resources/logic/MakeDecisionStateTSL.js
examples/examples-decisionmaker/src/main/resources/policy/DecisionMakerPolicyModel.apex
plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptExecutor.java
plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutor.java
plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutor.java
plugins/plugins-executor/plugins-executor-javascript/src/main/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutor.java
plugins/plugins-executor/plugins-executor-javascript/src/test/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptStateFinalizerExecutorTest.java
plugins/plugins-executor/plugins-executor-javascript/src/test/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskExecutorTest.java
plugins/plugins-executor/plugins-executor-javascript/src/test/java/org/onap/policy/apex/plugins/executor/javascript/JavascriptTaskSelectExecutorTest.java
plugins/plugins-executor/plugins-executor-javascript/src/test/resources/logback-test.xml [new file with mode: 0644]

index 33af495..e49438d 100644 (file)
@@ -34,9 +34,9 @@ else if (executor.inFields.get("mode").equals("optimistic")) {
 else if (executor.inFields.get("mode").equals("dithering")) {
     executor.subject.getTaskKey("DitheringAnswerTask").copyTo(executor.selectedTask);
 }
-//else if (executor.inFields.get("mode").equals("roundrobin")) {
-//    executor.subject.getTaskKey("RoundRobinAnswerTask").copyTo(executor.selectedTask);
-//}
+else if (executor.inFields.get("mode").equals("roundrobin")) {
+    executor.subject.getTaskKey("RoundRobinAnswerTask").copyTo(executor.selectedTask);
+}
 
 executor.logger.info("Answer Selected Task:" + executor.selectedTask);
 
index 295afa1..672ff89 100644 (file)
@@ -113,18 +113,18 @@ task logic create name=DitheringAnswerTask logicFlavour=JAVASCRIPT logic=LS
 #MACROFILE:"src/main/resources/logic/DitheringAnswerTask.js"
 LE
 
-#task create name=RoundRobinAnswerTask
-#
-#task inputfield create name=RoundRobinAnswerTask fieldName=mode schemaName=SimpleStringType
-#
-#task outputfield create name=RoundRobinAnswerTask fieldName=decision schemaName=SimpleStringType
-#
-#task contextref create name=RoundRobinAnswerTask albumName=AnswerAlbum
-#task contextref create name=RoundRobinAnswerTask albumName=LastAnswerAlbum
-#
-#task logic create name=RoundRobinAnswerTask logicFlavour=JAVASCRIPT logic=LS
-##MACROFILE:"src/main/resources/logic/RoundRobinAnswerTask.js"
-#LE
+task create name=RoundRobinAnswerTask
+
+task inputfield create name=RoundRobinAnswerTask fieldName=mode schemaName=SimpleStringType
+
+task outputfield create name=RoundRobinAnswerTask fieldName=decision schemaName=SimpleStringType
+
+task contextref create name=RoundRobinAnswerTask albumName=AnswerAlbum
+task contextref create name=RoundRobinAnswerTask albumName=LastAnswerAlbum
+
+task logic create name=RoundRobinAnswerTask logicFlavour=JAVASCRIPT logic=LS
+#MACROFILE:"src/main/resources/logic/RoundRobinAnswerTask.js"
+LE
 
 policy create name=AnswerInitPolicy template=freestyle firstState=AnswerInitState
 
@@ -140,7 +140,7 @@ policy state taskref create name=DecisionMakerPolicy stateName=MakeDecisionState
 policy state taskref create name=DecisionMakerPolicy stateName=MakeDecisionState taskName=PessimisticAnswerTask outputType=DIRECT outputName=DecisionFinalOutput
 policy state taskref create name=DecisionMakerPolicy stateName=MakeDecisionState taskName=OptimisticAnswerTask outputType=DIRECT outputName=DecisionFinalOutput
 policy state taskref create name=DecisionMakerPolicy stateName=MakeDecisionState taskName=DitheringAnswerTask outputType=DIRECT outputName=DecisionFinalOutput
-#policy state taskref create name=DecisionMakerPolicy stateName=MakeDecisionState taskName=RoundRobinAnswerTask outputType=DIRECT outputName=DecisionFinalOutput
+policy state taskref create name=DecisionMakerPolicy stateName=MakeDecisionState taskName=RoundRobinAnswerTask outputType=DIRECT outputName=DecisionFinalOutput
 
 policy state selecttasklogic create name=DecisionMakerPolicy stateName=MakeDecisionState logicFlavour=JAVASCRIPT logic=LS
 #MACROFILE:"src/main/resources/logic/MakeDecisionStateTSL.js"
index c80f58f..2394b83 100644 (file)
 
 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;
+    }
 }
index 27e649f..63e4948 100644 (file)
@@ -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();
     }
 }
index bec5670..a9dba27 100644 (file)
@@ -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;
     }
 }
index c32b709..93384c1 100644 (file)
@@ -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();
     }
 }
index 8be7955..d4a3461 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.apex.plugins.executor.javascript;
 
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -96,36 +97,59 @@ public class JavascriptStateFinalizerExecutorTest {
         assertThatThrownBy(() -> {
             jsfe.prepare();
         }).hasMessage("logic failed to compile for NULL:0.0.0:NULL:NULL "
-                + "with message: invalid return (NULL:0.0.0:NULL:NULL#1)");
+            + "with message: invalid return (NULL:0.0.0:NULL:NULL#1)");
 
         Map<String, Object> incomingParameters1 = new HashMap<>();
         assertThatThrownBy(() -> {
             jsfe.execute(-1, new Properties(), incomingParameters1);
-        }).hasMessage("logic failed to compile for NULL:0.0.0:NULL:NULL "
-                + "with message: invalid return (NULL:0.0.0:NULL:NULL#1)");
+        }).hasMessage("execution failed, executor NULL:0.0.0:NULL:NULL is not running");
+
+        assertThatThrownBy(() -> {
+            jsfe.prepare();
+        }).hasMessage("initiation failed, executor NULL:0.0.0:NULL:NULL failed to start");
 
+        assertThatThrownBy(() -> {
+            jsfe.cleanUp();
+        }).hasMessage("cleanup failed, executor NULL:0.0.0:NULL:NULL is not running");
+
+        JavascriptStateFinalizerExecutor jsfe1 = new JavascriptStateFinalizerExecutor();
         stateFinalizerLogic.setLogic("java.lang.String");
-        jsfe.prepare();
+        jsfe1.setContext(parentStateExcutor, stateFinalizerLogic, internalContext);
+        jsfe1.prepare();
 
         AxEvent axEvent = new AxEvent(new AxArtifactKey("Event", "0.0.1"));
         EnEvent event = new EnEvent(axEvent);
-        stateFinalizerLogic.setLogic("if(executor.executionId==-1)" + "{\r\n"
-                + "var returnValueType = java.lang.Boolean;" + "var returnValue = new returnValueType(false); }\n"
-                + "else{\n" + "executor.setSelectedStateOutputName(\"SelectedOutputIsMe\");\n"
-                + "var returnValueType = java.lang.Boolean;\n" + "\n"
-                + "var returnValue = new returnValueType(true);} true;");
 
         assertThatThrownBy(() -> {
-            jsfe.prepare();
-            jsfe.execute(-1, new Properties(), event);
-        }).hasMessage("execute-post: state finalizer logic \"NULL:0.0.0:NULL:NULL\" did not select an output state");
+            jsfe1.execute(-1, new Properties(), event);
+        }).hasMessage(
+            "execute: logic for NULL:0.0.0:NULL:NULL returned a non-boolean value [JavaClass java.lang.String]");
+
+        assertThatThrownBy(() -> {
+            jsfe1.execute(-1, new Properties(), event);
+        }).hasMessage(
+            "execute: logic for NULL:0.0.0:NULL:NULL returned a non-boolean value [JavaClass java.lang.String]");
+
+        assertThatCode(() -> {
+            jsfe1.cleanUp();
+        }).doesNotThrowAnyException();
+
+        JavascriptStateFinalizerExecutor jsfe2 = new JavascriptStateFinalizerExecutor();
+
+        stateFinalizerLogic.setLogic("executor.setSelectedStateOutputName(\"SelectedOutputIsMe\");\n true;");
+
+        jsfe2.setContext(parentStateExcutor, stateFinalizerLogic, internalContext);
+        assertThatCode(() -> {
+            jsfe2.prepare();
+        }).doesNotThrowAnyException();
 
         state.getStateOutputs().put("SelectedOutputIsMe", null);
 
-        jsfe.prepare();
-        String stateOutput = jsfe.execute(0, new Properties(), event);
+        String stateOutput = jsfe2.execute(0, new Properties(), event);
         assertEquals("SelectedOutputIsMe", stateOutput);
 
-        jsfe.cleanUp();
+        assertThatCode(() -> {
+            jsfe2.cleanUp();
+        }).doesNotThrowAnyException();
     }
 }
index c327ebb..b532d6c 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.apex.plugins.executor.javascript;
 
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -64,7 +65,7 @@ public class JavascriptTaskExecutorTest {
     public static void prepareForTest() {
         final ContextParameters contextParameters = new ContextParameters();
         contextParameters.getLockManagerParameters()
-                .setPluginClass("org.onap.policy.apex.context.impl.locking.jvmlocal.JvmLocalLockManager");
+            .setPluginClass("org.onap.policy.apex.context.impl.locking.jvmlocal.JvmLocalLockManager");
 
         contextParameters.setName(ContextParameterConstants.MAIN_GROUP_NAME);
         contextParameters.getDistributorParameters().setName(ContextParameterConstants.DISTRIBUTOR_GROUP_NAME);
@@ -98,32 +99,47 @@ public class JavascriptTaskExecutorTest {
 
     @Test
     public void testJavascriptTaskExecutor() throws Exception {
-        JavascriptTaskExecutor jte = new JavascriptTaskExecutor();
-        assertNotNull(jte);
-
         assertThatThrownBy(() -> {
-            jte.prepare();
+            JavascriptTaskExecutor jteBadPrep = new JavascriptTaskExecutor();
+            jteBadPrep.prepare();
         }).isInstanceOf(NullPointerException.class);
 
         AxTask task = new AxTask(new AxArtifactKey("TestTask:0.0.1"));
         final ApexInternalContext internalContext = new ApexInternalContext(new AxPolicyModel());
 
-        jte.setContext(null, task, internalContext);
+        JavascriptTaskExecutor jteBadLogic = new JavascriptTaskExecutor();
+        assertNotNull(jteBadLogic);
+
+        jteBadLogic.setContext(null, task, internalContext);
 
         task.getTaskLogic().setLogic("return boolean;");
 
         assertThatThrownBy(() -> {
-            jte.prepare();
-            jte.execute(-1, new Properties(), null);
+            jteBadLogic.prepare();
         }).hasMessage("logic failed to compile for TestTask:0.0.1 with message: invalid return (TestTask:0.0.1#1)");
 
         task.getTaskLogic().setLogic("var x = 5;");
 
+        JavascriptTaskExecutor jte = new JavascriptTaskExecutor();
+        jte.setContext(null, task, internalContext);
+
         jte.prepare();
+
+        assertThatThrownBy(() -> {
+            jte.prepare();
+        }).hasMessage("initiation failed, executor TestTask:0.0.1 is already running");
+
         assertThatThrownBy(() -> {
             jte.execute(-1, new Properties(), null);
         }).isInstanceOf(NullPointerException.class);
-        jte.cleanUp();
+
+        assertThatThrownBy(() -> {
+            jte.execute(-1, new Properties(), null);
+        }).isInstanceOf(NullPointerException.class);
+
+        assertThatCode(() -> {
+            jte.cleanUp();
+        }).doesNotThrowAnyException();
 
         task.getTaskLogic().setLogic("var returnValue = false;\nreturnValue;");
 
@@ -142,6 +158,10 @@ public class JavascriptTaskExecutorTest {
         Map<String, Object> returnMap = jte.execute(0, new Properties(), incomingParameters);
         assertEquals(0, returnMap.size());
         jte.cleanUp();
+
+        assertThatCode(() -> {
+            jte.cleanUp();
+        }).doesNotThrowAnyException();
     }
 
     @Test
@@ -188,12 +208,12 @@ public class JavascriptTaskExecutorTest {
     private ContextAlbum createTestContextAlbum() throws ContextException {
         AxContextSchemas schemas = new AxContextSchemas();
         AxContextSchema simpleStringSchema =
-                new AxContextSchema(new AxArtifactKey("SimpleStringSchema", "0.0.1"), "JAVA", "java.lang.String");
+            new AxContextSchema(new AxArtifactKey("SimpleStringSchema", "0.0.1"), "JAVA", "java.lang.String");
         schemas.getSchemasMap().put(simpleStringSchema.getKey(), simpleStringSchema);
         ModelService.registerModel(AxContextSchemas.class, schemas);
 
         AxContextAlbum axContextAlbum = new AxContextAlbum(new AxArtifactKey("TestContextAlbum", "0.0.1"), "Policy",
-                true, AxArtifactKey.getNullKey());
+            true, AxArtifactKey.getNullKey());
 
         axContextAlbum.setItemSchema(simpleStringSchema.getKey());
         Distributor distributor = new JvmLocalDistributor();
index 3acf132..cb57753 100644 (file)
@@ -20,6 +20,7 @@
 
 package org.onap.policy.apex.plugins.executor.javascript;
 
+import static org.assertj.core.api.Assertions.assertThatCode;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
@@ -90,7 +91,7 @@ public class JavascriptTaskSelectExecutorTest {
 
         assertThatThrownBy(() -> {
             jtse.execute(-1, new Properties(), event1);
-        }).hasMessage("no logic specified for NULL:0.0.0:NULL:NULL");
+        }).hasMessage("execution failed, executor NULL:0.0.0:NULL:NULL is not running");
 
         state.getTaskSelectionLogic().setLogic("java.lang.String");
         jtse.prepare();
@@ -105,20 +106,32 @@ public class JavascriptTaskSelectExecutorTest {
         assertThatThrownBy(() -> {
             jtse.execute(-1, new Properties(), event);
         }).hasMessage(
-                "execute: logic for NULL:0.0.0:NULL:NULL returned a non-boolean value [JavaClass java.lang.String]");
+            "execute: logic for NULL:0.0.0:NULL:NULL returned a non-boolean value [JavaClass java.lang.String]");
 
-        state.getTaskSelectionLogic().setLogic("var x=1;\n" + "false; ");
+        state.getTaskSelectionLogic().setLogic("var x=1;\n" + "false;");
 
         assertThatThrownBy(() -> {
-            jtse.prepare();
             jtse.execute(-1, new Properties(), event);
-        }).hasMessage("execute-post: task selection logic failed on state \"NULL:0.0.0:NULL:NULL\"");
-
-        state.getTaskSelectionLogic().setLogic("var x = 1\n" + "true; ");
+        }).hasMessage(
+            "execute: logic for NULL:0.0.0:NULL:NULL returned a non-boolean value [JavaClass java.lang.String]");
 
-        jtse.prepare();
-        AxArtifactKey taskKey = jtse.execute(0, new Properties(), event);
-        assertEquals("NULL:0.0.0", taskKey.getId());
-        jtse.cleanUp();
+        assertThatThrownBy(() -> {
+            jtse.prepare();
+        }).hasMessage("initiation failed, executor NULL:0.0.0:NULL:NULL is already running");
+
+        assertThatCode(() -> {
+            jtse.cleanUp();
+        }).doesNotThrowAnyException();
+
+        JavascriptTaskSelectExecutor jtse1 = new JavascriptTaskSelectExecutor();
+        jtse1.setContext(null, state, internalContext);
+        state.getTaskSelectionLogic().setLogic("var x = 1\n" + "true;");
+
+        assertThatCode(() -> {
+            jtse1.prepare();
+            AxArtifactKey taskKey = jtse1.execute(0, new Properties(), event);
+            assertEquals("NULL:0.0.0", taskKey.getId());
+            jtse1.cleanUp();
+        }).doesNotThrowAnyException();
     }
 }
diff --git a/plugins/plugins-executor/plugins-executor-javascript/src/test/resources/logback-test.xml b/plugins/plugins-executor/plugins-executor-javascript/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..f3bc0c8
--- /dev/null
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2016-2018 Ericsson. All rights reserved.
+   Modifications Copyright (C) 2020 Nordix Foundation.
+  ================================================================================
+  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.
+
+  SPDX-License-Identifier: Apache-2.0
+  ============LICENSE_END=========================================================
+-->
+
+<configuration>
+    <contextName>Apex</contextName>
+    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
+
+    <!-- USE FOR STD OUT ONLY -->
+    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+        <encoder>
+            <Pattern>%d %contextName [%t] %level %logger{36} - %msg%n</Pattern>
+        </encoder>
+    </appender>
+
+    <root level="INFO">
+        <appender-ref ref="STDOUT" />
+    </root>
+
+    <logger name="org.onap.policy.apex.plugins.executor.javascript" level="INFO" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+</configuration>