Adding apex core module to apex-pdp 29/49929/4
authorramverma <ram.krishna.verma@ericsson.com>
Fri, 1 Jun 2018 10:51:36 +0000 (11:51 +0100)
committerramverma <ram.krishna.verma@ericsson.com>
Mon, 4 Jun 2018 09:50:44 +0000 (10:50 +0100)
Change-Id: I4bfe1df3e44fe62ff6789e813e59836e267ab3b2
Issue-ID: POLICY-858
Signed-off-by: ramverma <ram.krishna.verma@ericsson.com>
123 files changed:
core/core-deployment/pom.xml [new file with mode: 0644]
core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/ApexDeploymentException.java [new file with mode: 0644]
core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/BatchDeployer.java [new file with mode: 0644]
core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/DeploymentClient.java [new file with mode: 0644]
core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/EngineServiceFacade.java [new file with mode: 0644]
core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/PeriodicEventManager.java [new file with mode: 0644]
core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/package-info.java [new file with mode: 0644]
core/core-engine/pom.xml [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/EngineParameters.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/ExecutorParameters.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/ApexInternalContext.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/ApexEngine.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/EnEventListener.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineFactory.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineImpl.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/StateMachineHandler.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnEvent.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnException.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnField.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/Executor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/ExecutorFactory.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateExecutor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateFinalizerExecutor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateMachineExecutor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateOutput.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskExecutor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskSelectExecutor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxStateFacade.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxTaskFacade.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/StateFinalizerExecutionContext.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskExecutionContext.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskSelectionExecutionContext.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineException.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineRuntimeException.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/ExecutorFactoryImpl.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/EventMonitor.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/package-info.java [new file with mode: 0644]
core/core-engine/src/main/java/org/onap/policy/apex/core/engine/package-info.java [new file with mode: 0644]
core/core-infrastructure/pom.xml [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/JavaHandlingException.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/ClassUtils.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageHolder.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageListener.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingException.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingService.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingServiceFactory.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/RawMessageHandler.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/WebSocketMessageListener.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/InternalMessageBusClient.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/MessagingClient.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/WebSocketClientImpl.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlock.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlockHandler.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/RawMessageBlock.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/InternalMessageBusServer.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/MessageServerImpl.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/WebSocketServerImpl.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageClient.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageListener.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageServer.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessager.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/MessagingUtils.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ApplicationThreadFactory.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ThreadUtilities.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/XPathReader.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassBuilder.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassByteCodeFileObject.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassCompilationUnit.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassLoader.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleFileManager.java [new file with mode: 0644]
core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/package-info.java [new file with mode: 0644]
core/core-infrastructure/src/main/resources/logback.xml [new file with mode: 0644]
core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/EndToEndStringMessagingTest.java [new file with mode: 0644]
core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/StringTestServer.java [new file with mode: 0644]
core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/TestMessageListener.java [new file with mode: 0644]
core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTest.java [new file with mode: 0644]
core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTestThread.java [new file with mode: 0644]
core/core-infrastructure/src/test/resources/logback-test.xml [new file with mode: 0644]
core/core-protocols/pom.xml [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Action.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Message.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/EngDepAction.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/EngineServiceInfoResponse.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineInfo.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineServiceInfo.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineStatus.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/Response.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartEngine.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartPeriodicEvents.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopEngine.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopPeriodicEvents.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/UpdateModel.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/package-info.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/package-info.java [new file with mode: 0644]
core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/package-info.java [new file with mode: 0644]
core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetExecutionStatusTest.java [new file with mode: 0644]
core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetPolicyStatusTest.java [new file with mode: 0644]
core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/ResponseTest.java [new file with mode: 0644]
core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/UpdateModelTest.java [new file with mode: 0644]
core/core-protocols/src/test/resources/logback-test.xml [new file with mode: 0644]
core/pom.xml [new file with mode: 0644]
model/engine-model/src/test/java/org/onap/policy/apex/model/enginemodel/concepts/TestEngineStats.java
pom.xml

diff --git a/core/core-deployment/pom.xml b/core/core-deployment/pom.xml
new file mode 100644 (file)
index 0000000..e267b87
--- /dev/null
@@ -0,0 +1,55 @@
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2018 Ericsson. All rights reserved.
+  ================================================================================
+  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=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onap.policy.apex-pdp.core</groupId>
+        <artifactId>core</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>core-deployment</artifactId>
+    <name>${project.artifactId}</name>
+    <description>The Apex policy deployer</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.model</groupId>
+            <artifactId>policy-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.model</groupId>
+            <artifactId>engine-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.core</groupId>
+            <artifactId>core-infrastructure</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.core</groupId>
+            <artifactId>core-protocols</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/ApexDeploymentException.java b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/ApexDeploymentException.java
new file mode 100644 (file)
index 0000000..e932bbd
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.deployment;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+
+/**
+ * The Class ApexDeploymentException is an exception that may be thrown on deployment errors in Apex.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ApexDeploymentException extends ApexException {
+    private static final long serialVersionUID = 1816909564890470707L;
+
+    /**
+     * Instantiates a new apex deployment exception.
+     *
+     * @param message the message
+     */
+    public ApexDeploymentException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new apex deployment exception.
+     *
+     * @param message the message
+     * @param e the e
+     */
+    public ApexDeploymentException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/BatchDeployer.java b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/BatchDeployer.java
new file mode 100644 (file)
index 0000000..499644f
--- /dev/null
@@ -0,0 +1,154 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.deployment;
+
+import java.io.IOException;
+import java.util.Arrays;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class {@link BatchDeployer} deploys an Apex model held as an XML or Json file onto an Apex engine. It uses the
+ * EngDep protocol to communicate with the engine, with the EngDep protocol being carried on Java web sockets.
+ *
+ * This deployer is a simple command line deployer that reads the communication parameters and the location of the Apex
+ * model file as arguments.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class BatchDeployer {
+    private static final int NUM_ARGUMENTS = 3;
+
+    // Get a reference to the logger
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(BatchDeployer.class);
+
+    // The facade that is handling messaging to the engine service
+    private EngineServiceFacade engineServiceFacade = null;
+
+    /**
+     * The main method, reads the Apex server host address, port and location of the Apex model file from the command
+     * line arguments.
+     *
+     * @param args the arguments that specify the Apex engine and the Apex model file
+     */
+    public static void main(final String[] args) {
+        if (args.length != NUM_ARGUMENTS) {
+            LOGGER.error("invalid arguments: " + Arrays.toString(args));
+            LOGGER.error("usage: Deployer <server address> <port address> <Apex Model file location>");
+            return;
+        }
+
+        BatchDeployer deployer = null;
+        try {
+            // Use a Deployer object to handle model deployment
+            deployer = new BatchDeployer(args[0], Integer.parseInt(args[1]));
+            deployer.init();
+            deployer.deployModel(args[2], false, false);
+            deployer.startEngines();
+        } catch (final ApexException | IOException e) {
+            LOGGER.error("model deployment failed on parameters {}", args, e);
+        } finally {
+            if (deployer != null) {
+                deployer.close();
+            }
+        }
+    }
+
+    /**
+     * Instantiates a new deployer.
+     *
+     * @param hostName the host name of the host running the Apex Engine
+     * @param port the port to use for EngDep communication with the Apex engine
+     */
+    public BatchDeployer(final String hostName, final int port) {
+        engineServiceFacade = new EngineServiceFacade(hostName, port);
+    }
+
+    /**
+     * Initializes the deployer, opens an EngDep communication session with the Apex engine.
+     *
+     * @throws ApexDeploymentException thrown on deployment and communication errors
+     */
+    public void init() throws ApexDeploymentException {
+        engineServiceFacade.init();
+    }
+
+    /**
+     * Close the EngDep connection to the Apex server.
+     */
+    public void close() {
+        engineServiceFacade.close();
+    }
+
+    /**
+     * Deploy an Apex model on the Apex server.
+     *
+     * @param modelFileName the name of the model file containing the model to deploy
+     * @param ignoreConflicts true if conflicts between context in polices is to be ignored
+     * @param force true if the model is to be applied even if it is incompatible with the existing model
+     * @throws ApexException on Apex errors
+     * @throws IOException on IO exceptions from the operating system
+     */
+    public void deployModel(final String modelFileName, final boolean ignoreConflicts, final boolean force)
+            throws ApexException, IOException {
+        engineServiceFacade.deployModel(modelFileName, ignoreConflicts, force);
+    }
+
+    /**
+     * Deploy an Apex model on the Apex server.
+     *
+     * @param policyModel the model to deploy
+     * @param ignoreConflicts true if conflicts between context in polices is to be ignored
+     * @param force true if the model is to be applied even if it is incompatible with the existing model
+     * @throws ApexException on Apex errors
+     * @throws IOException on IO exceptions from the operating system
+     */
+    public void deployModel(final AxPolicyModel policyModel, final boolean ignoreConflicts, final boolean force)
+            throws ApexException, IOException {
+        engineServiceFacade.deployModel(policyModel, ignoreConflicts, force);
+    }
+
+    /**
+     * Start the Apex engines on the engine service.
+     *
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void startEngines() throws ApexDeploymentException {
+        for (final AxArtifactKey engineKey : engineServiceFacade.getEngineKeyArray()) {
+            engineServiceFacade.startEngine(engineKey);
+        }
+    }
+
+    /**
+     * Stop the Apex engines on the engine service.
+     *
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void stopEngines() throws ApexDeploymentException {
+        for (final AxArtifactKey engineKey : engineServiceFacade.getEngineKeyArray()) {
+            engineServiceFacade.stopEngine(engineKey);
+        }
+    }
+}
diff --git a/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/DeploymentClient.java b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/DeploymentClient.java
new file mode 100644 (file)
index 0000000..c2a19a1
--- /dev/null
@@ -0,0 +1,202 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.deployment;
+
+import com.google.common.eventbus.Subscribe;
+
+import java.net.URI;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageHolder;
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingService;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingServiceFactory;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlock;
+import org.onap.policy.apex.core.infrastructure.messaging.util.MessagingUtils;
+import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities;
+import org.onap.policy.apex.core.protocols.Message;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class DeploymentClient handles the client side of an EngDep communication session with an Apex server. It runs a
+ * thread to handle message sending and session monitoring. It uses a sending queue to queue messages for sending by the
+ * client thread and a receiving queue to queue messages received from the Apex engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class DeploymentClient implements Runnable {
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(DeploymentClient.class);
+
+    private static final int CLIENT_STOP_WAIT_INTERVAL = 100;
+
+    // Host and port to use for EngDep messaging
+    private String host = null;
+    private int port = 0;
+
+    // Messaging service is used to transmit and receive messages over the web socket
+    private static MessagingServiceFactory<Message> factory = new MessagingServiceFactory<>();
+    private MessagingService<Message> service = null;
+
+    // Send and receive queues for message buffering
+    private final BlockingQueue<Message> sendQueue = new LinkedBlockingQueue<>();
+    private final BlockingQueue<Message> receiveQueue = new LinkedBlockingQueue<>();
+
+    // Thread management fields
+    private boolean started = false;
+    private Thread thisThread = null;
+
+    /**
+     * Instantiates a new deployment client.
+     *
+     * @param host the host name that the EngDep server is running on
+     * @param port the port the port the EngDep server is using
+     */
+    public DeploymentClient(final String host, final int port) {
+        this.host = host;
+        this.port = port;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Runnable#run()
+     */
+    @Override
+    public void run() {
+        LOGGER.debug("engine<-->deployment to \"ws://" + host + ":" + port + "\" thread starting . . .");
+
+        // Set up the thread name
+        thisThread = Thread.currentThread();
+        thisThread.setName(DeploymentClient.class.getName() + "-" + host + ":" + port);
+
+        try {
+            // Establish a connection to the Apex server for EngDep message communication over Web Sockets
+            service = factory.createClient(new URI("ws://" + host + ":" + port));
+            service.addMessageListener(new DeploymentClientListener());
+
+            service.startConnection();
+            started = true;
+            LOGGER.debug("engine<-->deployment client thread started");
+        } catch (final Exception e) {
+            LOGGER.error("engine<-->deployment client thread exception", e);
+            return;
+        }
+
+        // Loop forever, sending messages as they appear on the queue
+        while (true) {
+            try {
+                final Message messageForSending = sendQueue.take();
+                sendMessage(messageForSending);
+            } catch (final InterruptedException e) {
+                // Message sending has been interrupted, we are finished
+                LOGGER.debug("engine<-->deployment client interrupted");
+                break;
+            }
+        }
+
+        // Thread has been interrupted
+        thisThread = null;
+        LOGGER.debug("engine<-->deployment client thread finished");
+    }
+
+    /**
+     * Send an EngDep message to the Apex server.
+     *
+     * @param message the message to send to the Apex server
+     */
+    public void sendMessage(final Message message) {
+        final MessageHolder<Message> messageHolder = new MessageHolder<>(MessagingUtils.getHost());
+
+        // Send the message in its message holder
+        messageHolder.addMessage(message);
+        service.send(messageHolder);
+    }
+
+    /**
+     * Stop the deployment client.
+     */
+    public void stopClient() {
+        LOGGER.debug("engine<-->deployment test client stopping . . .");
+        thisThread.interrupt();
+
+        // Wait for the thread to stop
+        while (thisThread != null && thisThread.isAlive()) {
+            ThreadUtilities.sleep(CLIENT_STOP_WAIT_INTERVAL);
+        }
+
+        // Close the Web Services connection
+        service.stopConnection();
+        started = false;
+        LOGGER.debug("engine<-->deployment test client stopped . . .");
+    }
+
+    /**
+     * Checks if the client thread is started.
+     *
+     * @return true, if the client thread is started
+     */
+    public boolean isStarted() {
+        return started;
+    }
+
+    /**
+     * Allows users of this class to get a reference to the receive queue to receove messages.
+     *
+     * @return the receive queue
+     */
+    public BlockingQueue<Message> getReceiveQueue() {
+        return receiveQueue;
+    }
+
+    /**
+     * The listener interface for receiving deploymentClient events. The class that is interested in processing a
+     * deploymentClient event implements this interface, and the object created with that class is registered with a
+     * component using the component's {@code addDeploymentClientListener} method. When the deploymentClient event
+     * occurs, that object's appropriate method is invoked.
+     *
+     * @see DeploymentClientEvent
+     */
+    private class DeploymentClientListener implements MessageListener<Message> {
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(org.onap.policy.apex.core.
+         * infrastructure.messaging.impl.ws.messageblock. MessageBlock)
+         */
+        @Subscribe
+        @Override
+        public void onMessage(final MessageBlock<Message> messageData) {
+            receiveQueue.addAll(messageData.getMessages());
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(java.lang.String)
+         */
+        @Override
+        public void onMessage(final String messageString) {
+            throw new UnsupportedOperationException("String mesages are not supported on the EngDep protocol");
+        }
+    }
+}
diff --git a/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/EngineServiceFacade.java b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/EngineServiceFacade.java
new file mode 100644 (file)
index 0000000..d954fea
--- /dev/null
@@ -0,0 +1,480 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.deployment;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.concurrent.TimeUnit;
+
+import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities;
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.messages.EngineServiceInfoResponse;
+import org.onap.policy.apex.core.protocols.engdep.messages.GetEngineInfo;
+import org.onap.policy.apex.core.protocols.engdep.messages.GetEngineServiceInfo;
+import org.onap.policy.apex.core.protocols.engdep.messages.GetEngineStatus;
+import org.onap.policy.apex.core.protocols.engdep.messages.Response;
+import org.onap.policy.apex.core.protocols.engdep.messages.StartEngine;
+import org.onap.policy.apex.core.protocols.engdep.messages.StartPeriodicEvents;
+import org.onap.policy.apex.core.protocols.engdep.messages.StopEngine;
+import org.onap.policy.apex.core.protocols.engdep.messages.StopPeriodicEvents;
+import org.onap.policy.apex.core.protocols.engdep.messages.UpdateModel;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelReader;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelWriter;
+import org.onap.policy.apex.model.enginemodel.concepts.AxEngineModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.apex.model.utilities.ResourceUtils;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class Deployer deploys an Apex model held as an XML file onto an Apex engine. It uses the EngDep protocol to
+ * communicate with the engine, with the EngDep protocol being carried on Java web sockets.
+ *
+ * This deployer is a simple command line deployer that reads the communication parameters and the location of the XML
+ * model file as arguments.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EngineServiceFacade {
+    // Get a reference to the logger
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(EngineServiceFacade.class);
+
+    // The default message timeout and timeout increment (the amount of time between polls) in milliseconds
+    private static final int CLIENT_START_WAIT_INTERVAL = 100;
+    private static final int REPLY_MESSAGE_TIMEOUT_DEFAULT = 10000;
+    private static final int REPLY_MESSAGE_TIMEOUT_INCREMENT = 100;
+
+    // The Apex engine host and EngDep port
+    private final String hostName;
+    private final int port;
+
+    // The deployment client handles the EngDep communication session towards the Apex server
+    private DeploymentClient client = null;
+    private Thread clientThread = null;
+
+    // Information about the Engine service we are connected to
+    private AxArtifactKey engineServiceKey = null;
+    private AxArtifactKey[] engineKeyArray = null;
+    private AxArtifactKey apexModelKey = null;
+
+    /**
+     * Instantiates a new deployer.
+     *
+     * @param hostName the host name of the host running the Apex Engine
+     * @param port the port to use for EngDep communication with the Apex engine
+     */
+    public EngineServiceFacade(final String hostName, final int port) {
+        this.hostName = hostName;
+        this.port = port;
+    }
+
+    /**
+     * Initializes the facade, opens an EngDep communication session with the Apex engine.
+     *
+     * @throws ApexDeploymentException thrown on deployment and communication errors
+     */
+    public void init() throws ApexDeploymentException {
+        try {
+            LOGGER.debug("handshaking with server {}:{} . . .", hostName, port);
+
+            // Use the deployment client to handle the EngDep communication towards the Apex server. It runs a thread to
+            // monitor the session and to send
+            // messages
+            client = new DeploymentClient(hostName, port);
+            clientThread = new Thread(client);
+            clientThread.start();
+
+            // Wait for the connection to come up
+            while (!client.isStarted()) {
+                if (clientThread.isAlive()) {
+                    ThreadUtilities.sleep(CLIENT_START_WAIT_INTERVAL);
+                } else {
+                    LOGGER.error("cound not handshake with server {}:{}", hostName, port);
+                    throw new ApexDeploymentException("cound not handshake with server " + hostName + ":" + port);
+                }
+            }
+
+            LOGGER.debug("opened connection to server {}:{} . . .", hostName, port);
+
+            // Get engine service information to see what engines we're dealing with
+            final GetEngineServiceInfo engineServiceInfo = new GetEngineServiceInfo(null);
+            LOGGER.debug("sending get engine service info message {} to server {}:{} . . .", engineServiceInfo,
+                    hostName, port);
+            client.sendMessage(engineServiceInfo);
+            LOGGER.debug("sent get engine service info message to server {}:{} . . .", hostName, port);
+
+            final EngineServiceInfoResponse engineServiceInfoResponse =
+                    (EngineServiceInfoResponse) getResponse(engineServiceInfo);
+            if (engineServiceInfoResponse.isSuccessful()) {
+                engineServiceKey = engineServiceInfoResponse.getEngineServiceKey();
+                engineKeyArray = engineServiceInfoResponse.getEngineKeyArray();
+                apexModelKey = engineServiceInfoResponse.getApexModelKey();
+            }
+        } catch (final Exception e) {
+            LOGGER.error("cound not handshake with server {}:{}", hostName, port, e);
+            client.stopClient();
+            throw new ApexDeploymentException("cound not handshake with server " + hostName + ":" + port, e);
+        }
+
+    }
+
+    /**
+     * Get the engine service key.
+     *
+     * @return the engine service key
+     */
+    public AxArtifactKey getApexModelKey() {
+        return apexModelKey;
+    }
+
+    /**
+     * Get the keys of the engines on this engine service.
+     *
+     * @return the engine key array
+     */
+    public AxArtifactKey[] getEngineKeyArray() {
+        return engineKeyArray;
+    }
+
+    /**
+     * Get the engine service key.
+     *
+     * @return the engine service key
+     */
+    public AxArtifactKey getKey() {
+        return engineServiceKey;
+    }
+
+    /**
+     * Close the EngDep connection to the Apex server.
+     */
+    public void close() {
+        LOGGER.debug("closing connection to server {}:{} . . .", hostName, port);
+
+        client.stopClient();
+
+        LOGGER.debug("closed connection to server {}:{} . . .", hostName, port);
+    }
+
+    /**
+     * Deploy an Apex model on the Apex engine service.
+     *
+     * @param modelFileName the name of the model file containing the model to deploy
+     * @param ignoreConflicts true if conflicts between context in polices is to be ignored
+     * @param force true if the model is to be applied even if it is incompatible with the existing model
+     * @throws ApexException on Apex errors
+     * @throws IOException on IO exceptions from the operating system
+     */
+    public void deployModel(final String modelFileName, final boolean ignoreConflicts, final boolean force)
+            throws ApexException, IOException {
+        if (engineServiceKey == null || engineKeyArray == null || engineKeyArray.length == 0) {
+            LOGGER.error("cound not deploy apex model, deployer is not initialized");
+            throw new ApexDeploymentException("cound not deploy apex model, deployer is not initialized");
+        }
+
+        // Get the model file as a string
+        URL apexModelURL = ResourceUtils.getLocalFile(modelFileName);
+        if (apexModelURL == null) {
+            apexModelURL = ResourceUtils.getURLResource(modelFileName);
+            if (apexModelURL == null) {
+                LOGGER.error("cound not create apex model, could not read from XML file {}", modelFileName);
+                throw new ApexDeploymentException(
+                        "cound not create apex model, could not read XML file " + modelFileName);
+            }
+        }
+
+        deployModel(modelFileName, apexModelURL.openStream(), ignoreConflicts, force);
+    }
+
+    /**
+     * Deploy an Apex model on the Apex engine service.
+     *
+     * @param modelFileName the name of the model file containing the model to deploy
+     * @param modelInputStream the stream that holds the Apex model
+     * @param ignoreConflicts true if conflicts between context in polices is to be ignored
+     * @param force true if the model is to be applied even if it is incompatible with the existing model
+     * @throws ApexException on model deployment errors
+     */
+    public void deployModel(final String modelFileName, final InputStream modelInputStream,
+            final boolean ignoreConflicts, final boolean force) throws ApexException {
+        // Read the policy model from the stream
+        final ApexModelReader<AxPolicyModel> modelReader = new ApexModelReader<>(AxPolicyModel.class);
+        modelReader.setValidateFlag(!ignoreConflicts);
+        final AxPolicyModel apexPolicyModel = modelReader.read(modelInputStream);
+        if (apexPolicyModel == null) {
+            LOGGER.error("cound not create apex model, could not read model stream");
+            throw new ApexDeploymentException("cound not create apex model, could not read model stream");
+        }
+
+        // Deploy the model
+        deployModel(apexPolicyModel, ignoreConflicts, force);
+    }
+
+    /**
+     * Deploy an Apex model on the Apex engine service.
+     *
+     * @param apexPolicyModel the name of the model to deploy
+     * @param ignoreConflicts true if conflicts between context in polices is to be ignored
+     * @param force true if the model is to be applied even if it is incompatible with the existing model
+     * @throws ApexException on model deployment errors
+     */
+    public void deployModel(final AxPolicyModel apexPolicyModel, final boolean ignoreConflicts, final boolean force)
+            throws ApexException {
+        // Write the model into a byte array
+        final ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream();
+        final ApexModelWriter<AxPolicyModel> modelWriter = new ApexModelWriter<>(AxPolicyModel.class);
+        modelWriter.write(apexPolicyModel, baOutputStream);
+
+        // Create and send Update message
+        final UpdateModel umMessage =
+                new UpdateModel(engineServiceKey, baOutputStream.toString(), ignoreConflicts, force);
+
+        LOGGER.debug("sending update message {} to server {}:{} . . .", umMessage, hostName, port);
+        client.sendMessage(umMessage);
+        LOGGER.debug("sent update message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(umMessage);
+        if (response.isSuccessful()) {
+            LOGGER.debug(response.toString());
+        } else {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+    }
+
+    /**
+     * Start an Apex engine on the engine service.
+     *
+     * @param engineKey the key of the engine to start
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void startEngine(final AxArtifactKey engineKey) throws ApexDeploymentException {
+        final StartEngine startEngineMessage = new StartEngine(engineKey);
+        LOGGER.debug("sending start engine {} to server {}:{} . . .", startEngineMessage, hostName, port);
+        client.sendMessage(startEngineMessage);
+        LOGGER.debug("sent start engine message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(startEngineMessage);
+        if (response.isSuccessful()) {
+            LOGGER.debug(response.toString());
+        } else {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexDeploymentException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+    }
+
+    /**
+     * Stop an Apex engine on the engine service.
+     *
+     * @param engineKey the key of the engine to stop
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void stopEngine(final AxArtifactKey engineKey) throws ApexDeploymentException {
+        final StopEngine stopEngineMessage = new StopEngine(engineKey);
+        LOGGER.debug("sending stop engine {} to server {}:{} . . .", stopEngineMessage, hostName, port);
+        client.sendMessage(stopEngineMessage);
+        LOGGER.debug("sent stop engine message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(stopEngineMessage);
+        if (response.isSuccessful()) {
+            LOGGER.debug(response.toString());
+        } else {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexDeploymentException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+    }
+
+    /**
+     * Start periodic events on an Apex engine on the engine service.
+     *
+     * @param engineKey the key of the engine to start periodic events on
+     * @param period the period in milliseconds between periodic events
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void startPerioidicEvents(final AxArtifactKey engineKey, final long period) throws ApexDeploymentException {
+        final StartPeriodicEvents startPerioidicEventsMessage = new StartPeriodicEvents(engineKey);
+        startPerioidicEventsMessage.setMessageData(Long.toString(period));
+        LOGGER.debug("sending start perioidic events {} to server {}:{} . . .", startPerioidicEventsMessage, hostName,
+                port);
+        client.sendMessage(startPerioidicEventsMessage);
+        LOGGER.debug("sent start perioidic events message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(startPerioidicEventsMessage);
+        if (response.isSuccessful()) {
+            LOGGER.debug(response.toString());
+        } else {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexDeploymentException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+    }
+
+    /**
+     * Stop periodic events on an Apex engine on the engine service.
+     *
+     * @param engineKey the key of the engine to stop periodic events on
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void stopPerioidicEvents(final AxArtifactKey engineKey) throws ApexDeploymentException {
+        final StopPeriodicEvents stopPerioidicEventsMessage = new StopPeriodicEvents(engineKey);
+        LOGGER.debug("sending stop perioidic events {} to server {}:{} . . .", stopPerioidicEventsMessage, hostName,
+                port);
+        client.sendMessage(stopPerioidicEventsMessage);
+        LOGGER.debug("sent stop perioidic events message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(stopPerioidicEventsMessage);
+        if (response.isSuccessful()) {
+            LOGGER.debug(response.toString());
+        } else {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexDeploymentException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+    }
+
+    /**
+     * Get the status of an Apex engine.
+     *
+     * @param engineKey the key of the engine to get the status of
+     * @return an engine model containing the status of the engine for the given key
+     * @throws ApexException the apex exception
+     */
+    public AxEngineModel getEngineStatus(final AxArtifactKey engineKey) throws ApexException {
+        final GetEngineStatus engineStatusMessage = new GetEngineStatus(engineKey);
+        LOGGER.debug("sending get engine status message {} to server {}:{} . . .", engineStatusMessage, hostName, port);
+        client.sendMessage(engineStatusMessage);
+        LOGGER.debug("sent get engine status message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(engineStatusMessage);
+        if (!response.isSuccessful()) {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+
+        final ByteArrayInputStream baInputStream = new ByteArrayInputStream(response.getMessageData().getBytes());
+        final ApexModelReader<AxEngineModel> modelReader = new ApexModelReader<>(AxEngineModel.class);
+        modelReader.setValidateFlag(false);
+        return modelReader.read(baInputStream);
+    }
+
+    /**
+     * Get the runtime information of an Apex engine.
+     *
+     * @param engineKey the key of the engine to get information for
+     * @return an engine model containing information on the engine for the given key
+     * @throws ApexException the apex exception
+     */
+    public String getEngineInfo(final AxArtifactKey engineKey) throws ApexException {
+        final GetEngineInfo engineInfoMessage = new GetEngineInfo(engineKey);
+        LOGGER.debug("sending get engine information message {} to server {}:{} . . .", engineInfoMessage, hostName,
+                port);
+        client.sendMessage(engineInfoMessage);
+        LOGGER.debug("sent get engine information message to server {}:{} . . .", hostName, port);
+
+        // Check if we got a response
+        final Response response = getResponse(engineInfoMessage);
+        if (!response.isSuccessful()) {
+            LOGGER.warn("failed response {} received from server {}:{}", response.getMessageData(), hostName, port);
+            throw new ApexException(
+                    "failed response " + response.getMessageData() + " received from server" + hostName + ':' + port);
+        }
+
+        return response.getMessageData();
+    }
+
+    /**
+     * Check the response to a model deployment message from the Apex server.
+     *
+     * @param sentMessage the sent message
+     * @return the response message
+     * @throws ApexDeploymentException the apex deployment exception
+     */
+    private Response getResponse(final Message sentMessage) throws ApexDeploymentException {
+        // Get the amount of milliseconds we should wait for a timeout
+        int timeoutTime = sentMessage.getReplyTimeout();
+        if (timeoutTime <= 0) {
+            timeoutTime = REPLY_MESSAGE_TIMEOUT_DEFAULT;
+        }
+
+        // Wait for the required amount of milliseconds for the response from the Apex server
+        Message receivedMessage = null;
+        for (int timeWaitedSoFar = 0; receivedMessage == null && timeWaitedSoFar < timeoutTime; timeWaitedSoFar +=
+                REPLY_MESSAGE_TIMEOUT_INCREMENT) {
+            try {
+                receivedMessage = client.getReceiveQueue().poll(REPLY_MESSAGE_TIMEOUT_INCREMENT, TimeUnit.MILLISECONDS);
+            } catch (final InterruptedException e) {
+                LOGGER.warn("reception of response from server interrupted {}:{}", hostName, port, e);
+                throw new ApexDeploymentException(
+                        "reception of response from server interrupted " + hostName + ':' + port, e);
+            }
+        }
+
+        // Check if response to sent message
+        if (receivedMessage == null) {
+            LOGGER.warn("no response received to sent message " + sentMessage.getAction());
+            throw new ApexDeploymentException("no response received to sent message " + sentMessage.getAction());
+        }
+
+        // Check instance is a response message
+        if (!(receivedMessage instanceof Response)) {
+            LOGGER.warn("response received from server is of incorrect type {}, should be of type {}",
+                    receivedMessage.getClass().getName(), Response.class.getName());
+            throw new ApexDeploymentException("response received from server is of incorrect type "
+                    + receivedMessage.getClass().getName() + ", should be of type " + Response.class.getName());
+        }
+
+        // Cast the response message
+        final Response responseMessage = (Response) receivedMessage;
+
+        // Check if response to sent message
+        if (!responseMessage.getResponseTo().equals(sentMessage)) {
+            LOGGER.warn("response received is not response to sent message " + sentMessage.getAction());
+            throw new ApexDeploymentException(
+                    "response received is not correct response to sent message " + sentMessage.getAction());
+        }
+
+        // Check if successful
+        if (responseMessage.isSuccessful()) {
+            LOGGER.debug("response received: {} message was succssful: {}", sentMessage.getAction(),
+                    responseMessage.getMessageData());
+        } else {
+            LOGGER.debug("response received: {} message failed: {}", sentMessage.getAction(),
+                    responseMessage.getMessageData());
+        }
+
+        return responseMessage;
+    }
+}
diff --git a/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/PeriodicEventManager.java b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/PeriodicEventManager.java
new file mode 100644 (file)
index 0000000..bfaece4
--- /dev/null
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.deployment;
+
+import java.util.Arrays;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This utility class is used to start and stop periodic events on Apex engines over the EngDep protocol.
+ */
+public class PeriodicEventManager {
+    // Get a reference to the logger
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(BatchDeployer.class);
+
+    private static final int NUM_ARGUMENTS = 4;
+    private static final int PERIODIC_EVENT_INTERVAL = 3;
+
+    // The facade that is handling messaging to the engine service
+    private EngineServiceFacade engineServiceFacade = null;
+
+    /**
+     * The main method, reads the Apex server host address, port and location of the Apex model XML file from the
+     * command line arguments.
+     *
+     * @param args the arguments that specify the Apex engine and the Apex model file
+     */
+    public static void main(final String[] args) {
+        if (args.length != NUM_ARGUMENTS) {
+            LOGGER.error("invalid arguments: " + Arrays.toString(args));
+            LOGGER.error("usage: Deployer <server address> <port address> <start/stop> <periods in ms>");
+            return;
+        }
+
+        PeriodicEventManager deployer = null;
+        try {
+            // Use a Deployer object to handle model deployment
+            deployer = new PeriodicEventManager(args[0], Integer.parseInt(args[1]));
+            deployer.init();
+            if (args[2].equalsIgnoreCase("start")) {
+                deployer.startPerioidicEvents(Long.parseLong(args[PERIODIC_EVENT_INTERVAL]));
+            } else {
+                deployer.stopPerioidicEvents();
+            }
+        } catch (final ApexException e) {
+            LOGGER.error("model deployment failed on parameters {}", args, e);
+        } finally {
+            if (deployer != null) {
+                deployer.close();
+            }
+        }
+    }
+
+    /**
+     * Instantiates a new deployer.
+     *
+     * @param hostName the host name of the host running the Apex Engine
+     * @param port the port to use for EngDep communication with the Apex engine
+     */
+    public PeriodicEventManager(final String hostName, final int port) {
+        engineServiceFacade = new EngineServiceFacade(hostName, port);
+    }
+
+    /**
+     * Initializes the deployer, opens an EngDep communication session with the Apex engine.
+     *
+     * @throws ApexDeploymentException thrown on deployment and communication errors
+     */
+    public void init() throws ApexDeploymentException {
+        engineServiceFacade.init();
+    }
+
+    /**
+     * Close the EngDep connection to the Apex server.
+     */
+    public void close() {
+        engineServiceFacade.close();
+    }
+
+    /**
+     * Start the Apex engines on the engine service.
+     *
+     * @param period the interval in milliseconds between periodic events
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void startPerioidicEvents(final long period) throws ApexDeploymentException {
+        for (final AxArtifactKey engineKey : engineServiceFacade.getEngineKeyArray()) {
+            engineServiceFacade.startPerioidicEvents(engineKey, period);
+        }
+    }
+
+    /**
+     * Stop the Apex engines on the engine service.
+     *
+     * @throws ApexDeploymentException on messaging errors
+     */
+    public void stopPerioidicEvents() throws ApexDeploymentException {
+        for (final AxArtifactKey engineKey : engineServiceFacade.getEngineKeyArray()) {
+            engineServiceFacade.stopPerioidicEvents(engineKey);
+        }
+    }
+}
diff --git a/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/package-info.java b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/package-info.java
new file mode 100644 (file)
index 0000000..b2b7fda
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides a facade and client that allows Apex engines to be managed and monitored over the EngDep protocol. Some
+ * utility classes for deployment are also provided.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.deployment;
diff --git a/core/core-engine/pom.xml b/core/core-engine/pom.xml
new file mode 100644 (file)
index 0000000..9b33974
--- /dev/null
@@ -0,0 +1,54 @@
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2018 Ericsson. All rights reserved.
+  ================================================================================
+  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=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onap.policy.apex-pdp.core</groupId>
+        <artifactId>core</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>core-engine</artifactId>
+    <name>${project.artifactId}</name>
+    <description>The Apex policy execution engine</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.model</groupId>
+            <artifactId>policy-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.model</groupId>
+            <artifactId>engine-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.context</groupId>
+            <artifactId>context-management</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/EngineParameters.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/EngineParameters.java
new file mode 100644 (file)
index 0000000..ff61697
--- /dev/null
@@ -0,0 +1,95 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.parameters.ContextParameters;
+import org.onap.policy.apex.model.basicmodel.service.AbstractParameters;
+import org.onap.policy.apex.model.basicmodel.service.ParameterService;
+
+/**
+ * This class holds the parameters for a single Apex engine. This parameter class holds parameters for context schemas
+ * and context albums for the engine and a map of the logic flavour executors defined for the engine and the parameters
+ * for each of those executors.
+ * <p>
+ * The context parameters for the engine are held in a {@link ContextParameters} instance. This instance holds the
+ * parameters for context schema handling that will be used by the engine as well as the context album distribution,
+ * locking, and persistence parameters.
+ * <p>
+ * In Apex, an engine can be configured to use many logic flavours. The executors for each logic flavour are identified
+ * by their name. Each logic flavour executor must have an instance of {@link ExecutorParameters} defined for it, which
+ * specifies the executor plugins to use for that logic flavour executor and specific parameters for those executor
+ * plugins.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EngineParameters extends AbstractParameters {
+    private ContextParameters contextParameters = new ContextParameters();
+
+    // A map of parameters for executors of various logic types
+    private Map<String, ExecutorParameters> executorParameterMap = new TreeMap<String, ExecutorParameters>();
+
+    /**
+     * Constructor to create an engine parameters instance and register the instance with the parameter service.
+     */
+    public EngineParameters() {
+        super(EngineParameters.class.getCanonicalName());
+        ParameterService.registerParameters(EngineParameters.class, this);
+    }
+
+    /**
+     * Gets the parameters for context schema and album handling.
+     *
+     * @return the parameters for context schema and album handling
+     */
+    public ContextParameters getContextParameters() {
+        return contextParameters;
+    }
+
+    /**
+     * Sets the parameters for context schema and album handling.
+     *
+     * @param contextParameters the parameters for context schema and album handling
+     */
+    public void setContextParameters(final ContextParameters contextParameters) {
+        this.contextParameters = contextParameters;
+    }
+
+    /**
+     * Gets the executor parameter map of the engine.
+     *
+     * @return the executor parameter map of the engine
+     */
+    public Map<String, ExecutorParameters> getExecutorParameterMap() {
+        return executorParameterMap;
+    }
+
+    /**
+     * Sets the executor parameter map of the engine.
+     *
+     * @param executorParameterMap the executor parameter map of the engine
+     */
+    public void setExecutorParameterMap(final Map<String, ExecutorParameters> executorParameterMap) {
+        this.executorParameterMap = executorParameterMap;
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/ExecutorParameters.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/ExecutorParameters.java
new file mode 100644 (file)
index 0000000..d3a8ed5
--- /dev/null
@@ -0,0 +1,123 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine;
+
+import org.onap.policy.apex.model.basicmodel.service.AbstractParameters;
+import org.onap.policy.apex.model.basicmodel.service.ParameterService;
+
+/**
+ * This class provides the executors for a logic flavour. Plugin classes for execution of task logic, task selection
+ * logic, and state finalizer logic for the logic flavour must be specified.
+ * <p>
+ * Specializations of this class may provide extra parameters for their specific logic flavour executors.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ExecutorParameters extends AbstractParameters {
+    // Executor Plugin classes for executors
+    private String taskExecutorPluginClass;
+    private String taskSelectionExecutorPluginClass;
+    private String stateFinalizerExecutorPluginClass;
+
+    /**
+     * Constructor to create an executor parameters instance and register the instance with the parameter service.
+     */
+    public ExecutorParameters() {
+        super(ExecutorParameters.class.getCanonicalName());
+        ParameterService.registerParameters(ExecutorParameters.class, this);
+    }
+
+    /**
+     * Constructor to create an executor parameters instance with the name of a sub class of this class and register the
+     * instance with the parameter service.
+     *
+     * @param parameterClassName the class name of a sub class of this class
+     */
+    public ExecutorParameters(final String parameterClassName) {
+        super(parameterClassName);
+    }
+
+    /**
+     * Gets the task executor plugin class for the executor.
+     *
+     * @return the task executor plugin class for the executor
+     */
+    public String getTaskExecutorPluginClass() {
+        return taskExecutorPluginClass;
+    }
+
+    /**
+     * Sets the task executor plugin class for the executor.
+     *
+     * @param taskExecutorPluginClass the task executor plugin class for the executor
+     */
+    public void setTaskExecutorPluginClass(final String taskExecutorPluginClass) {
+        this.taskExecutorPluginClass = taskExecutorPluginClass;
+    }
+
+    /**
+     * Gets the task selection executor plugin class for the executor.
+     *
+     * @return the task selection executor plugin class for the executor
+     */
+    public String getTaskSelectionExecutorPluginClass() {
+        return taskSelectionExecutorPluginClass;
+    }
+
+    /**
+     * Sets the task selection executor plugin class for the executor.
+     *
+     * @param taskSelectionExecutorPluginClass the task selection executor plugin class for the executor
+     */
+    public void setTaskSelectionExecutorPluginClass(final String taskSelectionExecutorPluginClass) {
+        this.taskSelectionExecutorPluginClass = taskSelectionExecutorPluginClass;
+    }
+
+    /**
+     * Gets the state finalizer executor plugin class for the executor.
+     *
+     * @return the state finalizer executor plugin class for the executor
+     */
+    public String getStateFinalizerExecutorPluginClass() {
+        return stateFinalizerExecutorPluginClass;
+    }
+
+    /**
+     * Sets the state finalizer executor plugin class for the executor.
+     *
+     * @param stateFinalizerExecutorPluginClass the state finalizer executor plugin class for the executor
+     */
+    public void setStateFinalizerExecutorPluginClass(final String stateFinalizerExecutorPluginClass) {
+        this.stateFinalizerExecutorPluginClass = stateFinalizerExecutorPluginClass;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.model.basicmodel.service.AbstractParameters#toString()
+     */
+    @Override
+    public String toString() {
+        return "ExecutorParameters [taskExecutorPluginClass=" + taskExecutorPluginClass
+                + ", taskSelectionExecutorPluginClass=" + taskSelectionExecutorPluginClass
+                + ", StateFinalizerExecutorPluginClass=" + stateFinalizerExecutorPluginClass + "]";
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/ApexInternalContext.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/ApexInternalContext.java
new file mode 100644 (file)
index 0000000..2186ee3
--- /dev/null
@@ -0,0 +1,232 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.context;
+
+import com.google.common.collect.Maps;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.ContextAlbum;
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.context.Distributor;
+import org.onap.policy.apex.context.impl.distribution.DistributorFactory;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetterImpl;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbums;
+import org.onap.policy.apex.model.contextmodel.handling.ContextComparer;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapDifference;
+
+/**
+ * This class manages the internal context for an Apex engine. This class is not thread safe and need not be because
+ * each Context object is owned by one and only one ApexEngine, which runs in a single thread and only runs one policy
+ * at a time. Therefore there is only ever one policy using a Context object at a time. The currentPolicyContextAlbum is
+ * set on the Context object by the StateMachineExecutor each time a policy is triggered.
+ *
+ * @author Liam Fallon
+ */
+public final class ApexInternalContext implements AxConceptGetter<ContextAlbum> {
+    // The key of the currently running Apex model
+    private final AxArtifactKey key;
+
+    // The context albums being used in this engine
+    private final NavigableMap<AxArtifactKey, ContextAlbum> contextAlbums =
+            Maps.synchronizedNavigableMap(new TreeMap<AxArtifactKey, ContextAlbum>());
+
+    // The internal context uses a context distributor to handle distribution of context across multiple instances
+    private Distributor contextDistributor = null;
+
+    // The key of the current policy, used to return the correct policy context album to the user
+    private final AxArtifactKey currentPolicyKey = null;
+
+    /**
+     * Constructor, instantiate the context object from the Apex model.
+     *
+     * @param apexPolicyModel the apex model
+     * @throws ContextException On errors on context setting
+     */
+    public ApexInternalContext(final AxPolicyModel apexPolicyModel) throws ContextException {
+        apexPolicyModel.register();
+
+        // The context distributor used to distribute context across policy engine instances
+        contextDistributor = new DistributorFactory().getDistributor(apexPolicyModel.getKey());
+
+        // Set up the context albums for this engine
+        for (final AxArtifactKey contextAlbumKey : ModelService.getModel(AxContextAlbums.class).getAlbumsMap()
+                .keySet()) {
+            contextAlbums.put(contextAlbumKey, contextDistributor.createContextAlbum(contextAlbumKey));
+        }
+
+        // Record the key of the current model
+        key = apexPolicyModel.getKey();
+    }
+
+    /**
+     * Get the key of the internal context, which is the same as the key of the engine.
+     *
+     * @return the key
+     */
+    public AxArtifactKey getKey() {
+        return key;
+    }
+
+    /**
+     * Get the context albums of the engine.
+     *
+     * @return the context albums
+     */
+    public Map<AxArtifactKey, ContextAlbum> getContextAlbums() {
+        return contextAlbums;
+    }
+
+    /**
+     * Update the current context so that it aligns with this incoming model, transferring context values if they exist
+     * in the new model.
+     *
+     * @param newPolicyModel The new incoming Apex model to use for context
+     * @throws ContextException On errors on context setting
+     */
+    public void update(final AxPolicyModel newPolicyModel) throws ContextException {
+        if (newPolicyModel == null) {
+            throw new ContextException("internal context update failed, supplied model is null");
+        }
+
+        // Get the differences between the existing context and the new context
+        final KeyedMapDifference<AxArtifactKey, AxContextAlbum> contextDifference =
+                new ContextComparer().compare(ModelService.getModel(AxContextAlbums.class), newPolicyModel.getAlbums());
+
+        // Remove maps that are no longer used
+        for (final Entry<AxArtifactKey, AxContextAlbum> removedContextAlbumEntry : contextDifference.getLeftOnly()
+                .entrySet()) {
+            contextDistributor.removeContextAlbum(removedContextAlbumEntry.getValue());
+            contextAlbums.remove(removedContextAlbumEntry.getKey());
+        }
+
+        // We switch over to the new Apex model
+        newPolicyModel.register();
+
+        // Set up the new context albums
+        for (final AxArtifactKey contextAlbumKey : contextDifference.getRightOnly().keySet()) {
+            contextAlbums.put(contextAlbumKey, contextDistributor.createContextAlbum(contextAlbumKey));
+        }
+
+        // Handle the updated maps
+        for (final Entry<AxArtifactKey, List<AxContextAlbum>> contextAlbumEntry : contextDifference.getDifferentValues()
+                .entrySet()) {
+            // Compare the updated maps
+            final AxContextAlbum currentContextAlbum = contextAlbumEntry.getValue().get(0);
+            final AxContextAlbum newContextAlbum = contextAlbumEntry.getValue().get(1);
+
+            // Check that the schemas are the same on the old and new context albums
+            if (currentContextAlbum.getItemSchema().equals(newContextAlbum.getItemSchema())) {
+                // The schema is different, throw an exception because the schema should not change if the key of the
+                // album has not changed
+                throw new ContextException("internal context update failed on context album \""
+                        + contextAlbumEntry.getKey().getID() + "\" in model \"" + key.getID() + "\", schema \""
+                        + currentContextAlbum.getItemSchema().getID()
+                        + "\" on existing context model does not equal schema \""
+                        + newContextAlbum.getItemSchema().getID() + "\" on incoming model");
+            }
+        }
+
+    }
+
+    /**
+     * Clear the internal context.
+     *
+     * @throws ContextException on clearing errors
+     */
+    public void clear() throws ContextException {
+        // Clear all context in the distributor
+        contextDistributor.clear();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ApexInternalContext [contextAlbums=" + contextAlbums + ", contextDistributor=" + contextDistributor
+                + ", currentPolicyKey=" + currentPolicyKey + "]";
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.basicmodel.concepts.AxConceptGetter#get(org.onap.policy.apex.core.basicmodel.concepts.
+     * AxArtifactKey)
+     */
+    @Override
+    public ContextAlbum get(final AxArtifactKey conceptKey) {
+        return new AxConceptGetterImpl<>(contextAlbums).get(conceptKey);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.basicmodel.concepts.AxConceptGetter#get(java.lang.String)
+     */
+    @Override
+    public ContextAlbum get(final String conceptKeyName) {
+        return new AxConceptGetterImpl<>(contextAlbums).get(conceptKeyName);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.basicmodel.concepts.AxConceptGetter#get(java.lang.String, java.lang.String)
+     */
+    @Override
+    public ContextAlbum get(final String conceptKeyName, final String conceptKeyVersion) {
+        return new AxConceptGetterImpl<>(contextAlbums).get(conceptKeyName, conceptKeyVersion);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.basicmodel.concepts.AxConceptGetter#getAll(java.lang.String)
+     */
+    @Override
+    public Set<ContextAlbum> getAll(final String conceptKeyName) {
+        return new AxConceptGetterImpl<>(contextAlbums).getAll(conceptKeyName);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.basicmodel.concepts.AxConceptGetter#getAll(java.lang.String, java.lang.String)
+     */
+    @Override
+    public Set<ContextAlbum> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+        return new AxConceptGetterImpl<>(contextAlbums).getAll(conceptKeyName, conceptKeyVersion);
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/package-info.java
new file mode 100644 (file)
index 0000000..887914e
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Manages the context albums that an APEX engine requires during execution. It uses the policy model of the engine to
+ * determine what context albums the engine requires.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.context;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/ApexEngine.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/ApexEngine.java
new file mode 100644 (file)
index 0000000..9e27e5e
--- /dev/null
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.engine;
+
+import java.util.Map;
+
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.enginemodel.concepts.AxEngineModel;
+import org.onap.policy.apex.model.enginemodel.concepts.AxEngineState;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+
+/**
+ * The Interface ApexEngine is used to control the execution of a single Apex engine thread. This engine instance
+ * executes the policies in an {@link AxPolicyModel}, which defines the policies that are executed by the engine and the
+ * context in which they execute. Many instances of an Apex engine may run on the same Apex model, in which case they
+ * operate the same policy set in parallel over the same context. When the {@code handleEvent} method is passed to the
+ * Apex engine, the engine executes the policy triggered by that event. A single Apex engine instance does not executed
+ * multiple policies in parallel, it receives a trigger event and executes the policy for that event to completion
+ * before it is available to execute another policy.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public interface ApexEngine {
+    /**
+     * The amount of milliseconds to wait for the current Apex engine to timeout on engine stop requests. If the timeout
+     * is exceeded, the stop aborts.
+     */
+    int APEX_ENGINE_STOP_EXECUTION_WAIT_TIMEOUT = 3000;
+
+    /** The wait increment (or pause time) when waiting for the Apex engine to stop. */
+    int APEX_ENGINE_STOP_EXECUTION_WAIT_INCREMENT = 100;
+
+    /**
+     * Update the Apex model to be used by the Apex engine. The engine must be in state "STOPPED" when the model is
+     * updated. The engine will replace the current model with the incoming model if the model of the engine was
+     * previously updated and the value of common context is transferred if there is common context in the old and new
+     * models.
+     *
+     * @param apexModel the apex model
+     * @throws ApexException on model update errors
+     */
+    void updateModel(AxPolicyModel apexModel) throws ApexException;
+
+    /**
+     * Starts an Apex engine so that it can receive events.
+     *
+     * @throws ApexException on start errors
+     */
+    void start() throws ApexException;
+
+    /**
+     * Stops an Apex engine in an orderly way. This method must be called prior to model updates.
+     *
+     * @throws ApexException on stop errors
+     */
+    void stop() throws ApexException;
+
+    /**
+     * Clears all models and data from an Apex engine. The engine must be stopped.
+     *
+     * @throws ApexException on clear errors
+     */
+    void clear() throws ApexException;
+
+    /**
+     * This method constructs an event with the correct event context so that it can later be sent to the Apex engine.
+     *
+     * @param eventKey The key of the event in the Apex model
+     * @return the created event
+     */
+    EnEvent createEvent(AxArtifactKey eventKey);
+
+    /**
+     * This method passes an event to the Apex model to invoke a policy. If the event matches a policy, then that policy
+     * is executed.
+     *
+     * @return return true if a policy was invoked without error, otherwise false.
+     * @param incomingEvent the incoming event
+     */
+    boolean handleEvent(EnEvent incomingEvent);
+
+    /**
+     * A method to add a call back listener class that listens for action events from the engine.
+     *
+     * @param listenerName the unique name of the listener
+     * @param listener is an instance of type {@link EnEventListener}
+     */
+    void addEventListener(String listenerName, EnEventListener listener);
+
+    /**
+     * A method to remove a call back listener class.
+     *
+     * @param listenerName the name of the listener to remove
+     */
+    void removeEventListener(String listenerName);
+
+    /**
+     * Get the artifact key of the engine.
+     *
+     * @return the artifact key
+     */
+    AxArtifactKey getKey();
+
+    /**
+     * Get the state of the engine.
+     *
+     * @return the engine state
+     */
+    AxEngineState getState();
+
+    /**
+     * Get the engine status information, this is just the engine state.
+     *
+     * @return the Apex status information
+     */
+    AxEngineModel getEngineStatus();
+
+    /**
+     * Get the engine run time information, the status and context.
+     *
+     * @return the Apex runtime information
+     */
+    Map<AxArtifactKey, Map<String, Object>> getEngineContext();
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/EnEventListener.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/EnEventListener.java
new file mode 100644 (file)
index 0000000..32d638a
--- /dev/null
@@ -0,0 +1,41 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.engine;
+
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+
+/**
+ * This interface is used by users of an Apex engine to receive action events being emitted by the engine.
+ *
+ * @author Liam Fallon
+ *
+ */
+public interface EnEventListener {
+
+    /**
+     * This method is called when an Apex engine emits an event.
+     *
+     * @param enEvent the engine event
+     * @throws ApexException the apex exception
+     */
+    void onEnEvent(EnEvent enEvent) throws ApexException;
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineFactory.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineFactory.java
new file mode 100644 (file)
index 0000000..4920486
--- /dev/null
@@ -0,0 +1,43 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.engine.impl;
+
+import org.onap.policy.apex.core.engine.engine.ApexEngine;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * A factory class to create APEX engines of a given type. As there is only a single type of Apex engine in existence,
+ * this class is trivial.
+ *
+ * @author Liam Fallon
+ */
+public class ApexEngineFactory {
+
+    /**
+     * Create an Apex engine implementation.
+     *
+     * @param key the key
+     * @return the apex engine
+     */
+    public ApexEngine createApexEngine(final AxArtifactKey key) {
+        return new ApexEngineImpl(key);
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineImpl.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineImpl.java
new file mode 100644 (file)
index 0000000..12ba76a
--- /dev/null
@@ -0,0 +1,451 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.engine.impl;
+
+import static org.onap.policy.apex.model.utilities.Assertions.argumentNotNull;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.onap.policy.apex.context.ContextAlbum;
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.engine.ApexEngine;
+import org.onap.policy.apex.core.engine.engine.EnEventListener;
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.enginemodel.concepts.AxEngineModel;
+import org.onap.policy.apex.model.enginemodel.concepts.AxEngineState;
+import org.onap.policy.apex.model.enginemodel.concepts.AxEngineStats;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class controls the thread of execution of a single engine in an Apex system. An engine is a single thread in a
+ * pool of engines that are running a set of policies. An engine is either inactive, waiting for a policy to be
+ * triggered or executing a policy. The engine runs off a queue of triggers that trigger its state machine. If the queue
+ * is empty, it waits for the next trigger. The Apex engine holds its state machine in a {@link StateMachineHandler}
+ * instance and uses its state machine handler to execute events.
+ *
+ * @author Liam Fallon
+ */
+public class ApexEngineImpl implements ApexEngine {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexEngineImpl.class);
+
+    // The artifact key of this engine
+    private final AxArtifactKey key;
+
+    // The state of this engine
+    private AxEngineState state = AxEngineState.STOPPED;
+
+    // call back listeners
+    private final Map<String, EnEventListener> eventListeners = new LinkedHashMap<String, EnEventListener>();
+
+    // The context of this engine
+    private ApexInternalContext internalContext = null;
+
+    // The state machines
+    private StateMachineHandler stateMachineHandler = null;
+
+    // Statistics on engine execution
+    private final AxEngineStats engineStats;
+
+    /**
+     * Constructor, instantiate the engine with its state machine table.
+     *
+     * @param key the key of the engine
+     */
+    protected ApexEngineImpl(final AxArtifactKey key) {
+        argumentNotNull(key, "AxArtifactKey may not be null");
+
+        LOGGER.entry("ApexEngine()->" + key.getID() + "," + state);
+
+        this.key = key;
+
+        // Set up statistics collection
+        engineStats = new AxEngineStats();
+        engineStats.setKey(new AxReferenceKey(key, "_EngineStats"));
+
+        LOGGER.exit("ApexEngine()<-" + key.getID() + "," + state);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.engine.ApexEngine#updateModel(org.onap.policy.apex.model.policymodel.concepts.
+     * AxPolicyModel)
+     */
+    @Override
+    public void updateModel(final AxPolicyModel apexModel) throws ApexException {
+        if (apexModel != null) {
+            LOGGER.entry("updateModel()->" + key.getID() + ", apexPolicyModel=" + apexModel.getKey().getID());
+        } else {
+            LOGGER.warn("updateModel()<-" + key.getID() + ", Apex model not set");
+            throw new ApexException(
+                    "updateModel()<-" + key.getID() + ", Apex model is not defined, it has a null value");
+        }
+
+        // The engine must be stopped in order to do a model update
+        if (!state.equals(AxEngineState.STOPPED)) {
+            throw new ApexException("updateModel()<-" + key.getID()
+                    + ", cannot update model, engine should be stopped but is in state " + state);
+        }
+
+        // Create new internal context or update the existing one
+        try {
+            if (internalContext == null) {
+                /// New internal context
+                internalContext = new ApexInternalContext(apexModel);
+            } else {
+                // Exiting internal context which must be updated
+                internalContext.update(apexModel);
+            }
+        } catch (final ContextException e) {
+            LOGGER.warn(
+                    "updateModel()<-" + key.getID() + ", error setting the context for engine \"" + key.getID() + "\"",
+                    e);
+            throw new ApexException(
+                    "updateModel()<-" + key.getID() + ", error setting the context for engine \"" + key.getID() + "\"",
+                    e);
+        }
+
+        // Set up the state machines
+        try {
+            // We always set up state machines as new because it's only context that must be transferred; policies are
+            // always set up as new
+            stateMachineHandler = new StateMachineHandler(internalContext);
+        } catch (final StateMachineException e) {
+            LOGGER.warn("updateModel()<-" + key.getID() + ", error setting up the engine state machines \""
+                    + key.getID() + "\"", e);
+            throw new ApexException("updateModel()<-" + key.getID() + ", error setting up the engine state machines \""
+                    + key.getID() + "\"", e);
+        }
+
+        LOGGER.exit("updateModel()<-" + key.getID());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#start()
+     */
+    @Override
+    public void start() throws ApexException {
+        LOGGER.entry("start()" + key);
+
+        if (state != AxEngineState.STOPPED) {
+            LOGGER.warn("start()<-" + key.getID() + "," + state + ", cannot start engine, engine not in state STOPPED");
+            throw new ApexException(
+                    "start()<-" + key.getID() + "," + state + ", cannot start engine, engine not in state STOPPED");
+        }
+
+        if (stateMachineHandler == null || internalContext == null) {
+            LOGGER.warn("start()<-" + key.getID() + "," + state
+                    + ", cannot start engine, engine has not been initialized, its model is not loaded");
+            throw new ApexException("start()<-" + key.getID() + "," + state
+                    + ",  cannot start engine, engine has not been initialized, its model is not loaded");
+        }
+
+        // Set up the state machines
+        try {
+            // Start the state machines
+            stateMachineHandler.start();
+            engineStats.engineStart();
+        } catch (final StateMachineException e) {
+            LOGGER.warn("updateModel()<-" + key.getID() + ", error starting the engine state machines \"" + key.getID()
+                    + "\"", e);
+            throw new ApexException("updateModel()<-" + key.getID() + ", error starting the engine state machines \""
+                    + key.getID() + "\"", e);
+        }
+
+        // OK, we are good to go
+        state = AxEngineState.READY;
+
+        LOGGER.exit("start()" + key);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#stop()
+     */
+    @Override
+    public void stop() throws ApexException {
+        LOGGER.entry("stop()->" + key);
+
+        // Stop the engine if it is in state READY, if it is in state EXECUTING, wait for execution to finish
+        for (int increment = APEX_ENGINE_STOP_EXECUTION_WAIT_TIMEOUT; increment > 0; increment =
+                APEX_ENGINE_STOP_EXECUTION_WAIT_INCREMENT) {
+            synchronized (state) {
+                switch (state) {
+                    // Already stopped
+                    case STOPPED:
+
+                        throw new ApexException("stop()<-" + key.getID() + "," + state
+                                + ", cannot stop engine, engine is already stopped");
+                        // The normal case, the engine wasn't doing anything or it was executing
+                    case READY:
+                    case STOPPING:
+
+                        state = AxEngineState.STOPPED;
+                        stateMachineHandler.stop();
+                        engineStats.engineStop();
+                        LOGGER.exit("stop()" + key);
+                        return;
+                    // Engine is executing a policy, wait for it to stop
+                    case EXECUTING:
+                        state = AxEngineState.STOPPING;
+                        break;
+                    default:
+                        throw new ApexException("stop()<-" + key.getID() + "," + state
+                                + ", cannot stop engine, engine is in an undefined state");
+                }
+            }
+        }
+
+        throw new ApexException("stop()<-" + key.getID() + "," + state + ", cannot stop engine, engine stop timed out");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#clear()
+     */
+    @Override
+    public void clear() throws ApexException {
+        LOGGER.entry("clear()->" + key);
+        if (state != AxEngineState.STOPPED) {
+            throw new ApexException(
+                    "clear" + "()<-" + key.getID() + "," + state + ", cannot clear engine, engine is not stopped");
+        }
+
+        // Clear everything
+        stateMachineHandler = null;
+        engineStats.clean();
+        internalContext.clear();
+        internalContext = null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#createEvent(org.onap.policy.apex.core.model.concepts.
+     * AxArtifactKey)
+     */
+    @Override
+    public EnEvent createEvent(final AxArtifactKey eventKey) {
+        if (state != AxEngineState.READY && state != AxEngineState.EXECUTING) {
+            LOGGER.warn(
+                    "createEvent()<-" + key.getID() + "," + state + ", cannot create event, engine not in state READY");
+            return null;
+        }
+
+        try {
+            // Create an event using the internal context
+            return new EnEvent(eventKey);
+        } catch (final Exception e) {
+            LOGGER.warn("createEvent()<-" + key.getID() + "," + state + ", error on event creation", e);
+            return null;
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.engine.ApexEngine#handleEvent(org.onap.policy.apex.core.engine.event.EnEvent)
+     */
+    @Override
+    public boolean handleEvent(final EnEvent incomingEvent) {
+        boolean ret = false;
+        if (incomingEvent == null) {
+            LOGGER.warn("handleEvent()<-" + key.getID() + "," + state + ", cannot run engine, incoming event is null");
+            return ret;
+        }
+
+        synchronized (state) {
+            if (state != AxEngineState.READY) {
+                LOGGER.warn("handleEvent()<-" + key.getID() + "," + state
+                        + ", cannot run engine, engine not in state READY");
+                return ret;
+            }
+
+            state = AxEngineState.EXECUTING;
+        }
+
+        LOGGER.debug("execute(): triggered by event " + incomingEvent.toString());
+
+        // By default we return a null event on errors
+        EnEvent outgoingEvent = null;
+        try {
+            engineStats.executionEnter(incomingEvent.getKey());
+            outgoingEvent = stateMachineHandler.execute(incomingEvent);
+            engineStats.executionExit();
+            ret = true;
+        } catch (final StateMachineException e) {
+            LOGGER.warn("handleEvent()<-" + key.getID() + "," + state + ", engine execution error: ", e);
+
+            // Create an exception return event
+            outgoingEvent = createExceptionEvent(incomingEvent, e);
+        }
+
+        // Publish the outgoing event
+        try {
+            synchronized (eventListeners) {
+                if (eventListeners.isEmpty()) {
+                    LOGGER.debug("handleEvent()<-" + key.getID() + "," + state
+                            + ", There is no listener registered to recieve outgoing event: " + outgoingEvent);
+                }
+                for (final EnEventListener axEventListener : eventListeners.values()) {
+                    axEventListener.onEnEvent(outgoingEvent);
+                }
+            }
+        } catch (final ApexException e) {
+            LOGGER.warn("handleEvent()<-" + key.getID() + "," + state + ", outgoing event publishing error: ", e);
+            ret = false;
+        }
+        synchronized (state) {
+            // Only go to READY if we are still in state EXECUTING, we could be in state STOPPING
+            if (state == AxEngineState.EXECUTING) {
+                state = AxEngineState.READY;
+            }
+        }
+        return ret;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#addEventListener(java.lang.String,
+     * org.onap.policy.apex.core.engine.engine.EnEventListener)
+     */
+    @Override
+    public void addEventListener(final String listenerName, final EnEventListener listener) {
+        eventListeners.put(listenerName, listener);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#removeEventListener(java.lang.String)
+     */
+    @Override
+    public void removeEventListener(final String listenerName) {
+        eventListeners.remove(listenerName);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#getKey()
+     */
+    @Override
+    public AxArtifactKey getKey() {
+        return key;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#getState()
+     */
+    @Override
+    public final AxEngineState getState() {
+        return state;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#getEngineStatus()
+     */
+    @Override
+    public AxEngineModel getEngineStatus() {
+        final AxEngineModel engineModel = new AxEngineModel(key);
+        engineModel.setTimestamp(System.currentTimeMillis());
+        engineModel.setState(state);
+        engineModel.setStats(engineStats);
+        return engineModel;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.engine.ApexEngine#getEngineRuntime()
+     */
+    @Override
+    public Map<AxArtifactKey, Map<String, Object>> getEngineContext() {
+        final Map<AxArtifactKey, Map<String, Object>> currentContext =
+                new LinkedHashMap<AxArtifactKey, Map<String, Object>>();
+
+        for (final Entry<AxArtifactKey, ContextAlbum> contextAlbumEntry : internalContext.getContextAlbums()
+                .entrySet()) {
+            currentContext.put(contextAlbumEntry.getKey(), contextAlbumEntry.getValue());
+        }
+
+        return currentContext;
+    }
+
+    /**
+     * Get the internal context for the Apex engine.
+     *
+     * @return The Apex Internal Context
+     */
+    public ApexInternalContext getInternalContext() {
+        return internalContext;
+    }
+
+    /**
+     * Create an exception event from the incoming event including the exception information on the event.
+     *
+     * @param incomingEvent The incoming event that caused the exception
+     * @param eventException The exception that was thrown
+     * @return the exception event
+     */
+    private EnEvent createExceptionEvent(final EnEvent incomingEvent, final Exception eventException) {
+        // The exception event is a clone of the incoming event with the exception suffix added to its name and an extra
+        // field "ExceptionMessage" added
+        final EnEvent exceptionEvent = (EnEvent) incomingEvent.clone();
+
+        // Create the cascaded message string
+        final StringBuilder exceptionMessageStringBuilder = new StringBuilder();
+        exceptionMessageStringBuilder.append(eventException.getMessage());
+
+        Throwable subException = eventException.getCause();
+        while (subException != null) {
+            exceptionMessageStringBuilder.append("\ncaused by: ");
+            exceptionMessageStringBuilder.append(subException.getMessage());
+            subException = subException.getCause();
+        }
+
+        // Set the exception message on the event
+        exceptionEvent.setExceptionMessage(exceptionMessageStringBuilder.toString());
+
+        return exceptionEvent;
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/StateMachineHandler.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/StateMachineHandler.java
new file mode 100644 (file)
index 0000000..856136b
--- /dev/null
@@ -0,0 +1,186 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.engine.impl;
+
+import java.util.HashMap;
+
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.ExecutorFactory;
+import org.onap.policy.apex.core.engine.executor.StateMachineExecutor;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.core.engine.executor.impl.ExecutorFactoryImpl;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicies;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This handler holds and manages state machines for each policy in an Apex engine. When the class is instantiated, an
+ * executor {@link StateMachineExecutor} is created for each policy in the policy model the state machine handler will
+ * execute. The executors for each policy are held in a map indexed by event.
+ * <p>
+ * When an event is received on the policy, the state machine executor to execute that event is looked up on the
+ * executor map and the event is passed to the executor for execution.
+ *
+ * @author Liam Fallon
+ *
+ */
+public class StateMachineHandler {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateMachineHandler.class);
+
+    // The key of the Apex model we are executing
+    private final AxArtifactKey key;
+
+    // The state machines in this engine
+    private final HashMap<AxEvent, StateMachineExecutor> stateMachineExecutorMap = new HashMap<>();
+
+    // The executor factory is used to get logic executors for the particular type of executor we need for task
+    // selection logic or task logic
+    private final ExecutorFactory executorFactory;
+
+    /**
+     * This constructor builds the state machines for the policies in the apex model.
+     *
+     * @param internalContext The internal context we are using
+     * @throws StateMachineException On state machine initiation errors
+     */
+    protected StateMachineHandler(final ApexInternalContext internalContext) throws StateMachineException {
+        LOGGER.entry("StateMachineHandler()->" + internalContext.getKey().getID());
+
+        key = internalContext.getKey();
+
+        // Create the executor factory to generate executors as the engine runs policies
+        executorFactory = new ExecutorFactoryImpl();
+
+        // Iterate over the policies in the policy model and create a state machine for each one
+        for (final AxPolicy policy : ModelService.getModel(AxPolicies.class).getPolicyMap().values()) {
+            // Create a state machine for this policy
+            final StateMachineExecutor thisStateMachineExecutor =
+                    new StateMachineExecutor(executorFactory, policy.getKey());
+
+            // This executor is the top executor so has no parent
+            thisStateMachineExecutor.setContext(null, policy, internalContext);
+
+            // Get the incoming trigger event
+            final AxEvent triggerEvent = ModelService.getModel(AxEvents.class)
+                    .get(policy.getStateMap().get(policy.getFirstState()).getTrigger());
+
+            // Put the state machine executor on the map for this trigger
+            final StateMachineExecutor lastStateMachineExecutor =
+                    stateMachineExecutorMap.put(triggerEvent, thisStateMachineExecutor);
+            if (lastStateMachineExecutor != null
+                    && lastStateMachineExecutor.getSubject() != thisStateMachineExecutor.getSubject()) {
+                LOGGER.error("No more than one policy in a model can have the same trigger event. In model "
+                        + internalContext.getKey().getID() + " Policy ("
+                        + lastStateMachineExecutor.getSubject().getKey().getID() + ") and Policy ("
+                        + thisStateMachineExecutor.getSubject().getKey().getID() + ") have the same Trigger event ("
+                        + triggerEvent.getKey().getID() + ") ");
+                LOGGER.error(" Policy (" + lastStateMachineExecutor.getSubject().getKey() + ") has overwritten Policy ("
+                        + thisStateMachineExecutor.getSubject().getKey().getID()
+                        + " so this overwritten policy will never be triggered in this engine.");
+            }
+        }
+
+        LOGGER.exit("StateMachineHandler()<-" + internalContext.getKey().getID());
+    }
+
+    /**
+     * This constructor starts the state machines for each policy, carrying out whatever initialization executors need.
+     *
+     * @throws StateMachineException On state machine initiation errors
+     */
+    protected void start() throws StateMachineException {
+        LOGGER.entry("start()->" + key.getID());
+
+        // Iterate over the state machines
+        for (final StateMachineExecutor smExecutor : stateMachineExecutorMap.values()) {
+            try {
+                smExecutor.prepare();
+            } catch (final StateMachineException e) {
+                final String stateMachineID = smExecutor.getContext().getKey().getID();
+                LOGGER.warn("start()<-" + key.getID() + ", start failed, state machine \"" + stateMachineID + "\"", e);
+                throw new StateMachineException(
+                        "start()<-" + key.getID() + ", start failed, state machine \"" + stateMachineID + "\"", e);
+            }
+        }
+
+        LOGGER.exit("start()<-" + key.getID());
+    }
+
+    /**
+     * This method is called to execute an event on the state machines in an engine.
+     *
+     * @param event The trigger event for the state machine
+     * @return The result of the state machine execution run
+     * @throws StateMachineException On execution errors in a state machine
+     */
+    protected EnEvent execute(final EnEvent event) throws StateMachineException {
+        LOGGER.entry("execute()->" + event.getName());
+
+        // Try to execute the state machine for the trigger
+        final StateMachineExecutor stateMachineExecutor = stateMachineExecutorMap.get(event.getAxEvent());
+        if (stateMachineExecutor == null) {
+            final String exceptionMessage =
+                    "state machine execution not possible, policy not found for trigger event " + event.getName();
+            LOGGER.warn(exceptionMessage);
+
+            event.setExceptionMessage(exceptionMessage);
+            return event;
+        }
+
+        // Run the state machine
+        try {
+            LOGGER.debug("execute(): state machine \"{}\" execution starting  . . .", stateMachineExecutor);
+            final EnEvent outputObject = stateMachineExecutor.execute(event.getExecutionID(), event);
+
+            LOGGER.debug("execute()<-: state machine \"{}\" execution completed", stateMachineExecutor);
+            return outputObject;
+        } catch (final Exception e) {
+            LOGGER.warn("execute()<-: state machine \"" + stateMachineExecutor + "\" execution failed", e);
+            throw new StateMachineException("execute()<-: execution failed on state machine " + stateMachineExecutor,
+                    e);
+        }
+    }
+
+    /**
+     * Closes down the state machines of an engine.
+     */
+    protected void stop() {
+        LOGGER.entry("stop()->");
+
+        // Iterate through all state machines and clean them
+        for (final StateMachineExecutor smExecutor : stateMachineExecutorMap.values()) {
+            try {
+                smExecutor.cleanUp();
+            } catch (final StateMachineException e) {
+                final String smID = smExecutor.getContext().getKey().getID();
+                LOGGER.warn("stop()<-clean up failed, state machine \"" + smID + "\" cleanup failed", e);
+            }
+        }
+        LOGGER.exit("stop()<-");
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/package-info.java
new file mode 100644 (file)
index 0000000..ce86e27
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides the implementation of the {@link org.onap.policy.apex.core.engine.engine.ApexEngine} interface.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.engine.impl;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/package-info.java
new file mode 100644 (file)
index 0000000..5c99f91
--- /dev/null
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Defines the Apex engine Java API. The API is used to set up, control, send events to, and receive events from an APEX
+ * engine. The {@link ApexEngine} interface is used to control the execution of a single APEX engine thread and to send
+ * events to that APEX engine thread. The {@link EnEventListener} interface is used to listen for events being emitted
+ * by an APEX engine thread.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.engine;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnEvent.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnEvent.java
new file mode 100644 (file)
index 0000000..b072a89
--- /dev/null
@@ -0,0 +1,338 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.event;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import org.onap.policy.apex.core.engine.monitoring.EventMonitor;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Instances of the Class EnEvent are events being passed through the Apex system. All events in the system are
+ * instances of this class.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EnEvent extends HashMap<String, Object> {
+    private static final long serialVersionUID = 6311863111866294637L;
+
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(EnEvent.class);
+
+    // The definition of this event in the Apex model
+    private final AxEvent axEvent;
+
+    // The event monitor for this event
+    private final EventMonitor eventMonitor = new EventMonitor();
+
+    // The stack of execution of this event, used for monitoring
+    private AxConcept[] userArtifactStack;
+
+    private static Random rand = new Random(System.nanoTime());
+
+    // An identifier for the current event execution. The default value here will always be a random number, and should
+    // be reset
+    private long executionID = rand.nextLong();
+
+    // A string holding a message that indicates why processing of this event threw an exception
+    private String exceptionMessage;
+
+    /**
+     * Instantiates a new EnEvent, an Engine Event.
+     *
+     * @param eventKey the key of the event definition from the Apex model
+     */
+    public EnEvent(final AxArtifactKey eventKey) {
+        this(ModelService.getModel(AxEvents.class).get(eventKey));
+    }
+
+    /**
+     * Instantiates a new EnEvent, an Engine Event.
+     *
+     * @param axEvent the event definition from the Apex model
+     */
+    public EnEvent(final AxEvent axEvent) {
+        super();
+        // Save the event definition from the Apex model
+        this.axEvent = axEvent;
+    }
+
+    /**
+     * Gets the event definition of this event.
+     *
+     * @return the event definition
+     */
+    public AxEvent getAxEvent() {
+        return axEvent;
+    }
+
+    /**
+     * Get the name of the event.
+     *
+     * @return the event name
+     */
+    public String getName() {
+        return axEvent.getKey().getName();
+    }
+
+    /**
+     * Get the key of the event.
+     *
+     * @return the event key
+     */
+    public AxArtifactKey getKey() {
+        return axEvent.getKey();
+    }
+
+    /**
+     * Get the ID of the event.
+     *
+     * @return the event key
+     */
+    public String getID() {
+        return axEvent.getKey().getID();
+    }
+
+    /**
+     * Get the currently set value for the ExecutionID for this event. A ExecutionID in an EnEvent is used identify all
+     * EnEvents (input, internal and output events) used in a single Engine invocation. Therefore, a ExecutionID can be
+     * used to match which output event is the result of a particular input event. The default initialized value for the
+     * ExecutionID is always unique in a single JVM.
+     *
+     * @return the currently set value for the ExecutionID for this event.
+     */
+    public long getExecutionID() {
+        return executionID;
+    }
+
+    /**
+     * Set the value for the ExecutionID for this event. A ExecutionID in an EnEvent is used identify all EnEvents
+     * (input, internal and output events) used in a single Engine invocation. Therefore, a ExecutionID can be used to
+     * match which output event is the result of a particular input event. The default initialised value for the
+     * ExecutionID is always unique in a single JVM.
+     *
+     * @param executionID the new value for the ExecutionID for this event.
+     */
+    public void setExecutionID(final long executionID) {
+        this.executionID = executionID;
+    }
+
+    /**
+     * Gets the exception message explaining why processing of this event to fail.
+     *
+     * @return the exception message
+     */
+    public String getExceptionMessage() {
+        return exceptionMessage;
+    }
+
+    /**
+     * Sets the exception message explaining why processing of this event to fail.
+     *
+     * @param exceptionMessage the exception message
+     */
+    public void setExceptionMessage(final String exceptionMessage) {
+        this.exceptionMessage = exceptionMessage;
+    }
+
+    /**
+     * Store the user artifact stack of the event.
+     *
+     * @param usedArtifactStackArray the event user artifact stack
+     */
+    public void setUserArtifactStack(final AxConcept[] usedArtifactStackArray) {
+        userArtifactStack = usedArtifactStackArray;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#get(java.lang.Object)
+     */
+    @Override
+    public Object get(final Object key) {
+        if (key == null) {
+            LOGGER.warn("null values are illegal on method parameter \"key\"");
+            throw new EnException("null values are illegal on method parameter \"key\"");
+        }
+
+        // Check if this key is a parameter on our event
+        final AxField eventParameter = axEvent.getParameterMap().get(key);
+        if (eventParameter == null) {
+            LOGGER.warn("parameter with key " + key + " not defined on this event");
+            throw new EnException("parameter with key " + key + " not defined on this event");
+        }
+
+        // Get the item
+        final Object item = super.get(key);
+
+        // Get the parameter value and monitor it
+        eventMonitor.monitorGet(eventParameter, item, userArtifactStack);
+        return item;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#values()
+     */
+    @Override
+    public Collection<Object> values() {
+        // Build the key set and return it
+        final ArrayList<Object> valueList = new ArrayList<>();
+
+        // Override the generic "values()" call as we want to monitor the gets
+        for (final String key : super.keySet()) {
+            valueList.add(this.get(key));
+        }
+
+        return valueList;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#entrySet()
+     */
+    @Override
+    public Set<Map.Entry<String, Object>> entrySet() {
+        // Build the entry set and return it
+        final Set<Map.Entry<String, Object>> entrySet = new HashSet<>();
+
+        // Override the generic "entrySet()" call as we want to monitor the gets
+        for (final String key : super.keySet()) {
+            entrySet.add(new SimpleEntry<>(key, this.get(key)));
+        }
+
+        return entrySet;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#put(java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public Object put(final String key, final Object incomingValue) {
+        if (key == null) {
+            LOGGER.warn("null keys are illegal on method parameter \"key\"");
+            throw new EnException("null keys are illegal on method parameter \"key\"");
+        }
+
+        // Check if this key is a parameter on our event
+        final AxField eventParameter = axEvent.getParameterMap().get(key);
+        if (eventParameter == null) {
+            LOGGER.warn("parameter with key \"" + key + "\" not defined on event \"" + getName() + "\"");
+            throw new EnException("parameter with key \"" + key + "\" not defined on event \"" + getName() + "\"");
+        }
+
+        // We allow null values
+        if (incomingValue == null) {
+            eventMonitor.monitorSet(eventParameter, incomingValue, userArtifactStack);
+            return super.put(key, incomingValue);
+        }
+
+        // Holder for the object to assign
+        final Object valueToAssign = new EnField(eventParameter, incomingValue).getAssignableValue();
+
+        // Update the value in the parameter map
+        eventMonitor.monitorSet(eventParameter, valueToAssign, userArtifactStack);
+        return super.put(key, valueToAssign);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#putAll(java.util.Map)
+     */
+    @Override
+    public void putAll(final Map<? extends String, ? extends Object> incomingMap) {
+        // Override the generic "putAll()" call as we want to monitor the puts
+        for (final java.util.Map.Entry<? extends String, ? extends Object> incomingEntry : incomingMap.entrySet()) {
+            put(incomingEntry.getKey(), incomingEntry.getValue());
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#remove(java.lang.Object)
+     */
+    @Override
+    public Object remove(final Object key) {
+        if (key == null) {
+            LOGGER.warn("null keys are illegal on method parameter \"key\"");
+            throw new EnException("null keys are illegal on method parameter \"key\"");
+        }
+
+        // Check if this key is a parameter on our event
+        final AxField eventParameter = axEvent.getParameterMap().get(key);
+        if (eventParameter == null) {
+            LOGGER.warn("parameter with key " + key + " not defined on this event");
+            throw new EnException("parameter with key " + key + " not defined on this event");
+        }
+
+        final Object removedValue = super.remove(key);
+        eventMonitor.monitorRemove(eventParameter, removedValue, userArtifactStack);
+        return removedValue;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.Map#clear()
+     */
+    @Override
+    public void clear() {
+        // Override the generic "clear()" call as we want to monitor removals
+        final Set<String> deleteSet = new HashSet<>();
+        deleteSet.addAll(keySet());
+
+        for (final String deleteKey : deleteSet) {
+            this.remove(deleteKey);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.AbstractMap#toString()
+     */
+    @Override
+    public String toString() {
+        return "EnEvent [axEvent=" + axEvent + ", userArtifactStack=" + Arrays.toString(userArtifactStack) + ", map="
+                + super.toString() + "]";
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnException.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnException.java
new file mode 100644 (file)
index 0000000..79a65cb
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.event;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException;
+
+/**
+ * This class will be called if an error occurs in Apex event handling.
+ *
+ * @author Liam Fallon
+ */
+public class EnException extends ApexRuntimeException {
+    private static final long serialVersionUID = -8507246953751956974L;
+
+    /**
+     * Instantiates a new engine event exception.
+     *
+     * @param message the message
+     */
+    public EnException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new engine event exception.
+     *
+     * @param message the message
+     * @param e the e
+     */
+    public EnException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnField.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnField.java
new file mode 100644 (file)
index 0000000..5f12e87
--- /dev/null
@@ -0,0 +1,153 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.event;
+
+import java.io.Serializable;
+
+import org.onap.policy.apex.context.ContextRuntimeException;
+import org.onap.policy.apex.context.SchemaHelper;
+import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Instances of the Class EnField are event fields being passed through the Apex system.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EnField implements Serializable {
+    private static final long serialVersionUID = -5713525780081840333L;
+
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(EnField.class);
+
+    // The definition of this field in the Apex model
+    private final AxField axField;
+
+    // The schema helper for this field
+    private SchemaHelper schemaHelper;
+
+    // The value of this field
+    private final Object value;
+
+    /**
+     * Instantiates a new EnField, an Engine Field.
+     *
+     * @param axField the field definition from the Apex model
+     * @param value the value
+     */
+    public EnField(final AxField axField, final Object value) {
+        // Save the field definition from the Apex model
+        this.axField = axField;
+        this.value = value;
+
+        // Get a schema helper to handle translations of fields to and from the schema
+        try {
+            schemaHelper = new SchemaHelperFactory().createSchemaHelper(axField.getKey(), axField.getSchema());
+        } catch (final ContextRuntimeException e) {
+            final String message = "schema helper cannot be created for parameter with key \"" + axField.getID()
+                    + "\" with schema \"" + axField.getSchema() + "\"";
+            LOGGER.warn(message, e);
+            throw new EnException(message, e);
+        }
+    }
+
+    /**
+     * Gets the field definition of this field.
+     *
+     * @return the field definition
+     */
+    public AxField getAxField() {
+        return axField;
+    }
+
+    /**
+     * Gets the schema helper of this field.
+     *
+     * @return the schema helper for this field
+     */
+    public SchemaHelper getSchemaHelper() {
+        return schemaHelper;
+    }
+
+    /**
+     * Get the name of the field.
+     *
+     * @return the field name
+     */
+    public String getName() {
+        return axField.getKey().getLocalName();
+    }
+
+    /**
+     * Get the key of the field.
+     *
+     * @return the field key
+     */
+    public AxReferenceKey getKey() {
+        return axField.getKey();
+    }
+
+    /**
+     * Get the value of the field.
+     *
+     * @return the value
+     */
+    public Object getValue() {
+        return value;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "EnField [axField=" + axField + ", value=" + value + "]";
+    }
+
+    /**
+     * Get an assignable object that will work with the field.
+     *
+     * @return the assignable value
+     */
+    public Object getAssignableValue() {
+        // Use the schema helper to get the translated value of the object
+        return schemaHelper.unmarshal(value);
+    }
+
+    /**
+     * Is the value object assignable to this field.
+     *
+     * @return true if the value is assignable
+     */
+    public boolean isAssignableValue() {
+        try {
+            schemaHelper.unmarshal(value);
+            return true;
+        } catch (final Exception e) {
+            return false;
+        }
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/package-info.java
new file mode 100644 (file)
index 0000000..e94362a
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides the event handling classes that an APEX engine uses and which uses use to send and receive events to and
+ * from an APEX engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.event;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/Executor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/Executor.java
new file mode 100644 (file)
index 0000000..d659002
--- /dev/null
@@ -0,0 +1,161 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+
+/**
+ * This interface defines what operations must be provided by an executing entity in Apex. It is implemented by classes
+ * that execute logic in a state machine. Each executor has an incoming entity {@code IN} that triggers execution, an
+ * outgoing entity {@code OUT} that is produced by execution, a subject {@code SUBJECT} that is being executed, and a
+ * context {@code CONTEXT} in which execution is being carried out. An executor can be part of a chain of executors and
+ * the {@code setNext} method is used to set the next executor to be executed after this executor has completed.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ *
+ * @param <IN> type of the incoming entity
+ * @param <OUT> type of the outgoing entity
+ * @param <SUBJECT> type that is the subject of execution
+ * @param <CONTEXT> context holding the context of execution
+ */
+
+public interface Executor<IN, OUT, SUBJECT, CONTEXT> {
+    /**
+     * Save the subject and context of the executor.
+     *
+     * @param parent the parent executor of this executor or null if this executor is the top executor
+     * @param executorSubject the executor subject, the subject of execution
+     * @param executorContext the executor context, the context in which execution takes place
+     */
+    void setContext(Executor<?, ?, ?, ?> parent, SUBJECT executorSubject, CONTEXT executorContext);
+
+    /**
+     * Prepares the processing.
+     *
+     * @throws StateMachineException thrown when a state machine execution error occurs
+     */
+    void prepare() throws StateMachineException;
+
+    /**
+     * Executes the executor, running through its context in its natural order.
+     *
+     * @param executionID the execution ID of the current APEX execution policy thread
+     * @param incomingEntity the incoming entity that triggers execution
+     * @return The outgoing entity that is the result of execution
+     * @throws StateMachineException on an execution error
+     * @throws ContextException on context errors
+     */
+    OUT execute(long executionID, IN incomingEntity) throws StateMachineException, ContextException;
+
+    /**
+     * Carry out the preparatory work for execution.
+     *
+     * @param executionID the execution ID of the current APEX execution policy thread
+     * @param incomingEntity the incoming entity that triggers execution
+     * @throws StateMachineException on an execution error
+     * @throws ContextException on context errors
+     */
+    void executePre(long executionID, IN incomingEntity) throws StateMachineException, ContextException;
+
+    /**
+     * Carry out the post work for execution, the returning entity should be set by the child execution object.
+     *
+     * @param returnValue the return value indicates whether the execution was successful and, if it failed, how it
+     *        failed
+     * @throws StateMachineException on an execution error
+     * @throws ContextException On context errors
+     */
+    void executePost(boolean returnValue) throws StateMachineException, ContextException;
+
+    /**
+     * Cleans up after processing.
+     *
+     * @throws StateMachineException thrown when a state machine execution error occurs
+     */
+    void cleanUp() throws StateMachineException;
+
+    /**
+     * Get the key associated with the executor.
+     *
+     * @return The key associated with the executor
+     */
+    AxConcept getKey();
+
+    /**
+     * Get the parent executor of the executor.
+     *
+     * @return The parent executor of this executor
+     */
+    Executor<?, ?, ?, ?> getParent();
+
+    /**
+     * Get the subject of the executor.
+     *
+     * @return The subject for the executor
+     */
+    SUBJECT getSubject();
+
+    /**
+     * Get the context of the executor.
+     *
+     * @return The context for the executor
+     */
+    CONTEXT getContext();
+
+    /**
+     * Get the incoming object of the executor.
+     *
+     * @return The incoming object for the executor
+     */
+    IN getIncoming();
+
+    /**
+     * Get the outgoing object of the executor.
+     *
+     * @return The outgoing object for the executor
+     */
+    OUT getOutgoing();
+
+    /**
+     * Save the next executor for this executor.
+     *
+     * @param nextExecutor the next executor
+     */
+    void setNext(Executor<IN, OUT, SUBJECT, CONTEXT> nextExecutor);
+
+    /**
+     * Get the next executor to be run after this executor completes its execution.
+     *
+     * @return The next executor
+     */
+    Executor<IN, OUT, SUBJECT, CONTEXT> getNext();
+
+    /**
+     * Set parameters for this executor, overloaded by executors that use parameters.
+     *
+     * @param parameters executor parameters
+     */
+    void setParameters(ExecutorParameters parameters);
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/ExecutorFactory.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/ExecutorFactory.java
new file mode 100644 (file)
index 0000000..1bb0b05
--- /dev/null
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+
+/**
+ * This class is used by the state machine to get implementations of task selection and task executors.
+ *
+ * @author Liam Fallon
+ */
+
+public abstract class ExecutorFactory {
+    /**
+     * Get an executor for task selection logic.
+     *
+     * @param stateExecutor the state executor that is requesting the task selection executor
+     * @param state the state containing the task selection logic
+     * @param context the context the context in which the task selection logic will execute
+     * @return The executor that will run the task selection logic
+     */
+    public abstract TaskSelectExecutor getTaskSelectionExecutor(Executor<?, ?, ?, ?> stateExecutor, AxState state,
+            ApexInternalContext context);
+
+    /**
+     * Get an executor for task logic.
+     *
+     * @param stateExecutor the state executor that is requesting the task executor
+     * @param task the task containing the task logic
+     * @param context the context the context in which the task logic will execute
+     * @return The executor that will run the task logic
+     */
+    public abstract TaskExecutor getTaskExecutor(Executor<?, ?, ?, ?> stateExecutor, AxTask task,
+            ApexInternalContext context);
+
+    /**
+     * Get an executor for state finalizer logic.
+     *
+     * @param stateExecutor the state executor that is requesting the state finalizer executor
+     * @param logic the state finalizer logic to execute
+     * @param context the context the context in which the state finalizer logic will execute
+     * @return The executor that will run the state finalizer logic
+     */
+    public abstract StateFinalizerExecutor getStateFinalizerExecutor(Executor<?, ?, ?, ?> stateExecutor,
+            AxStateFinalizerLogic logic, ApexInternalContext context);
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateExecutor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateExecutor.java
new file mode 100644 (file)
index 0000000..ab5b669
--- /dev/null
@@ -0,0 +1,381 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineRuntimeException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskOutputType;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskReference;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.onap.policy.apex.model.policymodel.concepts.AxTasks;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is the executor for a state of a policy.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StateExecutor implements Executor<EnEvent, StateOutput, AxState, ApexInternalContext> {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateExecutor.class);
+
+    // Hold the state and context definitions for this state
+    private AxState axState = null;
+    private Executor<?, ?, ?, ?> parent = null;
+    private ApexInternalContext context = null;
+
+    // Holds the incoming event and the state output for this state
+    private EnEvent lastIncomingEvent = null;
+    private StateOutput lastStateOutput = null;
+
+    // The task selection logic executor
+    private TaskSelectExecutor taskSelectExecutor = null;
+
+    // The map of task executors for this state
+    private final Map<AxArtifactKey, TaskExecutor> taskExecutorMap = new HashMap<>();
+
+    // The map of state outputs used directly by tasks
+    private final Map<AxArtifactKey, String> directStateOutputMap = new HashMap<>();
+
+    // The map of state finalizer logic executors used by tasks
+    private final Map<AxArtifactKey, StateFinalizerExecutor> task2StateFinalizerMap = new HashMap<>();
+
+    // The next state executor
+    private Executor<EnEvent, StateOutput, AxState, ApexInternalContext> nextExecutor = null;
+
+    // The executor factory
+    private ExecutorFactory executorFactory = null;
+
+    /**
+     * Constructor, save the executor factory.
+     *
+     * @param executorFactory the executor factory to use for getting executors for task selection logic
+     */
+    public StateExecutor(final ExecutorFactory executorFactory) {
+        this.executorFactory = executorFactory;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setContext(org.onap.policy.apex.core.engine.executor.Executor,
+     * java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public void setContext(final Executor<?, ?, ?, ?> incomingParent, final AxState incomingAxState,
+            final ApexInternalContext incomingContext) {
+        // Save the state and context definition
+        this.parent = incomingParent;
+        this.axState = incomingAxState;
+        this.context = incomingContext;
+
+        // Set the task selection executor
+        taskSelectExecutor = executorFactory.getTaskSelectionExecutor(this, axState, context);
+
+        // Set a task executor for each task
+        for (final Entry<AxArtifactKey, AxStateTaskReference> stateTaskReferenceEntry : axState.getTaskReferences()
+                .entrySet()) {
+            final AxArtifactKey taskKey = stateTaskReferenceEntry.getKey();
+            final AxStateTaskReference taskReference = stateTaskReferenceEntry.getValue();
+
+            // Get the task
+            final AxTask task = ModelService.getModel(AxTasks.class).get(taskKey);
+
+            // Create a task executor for the task
+            taskExecutorMap.put(taskKey, executorFactory.getTaskExecutor(this, task, context));
+
+            // Check what type of output is specified for the task on this sate
+            if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
+                // Create a task state output reference for this task
+                directStateOutputMap.put(taskKey, taskReference.getOutput().getLocalName());
+            } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
+                // Get the state finalizer logic for this task
+                final AxStateFinalizerLogic finalizerLogic =
+                        axState.getStateFinalizerLogicMap().get(taskReference.getOutput().getLocalName());
+                if (finalizerLogic == null) {
+                    // Finalizer logic for the task does not exist
+                    throw new StateMachineRuntimeException("state finalizer logic on task reference \"" + taskReference
+                            + "\" on state \"" + axState.getID() + "\" does not exist");
+                }
+
+                // Create a state finalizer executor for the task
+                task2StateFinalizerMap.put(taskKey,
+                        executorFactory.getStateFinalizerExecutor(this, finalizerLogic, context));
+            } else {
+                // This should never happen but.....
+                throw new StateMachineRuntimeException("invalid state output type on task reference \"" + taskReference
+                        + "\" on state \"" + axState.getID() + "\"");
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#prepare()
+     */
+    @Override
+    public void prepare() throws StateMachineException {
+        // There may be no task selection logic
+        if (taskSelectExecutor != null) {
+            // Prepare the task selector
+            taskSelectExecutor.prepare();
+        }
+
+        // Prepare the tasks
+        for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
+            taskExecutor.prepare();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#execute(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public StateOutput execute(final long executionID, final EnEvent incomingEvent)
+            throws StateMachineException, ContextException {
+        this.lastIncomingEvent = incomingEvent;
+
+        // Check that the incoming event matches the trigger for this state
+        if (!incomingEvent.getAxEvent().getKey().equals(axState.getTrigger())) {
+            throw new StateMachineException("incoming event \"" + incomingEvent.getID() + "\" does not match trigger \""
+                    + axState.getTrigger().getID() + "\" of state \"" + axState.getID() + "\"");
+        }
+
+        // The key of the task to execute
+        AxArtifactKey taskKey = null;
+
+        try {
+            // There may be no task selection logic, in which case just return the default task
+            if (taskSelectExecutor != null) {
+                // Fire the task selector to find the task to run
+                taskKey = taskSelectExecutor.execute(executionID, incomingEvent);
+            }
+
+            // If there's no task selection logic or the TSL returned no task, just use the default task
+            if (taskKey == null) {
+                taskKey = axState.getDefaultTask();
+            }
+
+            // Execute the task
+            final TreeMap<String, Object> incomingValues = new TreeMap<>();
+            incomingValues.putAll(incomingEvent);
+            final Map<String, Object> taskExecutionResultMap =
+                    taskExecutorMap.get(taskKey).execute(executionID, incomingValues);
+            final AxTask task = taskExecutorMap.get(taskKey).getSubject();
+
+            // Check if this task has direct output
+            String stateOutputName = directStateOutputMap.get(taskKey);
+
+            // If a direct state output name was not found, state finalizer logic should be defined for the task
+            if (stateOutputName == null) {
+                // State finalizer logic should exist for the task
+                final StateFinalizerExecutor finalizerLogicExecutor = task2StateFinalizerMap.get(taskKey);
+                if (finalizerLogicExecutor == null) {
+                    throw new StateMachineException("state finalizer logic for task \"" + taskKey.getID()
+                            + "\" not found for state \"" + axState.getID() + "\"");
+                }
+
+                // Execute the state finalizer logic to select a state output and to adjust the taskExecutionResultMap
+                stateOutputName =
+                        finalizerLogicExecutor.execute(incomingEvent.getExecutionID(), taskExecutionResultMap);
+            }
+
+            // Now look up the the actual state output
+            final AxStateOutput stateOutputDefinition = axState.getStateOutputs().get(stateOutputName);
+            if (stateOutputDefinition == null) {
+                throw new StateMachineException("state output definition for state output \"" + stateOutputName
+                        + "\" not found for state \"" + axState.getID() + "\"");
+            }
+
+            // Create the state output and transfer all the fields across to its event
+            final StateOutput stateOutput = new StateOutput(stateOutputDefinition);
+            this.lastStateOutput = stateOutput;
+
+            stateOutput.setEventFields(task.getRawOutputFields(), taskExecutionResultMap);
+
+            // Copy across fields from the incoming event that are not set on the outgoing event
+            stateOutput.copyUnsetFields(incomingEvent);
+
+            // Set the ExecutionID for the outgoing event to the value in the incoming event.
+            if (stateOutput != null && stateOutput.getOutputEvent() != null) {
+                stateOutput.getOutputEvent().setExecutionID(incomingEvent.getExecutionID());
+            }
+
+            // That's it, the state execution is complete
+            return stateOutput;
+        } catch (final Exception e) {
+            final String errorMessage = "State execution of state \"" + axState.getID() + "\" on task \""
+                    + (taskKey != null ? taskKey.getID() : "null") + "\" failed: " + e.getMessage();
+
+            LOGGER.warn(errorMessage);
+            throw new StateMachineException(errorMessage, e);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePre(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public final void executePre(final long executionID, final EnEvent incomingEntity) throws StateMachineException {
+        throw new StateMachineException("execution pre work not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePost(boolean)
+     */
+    @Override
+    public final void executePost(final boolean returnValue) throws StateMachineException {
+        throw new StateMachineException("execution post work not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#cleanUp()
+     */
+    @Override
+    public void cleanUp() throws StateMachineException {
+        // Clean the tasks
+        for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
+            taskExecutor.cleanUp();
+        }
+
+        if (taskSelectExecutor != null) {
+            // Clean the task selector
+            taskSelectExecutor.cleanUp();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getKey()
+     */
+    @Override
+    public AxReferenceKey getKey() {
+        return axState.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getParent()
+     */
+    @Override
+    public Executor<?, ?, ?, ?> getParent() {
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getSubject()
+     */
+    @Override
+    public AxState getSubject() {
+        return axState;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getContext()
+     */
+    @Override
+    public final ApexInternalContext getContext() {
+        return context;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getIncoming()
+     */
+    @Override
+    public final EnEvent getIncoming() {
+        return lastIncomingEvent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getOutgoing()
+     */
+    @Override
+    public final StateOutput getOutgoing() {
+        return lastStateOutput;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setNext(org.onap.policy.apex.core.engine.executor.Executor)
+     */
+    @Override
+    public final void setNext(final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> incomingNextExecutor) {
+        this.nextExecutor = incomingNextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getNext()
+     */
+    @Override
+    public final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> getNext() {
+        return nextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#setParameters(org.onap.policy.apex.core.engine.
+     * ExecutorParameters)
+     */
+    @Override
+    public void setParameters(final ExecutorParameters parameters) {}
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateFinalizerExecutor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateFinalizerExecutor.java
new file mode 100644 (file)
index 0000000..1769101
--- /dev/null
@@ -0,0 +1,272 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import static org.onap.policy.apex.model.utilities.Assertions.argumentNotNull;
+
+import java.util.Map;
+
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.executor.context.StateFinalizerExecutionContext;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This abstract class executes state finalizer logic in a state of an Apex policy and is specialized by classes that
+ * implement execution of state finalizer logic.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public abstract class StateFinalizerExecutor
+        implements Executor<Map<String, Object>, String, AxStateFinalizerLogic, ApexInternalContext> {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateFinalizerExecutor.class);
+
+    // Hold the state and context definitions
+    private Executor<?, ?, ?, ?> parent = null;
+    private AxState axState = null;
+    private AxStateFinalizerLogic finalizerLogic = null;
+    private ApexInternalContext internalContext = null;
+
+    // Holds the incoming and outgoing fields
+    private Map<String, Object> incomingFields = null;
+
+    // The next state finalizer executor
+    private Executor<Map<String, Object>, String, AxStateFinalizerLogic, ApexInternalContext> nextExecutor = null;
+
+    // The execution context; contains the facades for events and context to be used by tasks executed by this task
+    // executor
+    private StateFinalizerExecutionContext executionContext = null;
+
+    /**
+     * Gets the execution internalContext.
+     *
+     * @return the execution context
+     */
+    protected StateFinalizerExecutionContext getExecutionContext() {
+        return executionContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setContext(org.onap.policy.apex.core.engine.executor.Executor,
+     * java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public void setContext(final Executor<?, ?, ?, ?> incomingParent,
+            final AxStateFinalizerLogic incomingFinalizerLogic, final ApexInternalContext incomingInternalContext) {
+        this.parent = incomingParent;
+        axState = (AxState) parent.getSubject();
+        this.finalizerLogic = incomingFinalizerLogic;
+        this.internalContext = incomingInternalContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#prepare()
+     */
+    @Override
+    public void prepare() throws StateMachineException {
+        LOGGER.debug("prepare:" + finalizerLogic.getID() + "," + finalizerLogic.getLogicFlavour() + ","
+                + finalizerLogic.getLogic());
+        argumentNotNull(finalizerLogic.getLogic(), StateMachineException.class, "task logic cannot be null.");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#execute(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public String execute(final long executionID, final Map<String, Object> newIncomingFields)
+            throws StateMachineException, ContextException {
+        throw new StateMachineException(
+                "execute() not implemented on abstract StateFinalizerExecutionContext class, only on its subclasses");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePre(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public final void executePre(final long executionID, final Map<String, Object> newIncomingFields)
+            throws StateMachineException, ContextException {
+        LOGGER.debug("execute-pre:" + finalizerLogic.getLogicFlavour() + "," + getSubject().getID() + ","
+                + finalizerLogic.getLogic());
+
+        // Record the incoming fields
+        this.incomingFields = newIncomingFields;
+
+        // Get state finalizer context object
+        executionContext = new StateFinalizerExecutionContext(this, executionID, axState, getIncoming(),
+                axState.getStateOutputs().keySet(), getContext());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePost(boolean)
+     */
+    @Override
+    public final void executePost(final boolean returnValue) throws StateMachineException, ContextException {
+        if (!returnValue) {
+            String errorMessage = "execute-post: state finalizer logic execution failure on state \"" + axState.getID()
+                    + "\" on finalizer logic " + finalizerLogic.getID();
+            if (executionContext.getMessage() != null) {
+                errorMessage += ", user message: " + executionContext.getMessage();
+            }
+            LOGGER.warn(errorMessage);
+            throw new StateMachineException(errorMessage);
+        }
+
+        // Check a state output has been selected
+        if (getOutgoing() == null) {
+            LOGGER.warn("execute-post: state finalizer logic \"" + finalizerLogic.getID()
+                    + "\" did not select an output state");
+            throw new StateMachineException("execute-post: state finalizer logic \"" + finalizerLogic.getID()
+                    + "\" did not select an output state");
+        }
+
+        if (!axState.getStateOutputs().keySet().contains(getOutgoing())) {
+            LOGGER.warn(
+                    "execute-post: state finalizer logic \"" + finalizerLogic.getID() + "\" selected output state \""
+                            + getOutgoing() + "\" that does not exsist on state \"" + axState.getID() + "\"");
+            throw new StateMachineException(
+                    "execute-post: state finalizer logic \"" + finalizerLogic.getID() + "\" selected output state \""
+                            + getOutgoing() + "\" that does not exsist on state \"" + axState.getID() + "\"");
+        }
+
+        LOGGER.debug("execute-post:" + finalizerLogic.getID() + ", returning  state output \"" + getOutgoing()
+                + " and fields " + incomingFields);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#cleanUp()
+     */
+    @Override
+    public void cleanUp() throws StateMachineException {
+        throw new StateMachineException("cleanUp() not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getKey()
+     */
+    @Override
+    public AxReferenceKey getKey() {
+        return finalizerLogic.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getParent()
+     */
+    @Override
+    public Executor<?, ?, ?, ?> getParent() {
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getSubject()
+     */
+    @Override
+    public AxStateFinalizerLogic getSubject() {
+        return finalizerLogic;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getContext()
+     */
+    @Override
+    public ApexInternalContext getContext() {
+        return internalContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getIncoming()
+     */
+    @Override
+    public Map<String, Object> getIncoming() {
+        return incomingFields;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getOutgoing()
+     */
+    @Override
+    public String getOutgoing() {
+        return executionContext.getSelectedStateOutputName();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setNext(org.onap.policy.apex.core.engine.executor.Executor)
+     */
+    @Override
+    public void setNext(
+            final Executor<Map<String, Object>, String, AxStateFinalizerLogic, ApexInternalContext> incomingNextExecutor) {
+        this.nextExecutor = incomingNextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getNext()
+     */
+    @Override
+    public Executor<Map<String, Object>, String, AxStateFinalizerLogic, ApexInternalContext> getNext() {
+        return nextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#setParameters(org.onap.policy.apex.core.engine.
+     * ExecutorParameters)
+     */
+    @Override
+    public void setParameters(final ExecutorParameters parameters) {}
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateMachineExecutor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateMachineExecutor.java
new file mode 100644 (file)
index 0000000..05e1b3b
--- /dev/null
@@ -0,0 +1,290 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
+
+/**
+ * This class is the executor for a state machine built from a policy.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StateMachineExecutor implements Executor<EnEvent, EnEvent, AxPolicy, ApexInternalContext> {
+    // The Apex Policy and context for this state machine
+    private AxPolicy axPolicy = null;
+    private Executor<?, ?, ?, ?> parent = null;
+    private ApexInternalContext internalContext = null;
+
+    // The list of state executors for this state machine
+    private final Map<AxReferenceKey, StateExecutor> stateExecutorMap = new TreeMap<>();
+
+    // The first executor
+    private StateExecutor firstExecutor = null;
+
+    // The next state machine executor
+    private Executor<EnEvent, EnEvent, AxPolicy, ApexInternalContext> nextExecutor = null;
+
+    // The executor factory
+    private ExecutorFactory executorFactory = null;
+
+    /**
+     * Constructor, save the executor factory that will give us executors for task selection logic and task logic.
+     *
+     * @param executorFactory the executor factory
+     * @param owner the artifact key of the owner of this state machine
+     */
+    public StateMachineExecutor(final ExecutorFactory executorFactory, final AxArtifactKey owner) {
+        this.executorFactory = executorFactory;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setContext(org.onap.policy.apex.core.engine.executor.Executor,
+     * java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public void setContext(final Executor<?, ?, ?, ?> newParent, final AxPolicy newAxPolicy,
+            final ApexInternalContext newInternalContext) {
+        // Save the policy and context for this state machine
+        this.parent = newParent;
+        this.axPolicy = newAxPolicy;
+        this.internalContext = newInternalContext;
+
+        // Clear the first executor, setContext can be called multiple times
+        firstExecutor = null;
+
+        // Create the state executors for this state machine
+        StateExecutor lastExecutor = null;
+        for (final AxState state : axPolicy.getStateMap().values()) {
+            // Create a state executor for this state and add its context (the state)
+            final StateExecutor stateExecutor = new StateExecutor(executorFactory);
+            stateExecutor.setContext(this, state, internalContext);
+
+            // Update the next executor on the last executor
+            if (lastExecutor != null) {
+                lastExecutor.setNext(stateExecutor);
+            }
+            lastExecutor = stateExecutor;
+
+            // Add the state executor to the executor list
+            stateExecutorMap.put(state.getKey(), stateExecutor);
+
+            // Set the first executor if it is not set
+            if (state.getKey().getLocalName().equals(axPolicy.getFirstState())) {
+                firstExecutor = stateExecutor;
+            }
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#prepare()
+     */
+    @Override
+    public void prepare() throws StateMachineException {
+        for (final StateExecutor stateExecutor : stateExecutorMap.values()) {
+            stateExecutor.prepare();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executeDirected(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public EnEvent execute(final long executionID, final EnEvent incomingEvent)
+            throws StateMachineException, ContextException {
+        // Check if there are any states on the state machine
+        if (stateExecutorMap.size() == 0) {
+            throw new StateMachineException("no states defined on state machine");
+        }
+
+        // Check if the first state of the machine is defined
+        if (firstExecutor == null) {
+            throw new StateMachineException("first state not defined on state machine");
+        }
+
+        // Get the first state of the state machine and define a state output that starts state execution
+        StateExecutor stateExecutor = firstExecutor;
+        StateOutput stateOutput = new StateOutput(new AxStateOutput(firstExecutor.getSubject().getKey(),
+                incomingEvent.getKey(), firstExecutor.getSubject().getKey()), incomingEvent);
+        while (true) {
+            // Execute the state
+            stateOutput = stateExecutor.execute(executionID, stateOutput.getOutputEvent());
+            if (stateOutput == null) {
+                throw new StateMachineException("state execution failed, invalid state output returned");
+            }
+
+            // Use the next state of the state output to find if all the states have executed
+            if (stateOutput.getNextState().equals(AxReferenceKey.getNullKey())) {
+                break;
+            }
+
+            // Use the next state of the state output to find the next state
+            stateExecutor = stateExecutorMap.get(stateOutput.getNextState());
+            if (stateExecutor == null) {
+                throw new StateMachineException(
+                        "state execution failed, next state \"" + stateOutput.getNextState().getID() + "\" not found");
+            }
+        }
+
+        return stateOutput.getOutputEvent();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePre(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public final void executePre(final long executionID, final EnEvent incomingEntity) throws StateMachineException {
+        throw new StateMachineException("execution pre work not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePost(boolean)
+     */
+    @Override
+    public final void executePost(final boolean returnValue) throws StateMachineException {
+        throw new StateMachineException("execution post work not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#cleanUp()
+     */
+    @Override
+    public void cleanUp() throws StateMachineException {
+        for (final StateExecutor stateExecutor : stateExecutorMap.values()) {
+            stateExecutor.cleanUp();
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getKey()
+     */
+    @Override
+    public AxArtifactKey getKey() {
+        return axPolicy.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getParent()
+     */
+    @Override
+    public final Executor<?, ?, ?, ?> getParent() {
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getSubject()
+     */
+    @Override
+    public final AxPolicy getSubject() {
+        return axPolicy;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getContext()
+     */
+    @Override
+    public final ApexInternalContext getContext() {
+        return internalContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getIncoming()
+     */
+    @Override
+    public final EnEvent getIncoming() {
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getOutgoing()
+     */
+    @Override
+    public final EnEvent getOutgoing() {
+        return null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setNext(org.onap.policy.apex.core.engine.executor.Executor)
+     */
+    @Override
+    public final void setNext(final Executor<EnEvent, EnEvent, AxPolicy, ApexInternalContext> newNextExecutor) {
+        this.nextExecutor = newNextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getNext()
+     */
+    @Override
+    public final Executor<EnEvent, EnEvent, AxPolicy, ApexInternalContext> getNext() {
+        return nextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#setParameters(org.onap.policy.apex.core.engine.
+     * ExecutorParameters)
+     */
+    @Override
+    public void setParameters(final ExecutorParameters parameters) {}
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateOutput.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateOutput.java
new file mode 100644 (file)
index 0000000..2274b7c
--- /dev/null
@@ -0,0 +1,167 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import java.util.Map;
+import java.util.Map.Entry;
+
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
+import org.onap.policy.apex.model.utilities.Assertions;
+
+/**
+ * This class is the output of a state, and is used by the engine to decide what the next state for execution is.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StateOutput {
+    // The state output has a state and an event
+    private final AxStateOutput stateOutputDefinition;
+    private final AxEvent outputEventDef;
+    private final EnEvent outputEvent;
+
+    /**
+     * Create a new state output from a state output definition.
+     *
+     * @param axStateOutput the state output definition
+     */
+    public StateOutput(final AxStateOutput axStateOutput) {
+        this(axStateOutput, new EnEvent(axStateOutput.getOutgingEvent()));
+    }
+
+    /**
+     * Create a new state output with the given definition and event key.
+     *
+     * @param stateOutputDefinition the state output definition
+     * @param outputEvent the output event
+     */
+    public StateOutput(final AxStateOutput stateOutputDefinition, final EnEvent outputEvent) {
+        Assertions.argumentNotNull(stateOutputDefinition, "stateOutputDefinition may not be null");
+        Assertions.argumentNotNull(outputEvent, "outputEvent may not be null");
+
+        this.stateOutputDefinition = stateOutputDefinition;
+        this.outputEvent = outputEvent;
+        outputEventDef = ModelService.getModel(AxEvents.class).get(stateOutputDefinition.getOutgingEvent());
+    }
+
+    /**
+     * Gets the next state.
+     *
+     * @return the next state
+     */
+    public AxReferenceKey getNextState() {
+        return stateOutputDefinition.getNextState();
+    }
+
+    /**
+     * Gets the state output definition.
+     *
+     * @return the state output definition
+     */
+    public AxStateOutput getStateOutputDefinition() {
+        return stateOutputDefinition;
+    }
+
+    /**
+     * Gets the output event.
+     *
+     * @return the output event
+     */
+    public EnEvent getOutputEvent() {
+        return outputEvent;
+    }
+
+    /**
+     * Transfer the fields from the incoming field map into the event.
+     *
+     * @param incomingFieldDefinitionMap definitions of the incoming fields
+     * @param eventFieldMap the event field map
+     * @throws StateMachineException on errors populating the event fields
+     */
+    public void setEventFields(final Map<String, AxField> incomingFieldDefinitionMap,
+            final Map<String, Object> eventFieldMap) throws StateMachineException {
+        Assertions.argumentNotNull(incomingFieldDefinitionMap, "incomingFieldDefinitionMap may not be null");
+        Assertions.argumentNotNull(eventFieldMap, "eventFieldMap may not be null");
+
+        if (!incomingFieldDefinitionMap.keySet().equals(eventFieldMap.keySet())) {
+            throw new StateMachineException(
+                    "field definitions and values do not match for event " + outputEventDef.getID() + '\n'
+                            + incomingFieldDefinitionMap.keySet() + '\n' + eventFieldMap.keySet());
+        }
+        for (final Entry<String, Object> incomingFieldEntry : eventFieldMap.entrySet()) {
+            final String fieldName = incomingFieldEntry.getKey();
+            final AxField fieldDef = incomingFieldDefinitionMap.get(fieldName);
+            try {
+
+                // Check if this field is a field in the event
+                if (!outputEventDef.getFields().contains(fieldDef)) {
+                    throw new StateMachineException(
+                            "field \"" + fieldName + "\" does not exist on event \"" + outputEventDef.getID() + "\"");
+                }
+            } catch (final Exception e) {
+                e.printStackTrace();
+            }
+
+            // Set the value in the output event
+            outputEvent.put(fieldName, incomingFieldEntry.getValue());
+        }
+    }
+
+    /**
+     * This method copies any fields that exist on the input event that also exist on the output event if they are not
+     * set on the output event.
+     *
+     * @param incomingEvent The incoming event to copy from
+     */
+    public void copyUnsetFields(final EnEvent incomingEvent) {
+        Assertions.argumentNotNull(incomingEvent, "incomingEvent may not be null");
+
+        for (final Entry<String, Object> incomingField : incomingEvent.entrySet()) {
+            final String fieldName = incomingField.getKey();
+
+            // Check if the field exists on the outgoing event
+            if (!outputEventDef.getParameterMap().containsKey(fieldName)) {
+                continue;
+            }
+
+            // Check if the field is set on the outgoing event
+            if (outputEvent.containsKey(fieldName)) {
+                continue;
+            }
+
+            // Now, check the fields have the same type
+            if (!incomingEvent.getAxEvent().getParameterMap().get(fieldName)
+                    .equals(outputEvent.getAxEvent().getParameterMap().get(fieldName))) {
+                continue;
+            }
+
+            // All checks done, we can copy the value
+            outputEvent.put(fieldName, incomingField.getValue());
+        }
+
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskExecutor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskExecutor.java
new file mode 100644 (file)
index 0000000..4a105b0
--- /dev/null
@@ -0,0 +1,328 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import static org.onap.policy.apex.model.utilities.Assertions.argumentNotNull;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.executor.context.TaskExecutionContext;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.eventmodel.concepts.AxInputField;
+import org.onap.policy.apex.model.eventmodel.concepts.AxOutputField;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This abstract class executes a task in a state of an Apex policy and is specialized by classes that implement
+ * execution of task logic.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public abstract class TaskExecutor
+        implements Executor<Map<String, Object>, Map<String, Object>, AxTask, ApexInternalContext> {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(TaskExecutor.class);
+
+    // Hold the task and context definitions for this task
+    private Executor<?, ?, ?, ?> parent = null;
+    private AxTask axTask = null;
+    private ApexInternalContext internalContext = null;
+
+    // Holds the incoming and outgoing fields
+    private Map<String, Object> incomingFields = null;
+    private Map<String, Object> outgoingFields = null;
+
+    // The next task executor
+    private Executor<Map<String, Object>, Map<String, Object>, AxTask, ApexInternalContext> nextExecutor = null;
+
+    // The task execution context; contains the facades for events and context to be used by tasks executed by this task
+    // executor
+    private TaskExecutionContext executionContext = null;
+
+    /**
+     * Gets the execution internalContext.
+     *
+     * @return the execution context
+     */
+    protected TaskExecutionContext getExecutionContext() {
+        return executionContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setContext(org.onap.policy.apex.core.engine.executor.Executor,
+     * java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public void setContext(final Executor<?, ?, ?, ?> newParent, final AxTask newAxTask,
+            final ApexInternalContext newInternalContext) {
+        this.parent = newParent;
+        this.axTask = newAxTask;
+        this.internalContext = newInternalContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#prepare()
+     */
+    @Override
+    public void prepare() throws StateMachineException {
+        LOGGER.debug("prepare:" + axTask.getKey().getID() + "," + axTask.getTaskLogic().getLogicFlavour() + ","
+                + axTask.getTaskLogic().getLogic());
+        argumentNotNull(axTask.getTaskLogic().getLogic(), StateMachineException.class, "task logic cannot be null.");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#execute(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public Map<String, Object> execute(final long executionID, final Map<String, Object> newIncomingFields)
+            throws StateMachineException, ContextException {
+        throw new StateMachineException(
+                "execute() not implemented on abstract TaskExecutor class, only on its subclasses");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePre(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public final void executePre(final long executionID, final Map<String, Object> newIncomingFields)
+            throws StateMachineException, ContextException {
+        LOGGER.debug("execute-pre:" + getSubject().getTaskLogic().getLogicFlavour() + ","
+                + getSubject().getKey().getID() + "," + getSubject().getTaskLogic().getLogic());
+
+        // Check that the incoming event has all the input fields for this state
+        final Set<String> missingTaskInputFields = new TreeSet<>(axTask.getInputFields().keySet());
+        missingTaskInputFields.removeAll(newIncomingFields.keySet());
+
+        // Remove fields from the set that are optional
+        for (final Iterator<String> missingFieldIterator = missingTaskInputFields.iterator(); missingFieldIterator
+                .hasNext();) {
+            final String missingField = missingFieldIterator.next();
+            if (axTask.getInputFields().get(missingField).getOptional()) {
+                missingTaskInputFields.remove(missingField);
+            }
+        }
+        if (!missingTaskInputFields.isEmpty()) {
+            throw new StateMachineException("task input fields \"" + missingTaskInputFields
+                    + "\" are missing for task \"" + axTask.getKey().getID() + "\"");
+        }
+
+        // Record the incoming fields
+        this.incomingFields = newIncomingFields;
+
+        // Initiate the outgoing fields
+        outgoingFields = new TreeMap<>();
+        for (final String outputFieldName : getSubject().getOutputFields().keySet()) {
+            outgoingFields.put(outputFieldName, null);
+        }
+
+        // Get task context object
+        executionContext =
+                new TaskExecutionContext(this, executionID, getSubject(), getIncoming(), getOutgoing(), getContext());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePost(boolean)
+     */
+    @Override
+    public final void executePost(final boolean returnValue) throws StateMachineException, ContextException {
+        if (!returnValue) {
+            String errorMessage = "execute-post: task logic execution failure on task \"" + axTask.getKey().getName()
+                    + "\" in model " + internalContext.getKey().getID();
+            if (executionContext.getMessage() != null) {
+                errorMessage += ", user message: " + executionContext.getMessage();
+            }
+            LOGGER.warn(errorMessage);
+            throw new StateMachineException(errorMessage);
+        }
+
+        // Copy any unset fields from the input to the output if their data type and names are identical
+        for (final String field : axTask.getOutputFields().keySet()) {
+            // Check if the field exists and is not set on the output
+            if (!getOutgoing().containsKey(field) || getOutgoing().get(field) != null) {
+                continue;
+            }
+
+            // This field is not in the output, check if it's on the input and is the same type (Note here, the output
+            // field definition has to exist so it's not
+            // null checked)
+            final AxInputField inputFieldDef = axTask.getInputFields().get(field);
+            final AxOutputField outputFieldDef = axTask.getOutputFields().get(field);
+            if (inputFieldDef == null || !inputFieldDef.getSchema().equals(outputFieldDef.getSchema())) {
+                continue;
+            }
+
+            // We have an input field that matches our output field, copy the value across
+            getOutgoing().put(field, getIncoming().get(field));
+        }
+
+        // Finally, check that the outgoing fields have all the output fields defined for this state and, if not, output
+        // a list of missing fields
+        final Set<String> missingTaskOutputFields = new TreeSet<>(axTask.getOutputFields().keySet());
+        missingTaskOutputFields.removeAll(outgoingFields.keySet());
+
+        // Remove fields from the set that are optional
+        for (final Iterator<String> missingFieldIterator = missingTaskOutputFields.iterator(); missingFieldIterator
+                .hasNext();) {
+            final String missingField = missingFieldIterator.next();
+            if (axTask.getInputFields().get(missingField).getOptional()) {
+                missingTaskOutputFields.remove(missingField);
+            }
+        }
+        if (!missingTaskOutputFields.isEmpty()) {
+            throw new StateMachineException("task output fields \"" + missingTaskOutputFields
+                    + "\" are missing for task \"" + axTask.getKey().getID() + "\"");
+        }
+
+        // Finally, check that the outgoing field map don't have any extra fields, if present, raise exception with the
+        // list of extra fields
+        final Set<String> extraTaskOutputFields = new TreeSet<>(outgoingFields.keySet());
+        extraTaskOutputFields.removeAll(axTask.getOutputFields().keySet());
+        if (!extraTaskOutputFields.isEmpty()) {
+            throw new StateMachineException("task output fields \"" + extraTaskOutputFields
+                    + "\" are unwanted for task \"" + axTask.getKey().getID() + "\"");
+        }
+
+        LOGGER.debug("execute-post:" + axTask.getKey().getID() + ", returning fields " + outgoingFields.toString());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#cleanUp()
+     */
+    @Override
+    public void cleanUp() throws StateMachineException {
+        throw new StateMachineException("cleanUp() not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getKey()
+     */
+    @Override
+    public AxArtifactKey getKey() {
+        return axTask.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getParent()
+     */
+    @Override
+    public Executor<?, ?, ?, ?> getParent() {
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getSubject()
+     */
+    @Override
+    public AxTask getSubject() {
+        return axTask;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getContext()
+     */
+    @Override
+    public ApexInternalContext getContext() {
+        return internalContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getIncoming()
+     */
+    @Override
+    public Map<String, Object> getIncoming() {
+        return incomingFields;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getOutgoing()
+     */
+    @Override
+    public Map<String, Object> getOutgoing() {
+        return outgoingFields;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setNext(org.onap.policy.apex.core.engine.executor.Executor)
+     */
+    @Override
+    public void setNext(
+            final Executor<Map<String, Object>, Map<String, Object>, AxTask, ApexInternalContext> newNextExecutor) {
+        this.nextExecutor = newNextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getNext()
+     */
+    @Override
+    public Executor<Map<String, Object>, Map<String, Object>, AxTask, ApexInternalContext> getNext() {
+        return nextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#setParameters(org.onap.policy.apex.core.engine.
+     * ExecutorParameters)
+     */
+    @Override
+    public void setParameters(final ExecutorParameters parameters) {}
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskSelectExecutor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskSelectExecutor.java
new file mode 100644 (file)
index 0000000..1aaa5cc
--- /dev/null
@@ -0,0 +1,263 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor;
+
+import static org.onap.policy.apex.model.utilities.Assertions.argumentNotNull;
+
+import org.onap.policy.apex.context.ContextException;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.context.TaskSelectionExecutionContext;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This abstract class executes a the task selection logic of a state of an Apex policy and is specialized by classes
+ * that implement execution of task selection logic.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public abstract class TaskSelectExecutor implements Executor<EnEvent, AxArtifactKey, AxState, ApexInternalContext> {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(TaskSelectExecutor.class);
+
+    // Hold the state and context definitions for this task selector
+    private Executor<?, ?, ?, ?> parent = null;
+    private AxState axState = null;
+    private ApexInternalContext context = null;
+
+    // Holds the incoming event and outgoing task keys
+    private EnEvent incomingEvent = null;
+    private AxArtifactKey outgoingTaskKey = null;
+
+    // The next task selection executor
+    private Executor<EnEvent, AxArtifactKey, AxState, ApexInternalContext> nextExecutor = null;
+
+    // The task selection execution context; contains the facades for events and context to be used by tasks executed by
+    // this task selection executor
+    private TaskSelectionExecutionContext executionContext;
+
+    /**
+     * Gets the execution context.
+     *
+     * @return the execution context
+     */
+    protected TaskSelectionExecutionContext getExecutionContext() {
+        return executionContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setContext(org.onap.policy.apex.core.engine.executor.Executor,
+     * java.lang.Object, java.lang.Object)
+     */
+    @Override
+    public void setContext(final Executor<?, ?, ?, ?> newParent, final AxState newAxState,
+            final ApexInternalContext newContext) {
+        this.parent = newParent;
+        this.axState = newAxState;
+        this.context = newContext;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#prepare()
+     */
+    @Override
+    public void prepare() throws StateMachineException {
+        LOGGER.debug("prepare:" + axState.getKey().getID() + "," + axState.getTaskSelectionLogic().getLogicFlavour()
+                + "," + axState.getTaskSelectionLogic().getLogic());
+        argumentNotNull(axState.getTaskSelectionLogic().getLogic(), "task selection logic cannot be null.");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#execute(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public AxArtifactKey execute(final long executionID, final EnEvent newIncomingEvent)
+            throws StateMachineException, ContextException {
+        throw new StateMachineException("execute() not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePre(java.lang.long, java.lang.Object)
+     */
+    @Override
+    public final void executePre(final long executionID, final EnEvent newIncomingEvent) throws StateMachineException {
+        LOGGER.debug("execute-pre:" + axState.getKey().getID() + "," + axState.getTaskSelectionLogic().getLogicFlavour()
+                + "," + axState.getTaskSelectionLogic().getLogic());
+
+        this.incomingEvent = newIncomingEvent;
+
+        // Initialize the returned task object so it can be set
+        outgoingTaskKey = new AxArtifactKey();
+
+        // Get task selection context object
+        executionContext = new TaskSelectionExecutionContext(this, executionID, getSubject(), getIncoming(),
+                getOutgoing(), getContext());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#executePost(boolean)
+     */
+    @Override
+    public final void executePost(final boolean returnValue) throws StateMachineException {
+        if (!returnValue) {
+            String errorMessage =
+                    "execute-post: task selection logic failed on state \"" + axState.getKey().getID() + "\"";
+            if (executionContext.getMessage() != null) {
+                errorMessage += ", user message: " + executionContext.getMessage();
+            }
+            LOGGER.warn(errorMessage);
+            throw new StateMachineException(errorMessage);
+        }
+
+        if (outgoingTaskKey == null || AxArtifactKey.getNullKey().getName().equals(outgoingTaskKey.getName())) {
+            outgoingTaskKey = axState.getDefaultTask();
+            LOGGER.debug("execute-post:" + axState.getKey().getID() + ", returning default task");
+            return;
+        }
+
+        if (!axState.getTaskReferences().containsKey(outgoingTaskKey)) {
+            LOGGER.error("execute-post: task \"" + outgoingTaskKey.getID()
+                    + "\" returned by task selection logic not defined on state \"" + axState.getKey().getID() + "\"");
+            throw new StateMachineException("task \"" + outgoingTaskKey.getID()
+                    + "\" returned by task selection logic not defined on state \"" + axState.getKey().getID() + "\"");
+        }
+
+        LOGGER.debug("execute-post:" + axState.getKey().getID() + "," + ", returning task " + outgoingTaskKey.getID());
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#cleanUp()
+     */
+    @Override
+    public void cleanUp() throws StateMachineException {
+        throw new StateMachineException("cleanUp() not implemented on class");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getKey()
+     */
+    @Override
+    public AxReferenceKey getKey() {
+        return axState.getKey();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getParent()
+     */
+    @Override
+    public Executor<?, ?, ?, ?> getParent() {
+        return parent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getSubject()
+     */
+    @Override
+    public AxState getSubject() {
+        return axState;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getContext()
+     */
+    @Override
+    public ApexInternalContext getContext() {
+        return context;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.Executor#setNext(org.onap.policy.apex.core.engine.executor.Executor)
+     */
+    @Override
+    public void setNext(final Executor<EnEvent, AxArtifactKey, AxState, ApexInternalContext> newNextExecutor) {
+        this.nextExecutor = newNextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getNext()
+     */
+    @Override
+    public Executor<EnEvent, AxArtifactKey, AxState, ApexInternalContext> getNext() {
+        return nextExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getIncoming()
+     */
+    @Override
+    public EnEvent getIncoming() {
+        return incomingEvent;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#getOutgoing()
+     */
+    @Override
+    public AxArtifactKey getOutgoing() {
+        return outgoingTaskKey;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.Executor#setParameters(org.onap.policy.apex.core.engine.
+     * ExecutorParameters)
+     */
+    @Override
+    public void setParameters(final ExecutorParameters parameters) {}
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxStateFacade.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxStateFacade.java
new file mode 100644 (file)
index 0000000..226f06a
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.context;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxTasks;
+
+/**
+ * The Class AxStateFacade acts as a facade into the AxState class so that task logic can easily access information in
+ * an AxState instance.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ */
+public class AxStateFacade {
+    // CHECKSTYLE:OFF: checkstyle:visibilityModifier Logic has access to this field
+
+    /** The full definition information for the state. */
+    public AxState state;
+
+    // CHECKSTYLE:ON: checkstyle:visibilityModifier
+
+    /**
+     * Instantiates a new AxState facade.
+     *
+     * @param state the state for which a facade is being presented
+     */
+    public AxStateFacade(final AxState state) {
+        this.state = state;
+    }
+
+    /**
+     * Gets the default task key of the state.
+     *
+     * @return the default task key
+     */
+    public AxArtifactKey getDefaultTaskKey() {
+        return state.getDefaultTask();
+    }
+
+    /**
+     * Gets the ID of the state.
+     *
+     * @return the ID
+     */
+    public String getId() {
+        return state.getKey().getID();
+    }
+
+    /**
+     * Gets the name of the state.
+     *
+     * @return the state name
+     */
+    public String getStateName() {
+        return state.getKey().getLocalName();
+    }
+
+    /**
+     * Check if a task is defined for a given task name on a state and, if so, return its key.
+     *
+     * @param taskName the name of the task to get
+     * @return the task key or null if it does not exist
+     */
+    public AxArtifactKey getTaskKey(final String taskName) {
+        if (taskName == null) {
+            return null;
+        }
+
+        return ModelService.getModel(AxTasks.class).get(taskName).getKey();
+    }
+
+    /**
+     * Check if a task is defined for a given task name on a state and, if so, return its key.
+     *
+     * @return unmodifiable list of names of tasks available
+     */
+    public List<String> getTaskNames() {
+        final Set<AxArtifactKey> tasks = state.getTaskReferences().keySet();
+        final List<String> ret = new ArrayList<>(tasks.size());
+        for (final AxArtifactKey task : tasks) {
+            ret.add(task.getName());
+        }
+        return Collections.unmodifiableList(ret);
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxTaskFacade.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxTaskFacade.java
new file mode 100644 (file)
index 0000000..015f3ae
--- /dev/null
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.context;
+
+import org.onap.policy.apex.context.ContextRuntimeException;
+import org.onap.policy.apex.context.SchemaHelper;
+import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
+import org.onap.policy.apex.core.engine.event.EnException;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineRuntimeException;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class AxTaskFacade acts as a facade into the AxTask class so that task logic can easily access information in an
+ * AxTask instance.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ */
+public class AxTaskFacade {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(AxTaskFacade.class);
+
+    // CHECKSTYLE:OFF: checkstyle:visibilityModifier Logic has access to this field
+
+    /**
+     * The full definition of the task we are presenting a facade to, executing logic has full access to the task
+     * definition.
+     */
+    public AxTask task;
+
+    // CHECKSTYLE:ON: checkstyle:visibilityModifier
+
+    /**
+     * Instantiates a new AxTask facade.
+     *
+     * @param task the task for which a facade is being presented
+     */
+    public AxTaskFacade(final AxTask task) {
+        this.task = task;
+    }
+
+    /**
+     * Gets the name of the task.
+     *
+     * @return the task name
+     */
+    public String getTaskName() {
+        return task.getKey().getName();
+    }
+
+    /**
+     * Gets the task ID.
+     *
+     * @return the task ID
+     */
+    public String getId() {
+        return task.getID();
+    }
+
+    /**
+     * Creates a schema helper for an incoming field of this task.
+     *
+     * @param fieldName The name of the field to get a schema helper for
+     * @return the schema helper for this field
+     */
+    public SchemaHelper getInFieldSchemaHelper(final String fieldName) {
+        // Find the field for the field name
+        return getFieldSchemaHelper(fieldName, task.getInputFields().get(fieldName), "incoming");
+    }
+
+    /**
+     * Creates a schema helper for an outgoing field of this task.
+     *
+     * @param fieldName The name of the field to get a schema helper for
+     * @return the schema helper for this field
+     */
+    public SchemaHelper getOutFieldSchemaHelper(final String fieldName) {
+        // Find the field for the field name
+        return getFieldSchemaHelper(fieldName, task.getOutputFields().get(fieldName), "outgoing");
+    }
+
+    /**
+     * Creates a schema helper for an incoming field of this task.
+     *
+     * @param fieldName The name of the field to get a schema helper for
+     * @param field the field
+     * @param directionString the direction string
+     * @return the schema helper for this field
+     */
+    private SchemaHelper getFieldSchemaHelper(final String fieldName, final AxField field,
+            final String directionString) {
+        // Find the field for the field name
+        if (field == null) {
+            final String message = "no " + directionString + " field with name \"" + fieldName + "\" defined on task \""
+                    + task.getID() + "\"";
+            LOGGER.warn(message);
+            throw new StateMachineRuntimeException(message);
+        }
+
+        // Get a schema helper to handle translations of fields to and from the schema
+        try {
+            return new SchemaHelperFactory().createSchemaHelper(field.getKey(), field.getSchema());
+        } catch (final ContextRuntimeException e) {
+            final String message = "schema helper cannot be created for task field \"" + fieldName + "\" with key \""
+                    + field.getID() + "\" with schema \"" + field.getSchema() + "\"";
+            LOGGER.warn(message, e);
+            throw new EnException(message, e);
+        }
+    }
+
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/StateFinalizerExecutionContext.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/StateFinalizerExecutionContext.java
new file mode 100644 (file)
index 0000000..2e59711
--- /dev/null
@@ -0,0 +1,194 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.ContextAlbum;
+import org.onap.policy.apex.context.ContextRuntimeException;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.executor.Executor;
+import org.onap.policy.apex.core.engine.executor.StateFinalizerExecutor;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Container class for the execution context for state finalizer logic executions in a state being executed in an Apex
+ * engine. The state finalizer must have easy access to the state definition, the fields, as well as the policy, global,
+ * and external context.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ */
+public class StateFinalizerExecutionContext {
+    /**
+     * Logger for state finalizer execution, state finalizer logic can use this field to access and log to Apex logging.
+     */
+    private static final XLogger EXCEUTION_LOGGER =
+            XLoggerFactory.getXLogger("org.onap.policy.apex.executionlogging.StateFinalizerExecutionLogging");
+
+    // CHECKSTYLE:OFF: checkstyle:VisibilityModifier Logic has access to these field
+
+    /** A facade to the full state definition for the state finalizer logic being executed. */
+    public final AxStateFacade subject;
+
+    /** the execution ID for the current APEX policy execution instance. */
+    public final Long executionID;
+
+    /**
+     * The list of state outputs for this state finalizer. The purpose of a state finalizer is to select a state output
+     * for a state from this list of state output names.
+     */
+    public final Set<String> stateOutputNames;
+
+    /**
+     * The fields of this state finalizer. A state finalizer receives this list of fields from a task and may use these
+     * fields to determine what state output to select. Once a state finalizer has selected a state output, it must
+     * marshal these fields so that they match the fields required for the event defined in the state output.
+     */
+    public Map<String, Object> fields;
+
+    // A message specified in the logic
+    private String message;
+
+    /**
+     * The state output that the state finalizer logic has selected for a state. The state finalizer logic sets this
+     * field in its logic after executing and the Apex engine uses this state output for this state.
+     */
+    private String selectedStateOutputName;
+
+    /**
+     * Logger for state finalizer execution, state finalizer logic can use this field to access and log to Apex logging.
+     */
+    public final XLogger logger = EXCEUTION_LOGGER;
+
+    // CHECKSTYLE:ON: checkstyle:visibilityModifier
+
+    // All available context albums
+    private final Map<String, ContextAlbum> context;
+
+    /**
+     * Instantiates a new state finalizer execution context.
+     *
+     * @param stateFinalizerExecutor the state finalizer executor that requires context
+     * @param executionID the execution ID for the current APEX policy execution instance
+     * @param axState the state definition that is the subject of execution
+     * @param fields the fields to be manipulated by the state finalizer
+     * @param stateOutputNames the state output names, one of which will be selected by the state finalizer
+     * @param internalContext the execution context of the Apex engine in which the task is being executed
+     */
+    public StateFinalizerExecutionContext(final StateFinalizerExecutor stateFinalizerExecutor, final long executionID,
+            final AxState axState, final Map<String, Object> fields, final Set<String> stateOutputNames,
+            final ApexInternalContext internalContext) {
+        subject = new AxStateFacade(axState);
+
+        // Execution ID is the current policy execution instance
+        this.executionID = executionID;
+
+        this.fields = fields;
+        this.stateOutputNames = stateOutputNames;
+
+        // Set up the context albums for this task
+        context = new TreeMap<>();
+        for (final AxArtifactKey mapKey : subject.state.getContextAlbumReferences()) {
+            context.put(mapKey.getName(), internalContext.getContextAlbums().get(mapKey));
+        }
+
+        // Get the artifact stack of the users of the policy
+        final List<AxConcept> usedArtifactStack = new ArrayList<>();
+        for (Executor<?, ?, ?, ?> parent = stateFinalizerExecutor.getParent(); parent != null; parent =
+                parent.getParent()) {
+            // Add each parent to the top of the stack
+            usedArtifactStack.add(0, parent.getKey());
+        }
+
+        // Change the stack to an array
+        final AxConcept[] usedArtifactStackArray = usedArtifactStack.toArray(new AxConcept[usedArtifactStack.size()]);
+
+        // Set the user of the context
+        // Set the user of the context
+        for (final ContextAlbum contextAlbum : context.values()) {
+            contextAlbum.setUserArtifactStack(usedArtifactStackArray);
+        }
+    }
+
+    /**
+     * Return a context album if it exists in the context definition of this state.
+     *
+     * @param contextAlbumName The context album name
+     * @return The context album
+     * @throws ContextRuntimeException if the context album does not exist on the state for this executor
+     */
+    public ContextAlbum getContextAlbum(final String contextAlbumName) throws ContextRuntimeException {
+        // Find the context album
+        final ContextAlbum foundContextAlbum = context.get(contextAlbumName);
+
+        // Check if the context album exists
+        if (foundContextAlbum != null) {
+            return foundContextAlbum;
+        } else {
+            throw new ContextRuntimeException("cannot find definition of context album \"" + contextAlbumName
+                    + "\" on state \"" + subject.getId() + "\"");
+        }
+    }
+
+    /**
+     * Return the state output name selected by the state finalizer logic.
+     *
+     * @return the state output name
+     */
+    public String getSelectedStateOutputName() {
+        return selectedStateOutputName;
+    }
+
+    /**
+     * Set the state output name selected by the state finalizer logic.
+     *
+     * @param selectedStateOutputName the state output name
+     */
+    public void setSelectedStateOutputName(final String selectedStateOutputName) {
+        this.selectedStateOutputName = selectedStateOutputName;
+    }
+
+    /**
+     * Gets the user message.
+     *
+     * @return the user message
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Sets the user message.
+     *
+     * @param message the message
+     */
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskExecutionContext.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskExecutionContext.java
new file mode 100644 (file)
index 0000000..2251cf5
--- /dev/null
@@ -0,0 +1,174 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.context;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.ContextAlbum;
+import org.onap.policy.apex.context.ContextRuntimeException;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.executor.Executor;
+import org.onap.policy.apex.core.engine.executor.TaskExecutor;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Container class for the execution context for Task logic executions in a task being executed in an Apex engine. The
+ * task must have easy access to the task definition, the incoming and outgoing field contexts, as well as the policy,
+ * global, and external context.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ */
+public class TaskExecutionContext {
+    // Logger for task execution
+    private static final XLogger EXECUTION_LOGGER =
+            XLoggerFactory.getXLogger("org.onap.policy.apex.executionlogging.TaskExecutionLogging");
+
+    // CHECKSTYLE:OFF: checkstyle:VisibilityModifier Logic has access to these field
+
+    /** A constant <code>boolean true</code> value available for reuse e.g., for the return value */
+    public final Boolean TRUE = true;
+
+    /** A constant <code>boolean false</code> value available for reuse e.g., for the return value */
+    public final Boolean FALSE = false;
+
+    /** A facade to the full task definition for the task logic being executed. */
+    public final AxTaskFacade subject;
+
+    /** the execution ID for the current APEX policy execution instance. */
+    public final Long executionID;
+
+    /**
+     * The incoming fields from the trigger event for the task. The task logic can access these fields when executing
+     * its logic.
+     */
+    public final Map<String, Object> inFields;
+
+    /**
+     * The outgoing fields from the task. The task logic can access and set these fields with its logic. A task outputs
+     * its result using these fields.
+     */
+    public final Map<String, Object> outFields;
+
+    /** Logger for task execution, task logic can use this field to access and log to Apex logging. */
+    public final XLogger logger = EXECUTION_LOGGER;
+
+    // CHECKSTYLE:ON: checkstyle:VisibilityModifier
+
+    // All available context albums
+    private final Map<String, ContextAlbum> context;
+
+    // The artifact stack of users of this context
+    private final List<AxConcept> usedArtifactStack;
+
+    // A message specified in the logic
+    private String message;
+
+    /**
+     * Instantiates a new task execution context.
+     *
+     * @param taskExecutor the task executor that requires context
+     * @param executionID the execution ID for the current APEX policy execution instance
+     * @param axTask the task definition that is the subject of execution
+     * @param inFields the in fields
+     * @param outFields the out fields
+     * @param internalContext the execution context of the Apex engine in which the task is being executed
+     */
+    public TaskExecutionContext(final TaskExecutor taskExecutor, final long executionID, final AxTask axTask,
+            final Map<String, Object> inFields, final Map<String, Object> outFields,
+            final ApexInternalContext internalContext) {
+        // The subject is the task definition
+        subject = new AxTaskFacade(axTask);
+
+        // Execution ID is the current policy execution instance
+        this.executionID = executionID;
+
+        // The input and output fields
+        this.inFields = Collections.unmodifiableMap(inFields);
+        this.outFields = outFields;
+
+        // Set up the context albums for this task
+        context = new TreeMap<>();
+        for (final AxArtifactKey mapKey : subject.task.getContextAlbumReferences()) {
+            context.put(mapKey.getName(), internalContext.getContextAlbums().get(mapKey));
+        }
+
+        // Get the artifact stack of the users of the policy
+        usedArtifactStack = new ArrayList<>();
+        for (Executor<?, ?, ?, ?> parent = taskExecutor.getParent(); parent != null; parent = parent.getParent()) {
+            // Add each parent to the top of the stack
+            usedArtifactStack.add(0, parent.getKey());
+        }
+
+        // Change the stack to an array
+        final AxConcept[] usedArtifactStackArray = usedArtifactStack.toArray(new AxConcept[usedArtifactStack.size()]);
+
+        // Set the user of the context
+        for (final ContextAlbum contextAlbum : context.values()) {
+            contextAlbum.setUserArtifactStack(usedArtifactStackArray);
+        }
+    }
+
+    /**
+     * Return a context album if it exists in the context definition of this task.
+     *
+     * @param contextAlbumName The context album name
+     * @return The context album
+     * @throws ContextRuntimeException if the context album does not exist on the task for this executor
+     */
+    public ContextAlbum getContextAlbum(final String contextAlbumName) throws ContextRuntimeException {
+        // Find the context album
+        final ContextAlbum foundContextAlbum = context.get(contextAlbumName);
+
+        // Check if the context album exists
+        if (foundContextAlbum != null) {
+            return foundContextAlbum;
+        } else {
+            throw new ContextRuntimeException("cannot find definition of context album \"" + contextAlbumName
+                    + "\" on task \"" + subject.getId() + "\"");
+        }
+    }
+
+    /**
+     * Get the user message.
+     *
+     * @return the user message
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Sets the user message.
+     *
+     * @param message the message
+     */
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskSelectionExecutionContext.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskSelectionExecutionContext.java
new file mode 100644 (file)
index 0000000..a196e36
--- /dev/null
@@ -0,0 +1,180 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.context;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.context.ContextAlbum;
+import org.onap.policy.apex.context.ContextRuntimeException;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.event.EnEvent;
+import org.onap.policy.apex.core.engine.executor.Executor;
+import org.onap.policy.apex.core.engine.executor.TaskSelectExecutor;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Container class for the execution context for Task Selection logic executions in a task being executed in an Apex
+ * engine. The task must have easy access to the state definition, the incoming and outgoing event contexts, as well as
+ * the policy, global, and external context.
+ *
+ * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
+ */
+public class TaskSelectionExecutionContext {
+    // Logger for task execution
+    private static final XLogger EXECUTION_LOGGER =
+            XLoggerFactory.getXLogger("org.onap.policy.apex.executionlogging.TaskSelectionExecutionLogging");
+
+    // CHECKSTYLE:OFF: checkstyle:VisibilityModifier Logic has access to these field
+
+    /** A constant <code>boolean true</code> value available for reuse e.g., for the return value */
+    public final Boolean TRUE = true;
+
+    /** A constant <code>boolean false</code> value available for reuse e.g., for the return value */
+    public final Boolean FALSE = false;
+
+    /** A facade to the full state definition for the task selection logic being executed. */
+    public final AxStateFacade subject;
+
+    /** the execution ID for the current APEX policy execution instance. */
+    public final Long executionID;
+
+    /**
+     * The incoming fields from the trigger event for the state. The task selection logic can access these fields to
+     * decide what task to select for the state.
+     */
+    public final Map<String, Object> inFields;
+
+    /**
+     * The task that the task selection logic has selected for a state. The task selection logic sets this field in its
+     * logic prior to executing and the Apex engine executes this task as the task for this state.
+     */
+    public final AxArtifactKey selectedTask;
+
+    /**
+     * Logger for task selection execution, task selection logic can use this field to access and log to Apex logging.
+     */
+    public final XLogger logger = EXECUTION_LOGGER;
+
+    // CHECKSTYLE:ON: checkstyle:VisibilityModifier
+
+    // All available context albums
+    private final Map<String, ContextAlbum> context;
+
+    // A message specified in the logic
+    private String message;
+
+    /**
+     * Instantiates a new task selection execution context.
+     *
+     * @param taskSelectExecutor the task selection executor that requires context
+     * @param executionID the execution identifier
+     * @param axState the state definition that is the subject of execution
+     * @param incomingEvent the incoming event for the state
+     * @param outgoingKey the outgoing key for the task to execute in this state
+     * @param internalContext the execution context of the Apex engine in which the task is being executed
+     */
+    public TaskSelectionExecutionContext(final TaskSelectExecutor taskSelectExecutor, final long executionID,
+            final AxState axState, final EnEvent incomingEvent, final AxArtifactKey outgoingKey,
+            final ApexInternalContext internalContext) {
+        // The subject is the state definition
+        subject = new AxStateFacade(axState);
+
+        // Execution ID is the current policy execution instance
+        this.executionID = executionID;
+
+        // The events
+        inFields = incomingEvent;
+        selectedTask = outgoingKey;
+
+        // Set up the context albums for this task
+        // Set up the context albums for this task
+        context = new TreeMap<>();
+        for (final AxArtifactKey mapKey : subject.state.getContextAlbumReferences()) {
+            context.put(mapKey.getName(), internalContext.getContextAlbums().get(mapKey));
+        }
+
+        // Get the artifact stack of the users of the policy
+        final List<AxConcept> usedArtifactStack = new ArrayList<>();
+        for (Executor<?, ?, ?, ?> parent = taskSelectExecutor.getParent(); parent != null; parent =
+                parent.getParent()) {
+            // Add each parent to the top of the stack
+            usedArtifactStack.add(0, parent.getKey());
+        }
+
+        // Add the events to the artifact stack
+        usedArtifactStack.add(incomingEvent.getKey());
+
+        // Change the stack to an array
+        final AxConcept[] usedArtifactStackArray = usedArtifactStack.toArray(new AxConcept[usedArtifactStack.size()]);
+
+        // Set the user of the context
+        // Set the user of the context
+        for (final ContextAlbum contextAlbum : context.values()) {
+            contextAlbum.setUserArtifactStack(usedArtifactStackArray);
+        }
+        incomingEvent.setUserArtifactStack(usedArtifactStackArray);
+    }
+
+    /**
+     * Return a context album if it exists in the context definition of this state.
+     *
+     * @param contextAlbumName The context album name
+     * @return The context albumxxxxxx
+     * @throws ContextRuntimeException if the context album does not exist on the state for this executor
+     */
+    public ContextAlbum getContextAlbum(final String contextAlbumName) throws ContextRuntimeException {
+        // Find the context album
+        final ContextAlbum foundContextAlbum = context.get(contextAlbumName);
+
+        // Check if the context album exists
+        if (foundContextAlbum != null) {
+            return foundContextAlbum;
+        } else {
+            throw new ContextRuntimeException("cannot find definition of context album \"" + contextAlbumName
+                    + "\" on state \"" + subject.getId() + "\"");
+        }
+    }
+
+    /**
+     * Gets the user message.
+     *
+     * @return the user message
+     */
+    public String getMessage() {
+        return message;
+    }
+
+    /**
+     * Sets the user message.
+     *
+     * @param message the message
+     */
+    public void setMessage(final String message) {
+        this.message = message;
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/package-info.java
new file mode 100644 (file)
index 0000000..6dc5551
--- /dev/null
@@ -0,0 +1,33 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides context and facades for executing tasks, task selection logic, and state finalizer logic. The public fields
+ * and methods of {@link TaskExecutionContext}, {@link TaskSelectionExecutionContext} and
+ * {@link StateFinalizerExecutionContext} are available to task logic, task selection logic, and state finalizer logic
+ * respectively when that logic is executing in an executor plugin under the control of an APEX engine.
+ *
+ * The {@link AxStateFacade} and {@link AxTaskFacade} classes provide facades and convenience methods for state and task
+ * definition information for logic at execution time.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.executor.context;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineException.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineException.java
new file mode 100644 (file)
index 0000000..9d6c559
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.exception;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+
+/**
+ * This class will be called if an error occurs in an Apex state machine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StateMachineException extends ApexException {
+    private static final long serialVersionUID = -4245694568321686450L;
+
+    /**
+     * Instantiates a new state machine exception.
+     *
+     * @param message the message
+     */
+    public StateMachineException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new state machine exception.
+     *
+     * @param message the message
+     * @param e the e
+     */
+    public StateMachineException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineRuntimeException.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineRuntimeException.java
new file mode 100644 (file)
index 0000000..9ce2d74
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.exception;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException;
+
+/**
+ * This class will be called if a runtime error occurs in an Apex state machine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StateMachineRuntimeException extends ApexRuntimeException {
+    private static final long serialVersionUID = -4245694568321686450L;
+
+    /**
+     * Instantiates a new state machine exception.
+     *
+     * @param message the message
+     */
+    public StateMachineRuntimeException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new state machine exception.
+     *
+     * @param message the message
+     * @param e the e
+     */
+    public StateMachineRuntimeException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/package-info.java
new file mode 100644 (file)
index 0000000..1cb4339
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains exceptions that may be thrown during execution of an APEX engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.executor.exception;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/ExecutorFactoryImpl.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/ExecutorFactoryImpl.java
new file mode 100644 (file)
index 0000000..58ee4c6
--- /dev/null
@@ -0,0 +1,235 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.executor.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+
+import org.onap.policy.apex.core.engine.EngineParameters;
+import org.onap.policy.apex.core.engine.ExecutorParameters;
+import org.onap.policy.apex.core.engine.context.ApexInternalContext;
+import org.onap.policy.apex.core.engine.executor.Executor;
+import org.onap.policy.apex.core.engine.executor.ExecutorFactory;
+import org.onap.policy.apex.core.engine.executor.StateFinalizerExecutor;
+import org.onap.policy.apex.core.engine.executor.TaskExecutor;
+import org.onap.policy.apex.core.engine.executor.TaskSelectExecutor;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
+import org.onap.policy.apex.core.engine.executor.exception.StateMachineRuntimeException;
+import org.onap.policy.apex.model.basicmodel.service.ParameterService;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.onap.policy.apex.model.utilities.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class ExecutorFactoryImpl is a factory class that returns task selection logic and task logic executors depending
+ * on the type of logic executor has been specified for the task selection logic in a state or task logic in a task.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ExecutorFactoryImpl extends ExecutorFactory {
+    // Get a reference to the logger
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(ExecutorFactoryImpl.class);
+
+    // A map of logic flavours mapped to executor classes for plugins to executors for those logic flavours
+    private Map<String, Class<Executor<?, ?, ?, ?>>> taskExecutorPluginClassMap =
+            new TreeMap<String, Class<Executor<?, ?, ?, ?>>>();
+    private Map<String, Class<Executor<?, ?, ?, ?>>> taskSelectionExecutorPluginClassMap =
+            new TreeMap<String, Class<Executor<?, ?, ?, ?>>>();
+    private Map<String, Class<Executor<?, ?, ?, ?>>> stateFinalizerExecutorPluginClassMap =
+            new TreeMap<String, Class<Executor<?, ?, ?, ?>>>();
+
+    // A map of parameters for executors
+    private final Map<String, ExecutorParameters> implementationParameterMap =
+            new TreeMap<String, ExecutorParameters>();
+
+    /**
+     * Constructor, builds the class map for executors.
+     *
+     * @throws StateMachineException on plugin creation errors
+     */
+    public ExecutorFactoryImpl() throws StateMachineException {
+        final EngineParameters engineParameters = ParameterService.getParameters(EngineParameters.class);
+
+        Assertions.argumentNotNull(engineParameters, StateMachineException.class,
+                "Parameter \"engineParameters\" may not be null");
+
+        // Instantiate each executor class map entry
+        for (final Entry<String, ExecutorParameters> executorParameterEntry : engineParameters.getExecutorParameterMap()
+                .entrySet()) {
+            // Get classes for all types of executors for this logic type
+            taskExecutorPluginClassMap.put(executorParameterEntry.getKey(),
+                    getExecutorPluginClass(executorParameterEntry.getValue().getTaskExecutorPluginClass()));
+            taskSelectionExecutorPluginClassMap.put(executorParameterEntry.getKey(),
+                    getExecutorPluginClass(executorParameterEntry.getValue().getTaskSelectionExecutorPluginClass()));
+            stateFinalizerExecutorPluginClassMap.put(executorParameterEntry.getKey(),
+                    getExecutorPluginClass(executorParameterEntry.getValue().getStateFinalizerExecutorPluginClass()));
+
+            // Save the executor implementation parameters
+            implementationParameterMap.put(executorParameterEntry.getKey(), executorParameterEntry.getValue());
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.ExecutorFactory#getTaskSelectionExecutor(org.onap.policy.apex.core.
+     * model. concepts.AxState, org.onap.policy.apex.core.engine.context.Context)
+     */
+    @Override
+    public TaskSelectExecutor getTaskSelectionExecutor(final Executor<?, ?, ?, ?> parentExecutor, final AxState state,
+            final ApexInternalContext context) {
+        if (!state.checkSetTaskSelectionLogic()) {
+            return null;
+        }
+
+        // Create task selection executor
+        final TaskSelectExecutor tsExecutor =
+                (TaskSelectExecutor) createExecutor(state.getTaskSelectionLogic().getLogicFlavour(),
+                        taskSelectionExecutorPluginClassMap.get(state.getTaskSelectionLogic().getLogicFlavour()),
+                        TaskSelectExecutor.class);
+        tsExecutor.setParameters(implementationParameterMap.get(state.getTaskSelectionLogic().getLogicFlavour()));
+        tsExecutor.setContext(parentExecutor, state, context);
+
+        return tsExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.engine.executor.ExecutorFactory#getTaskExecutor(org.onap.policy.apex.core.model.
+     * concepts. AxTask, org.onap.policy.apex.core.engine.context.Context)
+     */
+    @Override
+    public TaskExecutor getTaskExecutor(final Executor<?, ?, ?, ?> parentExecutor, final AxTask task,
+            final ApexInternalContext context) {
+        // Create task executor
+        final TaskExecutor taskExecutor = (TaskExecutor) createExecutor(task.getTaskLogic().getLogicFlavour(),
+                taskExecutorPluginClassMap.get(task.getTaskLogic().getLogicFlavour()), TaskExecutor.class);
+        taskExecutor.setParameters(implementationParameterMap.get(task.getTaskLogic().getLogicFlavour()));
+        taskExecutor.setContext(parentExecutor, task, context);
+
+        return taskExecutor;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.engine.executor.ExecutorFactory#getStateFinalizerExecutor(org.onap.policy.apex.core.
+     * engine. executor.Executor, org.onap.policy.apex.core.policymodel.concepts.AxStateFinalizerLogic,
+     * org.onap.policy.apex.core.engine.context.ApexInternalContext)
+     */
+    @Override
+    public StateFinalizerExecutor getStateFinalizerExecutor(final Executor<?, ?, ?, ?> parentExecutor,
+            final AxStateFinalizerLogic logic, final ApexInternalContext context) {
+        // Create state finalizer executor
+        final StateFinalizerExecutor sfExecutor = (StateFinalizerExecutor) createExecutor(logic.getLogicFlavour(),
+                stateFinalizerExecutorPluginClassMap.get(logic.getLogicFlavour()), StateFinalizerExecutor.class);
+        sfExecutor.setParameters(implementationParameterMap.get(logic.getLogicFlavour()));
+        sfExecutor.setContext(parentExecutor, logic, context);
+
+        return sfExecutor;
+    }
+
+    /**
+     * Get an executor class for a given executor plugin class name.
+     *
+     * @param executorClassName The name of the executor plugin class
+     * @return an executor class
+     * @throws StateMachineException on plugin instantiation errors
+     */
+    @SuppressWarnings("unchecked")
+    private Class<Executor<?, ?, ?, ?>> getExecutorPluginClass(final String executorClassName)
+            throws StateMachineException {
+        // It's OK for an executor class not to be defined as long as it's not called
+        if (executorClassName == null) {
+            return null;
+        }
+
+        // Get the class for the executor using reflection
+        Class<? extends Object> executorPluginClass = null;
+        try {
+            executorPluginClass = Class.forName(executorClassName);
+        } catch (final ClassNotFoundException e) {
+            LOGGER.error("Apex executor class not found for executor plugin \"" + executorClassName + "\"", e);
+            throw new StateMachineException(
+                    "Apex executor class not found for executor plugin \"" + executorClassName + "\"", e);
+        }
+
+        // Check the class is an executor
+        if (!Executor.class.isAssignableFrom(executorPluginClass)) {
+            LOGGER.error("Specified Apex executor plugin class \"" + executorClassName
+                    + "\" does not implment the Executor interface");
+            throw new StateMachineException("Specified Apex executor plugin class \"" + executorClassName
+                    + "\" does not implment the Executor interface");
+        }
+
+        return (Class<Executor<?, ?, ?, ?>>) executorPluginClass;
+    }
+
+    /**
+     * Get an instance of an executor plugin class of the specified type and super type.
+     *
+     * @param logicFlavour The logic flavour of the logic
+     * @param executorClass The sub-class of the executor type to be instantiated
+     * @param executorSuperClass The super type of the class of executor to be instantiated
+     * @return The instantiated class
+     */
+    private Executor<?, ?, ?, ?> createExecutor(final String logicFlavour,
+            final Class<Executor<?, ?, ?, ?>> executorClass,
+            final Class<? extends Executor<?, ?, ?, ?>> executorSuperClass) {
+        // It's OK for an executor class not to be defined but it's not all right to try and create a non-defined
+        // executor class
+        if (executorClass == null) {
+            final String errorMessage = "Executor plugin class not defined for \"" + logicFlavour
+                    + "\" executor of type \"" + executorSuperClass.getCanonicalName() + "\"";
+            LOGGER.error(errorMessage);
+            throw new StateMachineRuntimeException(errorMessage);
+        }
+
+        // Create an executor for the specified logic flavour
+        Object executorObject = null;
+        try {
+            executorObject = executorClass.newInstance();
+        } catch (InstantiationException | IllegalAccessException e) {
+            final String errorMessage = "Instantiation error on \"" + logicFlavour + "\" executor of type \""
+                    + executorClass.getCanonicalName() + "\"";
+            LOGGER.error(errorMessage, e);
+            throw new StateMachineRuntimeException(errorMessage, e);
+        }
+
+        // Check the class is a Task Selection Executor
+        if (!(executorSuperClass.isAssignableFrom(executorObject.getClass()))) {
+            final String errorMessage = "Executor on \"" + logicFlavour + "\" of type \"" + executorClass
+                    + "\" is not an instance of \"" + executorSuperClass.getCanonicalName() + "\"";
+
+            LOGGER.error(errorMessage);
+            throw new StateMachineRuntimeException(errorMessage);
+        }
+
+        return (Executor<?, ?, ?, ?>) executorObject;
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/package-info.java
new file mode 100644 (file)
index 0000000..66e23e6
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains factories for creating executors for tasks, state fianlizers, and task selectors.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.executor.impl;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/package-info.java
new file mode 100644 (file)
index 0000000..062e1ae
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Implements state, task, task selection, and state finalizer execution for the APEX engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.executor;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/EventMonitor.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/EventMonitor.java
new file mode 100644 (file)
index 0000000..9710b10
--- /dev/null
@@ -0,0 +1,114 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.engine.monitoring;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is used to monitor event parameter gets and sets.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EventMonitor {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventMonitor.class);
+
+    /**
+     * Monitor get on an event parameter.
+     *
+     * @param eventParameter The event parameter to monitor
+     * @param value the value of the event parameter
+     * @param userArtifactStack the keys of the artifacts using the event at the moment
+     */
+    public void monitorGet(final AxField eventParameter, final Object value, final AxConcept[] userArtifactStack) {
+        LOGGER.trace(monitor("GET", userArtifactStack, eventParameter, value));
+    }
+
+    /**
+     * Monitor set on an event parameter.
+     *
+     * @param eventParameter The event parameter to monitor
+     * @param value the value of the event parameter
+     * @param userArtifactStack the keys of the artifacts using the event at the moment
+     */
+    public void monitorSet(final AxField eventParameter, final Object value, final AxConcept[] userArtifactStack) {
+        LOGGER.trace(monitor("SET", userArtifactStack, eventParameter, value));
+    }
+
+    /**
+     * Monitor remove on an event parameter.
+     *
+     * @param eventParameter The event parameter to monitor
+     * @param removedValue the value of the event parameter
+     * @param userArtifactStack the keys of the artifacts using the event at the moment
+     */
+    public void monitorRemove(final AxField eventParameter, final Object removedValue,
+            final AxConcept[] userArtifactStack) {
+        LOGGER.trace(monitor("REMOVE", userArtifactStack, eventParameter, removedValue));
+    }
+
+    /**
+     * Monitor the user artifact stack.
+     *
+     * @param preamble the preamble
+     * @param userArtifactStack The user stack to print
+     * @param eventParameter The event parameter that we are monitoring
+     * @param value The value of the target object
+     * @return the string
+     */
+    private String monitor(final String preamble, final AxConcept[] userArtifactStack, final AxField eventParameter,
+            final Object value) {
+        final StringBuilder builder = new StringBuilder();
+
+        builder.append(preamble);
+        builder.append(",[");
+
+        if (userArtifactStack != null) {
+            boolean first = true;
+            for (final AxConcept stackKey : userArtifactStack) {
+                if (first) {
+                    first = false;
+                } else {
+                    builder.append(',');
+                }
+                if (stackKey instanceof AxArtifactKey) {
+                    builder.append(((AxArtifactKey) stackKey).getID());
+                } else if (stackKey instanceof AxReferenceKey) {
+                    builder.append(((AxReferenceKey) stackKey).getID());
+                } else {
+                    builder.append(stackKey.toString());
+                }
+            }
+        }
+        builder.append("],");
+
+        builder.append(eventParameter.toString());
+        builder.append("=");
+        builder.append(value);
+
+        return builder.toString();
+    }
+}
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/package-info.java
new file mode 100644 (file)
index 0000000..530041a
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides monitoring of APEX policy execution. It monitors events as they trigger Apex policies, pass between the
+ * various states of a policy, and are emitted by a policy.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine.monitoring;
diff --git a/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/package-info.java b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/package-info.java
new file mode 100644 (file)
index 0000000..a836cd9
--- /dev/null
@@ -0,0 +1,30 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides the core engine implementation for Apex. It builds a state machine for execution for each policy in its
+ * policy model. It provides the infrastructure for running policies and their states, for running executors provided by
+ * executor plugins, for supplying events and context to running policies, states, and tasks, and for handling event
+ * transmission into and out of policies and between states in policies.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.engine;
diff --git a/core/core-infrastructure/pom.xml b/core/core-infrastructure/pom.xml
new file mode 100644 (file)
index 0000000..84548c7
--- /dev/null
@@ -0,0 +1,48 @@
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2018 Ericsson. All rights reserved.
+  ================================================================================
+  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=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onap.policy.apex-pdp.core</groupId>
+        <artifactId>core</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>core-infrastructure</artifactId>
+    <name>${project.artifactId}</name>
+    <description>Common non-functional components for Apex</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.java-websocket</groupId>
+            <artifactId>Java-WebSocket</artifactId>
+            <version>1.3.4</version>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/JavaHandlingException.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/JavaHandlingException.java
new file mode 100644 (file)
index 0000000..f6ef681
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.java;
+
+/**
+ * This class will be called if an error occurs in Java handling.
+ *
+ * @author Liam Fallon
+ */
+public class JavaHandlingException extends Exception {
+    private static final long serialVersionUID = -6375859029774312663L;
+
+    /**
+     * Instantiates a new Java handling exception.
+     *
+     * @param message the message
+     */
+    public JavaHandlingException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new Java handling exception.
+     *
+     * @param e the exception to wrap
+     */
+    public JavaHandlingException(final Exception e) {
+        super(e);
+    }
+
+    /**
+     * Instantiates a new Java handling exception.
+     *
+     * @param message the message
+     * @param e the exception to wrap
+     */
+    public JavaHandlingException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/ClassUtils.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/ClassUtils.java
new file mode 100644 (file)
index 0000000..919d1b1
--- /dev/null
@@ -0,0 +1,249 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.java.classes;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.TreeSet;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipInputStream;
+
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is a utility class used to find Java classes on the class path, in directories, and in Jar files.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public abstract class ClassUtils {
+    // Get a reference to the logger
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(ClassUtils.class);
+
+    // The boot directory in Java for predefined JARs
+    private static final String SUN_BOOT_LIBRARY_PATH = "sun.boot.library.path";
+
+    // Token for Classes directory in paths
+    private static final String CLASSES_TOKEN = "/classes/";
+
+    // Token for library fragment in path
+    private static final String LIBRARAY_PATH_TOKEN = "/lib";
+
+    /**
+     * Private constructor used to prevent sub class instantiation.
+     */
+    private ClassUtils() {}
+
+    /**
+     * Get the class names of all classes on the class path. WARNING: This is a heavy call, use sparingly
+     *
+     * @return a set of class names for all classes in the class path
+     */
+    public static Set<String> getClassNames() {
+        // The return set of class names
+        final Set<String> classNameSet = new TreeSet<>();
+
+        try {
+            // The library path for predefined classes in Java
+            String sunBootLibraryPathString = System.getProperty(SUN_BOOT_LIBRARY_PATH);
+
+            // Check it exists and has a "lib" in it
+            if (sunBootLibraryPathString != null && sunBootLibraryPathString.contains(LIBRARAY_PATH_TOKEN)) {
+                // Strip any superfluous trailer from path
+                sunBootLibraryPathString = sunBootLibraryPathString.substring(0,
+                        sunBootLibraryPathString.lastIndexOf(LIBRARAY_PATH_TOKEN) + LIBRARAY_PATH_TOKEN.length());
+
+                final File bootLibraryFile = new File(sunBootLibraryPathString);
+                // The set used to hold class names is populated with predefined Java classes
+                classNameSet.addAll(processDir(bootLibraryFile, ""));
+            }
+
+            // Get the entries on the class path
+            URL[] urls = ((URLClassLoader) ClassLoader.getSystemClassLoader()).getURLs();
+
+            // Try get the classes in the bootstrap loader
+            try {
+                final Class<?> nullclassloader = Class.forName("sun.misc.Launcher");
+                if (nullclassloader != null) {
+                    // There a long way and a short way, Short way: causes a warning that cannot be suppressed
+                    // URL[] moreurls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
+                    // long way:
+                    Method m = nullclassloader.getMethod("getBootstrapClassPath");
+                    if (m != null) {
+                        final Object cp = m.invoke(null, (Object[]) null);
+                        if (cp != null) {
+                            m = cp.getClass().getMethod("getURLs");
+                            if (m != null) {
+                                final URL[] moreurls = (URL[]) (m.invoke(cp, (Object[]) null));
+                                if (moreurls != null && moreurls.length > 0) {
+                                    if (urls.length == 0) {
+                                        urls = moreurls;
+                                    } else {
+                                        final URL[] result = Arrays.copyOf(urls, urls.length + moreurls.length);
+                                        System.arraycopy(moreurls, 0, result, urls.length, moreurls.length);
+                                        urls = result;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    // end long way!
+                }
+            } catch (final ClassNotFoundException e) {
+                LOGGER.warn("Failed to find default path for JRE libraries", e);
+            }
+
+            // Iterate over the class path entries
+            for (final URL url : urls) {
+                if (url == null || url.getFile() == null) {
+                    continue;
+                }
+                final File urlFile = new File(url.getFile());
+                // Directories may contain ".class" files
+                if (urlFile.isDirectory()) {
+                    classNameSet.addAll(processDir(urlFile, url.getFile()));
+                }
+                // JARs are processed as well
+                else if (url.getFile().endsWith(".jar")) {
+                    classNameSet.addAll(processJar(urlFile));
+                } else {
+                    // It's a resource or some other non-executable thing
+                    continue;
+                }
+            }
+        } catch (final Exception e) {
+            LOGGER.warn("could not get the names of Java classes", e);
+        }
+
+        return classNameSet;
+    }
+
+    /**
+     * Find all classes in directories and JARs in those directories.
+     *
+     * @param classDirectory The directory to search for classes
+     * @param rootDir The root directory, to be removed from absolute paths
+     * @return a set of classes which may be empty
+     * @throws Exception on errors processing directories
+     */
+    public static Set<String> processDir(final File classDirectory, final String rootDir) throws Exception {
+        // The return set
+        final TreeSet<String> classNameSet = new TreeSet<>();
+
+        // Iterate over the directory
+        if (classDirectory == null || !classDirectory.isDirectory()) {
+            return classNameSet;
+        }
+        for (final File child : classDirectory.listFiles()) {
+            if (child.isDirectory()) {
+                // Recurse down
+                classNameSet.addAll(processDir(child, rootDir));
+            } else if (child.getName().endsWith(".jar")) {
+                // Process the JAR
+                classNameSet.addAll(processJar(child));
+            } else if (child.getName().endsWith(".class") && !child.getName().contains("$")) {
+                // Process the ".class" file
+                classNameSet.add(
+                        child.getAbsolutePath().replace(rootDir, "").replaceFirst("\\.class$", "").replace('/', '.'));
+            } else {
+                continue;
+            }
+        }
+        return classNameSet;
+    }
+
+    /**
+     * Condition the file name as a class name.
+     *
+     * @param fileNameIn The file name to convert to a class name
+     * @return the conditioned class name
+     */
+    public static String processFileName(final String fileNameIn) {
+        String fileName = fileNameIn;
+
+        if (fileName == null) {
+            return null;
+        }
+        final int classesPos = fileName.indexOf(CLASSES_TOKEN);
+
+        if (classesPos != -1) {
+            fileName = fileName.substring(classesPos + CLASSES_TOKEN.length());
+        }
+
+        return fileName.replaceFirst("\\.class$", "").replace('/', '.');
+    }
+
+    /**
+     * Read all the class names from a Jar.
+     *
+     * @param jarFile the JAR file
+     * @return a set of class names
+     * @throws Exception on errors processing JARs
+     */
+    public static Set<String> processJar(final File jarFile) throws Exception {
+        // Pass the file as an input stream
+        return processJar(new FileInputStream(jarFile.getAbsolutePath()));
+    }
+
+    /**
+     * Read all the class names from a Jar.
+     *
+     * @param jarInputStream the JAR input stream
+     * @return a set of class names
+     * @throws Exception on errors processing JARs
+     */
+    public static Set<String> processJar(final InputStream jarInputStream) throws Exception {
+        // The return set
+        final TreeSet<String> classPathSet = new TreeSet<>();
+
+        if (jarInputStream == null) {
+            return classPathSet;
+        }
+        // JARs are ZIP files
+        final ZipInputStream zip = new ZipInputStream(jarInputStream);
+
+        // Iterate over each entry in the JAR
+        for (ZipEntry entry = zip.getNextEntry(); entry != null; entry = zip.getNextEntry()) {
+            if (!entry.isDirectory() && entry.getName().endsWith(".class") && !entry.getName().contains("$")) {
+                classPathSet.add(entry.getName().replaceFirst("\\.class$", "").replace('/', '.'));
+            }
+        }
+        zip.close();
+        return classPathSet;
+    }
+
+    /**
+     * The main method exercises this class for test purposes.
+     *
+     * @param args the args
+     */
+    public static void main(final String[] args) {
+        for (final String clz : getClassNames()) {
+            System.out.println("Found class: " + clz);
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/package-info.java
new file mode 100644 (file)
index 0000000..c356580
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains support to find Java classes on the class path, in directories and in Jar files.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.java.classes;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/package-info.java
new file mode 100644 (file)
index 0000000..5a8b511
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Allows Java classes to be created by compiling Java source code and generating classes on the fly.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.java;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageHolder.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageHolder.java
new file mode 100644 (file)
index 0000000..f74ffa0
--- /dev/null
@@ -0,0 +1,168 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+import java.io.Serializable;
+import java.net.InetAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class MessageHolder holds a set of messages to be sent as a single block of messages in this messaging
+ * implementation.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type of message being handled by a message holder instance
+ */
+public class MessageHolder<MESSAGE> implements Serializable {
+    private static final int HASH_PRIME = 31;
+    private static final int FOUR_BYTES = 32;
+
+    // Serial ID
+    private static final long serialVersionUID = 1235487535388793719L;
+
+    // Get a reference to the logger
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(MessageHolder.class);
+
+    // Properties of the message holder
+    private final long creationTime;
+    private final InetAddress senderHostAddress;
+
+    // Sequence of message in the message holder
+    private final List<MESSAGE> messages;
+
+    /**
+     * Constructor, create the message holder.
+     *
+     * @param senderHostAddress the host address of the sender of the message holder container
+     */
+    public MessageHolder(final InetAddress senderHostAddress) {
+        LOGGER.entry(senderHostAddress);
+        messages = new ArrayList<>();
+        this.senderHostAddress = senderHostAddress;
+        creationTime = System.currentTimeMillis();
+    }
+
+    /**
+     * Return the messages in this message holder.
+     *
+     * @return the messages
+     */
+    public List<MESSAGE> getMessages() {
+        return messages;
+    }
+
+    /**
+     * Adds a message to this message holder.
+     *
+     * @param message the message to add
+     */
+    public void addMessage(final MESSAGE message) {
+        if (!messages.contains(message)) {
+            messages.add(message);
+        } else {
+            LOGGER.warn("duplicate message {} added to message holder", message);
+        }
+    }
+
+    /**
+     * Gets the creation time.
+     *
+     * @return the creation time
+     */
+    public long getCreationTime() {
+        return creationTime;
+    }
+
+    /**
+     * Gets the sender host address.
+     *
+     * @return the sender host address
+     */
+    public InetAddress getSenderHostAddress() {
+        return senderHostAddress;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ApexCommandProtocol [creationTime=" + creationTime + ", senderHostAddress=" + senderHostAddress + "]";
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        final int prime = HASH_PRIME;
+        int result = 1;
+        result = prime * result + ((senderHostAddress == null) ? 0 : senderHostAddress.hashCode());
+        result = prime * result + ((messages == null) ? 0 : messages.hashCode());
+        result = prime * result + (int) (creationTime ^ (creationTime >>> FOUR_BYTES));
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object obj) {
+        if (this == obj) {
+            return true;
+        }
+        if (obj == null) {
+            return false;
+        }
+        if (getClass() != obj.getClass()) {
+            return false;
+        }
+        final MessageHolder<?> other = (MessageHolder<?>) obj;
+        if (senderHostAddress == null) {
+            if (other.senderHostAddress != null) {
+                return false;
+            }
+        } else if (!senderHostAddress.equals(other.senderHostAddress)) {
+            return false;
+        }
+        if (messages == null) {
+            if (other.messages != null) {
+                return false;
+            }
+        } else if (!messages.equals(other.messages)) {
+            return false;
+        }
+        if (creationTime != other.creationTime) {
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageListener.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageListener.java
new file mode 100644 (file)
index 0000000..c8b1324
--- /dev/null
@@ -0,0 +1,47 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlock;
+
+/**
+ * The listener interface for receiving message events. The class that is interested in processing a message event
+ * implements this interface.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> of message of any given type that is being listened for and handled
+ */
+public interface MessageListener<MESSAGE> {
+
+    /**
+     * This method is called when a message block is received on a web socket and is to be forwarded to a listener.
+     *
+     * @param data the message data containing a message
+     */
+    void onMessage(MessageBlock<MESSAGE> data);
+
+    /**
+     * This method is called when a string message is received on a web socket and is to be forwarded to a listener.
+     *
+     * @param messageString the message string
+     */
+    void onMessage(String messageString);
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingException.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingException.java
new file mode 100644 (file)
index 0000000..ef435b2
--- /dev/null
@@ -0,0 +1,49 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+/**
+ * This class will be called if an error occurs in Java handling.
+ *
+ * @author Liam Fallon
+ */
+public class MessagingException extends Exception {
+    private static final long serialVersionUID = -6375859029774312663L;
+
+    /**
+     * Instantiates a new messaging exception.
+     *
+     * @param message the message
+     */
+    public MessagingException(final String message) {
+        super(message);
+    }
+
+    /**
+     * Instantiates a new messaging exception.
+     *
+     * @param message the message
+     * @param e the e
+     */
+    public MessagingException(final String message, final Exception e) {
+        super(message, e);
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingService.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingService.java
new file mode 100644 (file)
index 0000000..7e91b95
--- /dev/null
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+/**
+ * The Interface MessagingService specifies the methods that must be implemented by any implementation providing Apex
+ * messaging.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the type of message being passed by an implementation of Apex messaging
+ */
+public interface MessagingService<MESSAGE> {
+
+    /**
+     * Start the messaging connection.
+     */
+    void startConnection();
+
+    /**
+     * Stop the messaging connection.
+     */
+    void stopConnection();
+
+    /**
+     * Checks if the messaging connection is started.
+     *
+     * @return true, if is started
+     */
+    boolean isStarted();
+
+    /**
+     * Send a block of messages on the connection, the messages are contained in the the message holder container.
+     *
+     * @param messageHolder The message holder holding the messages to be sent
+     */
+    void send(MessageHolder<MESSAGE> messageHolder);
+
+    /**
+     * Send a string message on the connection.
+     *
+     * @param messageString The message string to be sent
+     */
+    void send(String messageString);
+
+    /**
+     * Adds a message listener that will be called when a message is received by this messaging service implementation.
+     *
+     * @param messageListener the message listener
+     */
+    void addMessageListener(MessageListener<MESSAGE> messageListener);
+
+    /**
+     * Removes the message listener.
+     *
+     * @param messageListener the message listener
+     */
+    void removeMessageListener(MessageListener<MESSAGE> messageListener);
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingServiceFactory.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingServiceFactory.java
new file mode 100644 (file)
index 0000000..1d08fac
--- /dev/null
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+import java.net.InetSocketAddress;
+import java.net.URI;
+
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.client.MessagingClient;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.server.MessageServerImpl;
+
+/**
+ * A factory class to create a "server" or "client" type Messaging Service.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type of message to be handled by this messaging service
+ */
+public class MessagingServiceFactory<MESSAGE> {
+
+    /**
+     * Create a web socket server instance and returns to the caller.
+     *
+     * @param address the address of the server machine
+     * @return the messaging service
+     */
+    public MessagingService<MESSAGE> createServer(final InetSocketAddress address) {
+        return new MessageServerImpl<>(address);
+    }
+
+    /**
+     * Create a web socket client instance and returns to the caller.
+     *
+     * @param uri the URI of the server to connect to
+     * @return an instance of {@link MessagingService}
+     */
+    public MessagingService<MESSAGE> createClient(final URI uri) {
+        if (uri == null) {
+            throw new IllegalArgumentException("URI cannot be null");
+        }
+        return new MessagingClient<>(uri);
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/RawMessageHandler.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/RawMessageHandler.java
new file mode 100644 (file)
index 0000000..534bee8
--- /dev/null
@@ -0,0 +1,253 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws;
+
+import com.google.common.eventbus.Subscribe;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.nio.ByteBuffer;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageHolder;
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlock;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlockHandler;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.RawMessageBlock;
+import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class RawMessageHandler handles raw messages being received on a Java web socket and forwards the messages to the
+ * DataHandler instance that has subscribed to the RawMessageHandler instance.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type of message being received
+ */
+public class RawMessageHandler<MESSAGE> implements WebSocketMessageListener<MESSAGE>, Runnable {
+    // The logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(RawMessageHandler.class);
+
+    // The amount of time to sleep during shutdown for the thread of this message handler to stop
+    private static final int SHUTDOWN_WAIT_TIME = 10;
+
+    // The timeout to wait between queue poll timeouts in milliseconds
+    private static final long QUEUE_POLL_TIMEOUT = 50;
+
+    // A queue that temporarily holds message blocks
+    private final BlockingQueue<MessageBlock<MESSAGE>> messageBlockQueue = new LinkedBlockingDeque<>();
+
+    // A queue that temporarily holds message blocks
+    private final BlockingQueue<String> stringMessageQueue = new LinkedBlockingDeque<>();
+
+    // Client applications that have subscribed for messages
+    private final MessageBlockHandler<MESSAGE> dataHandler = new MessageBlockHandler<MESSAGE>("data-processor");
+
+    // The thread that the raw message handler is receiving messages on
+    private Thread thisThread = null;
+
+    /**
+     * This method is called by the class with which this message listener has been registered.
+     *
+     * @param incomingData the data forwarded by the message reception class
+     */
+    @Override
+    @Subscribe
+    public void onMessage(final RawMessageBlock incomingData) {
+        // Sanity check and get incoming data
+        ByteBuffer dataByteBuffer = null;
+        if (incomingData != null && incomingData.getMessage() != null) {
+            dataByteBuffer = incomingData.getMessage();
+        } else {
+            return;
+        }
+
+        // Read the messages from the web socket and place them on the message queue for handling by the queue
+        // processing thread
+        ObjectInputStream ois = null;
+        try {
+            ois = new ObjectInputStream(new ByteArrayInputStream(dataByteBuffer.array()));
+            @SuppressWarnings("unchecked")
+            final MessageHolder<MESSAGE> messageHolder = (MessageHolder<MESSAGE>) ois.readObject();
+
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("message {} recieved from the client {} ", messageHolder.toString(),
+                        messageHolder == null ? "Apex Engine " : messageHolder.getSenderHostAddress());
+            }
+
+            final List<MESSAGE> messages = messageHolder.getMessages();
+            if (messages != null) {
+                messageBlockQueue.add(new MessageBlock<MESSAGE>(messages, incomingData.getConn()));
+            }
+        } catch (IOException | ClassNotFoundException e) {
+            LOGGER.error("Failed to process message received");
+            LOGGER.catching(e);
+        } finally {
+            closeObjectStream(ois);
+        }
+    }
+
+    /**
+     * This method is called when a string message is received on a web socket and is to be forwarded to a listener.
+     *
+     * @param messageString the message string
+     */
+    @Override
+    @Subscribe
+    public void onMessage(final String messageString) {
+        if (messageString == null) {
+            return;
+        }
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("message {} recieved from the client {} ", messageString);
+        }
+        stringMessageQueue.add(messageString);
+    }
+
+    /**
+     * Close the {@link ObjectInputStream} stream.
+     *
+     * @param ois is an instance of {@link ObjectInputStream}
+     */
+    private void closeObjectStream(final ObjectInputStream ois) {
+        if (ois != null) {
+            try {
+                ois.close();
+            } catch (final IOException e) {
+                LOGGER.catching(e);
+            }
+        }
+    }
+
+    /**
+     * This thread monitors the message queue and processes messages as they appear on the queue.
+     *
+     * @see java.lang.Runnable#run()
+     */
+    @Override
+    public void run() {
+        LOGGER.debug("raw message listening started");
+        thisThread = Thread.currentThread();
+
+        // Run until termination
+        while (thisThread.isAlive() && !thisThread.isInterrupted()) {
+            try {
+                // Read message block messages from the queue and pass it to the data handler
+                MessageBlock<MESSAGE> messageBlock = null;
+                while ((messageBlock = messageBlockQueue.poll(1, TimeUnit.MILLISECONDS)) != null) {
+                    dataHandler.post(messageBlock);
+                }
+            } catch (final InterruptedException e) {
+                LOGGER.debug("raw message listening has been interrupted");
+                break;
+            }
+
+            try {
+                // Read string messages from the queue and pass it to the data handler
+                String stringMessage = null;
+                while ((stringMessage = stringMessageQueue.poll(1, TimeUnit.MILLISECONDS)) != null) {
+                    dataHandler.post(stringMessage);
+                }
+            } catch (final InterruptedException e) {
+                LOGGER.debug("raw message listening has been interrupted");
+                break;
+            }
+
+            // Wait for new messages
+            try {
+                Thread.sleep(QUEUE_POLL_TIMEOUT);
+            } catch (final InterruptedException e) {
+                LOGGER.debug("raw message listening has been interrupted");
+                break;
+            }
+        }
+
+        LOGGER.debug("raw message listening stopped");
+    }
+
+    /**
+     * Shutdown the message handler.
+     */
+    public void shutdown() {
+        LOGGER.entry("shutting down raw message listening . . .");
+
+        // Interrupt the message handling thread
+        thisThread.interrupt();
+
+        // Wait for thread shutdown
+        while (thisThread.isAlive()) {
+            ThreadUtilities.sleep(SHUTDOWN_WAIT_TIME);
+        }
+
+        LOGGER.exit("shut down raw message listening");
+    }
+
+    /**
+     * This method is called when a message is received on a web socket and is to be forwarded to a listener.
+     *
+     * @param data the message data containing a message
+     */
+    @Override
+    public void onMessage(final MessageBlock<MESSAGE> data) {
+        throw new UnsupportedOperationException("this operation is not supported");
+    }
+
+    /**
+     * Register a data forwarder to which messages coming in on the web socket will be forwarded.
+     *
+     * @param listener The listener to register
+     */
+    @Override
+    public void registerDataForwarder(final MessageListener<MESSAGE> listener) {
+        stateCheck(listener);
+        dataHandler.registerMessageHandler(listener);
+    }
+
+    /**
+     * Unregister a data forwarder that was previously registered on the web socket listener.
+     *
+     * @param listener The listener to unregister
+     */
+    @Override
+    public void unRegisterDataForwarder(final MessageListener<MESSAGE> listener) {
+        stateCheck(listener);
+        dataHandler.unRegisterMessageHandler(listener);
+    }
+
+    /**
+     * Sanity check for the listener and data handler.
+     *
+     * @param listener the listener to check
+     */
+    private void stateCheck(final MessageListener<MESSAGE> listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("The listener object cannot be null");
+        }
+        if (dataHandler == null) {
+            throw new IllegalStateException("Data handler not initialized");
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/WebSocketMessageListener.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/WebSocketMessageListener.java
new file mode 100644 (file)
index 0000000..aa951b4
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.RawMessageBlock;
+
+/**
+ * The listener interface for receiving webSocketMessage events. The class that is interested in processing a
+ * webSocketMessage event implements this interface, and the object created with that class is registered with a
+ * component using the component's addWebSocketMessageListener method. When the webSocketMessage event occurs, that
+ * object's appropriate method is invoked.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type
+ * @see RawMessageBlock
+ */
+public interface WebSocketMessageListener<MESSAGE> extends MessageListener<MESSAGE>, Runnable {
+
+    /**
+     * This method is called by the class with which this message listener has been registered.
+     *
+     * @param incomingData the data forwarded by the message reception class
+     */
+    void onMessage(RawMessageBlock incomingData);
+
+    /**
+     * Register a data forwarder to which messages coming in on the web socket will be forwarded.
+     *
+     * @param listener The listener to register
+     */
+    void registerDataForwarder(MessageListener<MESSAGE> listener);
+
+    /**
+     * Unregister a data forwarder that was previously registered on the web socket listener.
+     *
+     * @param listener The listener to unregister
+     */
+    void unRegisterDataForwarder(MessageListener<MESSAGE> listener);
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/InternalMessageBusClient.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/InternalMessageBusClient.java
new file mode 100644 (file)
index 0000000..9f7f89d
--- /dev/null
@@ -0,0 +1,131 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.client;
+
+import java.net.URI;
+import java.nio.ByteBuffer;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.RawMessageHandler;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlockHandler;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.RawMessageBlock;
+import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class InternalMessageBusClient handles the client side of a web socket and handles the callback mechanism used to
+ * receive messages on the web socket.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type of message being handled
+ */
+abstract class InternalMessageBusClient<MESSAGE> extends WebSocketClientImpl {
+    private static final int THREAD_FACTORY_STACK_SIZE = 256;
+
+    // The logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(InternalMessageBusClient.class);
+
+    // Name of the event bus.
+    private static final String RAW_EVENT_BUS = "Raw-Event-Bus";
+
+    // This instance handles the raw data received from the web socket
+    private final RawMessageHandler<MESSAGE> rawMessageHandler = new RawMessageHandler<>();
+
+    // The message block handler to which to pass messages coming in on this client
+    private MessageBlockHandler<MESSAGE> messageBlockHandler = null;
+
+    // The raw message handler uses a thread to process incoming events off a queue, this class owns and controls that
+    // thread. These fields hold the thread and
+    // the thread factory for creating threads.
+    private ApplicationThreadFactory tFactory =
+            new ApplicationThreadFactory("ws-client-thread", THREAD_FACTORY_STACK_SIZE);
+    private Thread forwarderThread = null;
+
+    /**
+     * Construct the class and start the forwarding thread for received messages.
+     *
+     * @param serverUri the server URI to connect to
+     */
+    InternalMessageBusClient(final URI serverUri) {
+        // Call the super class to create the web socket
+        super(serverUri);
+        LOGGER.entry(serverUri.toString());
+
+        // Create the data handler for forwarding messages
+        messageBlockHandler = new MessageBlockHandler<>(RAW_EVENT_BUS);
+        messageBlockHandler.registerMessageHandler(rawMessageHandler);
+
+        // Create the thread that manages the queue in the data handler
+        forwarderThread = tFactory.newThread(rawMessageHandler);
+        forwarderThread.start();
+
+        LOGGER.exit();
+    }
+
+    /**
+     * Callback for binary messages received from the remote host.
+     *
+     * @param rawMessage the received raw message
+     * @see org.java_websocket.client.WebSocketClient#onMessage(java.nio.ByteBuffer)
+     */
+    @Override
+    public void onMessage(final ByteBuffer rawMessage) {
+        // Post the message to the data handler for forwarding to its listeners
+        messageBlockHandler.post(new RawMessageBlock(rawMessage, null));
+    }
+
+    /**
+     * Callback for binary messages received from the remote host.
+     *
+     * @param stringMessage the string message
+     * @see org.java_websocket.client.WebSocketClient#onMessage(java.lang.String)
+     */
+    @Override
+    public final void onMessage(final String stringMessage) {
+        messageBlockHandler.post(stringMessage);
+    }
+
+    /**
+     * Register a subscriber class to the raw message handler.
+     *
+     * @param listener a simple class, that listens for the events from Event
+     */
+    public void addMessageListener(final MessageListener<MESSAGE> listener) {
+        rawMessageHandler.registerDataForwarder(listener);
+    }
+
+    /**
+     * Removes the message listener.
+     *
+     * @param listener the listener
+     */
+    public void removeMessageListener(final MessageListener<MESSAGE> listener) {
+        rawMessageHandler.unRegisterDataForwarder(listener);
+    }
+
+    /**
+     * Stop the thread handling message forwarding.
+     */
+    protected void stopListener() {
+        rawMessageHandler.shutdown();
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/MessagingClient.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/MessagingClient.java
new file mode 100644 (file)
index 0000000..4a756d6
--- /dev/null
@@ -0,0 +1,162 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.client;
+
+import java.net.URI;
+
+import org.java_websocket.WebSocket;
+import org.onap.policy.apex.core.infrastructure.messaging.MessageHolder;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingService;
+import org.onap.policy.apex.core.infrastructure.messaging.util.MessagingUtils;
+import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities;
+
+/**
+ * The Class MessagingClient is the class that wraps web socket handling, message sending, and message reception on the
+ * client side of a web socket in Apex.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type
+ */
+public class MessagingClient<MESSAGE> extends InternalMessageBusClient<MESSAGE> implements MessagingService<MESSAGE> {
+    // The length of time to wait for a connection to a web socket server before aborting
+    private static final int CONNECTION_TIMEOUT_TIME_MS = 3000;
+
+    // The length of time to wait before checking if a connection to a web socket server has worked or not
+    private static final int CONNECTION_TRY_INTERVAL_MS = 100;
+
+    /**
+     * Constructor of this class, uses its {@link InternalMessageBusClient} superclass to set up the web socket and
+     * handle incoming message forwarding.
+     *
+     * @param serverUri The URI of the service
+     */
+    public MessagingClient(final URI serverUri) {
+        // Call the super class to create the web socket and set up received message forwarding
+        super(serverUri);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#stopConnection()
+     */
+    @Override
+    public void stopConnection() {
+        // Stop message reception in the super class
+        super.stopListener();
+
+        // Close the web socket
+        final WebSocket connection = super.getConnection();
+        if (connection != null && connection.isOpen()) {
+            connection.closeConnection(0, "");
+        }
+        this.close();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#startConnection()
+     */
+    @Override
+    public void startConnection() {
+        // Open the web socket
+        final WebSocket connection = super.getConnection();
+        if (connection != null && !connection.isOpen()) {
+            connect();
+        }
+
+        if (!waitforConnection(connection)) {
+            throw new IllegalStateException("Could not connect to the server");
+        }
+    }
+
+    /**
+     * This method waits for the timeout value for the client to connect to the web socket server.
+     *
+     * @param connection the connection to wait on
+     * @return true, if successful
+     */
+    private boolean waitforConnection(final WebSocket connection) {
+        // The total time we have before timeout
+        int timeoutMSCounter = CONNECTION_TIMEOUT_TIME_MS;
+
+        // Check the connection state
+        do {
+            switch (connection.getReadyState()) {
+                case NOT_YET_CONNECTED:
+                case CONNECTING:
+                case CLOSING:
+                    // Not connected yet so wait for the try interval
+                    ThreadUtilities.sleep(CONNECTION_TRY_INTERVAL_MS);
+                    timeoutMSCounter -= CONNECTION_TRY_INTERVAL_MS;
+                    break;
+                case OPEN:
+                    // Connection is open, happy days
+                    return true;
+                case CLOSED:
+                    // Connection is closed, bah
+                    return false;
+                default:
+                    break;
+            }
+        }
+        // While the timeout value has not expired
+        while (timeoutMSCounter > 0);
+
+        // We have timed out
+        return false;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#send(org.onap.policy.apex.core.
+     * infrastructure. messaging.MessageHolder)
+     */
+    @Override
+    public void send(final MessageHolder<MESSAGE> commands) {
+        // Get the connection and send the message
+        final WebSocket connection = super.getConnection();
+        connection.send(MessagingUtils.serializeObject(commands));
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#send(java.lang.String)
+     */
+    @Override
+    public void send(final String messageString) {
+        final WebSocket connection = super.getConnection();
+        connection.send(messageString);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#isStarted()
+     */
+    @Override
+    public boolean isStarted() {
+        return getConnection().isOpen();
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/WebSocketClientImpl.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/WebSocketClientImpl.java
new file mode 100644 (file)
index 0000000..b2e0953
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.client;
+
+import java.net.URI;
+
+import org.java_websocket.client.WebSocketClient;
+import org.java_websocket.handshake.ServerHandshake;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class implements {@link WebSocketClient} specific methods in order to act as a Java Web Socket client.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+abstract class WebSocketClientImpl extends WebSocketClient {
+    // The logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(WebSocketClientImpl.class);
+
+    /**
+     * Constructs a WebSocketClient instance and sets it to the connect to the specified URI. The channel does not
+     * attempt to connect automatically. You must call {@link connect} first to initiate the socket connection.
+     *
+     * @param serverUri the URI of the web socket server to connect to
+     */
+    WebSocketClientImpl(final URI serverUri) {
+        super(serverUri);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.client.WebSocketClient#onOpen(org.java_websocket.handshake.ServerHandshake)
+     */
+    @Override
+    public void onOpen(final ServerHandshake handshakedata) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Connection opened to server {} --> {}", this.getURI(), handshakedata.getHttpStatusMessage());
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.client.WebSocketClient#onClose(int, java.lang.String, boolean)
+     */
+    @Override
+    public void onClose(final int code, final String reason, final boolean remote) {
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("Connection closed to server {} --> code \"{}\", reason \"{}\"", this.getURI(), code, reason);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.client.WebSocketClient#onError(java.lang.Exception)
+     */
+    @Override
+    public void onError(final Exception ex) {
+        LOGGER.info("Failed to make a connection to the server {} ", getURI());
+        LOGGER.catching(ex);
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/package-info.java
new file mode 100644 (file)
index 0000000..d5344fe
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides the client side of messaging over web sockets.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.client;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlock.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlock.java
new file mode 100644 (file)
index 0000000..70b1d2c
--- /dev/null
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock;
+
+import java.util.List;
+
+import org.java_websocket.WebSocket;
+
+/**
+ * This class encapsulate messages and the web socket on which they are handled.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type of message being handled
+ */
+public final class MessageBlock<MESSAGE> {
+
+    // List of Messages received on a web socket
+    private final List<MESSAGE> messages;
+
+    // The web socket on which the messages are handled
+    private final WebSocket webSocket;
+
+    /**
+     * Instantiates a new message block.
+     *
+     * @param messages the messages in the message block
+     * @param webSocket the web socket used to handle the message block
+     */
+    public MessageBlock(final List<MESSAGE> messages, final WebSocket webSocket) {
+        this.messages = messages;
+        this.webSocket = webSocket;
+    }
+
+    /**
+     * Gets the messages.
+     *
+     * @return the messages
+     */
+    public List<MESSAGE> getMessages() {
+        return messages;
+    }
+
+    /**
+     * Gets the web socket.
+     *
+     * @return the web socket
+     */
+    public WebSocket getConnection() {
+        return webSocket;
+    }
+
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlockHandler.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlockHandler.java
new file mode 100644 (file)
index 0000000..4265718
--- /dev/null
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock;
+
+import com.google.common.eventbus.EventBus;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is used to pass messages received on a Java web socket to listening application class instances using an
+ * event bus.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type
+ */
+public class MessageBlockHandler<MESSAGE> {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(MessageBlockHandler.class);
+
+    /**
+     * This event bus will forward the events to all of its subscribers.
+     */
+    private EventBus eventBus = null;
+
+    /**
+     * Instantiates a new data handler.
+     *
+     * @param eventBusName the name of the event bus for this message block handler
+     */
+    public MessageBlockHandler(final String eventBusName) {
+        eventBus = new EventBus(eventBusName);
+        LOGGER.trace("message bus {} created ", eventBusName);
+    }
+
+    /**
+     * Post a raw message block on the data handler event bus of this class.
+     *
+     * @param rawMessageBlock the block containing raw messages
+     */
+    public void post(final RawMessageBlock rawMessageBlock) {
+        if (rawMessageBlock.getMessage() != null) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("new raw message recieved from {}", rawMessageBlock.getConn() == null ? "server"
+                        : rawMessageBlock.getConn().getRemoteSocketAddress().getHostName());
+            }
+            eventBus.post(rawMessageBlock);
+        }
+    }
+
+    /**
+     * Post a block of typed messages on the data handler event bus of this class.
+     *
+     * @param messageBlock the block containing typed messages
+     */
+    public void post(final MessageBlock<MESSAGE> messageBlock) {
+        if (messageBlock.getMessages() != null) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("new data message recieved from {}", messageBlock.getConnection() == null ? "server"
+                        : messageBlock.getConnection().getRemoteSocketAddress().getHostName());
+            }
+            eventBus.post(messageBlock);
+        }
+    }
+
+    /**
+     * Post a string message on the data handler event bus of this class.
+     *
+     * @param messageString the string message
+     */
+    public void post(final String messageString) {
+        if (messageString != null) {
+            if (LOGGER.isDebugEnabled()) {
+                LOGGER.debug("new string message recieved from server: " + messageString);
+            }
+            eventBus.post(messageString);
+        }
+    }
+
+    /**
+     * Register a listener to event bus.
+     *
+     * @param listener is an instance of WebSocketMessageListener
+     */
+    public void registerMessageHandler(final MessageListener<MESSAGE> listener) {
+        LOGGER.entry(listener);
+        if (listener == null) {
+            throw new IllegalArgumentException("listener object cannot be null");
+        }
+        eventBus.register(listener);
+        LOGGER.debug("message listener {} is registered with forwarder", listener);
+        LOGGER.exit();
+    }
+
+    /**
+     * Remove the listener subscribed to the event bus.
+     *
+     * @param listener the listener
+     */
+    public void unRegisterMessageHandler(final MessageListener<MESSAGE> listener) {
+        if (listener == null) {
+            throw new IllegalArgumentException("listener object cannot be null");
+        }
+        LOGGER.entry(listener);
+        eventBus.unregister(listener);
+        LOGGER.trace(" message listener {} unregistered from forwarder", listener);
+        LOGGER.exit();
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/RawMessageBlock.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/RawMessageBlock.java
new file mode 100644 (file)
index 0000000..3fa25b5
--- /dev/null
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock;
+
+import java.nio.ByteBuffer;
+
+import org.java_websocket.WebSocket;
+
+/**
+ * A container for a raw message block and the connection on which it is handled.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public final class RawMessageBlock {
+    // The raw message
+    private final ByteBuffer message;
+
+    // The web socket on which the message is handled
+    private final WebSocket webSocket;
+
+    /**
+     * Constructor, instantiate the bean.
+     *
+     * @param message {@link ByteBuffer} message from the web socket
+     * @param webSocket {@link WebSocket} the web socket on which the message is handled
+     */
+    public RawMessageBlock(final ByteBuffer message, final WebSocket webSocket) {
+        this.message = message;
+        this.webSocket = webSocket;
+    }
+
+    /**
+     * A getter method for message.
+     *
+     * @return the message
+     */
+    public ByteBuffer getMessage() {
+        return message;
+    }
+
+    /**
+     * A getter method for the web socket.
+     *
+     * @return the web socket
+     */
+    public WebSocket getConn() {
+        return webSocket;
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/package-info.java
new file mode 100644 (file)
index 0000000..c01c88a
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Pass blocks of messages on Web Sockets to clients using an event bus.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/package-info.java
new file mode 100644 (file)
index 0000000..c881181
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides a Web Service implementation of the Messaging interfaces.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/InternalMessageBusServer.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/InternalMessageBusServer.java
new file mode 100644 (file)
index 0000000..8e65bbf
--- /dev/null
@@ -0,0 +1,134 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.server;
+
+import java.net.InetSocketAddress;
+import java.nio.ByteBuffer;
+
+import org.java_websocket.WebSocket;
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingService;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.RawMessageHandler;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlockHandler;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.RawMessageBlock;
+import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class InternalMessageBusServer handles the server side of a web socket and handles the callback mechanism used to
+ * receive messages on the web socket.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type
+ */
+abstract class InternalMessageBusServer<MESSAGE> extends WebSocketServerImpl implements MessagingService<MESSAGE> {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(InternalMessageBusServer.class);
+
+    private static final int THREAD_FACTORY_STACK_SIZE = 256;
+
+    // Name of the event bus.
+    private static final String RAW_EVENT_BUS = "Raw-Event-Bus";
+
+    // This instance handles the raw data received from the web socket
+    private final RawMessageHandler<MESSAGE> rawMessageHandler = new RawMessageHandler<>();
+
+    // The message block handler to which to pass messages coming in on this client
+    private MessageBlockHandler<MESSAGE> messageBlockHandler = null;
+
+    // The raw message handler uses a thread to process incoming events off a queue, this class owns and controls that
+    // thread. These fields hold the thread and
+    // the thread factory for creating threads.
+    private ApplicationThreadFactory tFactory =
+            new ApplicationThreadFactory("ws-server-thread", THREAD_FACTORY_STACK_SIZE);
+    private Thread forwarderThread = null;
+
+    /**
+     * Construct the class and start the forwarding thread for received messages.
+     *
+     * @param address the address of the server machine
+     */
+    protected InternalMessageBusServer(final InetSocketAddress address) {
+        // Call the super class to create the web socket
+        super(address);
+        LOGGER.entry(address.getAddress().getHostAddress() + ":" + address.getPort());
+
+        // Create the data handler for forwarding messages
+        messageBlockHandler = new MessageBlockHandler<>(RAW_EVENT_BUS);
+        messageBlockHandler.registerMessageHandler(rawMessageHandler);
+
+        // Create the thread that manages the queue in the data handler
+        forwarderThread = tFactory.newThread(rawMessageHandler);
+        forwarderThread.start();
+
+        LOGGER.exit();
+    }
+
+    /**
+     * Callback for binary messages received from the remote host.
+     *
+     * @param webSocket the web socket on which the raw message was received
+     * @param rawMessage the received raw message
+     * @see #onMessage(WebSocket, String)
+     */
+    @Override
+    public void onMessage(final WebSocket webSocket, final ByteBuffer rawMessage) {
+        messageBlockHandler.post(new RawMessageBlock(rawMessage, webSocket));
+    }
+
+    /**
+     * Register a subscriber class to the raw message handler.
+     *
+     * @param subscriber the subscriber
+     */
+    @Override
+    public void addMessageListener(final MessageListener<MESSAGE> subscriber) {
+        rawMessageHandler.registerDataForwarder(subscriber);
+    }
+
+    /**
+     * Removes the message listener.
+     *
+     * @param subscriber the subscriber
+     */
+    @Override
+    public void removeMessageListener(final MessageListener<MESSAGE> subscriber) {
+        rawMessageHandler.unRegisterDataForwarder(subscriber);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.server.WebSocketServer#onMessage(org.java_websocket.WebSocket, java.lang.String)
+     */
+    @Override
+    public void onMessage(final WebSocket webSocket, final String stringMessage) {
+        messageBlockHandler.post(stringMessage);
+    }
+
+    /**
+     * Stop the thread handling message forwarding.
+     */
+    protected void stopListener() {
+        rawMessageHandler.shutdown();
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/MessageServerImpl.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/MessageServerImpl.java
new file mode 100644 (file)
index 0000000..fc40157
--- /dev/null
@@ -0,0 +1,161 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.server;
+
+import java.io.IOException;
+import java.net.InetSocketAddress;
+import java.util.Collection;
+
+import org.java_websocket.WebSocket;
+import org.onap.policy.apex.core.infrastructure.messaging.MessageHolder;
+import org.onap.policy.apex.core.infrastructure.messaging.util.MessagingUtils;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * A messaging server implementation using web socket.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ * @param <MESSAGE> the generic type of message being passed
+ */
+public class MessageServerImpl<MESSAGE> extends InternalMessageBusServer<MESSAGE> {
+    // The logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(MessageServerImpl.class);
+
+    // The Web Socket protocol for URIs and URLs
+    private static final String PROTOCOL = "ws://";
+
+    // URI of this server
+    private final String connectionURI;
+
+    // Indicates if the web socket server is started or not
+    private boolean isStarted = false;
+
+    /**
+     * Instantiates a new web socket messaging server for Apex.
+     *
+     * @param address the address of the server machine on which to start the server
+     */
+    public MessageServerImpl(final InetSocketAddress address) {
+        // Call the super class to create the web socket and set up received message forwarding
+        super(address);
+        LOGGER.entry(address);
+
+        // Compose the Web Socket URI
+        connectionURI = PROTOCOL + address.getHostString() + ":" + address.getPort();
+        LOGGER.debug("Server connection URI: {}", connectionURI);
+
+        LOGGER.exit();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.server.WebSocketServer#start()
+     */
+    @Override
+    public void startConnection() {
+        // Start reception of connections on the web socket
+        start();
+        isStarted = true;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.server.WebSocketServer#stop()
+     */
+    @Override
+    public void stopConnection() {
+        // Stop message listening using our super class
+        stopListener();
+
+        // Stop the web socket server
+        try {
+            // Close all connections on this web socket server
+            for (final WebSocket connection : connections()) {
+                connection.closeConnection(0, "");
+            }
+            stop();
+        } catch (final IOException ioe) {
+            LOGGER.catching(ioe);
+        } catch (final InterruptedException e) {
+            // This can happen in normal operation so ignore
+        }
+        isStarted = false;
+    }
+
+    /**
+     * This method returns the current connection URI , if the server started otherwise it throws
+     * {@link IllegalStateException}.
+     *
+     * @return connection URI
+     */
+    public String getConnectionURI() {
+        if (connectionURI == null) {
+            throw new IllegalStateException("URI not set - The server is not started");
+        }
+        return connectionURI;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#send(org.onap.policy.apex.core.
+     * infrastructure. messaging.MessageHolder)
+     */
+    @Override
+    public void send(final MessageHolder<MESSAGE> message) {
+        // Send the incoming message to all clients connected to this web socket
+        final Collection<WebSocket> connections = connections();
+        for (final WebSocket webSocket : connections) {
+            webSocket.send(MessagingUtils.serializeObject(message));
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#send(java.lang.String)
+     */
+    @Override
+    public void send(final String messageString) {
+        final Collection<WebSocket> connections = connections();
+        for (final WebSocket webSocket : connections) {
+            webSocket.send(messageString);
+        }
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessagingService#isStarted()
+     */
+    @Override
+    public boolean isStarted() {
+        return isStarted;
+    }
+
+    @Override
+    public void onStart() {
+        LOGGER.debug("started deployment server on URI: {}", connectionURI);
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/WebSocketServerImpl.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/WebSocketServerImpl.java
new file mode 100644 (file)
index 0000000..26acfe7
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.server;
+
+import java.net.InetSocketAddress;
+
+import org.java_websocket.WebSocket;
+import org.java_websocket.handshake.ClientHandshake;
+import org.java_websocket.server.WebSocketServer;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is the web socket server specific implementation for Apex.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+abstract class WebSocketServerImpl extends WebSocketServer {
+    // The logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(MessageServerImpl.class);
+
+    /**
+     * Constructor of this class.
+     *
+     * @param address host address of the local machine.
+     */
+    protected WebSocketServerImpl(final InetSocketAddress address) {
+        super(address);
+        LOGGER.entry(address.getAddress().getHostAddress() + ":" + address.getPort());
+        LOGGER.exit();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.server.WebSocketServer#onOpen(org.java_websocket.WebSocket ,
+     * org.java_websocket.handshake.ClientHandshake)
+     */
+    @Override
+    public void onOpen(final WebSocket conn, final ClientHandshake handshake) {
+        LOGGER.entry(conn, handshake);
+        LOGGER.debug("A client connection opened from machine {}.",
+                conn.getRemoteSocketAddress().getAddress().getHostAddress());
+        LOGGER.exit();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.server.WebSocketServer#onClose(org.java_websocket. WebSocket, int, java.lang.String,
+     * boolean)
+     */
+    @Override
+    public void onClose(final WebSocket conn, final int code, final String reason, final boolean remote) {
+        LOGGER.entry(conn, code, remote);
+        LOGGER.debug("A client  connection from machine {} closing with code {}.",
+                conn.getRemoteSocketAddress().getAddress().getHostAddress(), code);
+        LOGGER.exit();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.java_websocket.server.WebSocketServer#onError(org.java_websocket.WebSocket, java.lang.Exception)
+     */
+    @Override
+    public void onError(final WebSocket conn, final Exception ex) {
+        // some errors like port binding failed may not be assignable to a specific web socket
+        LOGGER.error("server error occurred", ex);
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/package-info.java
new file mode 100644 (file)
index 0000000..0a7235b
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides the server side of messaging over web sockets.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.impl.ws.server;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/package-info.java
new file mode 100644 (file)
index 0000000..adb17da
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides support for passing messages as POJOs and as strings over Web Sockets.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageClient.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageClient.java
new file mode 100644 (file)
index 0000000..00ade80
--- /dev/null
@@ -0,0 +1,147 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.stringmessaging;
+
+import com.google.common.eventbus.Subscribe;
+
+import java.net.URI;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingException;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingService;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingServiceFactory;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlock;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class uses a web socket client to send and receive strings over a web socket.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class WSStringMessageClient implements WSStringMessager {
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(WSStringMessageClient.class);
+
+    // Message service factory and the message service itself
+    private final MessagingServiceFactory<String> factory = new MessagingServiceFactory<>();
+    private MessagingService<String> service = null;
+
+    // The listener to use for reception of strings
+    private WSStringMessageListener wsStringMessageListener;
+
+    // Address of the server
+    private final String host;
+    private final int port;
+    private String uriString;
+
+    /**
+     * Constructor, define the host and port of the server to connect to.
+     *
+     * @param host the host of the server
+     * @param port the port of the server
+     */
+    public WSStringMessageClient(final String host, final int port) {
+        this.host = host;
+        this.port = port;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageSender#start(org.onap.policy.
+     * apex. core.infrastructure.messaging. stringmessaging.WSStringMessageListener)
+     */
+    @Override
+    public void start(final WSStringMessageListener newWsStringMessageListener) throws MessagingException {
+        this.wsStringMessageListener = newWsStringMessageListener;
+
+        uriString = "ws://" + host + ":" + port;
+        LOGGER.entry("web socket event consumer client to \"" + uriString + "\" starting . . .");
+
+        try {
+            service = factory.createClient(new URI(uriString));
+            service.addMessageListener(new WSStringMessageClientListener());
+            service.startConnection();
+        } catch (final Exception e) {
+            LOGGER.warn("web socket event consumer client to \"" + uriString + "\" start failed", e);
+            throw new MessagingException("web socket event consumer client to \"" + uriString + "\" start failed", e);
+        }
+
+        LOGGER.exit("web socket event consumer client to \"" + uriString + "\" started");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageSender#stop()
+     */
+    @Override
+    public void stop() {
+        LOGGER.entry("web socket event consumer client to \"" + uriString + "\" stopping . . .");
+        service.stopConnection();
+        LOGGER.exit("web socket event consumer client to \"" + uriString + "\" stopped");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageSender#sendString(java.lang.
+     * String)
+     */
+    @Override
+    public void sendString(final String stringMessage) {
+        service.send(stringMessage);
+
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("message sent to server: " + stringMessage);
+        }
+    }
+
+    /**
+     * The Class WSStringMessageClientListener.
+     */
+    private class WSStringMessageClientListener implements MessageListener<String> {
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(org.onap.policy.apex.core.
+         * infrastructure.messaging.impl.ws.messageblock. MessageBlock)
+         */
+        @Subscribe
+        @Override
+        public void onMessage(final MessageBlock<String> messageBlock) {
+            throw new UnsupportedOperationException("raw messages are not supported on string message clients");
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(java.lang.String)
+         */
+        @Subscribe
+        @Override
+        public void onMessage(final String messageString) {
+            wsStringMessageListener.receiveString(messageString);
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageListener.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageListener.java
new file mode 100644 (file)
index 0000000..e524b43
--- /dev/null
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.stringmessaging;
+
+/**
+ * This interface is used to call back the owner of a String Web socket message server or client.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public interface WSStringMessageListener {
+
+    /**
+     * Receive a string coming off a web socket.
+     *
+     * @param stringMessage the string message
+     */
+    void receiveString(String stringMessage);
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageServer.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageServer.java
new file mode 100644 (file)
index 0000000..4da478f
--- /dev/null
@@ -0,0 +1,150 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.stringmessaging;
+
+import com.google.common.eventbus.Subscribe;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingException;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingService;
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingServiceFactory;
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlock;
+import org.onap.policy.apex.core.infrastructure.messaging.util.MessagingUtils;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class runs a web socket server for sending and receiving of strings over a web socket.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class WSStringMessageServer implements WSStringMessager {
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(WSStringMessageServer.class);
+
+    // Message service factory and the message service itself
+    private final MessagingServiceFactory<String> factory = new MessagingServiceFactory<>();
+    private MessagingService<String> service = null;
+
+    // The listener to use for reception of strings
+    private WSStringMessageListener wsStringMessageListener;
+
+    // Address of the server
+    private final int port;
+
+    /**
+     * Constructor, define the port of the server.
+     *
+     * @param port the port of the server
+     */
+    public WSStringMessageServer(final int port) {
+        this.port = port;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageSender#start(org.onap.policy.
+     * apex. core.infrastructure.messaging. stringmessaging.WSStringMessageListener)
+     */
+    @Override
+    public void start(final WSStringMessageListener newWsStringMessageListener) throws MessagingException {
+        this.wsStringMessageListener = newWsStringMessageListener;
+
+        LOGGER.entry("web socket event consumer server starting . . .");
+
+        try {
+            final InetAddress addrLan = MessagingUtils.getLocalHostLANAddress();
+            LOGGER.debug("web socket string message server LAN address=" + addrLan.getHostAddress());
+            final InetAddress addr = InetAddress.getLocalHost();
+            LOGGER.debug("web socket string message server host address=" + addr.getHostAddress());
+
+            service = factory.createServer(new InetSocketAddress(port));
+            service.addMessageListener(new WSStringMessageServerListener());
+
+            service.startConnection();
+        } catch (final Exception e) {
+            LOGGER.warn("web socket string message server start failed", e);
+            throw new MessagingException("web socket string message start failed", e);
+        }
+
+        LOGGER.exit("web socket string message server started");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageSender#stop()
+     */
+    @Override
+    public void stop() {
+        LOGGER.entry("web socket string message server stopping . . .");
+        service.stopConnection();
+        LOGGER.exit("web socket string message server stopped");
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see
+     * org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageSender#sendString(java.lang.
+     * String)
+     */
+    @Override
+    public void sendString(final String stringMessage) {
+        service.send(stringMessage);
+        if (LOGGER.isDebugEnabled()) {
+            LOGGER.debug("server sent message: " + stringMessage);
+        }
+    }
+
+    /**
+     * The listener for strings coming into the server.
+     */
+    private class WSStringMessageServerListener implements MessageListener<String> {
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(org.onap.policy.apex.core.
+         * infrastructure.messaging.impl.ws.messageblock. MessageBlock)
+         */
+        @Subscribe
+        @Override
+        public void onMessage(final MessageBlock<String> messageBlock) {
+            throw new UnsupportedOperationException("raw messages are not supported on string message clients");
+        }
+
+        /*
+         * (non-Javadoc)
+         *
+         * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(java.lang.String)
+         */
+        @Subscribe
+        @Override
+        public void onMessage(final String messageString) {
+            wsStringMessageListener.receiveString(messageString);
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessager.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessager.java
new file mode 100644 (file)
index 0000000..a2781e9
--- /dev/null
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.stringmessaging;
+
+import org.onap.policy.apex.core.infrastructure.messaging.MessagingException;
+
+/**
+ * This interface is used to call a String Web socket message server or client to send a string.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public interface WSStringMessager {
+
+    /**
+     * Start the string message sender.
+     *
+     * @param wsStringMessageListener the listener to use for listening for string messages
+     * @throws MessagingException the messaging exception
+     */
+    void start(WSStringMessageListener wsStringMessageListener) throws MessagingException;
+
+    /**
+     * Stop the string messaging sender.
+     */
+    void stop();
+
+    /**
+     * Send a string on a web socket.
+     *
+     * @param stringMessage the string message to send
+     */
+    void sendString(String stringMessage);
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/package-info.java
new file mode 100644 (file)
index 0000000..a8e679c
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides string messaging over Web Sockets.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.stringmessaging;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/MessagingUtils.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/MessagingUtils.java
new file mode 100644 (file)
index 0000000..d15f86c
--- /dev/null
@@ -0,0 +1,260 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.net.InetAddress;
+import java.net.NetworkInterface;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.util.Enumeration;
+
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class MessagingUtils is a class with static methods used in IPC messaging for finding free ports, translating
+ * host names to addresses, serializing objects and flushing object streams.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public final class MessagingUtils {
+    // The port number of the lowest user port, ports 0-1023 are system ports
+    private static final int LOWEST_USER_PORT = 1024;
+
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(MessagingUtils.class);
+
+    /**
+     * Private constructor used to prevent sub class instantiation.
+     */
+    private MessagingUtils() {}
+
+    /**
+     * This method searches the availability of the port, if the requested port not available, this method will throw an
+     * exception.
+     *
+     * @param port the port to check
+     * @return the port verified as being free
+     * @throws RuntimeException on port allocation errors
+     */
+    public static int checkPort(final int port) {
+        LOGGER.entry("Checking availability of  port {}", port);
+
+        Socket s = null;
+        try {
+            // Try to connect to the port, if we can connect then the port is occupied
+            s = new Socket("localhost", port);
+            LOGGER.debug("Port {} is not available", port);
+
+            throw new RuntimeException("could not allocate requested port: " + port);
+        } catch (final IOException e) {
+            // We found a free port
+            LOGGER.debug("Port {} is available ", port);
+            return port;
+        } finally {
+            // Close the socket used to check if the port was free
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (final IOException e) {
+                    LOGGER.catching(e);
+                    LOGGER.warn("could not allocate requested port " + port, e);
+                }
+            }
+        }
+    }
+
+    /**
+     * This method searches the availability of the port, if the requested port not available,this method will increment
+     * the port number and check the availability of that port, this process will continue until it find port available.
+     *
+     * @param port the first port to check
+     * @return the port that was found
+     * @throws RuntimeException on port allocation errors
+     */
+    public static int findPort(final int port) {
+        LOGGER.entry("Checking availability of  port {}", port);
+
+        Socket s = null;
+        try {
+            // Try to connect to the port, if we can connect then the port is occupied
+            s = new Socket("localhost", port);
+            LOGGER.debug("Port {} is not available", port);
+
+            // Recurse and try the next port
+            return findPort(port + 1);
+        } catch (final IOException e) {
+            // We found a free port
+            LOGGER.debug("Port {} is available ", port);
+            return port;
+        } finally {
+            // Close the socket used to check if the port was free
+            if (s != null) {
+                try {
+                    s.close();
+                } catch (final IOException e) {
+                    LOGGER.catching(e);
+                    LOGGER.warn("could not allocate requested port " + port, e);
+                    throw new RuntimeException("could not allocate requested port " + port, e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the local host address.
+     *
+     * @return the local host address
+     */
+    public static InetAddress getHost() {
+        try {
+            return InetAddress.getLocalHost();
+        } catch (final UnknownHostException e) {
+            throw new IllegalStateException(e.getMessage(), e);
+        }
+    }
+
+    /**
+     * This method searches the availability of the port, if the requested port not available,this method will increment
+     * the port number and check the availability, this process will continue until it find port available.
+     *
+     * @param port the first port to check
+     * @return the port that was found
+     * @throws RuntimeException on port allocation errors
+     */
+    public static int allocateAddress(final int port) {
+        if (port < LOWEST_USER_PORT) {
+            throw new IllegalArgumentException("The port " + port + "  is already in use");
+        }
+        return MessagingUtils.findPort(port);
+    }
+
+    /**
+     * Get an Internet Address for the local host.
+     *
+     * @return an Internet address
+     * @throws UnknownHostException if the address of the local host cannot be found
+     */
+    public static InetAddress getLocalHostLANAddress() throws UnknownHostException {
+        try {
+            InetAddress candidateAddress = null;
+            // Iterate all NICs (network interface cards)...
+            for (final Enumeration<NetworkInterface> ifaces = NetworkInterface.getNetworkInterfaces(); ifaces
+                    .hasMoreElements();) {
+                final NetworkInterface iface = ifaces.nextElement();
+                // Iterate all IP addresses assigned to each card...
+                for (final Enumeration<InetAddress> inetAddrs = iface.getInetAddresses(); inetAddrs
+                        .hasMoreElements();) {
+                    final InetAddress inetAddr = inetAddrs.nextElement();
+                    if (!inetAddr.isLoopbackAddress()) {
+
+                        if (inetAddr.isSiteLocalAddress()) {
+                            // Found non-loopback site-local address. Return it
+                            // immediately...
+                            return inetAddr;
+                        } else if (candidateAddress == null) {
+                            // Found non-loopback address, but not
+                            // necessarily site-local.
+                            // Store it as a candidate to be returned if
+                            // site-local address is not subsequently
+                            // found...
+                            candidateAddress = inetAddr;
+                            // Note that we don't repeatedly assign
+                            // non-loopback non-site-local addresses as
+                            // candidates,
+                            // only the first. For subsequent iterations,
+                            // candidate will be non-null.
+                        }
+                    }
+                }
+            }
+            if (candidateAddress != null) {
+                // We did not find a site-local address, but we found some other
+                // non-loopback address.
+                // Server might have a non-site-local address assigned to its
+                // NIC (or it might be running
+                // IPv6 which deprecates the "site-local" concept).
+                // Return this non-loopback candidate address...
+                return candidateAddress;
+            }
+            // At this point, we did not find a non-loopback address.
+            // Fall back to returning whatever InetAddress.getLocalHost()
+            // returns...
+            final InetAddress jdkSuppliedAddress = InetAddress.getLocalHost();
+            if (jdkSuppliedAddress == null) {
+                throw new UnknownHostException("The JDK InetAddress.getLocalHost() method unexpectedly returned null.");
+            }
+            return jdkSuppliedAddress;
+        } catch (final Exception e) {
+            final UnknownHostException unknownHostException =
+                    new UnknownHostException("Failed to determine LAN address: " + e);
+            unknownHostException.initCause(e);
+            throw unknownHostException;
+        }
+    }
+
+    /**
+     * This method serializes the message holder objects.
+     *
+     * @param object the object
+     * @return byte[]
+     */
+    public static byte[] serializeObject(final Object object) {
+        LOGGER.entry(object.getClass().getName());
+        final ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+        ObjectOutputStream oos = null;
+        try {
+            oos = new ObjectOutputStream(bytesOut);
+            oos.writeObject(object);
+        } catch (final IOException e) {
+            LOGGER.warn("error on object serialization", e);
+        } finally {
+            flushAndClose(oos, bytesOut);
+        }
+        final byte[] bytes = bytesOut.toByteArray();
+        return bytes;
+    }
+
+    /**
+     * Flush and close an object stream and a byte array output stream.
+     *
+     * @param oos the object output stream
+     * @param bytesOut the byte array output stream
+     */
+    private static void flushAndClose(final ObjectOutputStream oos, final ByteArrayOutputStream bytesOut) {
+        try {
+            if (oos != null) {
+                oos.flush();
+                oos.close();
+            }
+            if (bytesOut != null) {
+                bytesOut.close();
+            }
+
+        } catch (final IOException e) {
+            LOGGER.error("Failed to close the Srialization operation");
+            LOGGER.catching(e);
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/package-info.java
new file mode 100644 (file)
index 0000000..ccb12f2
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains utility classes for messaging using sockets and web sockets.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging.util;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/package-info.java
new file mode 100644 (file)
index 0000000..8bd0a09
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides infrastructure and utility functions for use by other classes and modules in APEX.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ApplicationThreadFactory.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ApplicationThreadFactory.java
new file mode 100644 (file)
index 0000000..45579c7
--- /dev/null
@@ -0,0 +1,142 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.threading;
+
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.atomic.AtomicInteger;
+
+/**
+ * This class provides a thread factory for use by classes that require thread factories to handle concurrent operation.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public class ApplicationThreadFactory implements ThreadFactory {
+    private static final String HYPHEN = "-";
+    private static final String APPLICATION_NAME = "Apex-";
+    private static final AtomicInteger NEXT_POOL_NUMBER = new AtomicInteger();
+    private final ThreadGroup group;
+    private final AtomicInteger nextThreadNumber = new AtomicInteger();
+    private final String name;
+    private final long stackSize;
+    private final int threadPriority;
+
+    /**
+     * Instantiates a new application thread factory with a default stack size and normal thread priority.
+     *
+     * @param nameLocal the name local
+     */
+    public ApplicationThreadFactory(final String nameLocal) {
+        this(nameLocal, 0);
+    }
+
+    /**
+     * Instantiates a new application thread factory with a default normal thread priority.
+     *
+     * @param nameLocal the name local
+     * @param stackSize the stack size
+     */
+    public ApplicationThreadFactory(final String nameLocal, final long stackSize) {
+        this(nameLocal, stackSize, Thread.NORM_PRIORITY);
+    }
+
+    /**
+     * Instantiates a new application thread factory with a specified thread priority.
+     *
+     * @param nameLocal the name local
+     * @param stackSize the stack size
+     * @param threadPriority the thread priority
+     */
+    public ApplicationThreadFactory(final String nameLocal, final long stackSize, final int threadPriority) {
+        final SecurityManager s = System.getSecurityManager();
+        group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
+        name = APPLICATION_NAME + nameLocal + HYPHEN + NEXT_POOL_NUMBER.getAndIncrement();
+        this.stackSize = stackSize;
+        this.threadPriority = threadPriority;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.util.concurrent.ThreadFactory#newThread(java.lang.Runnable)
+     */
+    @Override
+    public Thread newThread(final Runnable r) {
+        final Thread thisThread;
+        if (stackSize > 0) {
+            thisThread = new Thread(group, r, name + ':' + nextThreadNumber.getAndIncrement(), stackSize);
+        } else {
+            thisThread = new Thread(group, r, name + ':' + nextThreadNumber.getAndIncrement());
+        }
+        if (thisThread.isDaemon()) {
+            thisThread.setDaemon(false);
+        }
+        thisThread.setPriority(threadPriority);
+
+        return thisThread;
+    }
+
+    /**
+     * Stop group threads.
+     */
+    public void stopGroupThreads() {
+        group.interrupt();
+        group.list();
+
+    }
+
+    /**
+     * Gets the name of the thread factory.
+     *
+     * @return the name
+     */
+    public String getName() {
+        return name;
+    }
+
+    /**
+     * Gets the stack size of the threads created by this thread factory.
+     *
+     * @return the stack size
+     */
+    public long getStackSize() {
+        return stackSize;
+    }
+
+    /**
+     * Gets the thread priority of the threads created by this thread factory.
+     *
+     * @return the thread priority
+     */
+    public int getThreadPriority() {
+        return threadPriority;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "ApplicationThreadFactory [nextPollNumber=" + NEXT_POOL_NUMBER + ",nextThreadNumber=" + nextThreadNumber
+                + ", name=" + name + ", stackSize=" + stackSize + ", threadPriority=" + threadPriority + "]";
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ThreadUtilities.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ThreadUtilities.java
new file mode 100644 (file)
index 0000000..56b903f
--- /dev/null
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.threading;
+
+/**
+ * This class is a helper class for carrying out common threading tasks.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public final class ThreadUtilities {
+
+    /**
+     * Private constructor to prevent sub-classing of this class.
+     */
+    private ThreadUtilities() {}
+
+    /**
+     * Sleeps for the specified number of milliseconds, hiding interrupt handling.
+     *
+     * @param milliseconds the milliseconds
+     * @return true, if successful
+     */
+    public static boolean sleep(final long milliseconds) {
+        try {
+            Thread.sleep(milliseconds);
+        } catch (final InterruptedException e) {
+            return false;
+        }
+
+        return true;
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/package-info.java
new file mode 100644 (file)
index 0000000..dc0b9ee
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides factories and utility functions for threads.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.threading;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/XPathReader.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/XPathReader.java
new file mode 100644 (file)
index 0000000..f677e6b
--- /dev/null
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.xml;
+
+import java.io.InputStream;
+
+import javax.xml.namespace.QName;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.xpath.XPath;
+import javax.xml.xpath.XPathExpression;
+import javax.xml.xpath.XPathExpressionException;
+import javax.xml.xpath.XPathFactory;
+
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+import org.w3c.dom.Document;
+
+/**
+ * A generic class for applying the XPATH queries on XML files.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public class XPathReader {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(XPathReader.class);
+
+    private String xmlFileName = null;
+    private InputStream xmlStream = null;
+    private Document xmlDocument;
+    private XPath xPath;
+
+    /**
+     * Construct Reader for the file passed in.
+     *
+     * @param xmlFileName the xml file name
+     */
+    public XPathReader(final String xmlFileName) {
+        this.xmlFileName = xmlFileName;
+        init();
+    }
+
+    /**
+     * Construct Reader for the stream passed in.
+     *
+     * @param xmlStream a stream of XML
+     */
+    public XPathReader(final InputStream xmlStream) {
+        this.xmlStream = xmlStream;
+        init();
+    }
+
+    /**
+     * Initialise the x-path reader.
+     */
+    private void init() {
+        try {
+            LOGGER.info("Initializing XPath reader");
+
+            // Check if this is operating on a file
+            if (xmlFileName != null) {
+                xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFileName);
+            }
+            // Check if this is operating on a stream
+            else if (xmlStream != null) {
+                xmlDocument = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlStream);
+
+            }
+            // We have an error
+            else {
+                LOGGER.error("XPath reader not initialized with either a file or a stream");
+                return;
+            }
+
+            xPath = XPathFactory.newInstance().newXPath();
+            LOGGER.info("Initialized XPath reader");
+        } catch (final Exception ex) {
+            LOGGER.error("Error parsing XML file/stream from XPath reading, reason :\n" + ex.getMessage());
+        }
+    }
+
+    /**
+     * Read items from the file using xpath.
+     *
+     * @param expression x-path expression
+     * @param returnType XML node Set
+     * @return last node collected
+     */
+    public Object read(final String expression, final QName returnType) {
+        try {
+            final XPathExpression xPathExpression = xPath.compile(expression);
+            return xPathExpression.evaluate(xmlDocument, returnType);
+        } catch (final XPathExpressionException ex) {
+            LOGGER.error("Failed to read XML file for XPath processing, reason:\n" + ex.getMessage());
+            return null;
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/package-info.java
new file mode 100644 (file)
index 0000000..5631bd1
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides utility XML classes for use by other classes and modules in APEX.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.infrastructure.xml;
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassBuilder.java b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassBuilder.java
new file mode 100644 (file)
index 0000000..5765eee
--- /dev/null
@@ -0,0 +1,133 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.core.infrastructure.java.compile.singleclass;
+
+import java.util.Arrays;
+import java.util.List;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.ToolProvider;
+
+import org.onap.policy.apex.core.infrastructure.java.JavaHandlingException;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class SingleClassBuilder is used to compile the Java code for a Java object and to create an instance of the
+ * object.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class SingleClassBuilder {
+    // Logger for this class
+    private static final XLogger LOGGER = XLoggerFactory.getXLogger(SingleClassBuilder.class);
+
+    // The class name and source code for the class that we are compiling and instantiating
+    private final String className;
+    private final String sourceCode;
+
+    // This specialized JavaFileManager handles class loading for the single Java class
+    private SingleFileManager singleFileManager = null;
+
+    /**
+     * Instantiates a new single class builder.
+     *
+     * @param className the class name
+     * @param sourceCode the source code
+     */
+    public SingleClassBuilder(final String className, final String sourceCode) {
+        // Save the fields of the class
+        this.className = className;
+        this.sourceCode = sourceCode;
+    }
+
+    /**
+     * Compile the single class into byte code.
+     *
+     * @throws JavaHandlingException Thrown on compilation errors or handling errors on the single Java class
+     */
+    public void compile() throws JavaHandlingException {
+        // Get the list of compilation units, there is only one here
+        final List<? extends JavaFileObject> compilationUnits =
+                Arrays.asList(new SingleClassCompilationUnit(className, sourceCode));
+
+        // Allows us to get diagnostics from the compilation
+        final DiagnosticCollector<JavaFileObject> diagnosticListener = new DiagnosticCollector<>();
+
+        // Get the Java compiler
+        final JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+
+        // Set up the target file manager and call the compiler
+        singleFileManager = new SingleFileManager(compiler, new SingleClassByteCodeFileObject(className));
+        final JavaCompiler.CompilationTask task =
+                compiler.getTask(null, singleFileManager, diagnosticListener, null, null, compilationUnits);
+
+        // Check if the compilation worked
+        if (!task.call()) {
+            final StringBuilder builder = new StringBuilder();
+            for (final Diagnostic<? extends JavaFileObject> diagnostic : diagnosticListener.getDiagnostics()) {
+                builder.append("code:");
+                builder.append(diagnostic.getCode());
+                builder.append(", kind:");
+                builder.append(diagnostic.getKind());
+                builder.append(", position:");
+                builder.append(diagnostic.getPosition());
+                builder.append(", start position:");
+                builder.append(diagnostic.getStartPosition());
+                builder.append(", end position:");
+                builder.append(diagnostic.getEndPosition());
+                builder.append(", source:");
+                builder.append(diagnostic.getSource());
+                builder.append(", message:");
+                builder.append(diagnostic.getMessage(null));
+                builder.append("\n");
+            }
+
+            LOGGER.warn("error compiling Java code for class \"" + className + "\": " + builder.toString());
+            throw new JavaHandlingException(
+                    "error compiling Java code for class \"" + className + "\": " + builder.toString());
+        }
+    }
+
+    /**
+     * Create a new instance of the Java class using its byte code definition.
+     *
+     * @return A new instance of the object
+     * @throws InstantiationException if an instance of the object cannot be created, for example if the class has no
+     *         default constructor
+     * @throws IllegalAccessException the caller does not have permission to call the class
+     * @throws ClassNotFoundException the byte code for the class is not found in the class loader
+     * @throws JavaHandlingException the java handling exception if the Java class source code is not compiled
+     */
+    public Object createObject()
+            throws InstantiationException, IllegalAccessException, ClassNotFoundException, JavaHandlingException {
+        if (singleFileManager == null) {
+            LOGGER.warn("error instantiating instance for class \"" + className + "\": code may not be compiled");
+            throw new JavaHandlingException(
+                    "error instantiating instance for class \"" + className + "\": code may not be compiled");
+        }
+
+        return singleFileManager.getClassLoader(null).findClass(className).newInstance();
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassByteCodeFileObject.java b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassByteCodeFileObject.java
new file mode 100644 (file)
index 0000000..c0a2ef7
--- /dev/null
@@ -0,0 +1,89 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.core.infrastructure.java.compile.singleclass;
+
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+import javax.tools.SimpleJavaFileObject;
+
+/**
+ * The Class SingleClassByteCodeFileObject is a specialization of the {@link SimpleJavaFileObject} class, which is
+ * itself an implementation of the {@code JavaFileObject} interface, which provides a file abstraction for tools
+ * operating on Java programming language source and class files. The {@link SimpleJavaFileObject} class provides simple
+ * implementations for most methods in {@code JavaFileObject}. This class is designed to be sub classed and used as a
+ * basis for {@code JavaFileObject} implementations. Subclasses can override the implementation and specification of any
+ * method of this class as long as the general contract of {@code JavaFileObject} is obeyed.
+ *
+ * This class holds the byte code for a single class in memory.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class SingleClassByteCodeFileObject extends SimpleJavaFileObject {
+
+    // The ByteArrayOutputStream holds the byte code for the class
+    private ByteArrayOutputStream byteArrayOutputStream;
+
+    /**
+     * Instantiates the byte code for the class in memory.
+     *
+     * @param className the class name is used to compose a URI for the class
+     */
+    public SingleClassByteCodeFileObject(final String className) {
+        super(URI.create("byte:///" + className + ".class"), Kind.CLASS);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.SimpleJavaFileObject#openOutputStream()
+     */
+    @Override
+    public OutputStream openOutputStream() {
+        // Create the byte array output stream that will hold the byte code for the class, when the class source code is
+        // compiled, this output stream is passed
+        // to the compiler and the byte code for the class is written into the output stream.
+        byteArrayOutputStream = new ByteArrayOutputStream();
+        return byteArrayOutputStream;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.SimpleJavaFileObject#openInputStream()
+     */
+    @Override
+    public InputStream openInputStream() {
+        // No input stream for streaming out the byte code
+        return null;
+    }
+
+    /**
+     * Gets the byte code of the class.
+     *
+     * @return the byte code of the class
+     */
+    public byte[] getByteCode() {
+        return byteArrayOutputStream.toByteArray();
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassCompilationUnit.java b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassCompilationUnit.java
new file mode 100644 (file)
index 0000000..9a001d2
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.core.infrastructure.java.compile.singleclass;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+
+import javax.tools.SimpleJavaFileObject;
+
+/**
+ * The Class SingleClassCompilationUnit is a container for the source code of the single Java class in memory. The class
+ * uses a {@link String} to hold the source code.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class SingleClassCompilationUnit extends SimpleJavaFileObject {
+
+    private final String source;
+
+    /**
+     * Instantiates a new compilation unit.
+     *
+     * @param className the class name for the source code
+     * @param source the source code for the class
+     */
+    public SingleClassCompilationUnit(final String className, final String source) {
+        // Create a URI for the source code of the class
+        super(URI.create("file:///" + className + ".java"), Kind.SOURCE);
+        this.source = source;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.SimpleJavaFileObject#getCharContent(boolean)
+     */
+    @Override
+    public CharSequence getCharContent(final boolean ignoreEncodingErrors) {
+        // Return the source code to toe caller, the compiler
+        return source;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.SimpleJavaFileObject#openOutputStream()
+     */
+    @Override
+    public OutputStream openOutputStream() {
+        throw new IllegalStateException();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.SimpleJavaFileObject#openInputStream()
+     */
+    @Override
+    public InputStream openInputStream() {
+        // Return the source code as a stream
+        return new ByteArrayInputStream(source.getBytes());
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassLoader.java b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassLoader.java
new file mode 100644 (file)
index 0000000..befed6d
--- /dev/null
@@ -0,0 +1,61 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.core.infrastructure.java.compile.singleclass;
+
+/**
+ * The Class SingleClassLoader is responsible for class loading the single Java class being held in memory.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class SingleClassLoader extends ClassLoader {
+    // The byte code of the class held in memory as byte code in a ByteCodeFileObject
+    private final SingleClassByteCodeFileObject byteCodeFileObject;
+
+    /**
+     * Instantiates a new single class loader to load the byte code of the class that is being held in memory.
+     *
+     * @param byteCodeFileObject the byte code of the class
+     */
+    public SingleClassLoader(final SingleClassByteCodeFileObject byteCodeFileObject) {
+        this.byteCodeFileObject = byteCodeFileObject;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.ClassLoader#findClass(java.lang.String)
+     */
+    @Override
+    protected Class<?> findClass(final String className) throws ClassNotFoundException {
+        // Creates a java Class that can be instantiated from the class defined in the byte code in the
+        // ByteCodeFileObejct
+        return defineClass(className, byteCodeFileObject.getByteCode(), 0, byteCodeFileObject.getByteCode().length);
+    }
+
+    /**
+     * Gets the file object.
+     *
+     * @return the file object
+     */
+    SingleClassByteCodeFileObject getFileObject() {
+        return byteCodeFileObject;
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleFileManager.java b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleFileManager.java
new file mode 100644 (file)
index 0000000..ce92f8e
--- /dev/null
@@ -0,0 +1,79 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.core.infrastructure.java.compile.singleclass;
+
+import java.io.IOException;
+
+import javax.tools.FileObject;
+import javax.tools.ForwardingJavaFileManager;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+
+/**
+ * The Class SingleFileManager is a {@link ForwardingJavaFileManager} which in turn implements {@code JavaFileManager}.
+ * A {@code JavaFileManager} handles source files for Java language handling tools. A {@link ForwardingJavaFileManager}
+ * is an implementation of {@code JavaFileManager} that forwards the {@code JavaFileManager} methods to a given file
+ * manager.
+ *
+ * This class instantiates and forwards those requests to a {@link StandardJavaFileManager} instance to act as a
+ * {@code JavaFileManager} for a Java single file, managing class loading for the class.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class SingleFileManager extends ForwardingJavaFileManager<StandardJavaFileManager> {
+    // THe class loader for our single class
+    private final SingleClassLoader singleClassLoader;
+
+    /**
+     * Instantiates a new single file manager.
+     *
+     * @param compiler the compiler we are using
+     * @param byteCodeFileObject the byte code for the compiled class
+     */
+    public SingleFileManager(final JavaCompiler compiler, final SingleClassByteCodeFileObject byteCodeFileObject) {
+        super(compiler.getStandardFileManager(null, null, null));
+        singleClassLoader = new SingleClassLoader(byteCodeFileObject);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.ForwardingJavaFileManager#getJavaFileForOutput(javax.tools.JavaFileManager.Location,
+     * java.lang.String, javax.tools.JavaFileObject.Kind, javax.tools.FileObject)
+     */
+    @Override
+    public JavaFileObject getJavaFileForOutput(final Location notUsed, final String className,
+            final JavaFileObject.Kind kind, final FileObject sibling) throws IOException {
+        // Return the JavaFileObject to the compiler so that it can write byte code into it
+        return singleClassLoader.getFileObject();
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see javax.tools.ForwardingJavaFileManager#getClassLoader(javax.tools.JavaFileManager.Location)
+     */
+    @Override
+    public SingleClassLoader getClassLoader(final Location location) {
+        return singleClassLoader;
+    }
+}
diff --git a/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/package-info.java b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/package-info.java
new file mode 100644 (file)
index 0000000..5e43681
--- /dev/null
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Generates classes from source code by compiling source code and placing the resultant classes on the class path on
+ * the fly.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.core.infrastructure.java.compile.singleclass;
diff --git a/core/core-infrastructure/src/main/resources/logback.xml b/core/core-infrastructure/src/main/resources/logback.xml
new file mode 100644 (file)
index 0000000..b1d8554
--- /dev/null
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2016-2018 Ericsson. All rights reserved.
+  ================================================================================
+  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 debug="false">
+
+    <contextName>Apex</contextName>
+    <statusListener class="ch.qos.logback.core.status.OnConsoleStatusListener" />
+    <property name="VAR_LOG" value="/var/log/ericsson/apex/" />
+
+       <!-- 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>
+
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <file>${VAR_LOG}/apex.log</file>
+        <encoder>
+            <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level
+                %logger{26} - %msg %n %ex{full}</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="org.onap.policy.apex" level="info" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+</configuration>
diff --git a/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/EndToEndStringMessagingTest.java b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/EndToEndStringMessagingTest.java
new file mode 100644 (file)
index 0000000..e18c327
--- /dev/null
@@ -0,0 +1,87 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+import org.junit.Test;
+import org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageClient;
+import org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageServer;
+import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class EndToEndMessagingTest.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EndToEndStringMessagingTest {
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(EndToEndStringMessagingTest.class);
+
+    private WSStringMessageServer server;
+    private WSStringMessageClient client;
+
+    private boolean finished = false;
+
+    @Test
+    public void testEndToEndMessaging() throws MessagingException {
+        logger.debug("end to end messaging test starting . . .");
+        server = new WSStringMessageServer(44441);
+        assertNotNull(server);
+        server.start(new WSStringServerMessageListener());
+
+        client = new WSStringMessageClient("localhost", 44441);
+        assertNotNull(client);
+        client.start(new WSStringClientMessageListener());
+
+        client.sendString("Hello, client here");
+
+        while (!finished) {
+            ThreadUtilities.sleep(50);
+        }
+        client.stop();
+
+        server.stop();
+        logger.debug("end to end messaging test finished");
+    }
+
+    private class WSStringServerMessageListener implements WSStringMessageListener {
+        @Override
+        public void receiveString(final String stringMessage) {
+            logger.debug(stringMessage);
+            assertEquals("Hello, client here", stringMessage);
+            server.sendString("Hello back from server");
+        }
+    }
+
+    private class WSStringClientMessageListener implements WSStringMessageListener {
+        @Override
+        public void receiveString(final String stringMessage) {
+            logger.debug(stringMessage);
+            assertEquals("Hello back from server", stringMessage);
+            finished = true;
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/StringTestServer.java b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/StringTestServer.java
new file mode 100644 (file)
index 0000000..09fa62d
--- /dev/null
@@ -0,0 +1,83 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+import static org.junit.Assert.assertNotNull;
+
+import org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageListener;
+import org.onap.policy.apex.core.infrastructure.messaging.stringmessaging.WSStringMessageServer;
+import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities;
+
+public class StringTestServer {
+    private WSStringMessageServer server;
+
+    public StringTestServer(final int port, long timeToLive) throws MessagingException {
+        System.out.println("StringTestServer starting on port " + port + " for " + timeToLive + " seconds . . .");
+        server = new WSStringMessageServer(port);
+        assertNotNull(server);
+        server.start(new WSStringServerMessageListener());
+
+        System.out.println("StringTestServer started on port " + port + " for " + timeToLive + " seconds");
+
+        for (; timeToLive > 0; timeToLive--) {
+            ThreadUtilities.sleep(1000);
+        }
+
+        server.stop();
+        System.out.println("StringTestServer completed");
+    }
+
+    private class WSStringServerMessageListener implements WSStringMessageListener {
+        @Override
+        public void receiveString(final String stringMessage) {
+            System.out.println("Server received string \"" + stringMessage + "\"");
+            server.sendString("Server echoing back the message: \"" + stringMessage + "\"");
+        }
+    }
+
+    public static void main(final String[] args) throws MessagingException {
+        if (args.length != 2) {
+            System.err.println("Usage: StringTestServer port timeToLive");
+            return;
+        }
+
+        int port = 0;
+        try {
+            port = Integer.parseInt(args[0]);
+        } catch (final Exception e) {
+            System.err.println("Usage: StringTestServer port timeToLive");
+            e.printStackTrace();
+            return;
+        }
+
+        long timeToLive = 0;
+        try {
+            timeToLive = Long.parseLong(args[1]);
+        } catch (final Exception e) {
+            System.err.println("Usage: StringTestServer port timeToLive");
+            e.printStackTrace();
+            return;
+        }
+
+        new StringTestServer(port, timeToLive);
+
+    }
+}
diff --git a/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/TestMessageListener.java b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/TestMessageListener.java
new file mode 100644 (file)
index 0000000..9e7562b
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.messaging;
+
+import com.google.common.eventbus.Subscribe;
+
+import org.onap.policy.apex.core.infrastructure.messaging.impl.ws.messageblock.MessageBlock;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The listener interface for receiving testMessage events. The class that is interested in processing a testMessage
+ * event implements this interface, and the object created with that class is registered with a component using the
+ * component's <code>addTestMessageListener</code> method. When the testMessage event occurs, that object's appropriate
+ * method is invoked.
+ *
+ */
+public abstract class TestMessageListener implements MessageListener<String> {
+
+    /** The Constant logger. */
+    private static final XLogger logger = XLoggerFactory.getXLogger(TestMessageListener.class);
+
+    /**
+     * On command.
+     *
+     * @param data the data
+     */
+    public abstract void onCommand(MessageBlock<String> data);
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage(org.onap.policy.apex.core.
+     * infrastructure. messaging.impl.ws.data.Data)
+     */
+    @Subscribe
+    @Override
+    public final void onMessage(final MessageBlock<String> data) {
+        if (data != null) {
+            if (logger.isDebugEnabled()) {
+                logger.debug("{} command recieved from machine {} ", data.getMessages().size(),
+                        data.getConnection().getRemoteSocketAddress().getHostString());
+            }
+            onCommand(data);
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTest.java b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTest.java
new file mode 100644 (file)
index 0000000..e570ae0
--- /dev/null
@@ -0,0 +1,92 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.threading;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Test;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class ThreadingTest.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ThreadingTest {
+
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(ThreadingTest.class);
+
+    /**
+     * Test thread factory initialization.
+     */
+    @Test
+    public void testThreadFactoryInitialization() {
+        final ApplicationThreadFactory threadFactory0 = new ApplicationThreadFactory("localName", 0);
+        assertNotNull("Failed to create ApplicationThreadFactory threadFactory0", threadFactory0);
+        logger.debug(threadFactory0.toString());
+        assertTrue("Failed to name ApplicationThreadFactory threadFactory0",
+                threadFactory0.getName().startsWith("Apex-localName"));
+        final ApplicationThreadFactory threadFactory1 = new ApplicationThreadFactory("localName", 0);
+        assertNotNull("Failed to create ApplicationThreadFactory threadFactory1", threadFactory1);
+        logger.debug(threadFactory1.toString());
+        assertTrue("Failed to name ApplicationThreadFactory threadFactory1",
+                threadFactory1.getName().startsWith("Apex-localName"));
+
+        testThreadFactory(threadFactory0, 0);
+        testThreadFactory(threadFactory1, 1);
+    }
+
+    /**
+     * Test thread factory.
+     *
+     * @param threadFactory the thread factory
+     * @param factoryId the factory id
+     */
+    private void testThreadFactory(final ApplicationThreadFactory threadFactory, final int factoryId) {
+        final List<ThreadingTestThread> threadList = new ArrayList<ThreadingTestThread>();
+
+        for (int i = 0; i < 5; i++) {
+            threadList.add(new ThreadingTestThread());
+            threadList.get(i).setThread(threadFactory.newThread(threadList.get(i)));
+            assertTrue(threadList.get(i).getName().startsWith("Apex-localName"));
+            assertTrue(threadList.get(i).getName().contains(":" + i));
+            threadList.get(i).getThread().start();
+        }
+
+        // Threads should need a little more than 300ms to count to 3
+        ThreadUtilities.sleep(380);
+
+        for (int i = 0; i < 5; i++) {
+            threadList.get(i).interrupt();
+        }
+
+        for (int i = 0; i < 5; i++) {
+            assertTrue("Thread (" + i + ") failed to get count (" + threadList.get(i).getCounter() + ") up to 3",
+                    threadList.get(i).getCounter() == 3);
+        }
+    }
+}
diff --git a/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTestThread.java b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTestThread.java
new file mode 100644 (file)
index 0000000..1950728
--- /dev/null
@@ -0,0 +1,110 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.infrastructure.threading;
+
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class ThreadingTestThread.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ThreadingTestThread implements Runnable {
+
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(ThreadingTestThread.class);
+
+    private boolean interrupted = false;
+
+    private long counter = -1;
+
+    private Thread thread = null;
+
+    /**
+     * Sets the thread.
+     *
+     * @param thread the new thread
+     */
+    public void setThread(final Thread thread) {
+        this.thread = thread;
+    }
+
+    /**
+     * Gets the thread.
+     *
+     * @return the thread
+     */
+    public Thread getThread() {
+        return thread;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Runnable#run()
+     */
+    @Override
+    public void run() {
+        if (logger.isDebugEnabled()) {
+            logger.debug("starting threading test thread \"" + thread.getName() + "\" . . .");
+        }
+
+        while (!interrupted) {
+            counter++;
+            if (logger.isDebugEnabled()) {
+                logger.debug("in threading test thread \"" + thread.getName() + "\", counter=" + counter + " . . .");
+            }
+
+            if (!ThreadUtilities.sleep(100)) {
+                interrupted = true;
+            }
+        }
+        if (logger.isDebugEnabled()) {
+            logger.debug("stopped threading test thread \"" + thread.getName() + "\"");
+        }
+    }
+
+    /**
+     * Gets the name.
+     *
+     * @return the name
+     */
+    public String getName() {
+        return thread.getName();
+    }
+
+    /**
+     * Interrupt.
+     */
+    public void interrupt() {
+        interrupted = true;
+    }
+
+    /**
+     * Gets the counter.
+     *
+     * @return the counter
+     */
+    public Long getCounter() {
+        return counter;
+    }
+}
diff --git a/core/core-infrastructure/src/test/resources/logback-test.xml b/core/core-infrastructure/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..0345113
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2016-2018 Ericsson. All rights reserved.
+  ================================================================================
+  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" />
+    <property name="LOG_DIR" value="${java.io.tmpdir}/apex_logging/" />
+
+       <!-- 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.infinispan" level="INFO" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+
+    <logger name="org.apache.zookeeper.ClientCnxn" level="OFF" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>${LOG_DIR}/apex.log</file>
+        <encoder>
+            <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level
+                %logger{26} - %msg %n %ex{full}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="CTXT_FILE" class="ch.qos.logback.core.FileAppender">
+        <file>${LOG_DIR}/apex_ctxt.log</file>
+        <encoder>
+            <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level
+                %logger{26} - %msg %n %ex{full}</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="org.onap.policy.apex.core.context.impl.monitoring" level="TRACE" additivity="false">
+        <appender-ref ref="CTXT_FILE" />
+    </logger>
+
+    <logger name="org.onap.policy.apex.core.context" level="INFO" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+</configuration>
diff --git a/core/core-protocols/pom.xml b/core/core-protocols/pom.xml
new file mode 100644 (file)
index 0000000..f308af0
--- /dev/null
@@ -0,0 +1,40 @@
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2018 Ericsson. All rights reserved.
+  ================================================================================
+  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=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onap.policy.apex-pdp.core</groupId>
+        <artifactId>core</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>core-protocols</artifactId>
+    <name>${project.artifactId}</name>
+    <description>Protocols used to transfer information between components in Apex</description>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.onap.policy.apex-pdp.model</groupId>
+            <artifactId>basic-model</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+    </dependencies>
+</project>
\ No newline at end of file
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Action.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Action.java
new file mode 100644 (file)
index 0000000..62a2e0a
--- /dev/null
@@ -0,0 +1,37 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols;
+
+/**
+ * This interface is used to enforce a common type on actions in the Apex messasging protocol. Action types the Apex
+ * messaging protocol supports implement this interface.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public interface Action {
+
+    /**
+     * This method returns a string representation of each action.
+     *
+     * @return the action string
+     */
+    String getActionString();
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Message.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Message.java
new file mode 100644 (file)
index 0000000..73465ce
--- /dev/null
@@ -0,0 +1,200 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols;
+
+import java.io.Serializable;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class Message is used to pass protocol messages between Apex components.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public abstract class Message implements Serializable {
+    private static final int HASH_PRIME = 31;
+
+    // Serialization ID
+    private static final long serialVersionUID = 2271443377544488309L;
+
+    // Default timeout on server side should be used
+    private static final int DEFAULT_REPLY_TIMEOUT = -1;
+
+    // The Action or message type of the message
+    private Action action = null;
+
+    // The artifact key of the artifact to which this message is related
+    private AxArtifactKey targetKey = null;
+
+    // The data of the message
+    private String messageData = null;
+
+    // The timeout time for replies in milliseconds
+    private int replyTimeout = DEFAULT_REPLY_TIMEOUT;
+
+    /**
+     * Instantiates a new message.
+     *
+     * @param action the action or message type of the message
+     * @param targetKey the artifact key of the artifact to which this message relates
+     */
+    public Message(final Action action, final AxArtifactKey targetKey) {
+        this(action, targetKey, null);
+    }
+
+    /**
+     * Instantiates a new message.
+     *
+     * @param action the action or message type of the message
+     * @param targetKey the artifact key of the artifact to which this message relates
+     * @param messageData the message data to deliver
+     */
+    public Message(final Action action, final AxArtifactKey targetKey, final String messageData) {
+        this.action = action;
+        this.targetKey = targetKey;
+        this.messageData = messageData;
+    }
+
+    /**
+     * Set the message timeout.
+     *
+     * @param replyTimeout the timeout on reply messages in milliseconds
+     */
+    public void setReplyTimeout(final int replyTimeout) {
+        this.replyTimeout = replyTimeout;
+    }
+
+    /**
+     * Sets the message data.
+     *
+     * @param messageData the new message data
+     */
+    public void setMessageData(final String messageData) {
+        this.messageData = messageData;
+    }
+
+    /**
+     * Append to the message data.
+     *
+     * @param newMessageData the message data
+     */
+    public void appendMessageData(final String newMessageData) {
+        if (this.messageData == null) {
+            this.messageData = newMessageData;
+        } else {
+            this.messageData += newMessageData;
+        }
+    }
+
+    /**
+     * Gets the artifact key of the target of the message.
+     *
+     * @return the target
+     */
+    public final AxArtifactKey getTarget() {
+        return targetKey;
+    }
+
+    /**
+     * Gets the artifact key name of the target of the message.
+     *
+     * @return the target name
+     */
+    public final String getTargetName() {
+        return targetKey.getName();
+    }
+
+    /**
+     * Gets the action or message type of this message.
+     *
+     * @return the action
+     */
+    public final Action getAction() {
+        return action;
+    }
+
+    /**
+     * Gets the message data.
+     *
+     * @return the message data
+     */
+    public final String getMessageData() {
+        return messageData;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final Message message = (Message) o;
+
+        if (action != null ? !action.equals(message.action) : message.action != null) {
+            return false;
+        }
+        if (targetKey != null ? !targetKey.equals(message.targetKey) : message.targetKey != null) {
+            return false;
+        }
+        return !(messageData != null ? !messageData.equals(message.messageData) : message.messageData != null);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int result = action != null ? action.hashCode() : 0;
+        result = HASH_PRIME * result + (targetKey != null ? targetKey.hashCode() : 0);
+        result = HASH_PRIME * result + (messageData != null ? messageData.hashCode() : 0);
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see java.lang.Object#toString()
+     */
+    @Override
+    public String toString() {
+        return "Message [action=" + action + ", targetKey=" + targetKey + ", data=" + messageData + "]";
+    }
+
+    /**
+     * Get the timeout to wait for a reply.
+     *
+     * @return the timeout in milliseconds
+     */
+    public int getReplyTimeout() {
+        return replyTimeout;
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/EngDepAction.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/EngDepAction.java
new file mode 100644 (file)
index 0000000..b46fe59
--- /dev/null
@@ -0,0 +1,94 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep;
+
+import org.onap.policy.apex.core.protocols.Action;
+
+/**
+ * Action types the EngDep messaging protocol supports.
+ *
+ * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com)
+ */
+public enum EngDepAction implements Action {
+    /** Action to get information on the running engine service. */
+    GET_ENGINE_SERVICE_INFO {
+        @Override
+        public String getActionString() {
+            return "Apex engine service information";
+        }
+    },
+    /** Action to update the policy model in an engine service. */
+    UPDATE_MODEL {
+        @Override
+        public String getActionString() {
+            return "update model on Apex engine service";
+        }
+    },
+    /** Action to start an engine service. */
+    START_ENGINE {
+        @Override
+        public String getActionString() {
+            return "starts an Apex engine";
+        }
+    },
+    /** Action to stop an engine service. */
+    STOP_ENGINE {
+        @Override
+        public String getActionString() {
+            return "stops an Apex engine service";
+        }
+    },
+    /** Action to start sending periodic events to an engine service. */
+    START_PERIODIC_EVENTS {
+        @Override
+        public String getActionString() {
+            return "starts periodic events on an Apex engine service";
+        }
+    },
+    /** Action to stop sending periodic events to an engine service. */
+    STOP_PERIODIC_EVENTS {
+        @Override
+        public String getActionString() {
+            return "stops periodic events on an Apex engine service";
+        }
+    },
+    /** Action to get the status of an engine in the engine service. */
+    GET_ENGINE_STATUS {
+        @Override
+        public String getActionString() {
+            return "gets the status of an Apex engine service";
+        }
+    },
+    /** Action to get information on an engine in the engine service. */
+    GET_ENGINE_INFO {
+        @Override
+        public String getActionString() {
+            return "gets runtime information an Apex engine service";
+        }
+    },
+    /** The response message to all actions. */
+    RESPONSE {
+        @Override
+        public String getActionString() {
+            return "response from Apex engine service";
+        }
+    };
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/EngineServiceInfoResponse.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/EngineServiceInfoResponse.java
new file mode 100644 (file)
index 0000000..be0bfb2
--- /dev/null
@@ -0,0 +1,124 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import java.util.Collection;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class Response is a message that holds the response by an Apex engine to another Actino message sent to that
+ * engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EngineServiceInfoResponse extends Response {
+    private static final long serialVersionUID = -7895025789667402067L;
+
+    // The engine service key
+    private AxArtifactKey engineServiceKey;
+
+    // The engines under the control of this engine service
+    private AxArtifactKey[] engineKeyArray;
+
+    // The engine service key
+    private AxArtifactKey apexModelKey;
+
+    /**
+     * Instantiates a new EngineServiceInfoResponse message.
+     *
+     * @param targetKey the target key of the entity that asked for the action that triggered this response message
+     * @param successful the successful if the action in the triggering message worked
+     * @param responseTo the message to which this message is a response
+     */
+    public EngineServiceInfoResponse(final AxArtifactKey targetKey, final boolean successful,
+            final Message responseTo) {
+        super(targetKey, successful, null, responseTo);
+    }
+
+    /**
+     * Instantiates a new EngineServiceInfoResponse message.
+     *
+     * @param targetKey the target key of the entity that asked for the action that triggered this response message
+     * @param successful the successful if the action in the triggering message worked
+     * @param messageData the message data which may indicate specific conditions for the response
+     * @param responseTo the message to which this message is a response
+     */
+    public EngineServiceInfoResponse(final AxArtifactKey targetKey, final boolean successful, final String messageData,
+            final Message responseTo) {
+        super(targetKey, successful, messageData, responseTo);
+    }
+
+    /**
+     * Gets the engine service key.
+     *
+     * @return the engine service key
+     */
+    public AxArtifactKey getEngineServiceKey() {
+        return engineServiceKey;
+    }
+
+    /**
+     * Sets the engine service key.
+     *
+     * @param engineServiceKey the engine service key
+     */
+    public void setEngineServiceKey(final AxArtifactKey engineServiceKey) {
+        this.engineServiceKey = engineServiceKey;
+    }
+
+    /**
+     * Gets the engine key array.
+     *
+     * @return the engine key array
+     */
+    public AxArtifactKey[] getEngineKeyArray() {
+        return engineKeyArray;
+    }
+
+    /**
+     * Sets the engine key array.
+     *
+     * @param engineKeyCollection the engine key array
+     */
+    public void setEngineKeyArray(final Collection<AxArtifactKey> engineKeyCollection) {
+        engineKeyArray = engineKeyCollection.toArray(new AxArtifactKey[engineKeyCollection.size()]);
+    }
+
+    /**
+     * Gets the apex model key.
+     *
+     * @return the apex model key
+     */
+    public AxArtifactKey getApexModelKey() {
+        return apexModelKey;
+    }
+
+    /**
+     * Sets the apex model key.
+     *
+     * @param apexModelKey the apex model key
+     */
+    public void setApexModelKey(final AxArtifactKey apexModelKey) {
+        this.apexModelKey = apexModelKey;
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineInfo.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineInfo.java
new file mode 100644 (file)
index 0000000..5b53856
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class GetEngineInfo is a message that requests information on Apex engines and the policies they are running.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class GetEngineInfo extends Message {
+    private static final long serialVersionUID = 5885214410842753037L;
+
+    /**
+     * Instantiates a new GetEngineInfo message.
+     *
+     * @param engineKey the key the engine for which the runtime information is requested
+     */
+    public GetEngineInfo(final AxArtifactKey engineKey) {
+        this(engineKey, null);
+    }
+
+    /**
+     * Instantiates a new GetEngineInfo message.
+     *
+     * @param engineKey the key the engine for which the runtime information is requested
+     * @param messageData the message data that may give specifics on what information to return
+     */
+    public GetEngineInfo(final AxArtifactKey engineKey, final String messageData) {
+        super(EngDepAction.GET_ENGINE_INFO, engineKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "GetEngineInfo {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineServiceInfo.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineServiceInfo.java
new file mode 100644 (file)
index 0000000..bdab1ce
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class GetEngineServiceInfo is a message that requests information on what is in an Apex engine service.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class GetEngineServiceInfo extends Message {
+    private static final long serialVersionUID = 5885214410842753037L;
+
+    /**
+     * Instantiates a new GetEngineServiceInfo message.
+     *
+     * @param nullKey not used, set to null
+     */
+    public GetEngineServiceInfo(final AxArtifactKey nullKey) {
+        this(nullKey, null);
+    }
+
+    /**
+     * Instantiates a new GetEngineServiceInfo message.
+     *
+     * @param nullKey not used, set to null
+     * @param messageData the message data that may give specifics on what information to return
+     */
+    public GetEngineServiceInfo(final AxArtifactKey nullKey, final String messageData) {
+        super(EngDepAction.GET_ENGINE_SERVICE_INFO, nullKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "GetEngineServiceInfo {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineStatus.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineStatus.java
new file mode 100644 (file)
index 0000000..59c3456
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class GetEngineInfo is a message that requests information on Apex engines and the policies they are running.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class GetEngineStatus extends Message {
+    private static final long serialVersionUID = 5885214410842753037L;
+
+    /**
+     * Instantiates a new GetEngineStatus message.
+     *
+     * @param engineKey the key of the engine for which the status information is requested
+     */
+    public GetEngineStatus(final AxArtifactKey engineKey) {
+        this(engineKey, null);
+    }
+
+    /**
+     * Instantiates a new GetEngineStatus message.
+     *
+     * @param engineKey the key of the engine for which the status information is requested
+     * @param messageData the message data that may give specifics on what information to return
+     */
+    public GetEngineStatus(final AxArtifactKey engineKey, final String messageData) {
+        super(EngDepAction.GET_ENGINE_STATUS, engineKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "GetEngineStatus {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/Response.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/Response.java
new file mode 100644 (file)
index 0000000..69e36ba
--- /dev/null
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class Response is a message that holds the response by an Apex engine to another Actino message sent to that
+ * engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class Response extends Message {
+    private static final int HASH_PRIME = 31;
+
+    private static final long serialVersionUID = -4162385039044294476L;
+
+    private boolean successful = false;
+    private Message responseTo = null;
+
+    /**
+     * Instantiates a new Response message.
+     *
+     * @param targetKey the target key of the entity that asked for the action that triggered this response message
+     * @param successful the successful if the action in the triggering message worked
+     * @param responseTo the message to which this message is a response
+     */
+    public Response(final AxArtifactKey targetKey, final boolean successful, final Message responseTo) {
+        this(targetKey, successful, null, responseTo);
+    }
+
+    /**
+     * Instantiates a new Response message.
+     *
+     * @param targetKey the target key of the entity that asked for the action that triggered this response message
+     * @param successful the successful if the action in the triggering message worked
+     * @param messageData the message data which may indicate specific conditions for the response
+     * @param responseTo the message to which this message is a response
+     */
+    public Response(final AxArtifactKey targetKey, final boolean successful, final String messageData,
+            final Message responseTo) {
+        super(EngDepAction.RESPONSE, targetKey, messageData);
+        this.successful = successful;
+        this.responseTo = responseTo;
+    }
+
+    /**
+     * Checks if the action to which this is a response was successful.
+     *
+     * @return true, if is successful
+     */
+    public boolean isSuccessful() {
+        return successful;
+    }
+
+    /**
+     * Gets the message to which this message is a response to.
+     *
+     * @return the the message to which this message is a response to
+     */
+    public Message getResponseTo() {
+        return responseTo;
+    }
+
+    /**
+     * Compare this message to another Response message.
+     *
+     * @param otherMessage the other message
+     * @return true, if successful
+     */
+    public boolean equals(final Response otherMessage) {
+        return super.equals(otherMessage) && successful == otherMessage.successful
+                && responseTo.equals(otherMessage.responseTo);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.protocols.Message#equals(java.lang.Object)
+     */
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        if (!super.equals(o)) {
+            return false;
+        }
+
+        final Response response = (Response) o;
+
+        if (successful != response.successful) {
+            return false;
+        }
+        return !(responseTo != null ? !responseTo.equals(response.responseTo) : response.responseTo != null);
+
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.protocols.Message#hashCode()
+     */
+    @Override
+    public int hashCode() {
+        int result = super.hashCode();
+        result = HASH_PRIME * result + (successful ? 1 : 0);
+        result = HASH_PRIME * result + (responseTo != null ? responseTo.hashCode() : 0);
+        return result;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "Response {" + super.toString() + "}[successful=" + successful + "]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartEngine.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartEngine.java
new file mode 100644 (file)
index 0000000..8135323
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class StartEngine is a message that requests that an Apex engine in an engine service be started.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StartEngine extends Message {
+    private static final long serialVersionUID = 5885214410842753037L;
+
+    /**
+     * Instantiates a new StartEngine message.
+     *
+     * @param engineKey the key of the engine to start
+     */
+    public StartEngine(final AxArtifactKey engineKey) {
+        this(engineKey, null);
+    }
+
+    /**
+     * Instantiates a new StartEngine message.
+     *
+     * @param engineKey the key of the engine to start
+     * @param messageData the message data that may give specifics on what way to start
+     */
+    public StartEngine(final AxArtifactKey engineKey, final String messageData) {
+        super(EngDepAction.START_ENGINE, engineKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "StartEngine {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartPeriodicEvents.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartPeriodicEvents.java
new file mode 100644 (file)
index 0000000..421669f
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class StartEngine is a message that requests that an Apex engine in an engine service be started.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StartPeriodicEvents extends Message {
+    private static final long serialVersionUID = -9172376034035242135L;
+
+    /**
+     * Instantiates a new StartEngine message.
+     *
+     * @param engineKey the key of the engine to start
+     */
+    public StartPeriodicEvents(final AxArtifactKey engineKey) {
+        this(engineKey, null);
+    }
+
+    /**
+     * Instantiates a new StartEngine message.
+     *
+     * @param engineKey the key of the engine to start
+     * @param messageData the message data that may give specifics on what way to start
+     */
+    public StartPeriodicEvents(final AxArtifactKey engineKey, final String messageData) {
+        super(EngDepAction.START_PERIODIC_EVENTS, engineKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "StartPeriodicEvents {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopEngine.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopEngine.java
new file mode 100644 (file)
index 0000000..33e66ba
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class StopEngine is a message that requests that an Apex engine in an engine service be stopped.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StopEngine extends Message {
+    private static final long serialVersionUID = 5885214410842753037L;
+
+    /**
+     * Instantiates a new StopEngine message.
+     *
+     * @param engineKey the key of the engine to stop
+     */
+    public StopEngine(final AxArtifactKey engineKey) {
+        this(engineKey, null);
+    }
+
+    /**
+     * Instantiates a new StopEngine message.
+     *
+     * @param engineKey the key of the engine to stop
+     * @param messageData the message data that may give specifics on what way to stop
+     */
+    public StopEngine(final AxArtifactKey engineKey, final String messageData) {
+        super(EngDepAction.STOP_ENGINE, engineKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "StopEngine {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopPeriodicEvents.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopPeriodicEvents.java
new file mode 100644 (file)
index 0000000..49f80ec
--- /dev/null
@@ -0,0 +1,63 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class StopEngine is a message that requests that an Apex engine in an engine service be stopped.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class StopPeriodicEvents extends Message {
+    private static final long serialVersionUID = -1796422638427413285L;
+
+    /**
+     * Instantiates a new StopEngine message.
+     *
+     * @param engineKey the key of the engine to stop
+     */
+    public StopPeriodicEvents(final AxArtifactKey engineKey) {
+        this(engineKey, null);
+    }
+
+    /**
+     * Instantiates a new StopEngine message.
+     *
+     * @param engineKey the key of the engine to stop
+     * @param messageData the message data that may give specifics on what way to stop
+     */
+    public StopPeriodicEvents(final AxArtifactKey engineKey, final String messageData) {
+        super(EngDepAction.STOP_PERIODIC_EVENTS, engineKey, messageData);
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "StopPeriodicEvents {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/UpdateModel.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/UpdateModel.java
new file mode 100644 (file)
index 0000000..8d6d315
--- /dev/null
@@ -0,0 +1,100 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
+
+import org.onap.policy.apex.core.protocols.Message;
+import org.onap.policy.apex.core.protocols.engdep.EngDepAction;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+
+/**
+ * The Class UpdateModel is a message that requests an Apex engine to update its model using the data provided in the
+ * message.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class UpdateModel extends Message {
+    private static final long serialVersionUID = 5885214410842753037L;
+
+    // The reply timeout value for update messages
+    private static final int UPDATE_MODEL_REPLY_TIMEOUT = 30000;
+
+    // Flags indicating whether conflicts in context should be ignored and whether the model should be forced even if it
+    // is incompatible
+    private boolean ignoreConflicts = false;
+    private boolean forceInstall = false;
+
+    /**
+     * Instantiates a new update model message.
+     *
+     * @param engineServiceKey the key of the engine service in which the model of all engines will be updated
+     */
+    public UpdateModel(final AxArtifactKey engineServiceKey) {
+        this(engineServiceKey, null, false, false);
+    }
+
+    /**
+     * Instantiates a new update model message.
+     *
+     * @param engineServiceKey the key of the engine service in which the model of all engines will be updated
+     * @param messageData the message data that indicates to the Apex engine the manner in which its model should be
+     *        updated
+     * @param ignoreConflicts true if conflicts between context in polices is to be ignored
+     * @param force true if the model is to be applied even if it is incompatible with the existing model
+     */
+    public UpdateModel(final AxArtifactKey engineServiceKey, final String messageData, final boolean ignoreConflicts,
+            final boolean force) {
+        super(EngDepAction.UPDATE_MODEL, engineServiceKey, messageData);
+
+        this.ignoreConflicts = ignoreConflicts;
+        this.forceInstall = force;
+
+        // Update messages have a longer timeout
+        setReplyTimeout(UPDATE_MODEL_REPLY_TIMEOUT);
+    }
+
+    /**
+     * Check if context conflicts should be ignored.
+     *
+     * @return true if conflicts should be ignored
+     */
+    public boolean isIgnoreConflicts() {
+        return ignoreConflicts;
+    }
+
+    /**
+     * Check if version checks should be overridden.
+     *
+     * @return true if version checks should be overridden
+     */
+    public boolean isForceInstall() {
+        return forceInstall;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see org.onap.policy.apex.core.model.protocols.Message#toString()
+     */
+    @Override
+    public String toString() {
+        return "UpdateModel {" + super.toString() + "}[]";
+    }
+}
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/package-info.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/package-info.java
new file mode 100644 (file)
index 0000000..3c9a5e8
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides classes that define the EngDep messages.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.protocols.engdep.messages;
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/package-info.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/package-info.java
new file mode 100644 (file)
index 0000000..5d8fd78
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides the EngDep protocol for communication between the APEX Engine and Apex deployment.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.protocols.engdep;
diff --git a/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/package-info.java b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/package-info.java
new file mode 100644 (file)
index 0000000..fceef33
--- /dev/null
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains protocols that are used for communication in APEX.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.core.protocols;
diff --git a/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetExecutionStatusTest.java b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetExecutionStatusTest.java
new file mode 100644 (file)
index 0000000..d5cf2bc
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.UnknownHostException;
+
+import org.junit.Test;
+import org.onap.policy.apex.core.protocols.engdep.messages.GetEngineStatus;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class GetExecutionStatusTest.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class GetExecutionStatusTest {
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(GetExecutionStatusTest.class);
+
+    GetEngineStatus message = null;
+
+    /**
+     * Test register entity.
+     *
+     * @throws UnknownHostException the unknown host exception
+     */
+    @Test
+    public void testRegisterEntity() throws UnknownHostException {
+        final AxArtifactKey targetKey = new AxArtifactKey("UpdateModelTest", "0.0.1");
+        message = new GetEngineStatus(targetKey);
+        assertNotNull(message);
+        logger.debug(message.toString());
+        assertTrue((message.toString()).contains("UpdateModelTest"));
+    }
+}
diff --git a/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetPolicyStatusTest.java b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetPolicyStatusTest.java
new file mode 100644 (file)
index 0000000..97cc8d5
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.UnknownHostException;
+
+import org.junit.Test;
+import org.onap.policy.apex.core.protocols.engdep.messages.GetEngineStatus;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class GetPolicyStatusTest.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class GetPolicyStatusTest {
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(GetPolicyStatusTest.class);
+
+    GetEngineStatus message = null;
+
+    /**
+     * Test register entity.
+     *
+     * @throws UnknownHostException the unknown host exception
+     */
+    @Test
+    public void testRegisterEntity() throws UnknownHostException {
+        final AxArtifactKey targetKey = new AxArtifactKey("PolicyStatusTest", "0.0.1");
+        message = new GetEngineStatus(targetKey);
+        assertNotNull(message);
+        logger.debug(message.toString());
+        assertTrue((message.toString()).contains("PolicyStatusTest"));
+    }
+}
diff --git a/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/ResponseTest.java b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/ResponseTest.java
new file mode 100644 (file)
index 0000000..74def7c
--- /dev/null
@@ -0,0 +1,65 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.net.UnknownHostException;
+
+import org.junit.Test;
+import org.onap.policy.apex.core.protocols.engdep.messages.Response;
+import org.onap.policy.apex.core.protocols.engdep.messages.UpdateModel;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class ResponseTest.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ResponseTest {
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(ResponseTest.class);
+
+    Response message = null;
+
+    /**
+     * Test response.
+     *
+     * @throws UnknownHostException the unknown host exception
+     */
+    @Test
+    public void testResponse() throws UnknownHostException {
+        final AxArtifactKey responseKey = new AxArtifactKey("ResponseTest", "0.0.1");
+        final AxArtifactKey responseToKey = new AxArtifactKey("ResponseTestTO", "0.0.1");
+        message = new Response(responseKey, false, new UpdateModel(responseToKey));
+        logger.debug(message.toString());
+        assertTrue(message.toString().contains("ResponseTest"));
+        assertFalse(message.isSuccessful());
+
+        message = new Response(responseKey, true, new UpdateModel(responseToKey));
+        logger.debug(message.toString());
+        assertTrue(message.toString().contains("ResponseTest"));
+        assertTrue(message.isSuccessful());
+    }
+}
diff --git a/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/UpdateModelTest.java b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/UpdateModelTest.java
new file mode 100644 (file)
index 0000000..ca919c3
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+package org.onap.policy.apex.core.protocols.engdep;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.net.UnknownHostException;
+
+import org.junit.Test;
+import org.onap.policy.apex.core.protocols.engdep.messages.UpdateModel;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class UpdateModelTest.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class UpdateModelTest {
+    // Logger for this class
+    private static final XLogger logger = XLoggerFactory.getXLogger(UpdateModelTest.class);
+
+    UpdateModel message = null;
+
+    /**
+     * Test register entity.
+     *
+     * @throws UnknownHostException the unknown host exception
+     */
+    @Test
+    public void testRegisterEntity() throws UnknownHostException {
+        final AxArtifactKey targetKey = new AxArtifactKey("UpdateModelTest", "0.0.1");
+        message = new UpdateModel(targetKey, new String("Placeholder for Apex model XML"), false, false);
+        assertNotNull(message);
+        logger.debug(message.toString());
+        assertTrue((message.toString()).contains("Placeholder for Apex model XML"));
+    }
+}
diff --git a/core/core-protocols/src/test/resources/logback-test.xml b/core/core-protocols/src/test/resources/logback-test.xml
new file mode 100644 (file)
index 0000000..0345113
--- /dev/null
@@ -0,0 +1,70 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2016-2018 Ericsson. All rights reserved.
+  ================================================================================
+  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" />
+    <property name="LOG_DIR" value="${java.io.tmpdir}/apex_logging/" />
+
+       <!-- 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.infinispan" level="INFO" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+
+    <logger name="org.apache.zookeeper.ClientCnxn" level="OFF" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+
+    <appender name="FILE" class="ch.qos.logback.core.FileAppender">
+        <file>${LOG_DIR}/apex.log</file>
+        <encoder>
+            <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level
+                %logger{26} - %msg %n %ex{full}</pattern>
+        </encoder>
+    </appender>
+
+    <appender name="CTXT_FILE" class="ch.qos.logback.core.FileAppender">
+        <file>${LOG_DIR}/apex_ctxt.log</file>
+        <encoder>
+            <pattern>%d %-5relative [procId=${processId}] [%thread] %-5level
+                %logger{26} - %msg %n %ex{full}</pattern>
+        </encoder>
+    </appender>
+
+    <logger name="org.onap.policy.apex.core.context.impl.monitoring" level="TRACE" additivity="false">
+        <appender-ref ref="CTXT_FILE" />
+    </logger>
+
+    <logger name="org.onap.policy.apex.core.context" level="INFO" additivity="false">
+        <appender-ref ref="STDOUT" />
+    </logger>
+</configuration>
diff --git a/core/pom.xml b/core/pom.xml
new file mode 100644 (file)
index 0000000..eac818d
--- /dev/null
@@ -0,0 +1,42 @@
+<!--
+  ============LICENSE_START=======================================================
+   Copyright (C) 2018 Ericsson. All rights reserved.
+  ================================================================================
+  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=========================================================
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.onap.policy.apex-pdp</groupId>
+        <artifactId>apex-pdp</artifactId>
+        <version>2.0.0-SNAPSHOT</version>
+    </parent>
+
+    <groupId>org.onap.policy.apex-pdp.core</groupId>
+    <artifactId>core</artifactId>
+    <packaging>pom</packaging>
+
+    <name>${project.artifactId}</name>
+    <description>The main core of Apex, deployment independent.</description>
+
+    <modules>
+        <module>core-infrastructure</module>
+        <module>core-protocols</module>
+        <module>core-engine</module>
+        <module>core-deployment</module>
+    </modules>
+</project>
\ No newline at end of file
index 2d20964..1cbe193 100644 (file)
@@ -5,15 +5,15 @@
  * 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=========================================================
  */
@@ -104,7 +104,7 @@ public class TestEngineStats {
         }
         stats.executionExit();
         final double avExecutionTime = stats.getAverageExecutionTime();
-        assertTrue(avExecutionTime >= 2.0 && avExecutionTime < 3.0);
+        assertTrue(avExecutionTime >= 2.0 && avExecutionTime < 10.0);
         stats.engineStop();
 
         AxValidationResult result = new AxValidationResult();
diff --git a/pom.xml b/pom.xml
index 5fb264f..d066e09 100644 (file)
--- a/pom.xml
+++ b/pom.xml
     <modules>
         <module>model</module>
         <module>context</module>
+        <module>core</module>
         <module>auth</module>
     </modules>
 </project>
\ No newline at end of file