diff options
author | ramverma <ram.krishna.verma@ericsson.com> | 2018-06-01 11:51:36 +0100 |
---|---|---|
committer | ramverma <ram.krishna.verma@ericsson.com> | 2018-06-04 10:50:44 +0100 |
commit | 37d6fd9069eb30d88c4ad80b5f35099ed173cc13 (patch) | |
tree | 0f30a71577644047feee43bd8857dc5a82a51c87 /core | |
parent | 5722440b2eb8ff1923dda9d4d856f0adc1ac8e6f (diff) |
Adding apex core module to apex-pdp
Change-Id: I4bfe1df3e44fe62ff6789e813e59836e267ab3b2
Issue-ID: POLICY-858
Signed-off-by: ramverma <ram.krishna.verma@ericsson.com>
Diffstat (limited to 'core')
121 files changed, 12465 insertions, 0 deletions
diff --git a/core/core-deployment/pom.xml b/core/core-deployment/pom.xml new file mode 100644 index 000000000..e267b8746 --- /dev/null +++ b/core/core-deployment/pom.xml @@ -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 index 000000000..e932bbd45 --- /dev/null +++ b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/ApexDeploymentException.java @@ -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 index 000000000..499644fd9 --- /dev/null +++ b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/BatchDeployer.java @@ -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 index 000000000..c2a19a167 --- /dev/null +++ b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/DeploymentClient.java @@ -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 index 000000000..d954feaa3 --- /dev/null +++ b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/EngineServiceFacade.java @@ -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 index 000000000..bfaece4c6 --- /dev/null +++ b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/PeriodicEventManager.java @@ -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 index 000000000..b2b7fda2d --- /dev/null +++ b/core/core-deployment/src/main/java/org/onap/policy/apex/core/deployment/package-info.java @@ -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 index 000000000..9b33974ee --- /dev/null +++ b/core/core-engine/pom.xml @@ -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 index 000000000..ff616979d --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/EngineParameters.java @@ -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 index 000000000..d3a8ed5ac --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/ExecutorParameters.java @@ -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 index 000000000..2186ee31d --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/ApexInternalContext.java @@ -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 index 000000000..887914ee0 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/context/package-info.java @@ -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 index 000000000..9e27e5e86 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/ApexEngine.java @@ -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 index 000000000..32d638aef --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/EnEventListener.java @@ -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 index 000000000..4920486de --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineFactory.java @@ -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 index 000000000..12ba76afb --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/ApexEngineImpl.java @@ -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 index 000000000..856136bab --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/StateMachineHandler.java @@ -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 index 000000000..ce86e2745 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/impl/package-info.java @@ -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 index 000000000..5c99f917e --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/engine/package-info.java @@ -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 index 000000000..b072a89d0 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnEvent.java @@ -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 index 000000000..79a65cba4 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnException.java @@ -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 index 000000000..5f12e8764 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/EnField.java @@ -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 index 000000000..e94362a75 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/event/package-info.java @@ -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 index 000000000..d659002b2 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/Executor.java @@ -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 index 000000000..1bb0b0578 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/ExecutorFactory.java @@ -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 index 000000000..ab5b66969 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateExecutor.java @@ -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 index 000000000..17691011f --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateFinalizerExecutor.java @@ -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 index 000000000..05e1b3b4b --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateMachineExecutor.java @@ -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 index 000000000..2274b7c23 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateOutput.java @@ -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 index 000000000..4a105b03f --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskExecutor.java @@ -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 index 000000000..1aaa5ccf5 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/TaskSelectExecutor.java @@ -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 index 000000000..226f06ade --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxStateFacade.java @@ -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 index 000000000..015f3ae80 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/AxTaskFacade.java @@ -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 index 000000000..2e5971142 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/StateFinalizerExecutionContext.java @@ -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 index 000000000..2251cf5c1 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskExecutionContext.java @@ -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 index 000000000..a196e360e --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/TaskSelectionExecutionContext.java @@ -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 index 000000000..6dc555107 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/context/package-info.java @@ -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 index 000000000..9d6c55900 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineException.java @@ -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 index 000000000..9ce2d7499 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/StateMachineRuntimeException.java @@ -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 index 000000000..1cb433946 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/exception/package-info.java @@ -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 index 000000000..58ee4c6cc --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/ExecutorFactoryImpl.java @@ -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 index 000000000..66e23e667 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/impl/package-info.java @@ -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 index 000000000..062e1ae49 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/package-info.java @@ -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 index 000000000..9710b106e --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/EventMonitor.java @@ -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 index 000000000..530041a23 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/monitoring/package-info.java @@ -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 index 000000000..a836cd949 --- /dev/null +++ b/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/package-info.java @@ -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 index 000000000..84548c7e2 --- /dev/null +++ b/core/core-infrastructure/pom.xml @@ -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 index 000000000..f6ef68105 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/JavaHandlingException.java @@ -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 index 000000000..919d1b122 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/ClassUtils.java @@ -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 index 000000000..c356580b3 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/classes/package-info.java @@ -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 index 000000000..5a8b51132 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/java/package-info.java @@ -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 index 000000000..f74ffa0b3 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageHolder.java @@ -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 index 000000000..c8b132423 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessageListener.java @@ -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 index 000000000..ef435b2a5 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingException.java @@ -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 index 000000000..7e91b95ea --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingService.java @@ -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 index 000000000..1d08fac74 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/MessagingServiceFactory.java @@ -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 index 000000000..534bee8af --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/RawMessageHandler.java @@ -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 index 000000000..aa951b4ec --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/WebSocketMessageListener.java @@ -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 index 000000000..9f7f89d8c --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/InternalMessageBusClient.java @@ -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 index 000000000..4a756d6f0 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/MessagingClient.java @@ -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 index 000000000..b2e0953c7 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/WebSocketClientImpl.java @@ -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 index 000000000..d5344fe14 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/client/package-info.java @@ -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 index 000000000..70b1d2c3a --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlock.java @@ -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 index 000000000..4265718db --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/MessageBlockHandler.java @@ -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 index 000000000..3fa25b56b --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/RawMessageBlock.java @@ -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 index 000000000..c01c88aed --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/messageblock/package-info.java @@ -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 index 000000000..c8811817b --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/package-info.java @@ -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 index 000000000..8e65bbf98 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/InternalMessageBusServer.java @@ -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 index 000000000..fc401576f --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/MessageServerImpl.java @@ -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 index 000000000..26acfe70c --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/WebSocketServerImpl.java @@ -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 index 000000000..0a7235b05 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/impl/ws/server/package-info.java @@ -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 index 000000000..adb17da04 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/package-info.java @@ -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 index 000000000..00ade8047 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageClient.java @@ -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 index 000000000..e524b43d7 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageListener.java @@ -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 index 000000000..4da478f6a --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessageServer.java @@ -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 index 000000000..a2781e932 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/WSStringMessager.java @@ -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 index 000000000..a8e679c70 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/stringmessaging/package-info.java @@ -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 index 000000000..d15f86c8a --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/MessagingUtils.java @@ -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 index 000000000..ccb12f2dd --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/messaging/util/package-info.java @@ -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 index 000000000..8bd0a093f --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/package-info.java @@ -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 index 000000000..45579c7ba --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ApplicationThreadFactory.java @@ -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 index 000000000..56b903f38 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/ThreadUtilities.java @@ -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 index 000000000..dc0b9ee40 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/threading/package-info.java @@ -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 index 000000000..f677e6b4a --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/XPathReader.java @@ -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 index 000000000..5631bd15c --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/apex/core/infrastructure/xml/package-info.java @@ -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 index 000000000..5765eee01 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassBuilder.java @@ -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 index 000000000..c0a2ef7a1 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassByteCodeFileObject.java @@ -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 index 000000000..9a001d2c2 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassCompilationUnit.java @@ -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 index 000000000..befed6d40 --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleClassLoader.java @@ -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 index 000000000..ce92f8e4d --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/SingleFileManager.java @@ -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 index 000000000..5e436818c --- /dev/null +++ b/core/core-infrastructure/src/main/java/org/onap/policy/core/infrastructure/java/compile/singleclass/package-info.java @@ -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 index 000000000..b1d855419 --- /dev/null +++ b/core/core-infrastructure/src/main/resources/logback.xml @@ -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 index 000000000..e18c327c0 --- /dev/null +++ b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/EndToEndStringMessagingTest.java @@ -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 index 000000000..09fa62d59 --- /dev/null +++ b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/StringTestServer.java @@ -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 index 000000000..9e7562b5e --- /dev/null +++ b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/messaging/TestMessageListener.java @@ -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 index 000000000..e570ae0c7 --- /dev/null +++ b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTest.java @@ -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 index 000000000..195072886 --- /dev/null +++ b/core/core-infrastructure/src/test/java/org/onap/policy/apex/core/infrastructure/threading/ThreadingTestThread.java @@ -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 index 000000000..034511335 --- /dev/null +++ b/core/core-infrastructure/src/test/resources/logback-test.xml @@ -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 index 000000000..f308af0c3 --- /dev/null +++ b/core/core-protocols/pom.xml @@ -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 index 000000000..62a2e0a75 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Action.java @@ -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 index 000000000..73465cef6 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/Message.java @@ -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 index 000000000..b46fe59b9 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/EngDepAction.java @@ -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 index 000000000..be0bfb2c5 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/EngineServiceInfoResponse.java @@ -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 index 000000000..5b53856d8 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineInfo.java @@ -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 index 000000000..bdab1ce9f --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineServiceInfo.java @@ -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 index 000000000..59c345647 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/GetEngineStatus.java @@ -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 index 000000000..69e36baed --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/Response.java @@ -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 index 000000000..81353236f --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartEngine.java @@ -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 index 000000000..421669f97 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StartPeriodicEvents.java @@ -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 index 000000000..33e66ba6d --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopEngine.java @@ -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 index 000000000..49f80ecb0 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/StopPeriodicEvents.java @@ -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 index 000000000..8d6d315ce --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/UpdateModel.java @@ -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 index 000000000..3c9a5e856 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/messages/package-info.java @@ -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 index 000000000..5d8fd7893 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/engdep/package-info.java @@ -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 index 000000000..fceef33e2 --- /dev/null +++ b/core/core-protocols/src/main/java/org/onap/policy/apex/core/protocols/package-info.java @@ -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 index 000000000..d5cf2bcba --- /dev/null +++ b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetExecutionStatusTest.java @@ -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 index 000000000..97cc8d5b9 --- /dev/null +++ b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/GetPolicyStatusTest.java @@ -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 index 000000000..74def7cc0 --- /dev/null +++ b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/ResponseTest.java @@ -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 index 000000000..ca919c389 --- /dev/null +++ b/core/core-protocols/src/test/java/org/onap/policy/apex/core/protocols/engdep/UpdateModelTest.java @@ -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 index 000000000..034511335 --- /dev/null +++ b/core/core-protocols/src/test/resources/logback-test.xml @@ -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 index 000000000..eac818d68 --- /dev/null +++ b/core/pom.xml @@ -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 |