diff options
Diffstat (limited to 'services/services-engine/src/main/java/org')
81 files changed, 10649 insertions, 0 deletions
diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/EngDepMessageListener.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/EngDepMessageListener.java new file mode 100644 index 000000000..311e3b660 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/EngDepMessageListener.java @@ -0,0 +1,364 @@ +/*- + * ============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.service.engine.engdep; + +import java.util.Collection; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingDeque; +import java.util.concurrent.TimeUnit; + +import org.java_websocket.WebSocket; +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.util.MessagingUtils; +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.EngDepAction; +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.service.engine.runtime.EngineService; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.common.eventbus.Subscribe; + +/** + * The listener interface for receiving engDepMessage events. The class that is interested in + * processing a engDepMessage event implements this interface, and the object created with that + * class is registered with a component using the component's <code>addEngDepMessageListener</code> + * method. When the engDepMessage event occurs, that object's appropriate method is invoked. + * + * This class uses a queue to buffer incoming messages. When the listener is called, it places the + * incoming message on the queue. A thread runs which removes the messages from the queue and + * forwards them to the Apex engine. + * + * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com) + */ +public class EngDepMessageListener implements MessageListener<Message>, Runnable { + private static final int LISTENER_STOP_WAIT_INTERVAL = 10; + + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EngDepMessageListener.class); + + // The timeout to wait between queue poll timeouts in milliseconds + private static final long QUEUE_POLL_TIMEOUT = 50; + + // The Apex service itself + private final EngineService apexService; + + // The message listener thread and stopping flag + private Thread messageListenerThread; + private boolean stopOrderedFlag = false; + + // The message queue is used to hold messages prior to forwarding to Apex + private final BlockingQueue<MessageBlock<Message>> messageQueue = new LinkedBlockingDeque<>(); + + /** + * Instantiates a new EngDep message listener for listening for messages coming in from the + * Deployment client. The <code>apexService</code> is the Apex service to send the messages + * onto. + * + * @param apexService the Apex engine service + */ + protected EngDepMessageListener(final EngineService apexService) { + this.apexService = apexService; + } + + /** + * This method is an implementation of the message listener. It receives a message and places it + * on the queue for processing by the message listening thread. + * + * @param data the data + * @see org.onap.policy.apex.core.infrastructure.messaging.MessageListener#onMessage + * (org.onap.policy.apex.core.infrastructure.messaging.impl.ws.data.Data) + */ + @Subscribe + @Override + public void onMessage(final MessageBlock<Message> data) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("message received from client application {} port {}", + data.getConnection().getRemoteSocketAddress().getAddress(), + data.getConnection().getRemoteSocketAddress().getPort()); + } + messageQueue.add(data); + } + + /* + * (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 messages are not supported on the EngDep protocol"); + } + + /** + * This method gets a new message listening thread from the thread factory and starts it. + */ + public void startProcessorThread() { + LOGGER.entry(); + messageListenerThread = new Thread(this); + messageListenerThread.setDaemon(true); + messageListenerThread.start(); + LOGGER.exit(); + } + + /** + * Stops the message listening threads. + */ + public void stopProcessorThreads() { + LOGGER.entry(); + stopOrderedFlag = true; + + while (messageListenerThread.isAlive()) { + ThreadUtilities.sleep(LISTENER_STOP_WAIT_INTERVAL); + } + LOGGER.exit(); + } + + /** + * Runs the message listening thread. Here, the messages come in on the message queue and are + * processed one by one + */ + @Override + public void run() { + // Take messages off the queue and forward them to the Apex engine + while (messageListenerThread.isAlive() && !stopOrderedFlag) { + try { + final MessageBlock<Message> data = messageQueue.poll(QUEUE_POLL_TIMEOUT, TimeUnit.MILLISECONDS); + if (data != null) { + final List<Message> messages = data.getMessages(); + for (final Message message : messages) { + handleMessage(message, data.getConnection()); + } + } + } catch (final InterruptedException e) { + LOGGER.debug("message listener execution has been interrupted"); + break; + } + } + } + + /** + * This method handles EngDep messages as they come in. It uses the inevitable switch statement + * to handle the messages. + * + * @param message the incoming EngDep message + * @param webSocket the web socket on which the message came in + */ + private void handleMessage(final Message message, final WebSocket webSocket) { + LOGGER.entry(webSocket.getRemoteSocketAddress().toString()); + if (message.getAction() == null) { + // This is a response message + return; + } + + try { + LOGGER.debug("Manager action {} being applied to engine", message.getAction()); + + // Get and check the incoming action for validity + EngDepAction enDepAction = null; + if (message.getAction() instanceof EngDepAction) { + enDepAction = (EngDepAction) message.getAction(); + } else { + throw new ApexException(message.getAction().getClass().getName() + + "action on received message invalid, action must be of type \"EnDepAction\""); + } + + // Handle each incoming message using the inevitable switch statement for the EngDep + // protocol + switch (enDepAction) { + case GET_ENGINE_SERVICE_INFO: + final GetEngineServiceInfo engineServiceInformationMessage = (GetEngineServiceInfo) message; + LOGGER.debug("getting engine service information for engine service " + apexService.getKey().getID() + + " . . ."); + // Send a reply with the engine service information + sendServiceInfoReply(webSocket, engineServiceInformationMessage, apexService.getKey(), + apexService.getEngineKeys(), apexService.getApexModelKey()); + LOGGER.debug( + "returned engine service information for engine service " + apexService.getKey().getID()); + break; + + case UPDATE_MODEL: + final UpdateModel updateModelMessage = (UpdateModel) message; + LOGGER.debug("updating model in engine {} . . .", updateModelMessage.getTarget().getID()); + // Update the model + apexService.updateModel(updateModelMessage.getTarget(), updateModelMessage.getMessageData(), + updateModelMessage.isForceInstall()); + // Send a reply indicating the message action worked + sendReply(webSocket, updateModelMessage, true, + "updated model in engine " + updateModelMessage.getTarget().getID()); + LOGGER.debug("updated model in engine service {}", updateModelMessage.getTarget().getID()); + break; + + case START_ENGINE: + final StartEngine startEngineMessage = (StartEngine) message; + LOGGER.debug("starting engine {} . . .", startEngineMessage.getTarget().getID()); + // Start the engine + apexService.start(startEngineMessage.getTarget()); + // Send a reply indicating the message action worked + sendReply(webSocket, startEngineMessage, true, + "started engine " + startEngineMessage.getTarget().getID()); + LOGGER.debug("started engine {}", startEngineMessage.getTarget().getID()); + break; + + case STOP_ENGINE: + final StopEngine stopEngineMessage = (StopEngine) message; + LOGGER.debug("stopping engine {} . . .", stopEngineMessage.getTarget().getID()); + // Stop the engine + apexService.stop(stopEngineMessage.getTarget()); + // Send a reply indicating the message action worked + sendReply(webSocket, stopEngineMessage, true, + "stopped engine " + stopEngineMessage.getTarget().getID()); + LOGGER.debug("stopping engine {}", stopEngineMessage.getTarget().getID()); + break; + + case START_PERIODIC_EVENTS: + final StartPeriodicEvents startPeriodicEventsMessage = (StartPeriodicEvents) message; + LOGGER.debug("starting periodic events on engine {} . . .", + startPeriodicEventsMessage.getTarget().getID()); + // Start periodic events with the period specified in the message + final Long period = Long.parseLong(startPeriodicEventsMessage.getMessageData()); + apexService.startPeriodicEvents(period); + // Send a reply indicating the message action worked + sendReply(webSocket, startPeriodicEventsMessage, true, "started periodic events on engine " + + startPeriodicEventsMessage.getTarget().getID() + " with period " + period); + LOGGER.debug("started periodic events on engine " + startPeriodicEventsMessage.getTarget().getID() + + " with period " + period); + break; + + case STOP_PERIODIC_EVENTS: + final StopPeriodicEvents stopPeriodicEventsMessage = (StopPeriodicEvents) message; + LOGGER.debug("stopping periodic events on engine {} . . .", + stopPeriodicEventsMessage.getTarget().getID()); + // Stop periodic events + apexService.stopPeriodicEvents(); + // Send a reply indicating the message action worked + sendReply(webSocket, stopPeriodicEventsMessage, true, + "stopped periodic events on engine " + stopPeriodicEventsMessage.getTarget().getID()); + LOGGER.debug("stopped periodic events on engine " + stopPeriodicEventsMessage.getTarget().getID()); + break; + + case GET_ENGINE_STATUS: + final GetEngineStatus getEngineStatusMessage = (GetEngineStatus) message; + LOGGER.debug("getting status for engine{} . . .", getEngineStatusMessage.getTarget().getID()); + // Send a reply with the engine status + sendReply(webSocket, getEngineStatusMessage, true, + apexService.getStatus(getEngineStatusMessage.getTarget())); + LOGGER.debug("returned status for engine {}", getEngineStatusMessage.getTarget().getID()); + break; + + case GET_ENGINE_INFO: + final GetEngineInfo getEngineInfo = (GetEngineInfo) message; + LOGGER.debug("getting runtime information for engine {} . . .", getEngineInfo.getTarget().getID()); + // Send a reply with the engine runtime information + sendReply(webSocket, getEngineInfo, true, apexService.getRuntimeInfo(getEngineInfo.getTarget())); + LOGGER.debug("returned runtime information for engine {}", getEngineInfo.getTarget().getID()); + break; + case RESPONSE: + throw new ApexException("RESPONSE action on received message not handled by engine"); + + default: + break; + } + } catch (final ApexException e) { + LOGGER.warn("apex failed to execute message", e); + sendReply(webSocket, message, false, e.getCascadedMessage()); + } catch (final Exception e) { + LOGGER.warn("system failure executing message", e); + sendReply(webSocket, message, false, e.getMessage()); + } + LOGGER.exit(); + } + + /** + * Send the Response message to the client. + * + * @param client the client to which to send the response message + * @param requestMessage the message to which we are responding + * @param result the result indicating success or failure + * @param messageData the message data + */ + private void sendReply(final WebSocket client, final Message requestMessage, final boolean result, + final String messageData) { + LOGGER.entry(result, messageData); + + if (client == null || !client.isOpen()) { + LOGGER.debug("error sending reply {}, client has disconnected", requestMessage.getAction()); + return; + } + + LOGGER.debug("sending {} to web socket {}", requestMessage.getAction(), + client.getRemoteSocketAddress().toString()); + + final Response responseMessage = new Response(requestMessage.getTarget(), result, requestMessage); + responseMessage.setMessageData(messageData); + + final MessageHolder<Message> messageHolder = new MessageHolder<>(MessagingUtils.getHost()); + messageHolder.addMessage(responseMessage); + client.send(MessagingUtils.serializeObject(messageHolder)); + + LOGGER.exit(); + } + + /** + * Send the EngineServiceInfoResponse message to the client. + * + * @param client the client to which to send the response message + * @param requestMessage the message to which we are responding + * @param engineServiceKey The key of this engine service + * @param engineKeyCollection The keys of the engines in this engine service + * @param apexModelKey the apex model key + */ + private void sendServiceInfoReply(final WebSocket client, final Message requestMessage, + final AxArtifactKey engineServiceKey, final Collection<AxArtifactKey> engineKeyCollection, + final AxArtifactKey apexModelKey) { + LOGGER.entry(); + LOGGER.debug("sending {} to web socket {}", requestMessage.getAction(), + client.getRemoteSocketAddress().toString()); + + final EngineServiceInfoResponse responseMessage = + new EngineServiceInfoResponse(requestMessage.getTarget(), true, requestMessage); + responseMessage.setMessageData("engine service information"); + responseMessage.setEngineServiceKey(engineServiceKey); + responseMessage.setEngineKeyArray(engineKeyCollection); + responseMessage.setApexModelKey(apexModelKey); + + final MessageHolder<Message> messageHolder = new MessageHolder<>(MessagingUtils.getHost()); + messageHolder.addMessage(responseMessage); + client.send(MessagingUtils.serializeObject(messageHolder)); + + LOGGER.exit(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/EngDepMessagingService.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/EngDepMessagingService.java new file mode 100644 index 000000000..86589ac81 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/EngDepMessagingService.java @@ -0,0 +1,107 @@ +/*- + * ============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.service.engine.engdep; + +import java.net.InetSocketAddress; + +import org.onap.policy.apex.service.engine.runtime.EngineService; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +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.util.MessagingUtils; +import org.onap.policy.apex.core.protocols.Message; + +/** + * The Class EngDepMessagingService is used to encapsulate the server side of EngDep communication. + * This class allows users to create and start an EngDep server. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EngDepMessagingService { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EngDepMessagingService.class); + + // Messaging service is used to transmit and receive messages over a communication protocol + private static MessagingServiceFactory<Message> messageServiceFactory = new MessagingServiceFactory<>(); + private final MessagingService<Message> messageService; + + // The listener that is listening for messages coming in on the EngDep protocol from clients + private final EngDepMessageListener messageListener; + + /** + * Instantiates a new EngDep messaging service. It creates the message service instance, a + * listener for incoming messages, and starts the message listener thread for handling incoming + * messages. + * + * @param service the Apex engine service that this EngDep service is running for + * @param port the port The port to use for EngDep communication + */ + public EngDepMessagingService(final EngineService service, final int port) { + LOGGER.entry(service); + + // Create the service and listener and add the listener. + messageService = messageServiceFactory.createServer(new InetSocketAddress(MessagingUtils.checkPort(port))); + messageListener = new EngDepMessageListener(service); + messageService.addMessageListener(messageListener); + + // Start incoming message processing on the listener + messageListener.startProcessorThread(); + LOGGER.exit(); + } + + /** + * Start the server, open the communication mechanism for connections. + */ + public void start() { + LOGGER.info("engine<-->deployment messaging starting . . ."); + messageService.startConnection(); + LOGGER.info("engine<-->deployment messaging started"); + } + + /** + * Start the server, close the communication mechanism. + */ + public void stop() { + LOGGER.info("engine<-->deployment messaging stopping . . ."); + messageService.stopConnection(); + messageListener.stopProcessorThreads(); + LOGGER.info("engine<-->deployment messaging stopped"); + } + + /** + * Is the server started?. + * + * @return true, if checks if is started + */ + public boolean isStarted() { + return messageService.isStarted(); + } + + /** + * Is the server stopped?. + * + * @return true, if checks if is stopped + */ + public boolean isStopped() { + return !messageService.isStarted(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/package-info.java new file mode 100644 index 000000000..41f644465 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/engdep/package-info.java @@ -0,0 +1,26 @@ +/*- + * ============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========================================================= + */ + +/** + * Gives access to the APEX EngDep protocol for APEX engine management at runtime over a Java API. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.engdep; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEvent.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEvent.java new file mode 100644 index 000000000..552f949a2 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEvent.java @@ -0,0 +1,342 @@ +/*- + * ============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.service.engine.event; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * The Class ApexEvent is an event class that external systems use to send events to and receive events from Apex engines. The event itself is a hash map of + * string keys and object values, used to pass data. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexEvent extends HashMap<String, Object> implements Serializable { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexEvent.class); + + private static final long serialVersionUID = -4451918242101961685L; + + // Holds the next identifier for event execution. + private static AtomicLong nextExecutionID = new AtomicLong(0L); + + /** The name of the Apex event, a mandatory field. All Apex events must have a name so that the event can be looked up in the Apex policy model. */ + public static final String NAME_HEADER_FIELD = "name"; + + /** + * The version of the Apex event, an optional field. If a version is specified on an Apex event, the definition of that version of the event is taken from + * the Apex policy model. If no version is specified, the latest version of the event is used. + */ + public static final String VERSION_HEADER_FIELD = "version"; + + /** + * The name space of the Apex event, an optional field. If a name space is specified on an Apex event it must match the name space on the event definition + * taken from the Apex policy model. If no name space is specified, the name space from the event definition in the Apex policy model is used. + */ + public static final String NAMESPACE_HEADER_FIELD = "nameSpace"; + + /** + * The source of the Apex event, an optional field. It specifies where the Apex event has come from and its use is reserved for now. If no source is + * specified, the source from the event definition in the Apex policy model is used. + */ + public static final String SOURCE_HEADER_FIELD = "source"; + + /** + * The target of the Apex event, an optional field. It specifies where the Apex event is going to and its use is reserved for now. If no target is + * specified, the target from the event definition in the Apex policy model is used. + */ + public static final String TARGET_HEADER_FIELD = "target"; + + /** + * The exception message field of an Apex event is an exception message indicating that an event failed. + */ + public static final String EXCEPTION_MESSAGE_HEADER_FIELD = "exceptionMessage"; + + /** The name of an Apex event must match this regular expression. */ + public static final String NAME_REGEXP = "[A-Za-z0-9\\-_.]+"; + + /** The version of an Apex event must match this regular expression. */ + public static final String VERSION_REGEXP = "[A-Za-z0-9.]+"; + + /** The name space of an Apex event must match this regular expression. */ + public static final String NAMESPACE_REGEXP = "([a-zA_Z_][\\.\\w]*)"; + + /** The source of an Apex event must match this regular expression. */ + public static final String SOURCE_REGEXP = "^$|[A-Za-z0-9\\.\\-_:]+"; + + /** The target of an Apex event must match this regular expression. */ + public static final String TARGET_REGEXP = "^$|[A-Za-z0-9\\.\\-_:]+"; + + // The fields of the event + // @formatter:off + private final String name; + private final String version; + private final String nameSpace; + private final String source; + private final String target; + // @formatter:on + + // An identifier for the current event execution. The default value here will always be unique in a single JVM + private long executionID = ApexEvent.getNextExecutionID(); + + // A string holding a message that indicates why processing of this event threw an exception + private String exceptionMessage; + + /** + * Private utility to get the next candidate value for a Execution ID. This value will always be unique in a single JVM + * + * @return the next candidate value for a Execution ID + */ + private static synchronized long getNextExecutionID() { + return nextExecutionID.getAndIncrement(); + } + + /** + * Instantiates a new apex event. + * + * @param name the name of the event + * @param version the version of the event + * @param nameSpace the name space (java package) of the event + * @param source the source of the event + * @param target the target of the event + * @throws ApexEventException thrown on validation errors on event names and versions + */ + public ApexEvent(final String name, final String version, final String nameSpace, final String source, final String target) throws ApexEventException { + // @formatter:off + this.name = validateField("name", name, NAME_REGEXP); + this.version = validateField("version", version, VERSION_REGEXP); + this.nameSpace = validateField("nameSpace", nameSpace, NAMESPACE_REGEXP); + this.source = validateField("source", source, SOURCE_REGEXP); + this.target = validateField("target", target, TARGET_REGEXP); + // @formatter:on + } + + /** + * Check that a field of the event is valid. + * + * @param fieldName the name of the field to check + * @param fieldValue the value of the field to check + * @param fieldRegexp the regular expression to check the field against + * @return the validated field value + * @throws ApexEventException thrown if the field is invalid + */ + private String validateField(final String fieldName, final String fieldValue, final String fieldRegexp) throws ApexEventException { + if (fieldValue.matches(fieldRegexp)) { + return fieldValue; + } + else { + LOGGER.warn("event \"" + name + ": field \"" + fieldName + "=" + fieldValue + "\" is illegal. It doesn't match regex '" + fieldRegexp + "'"); + throw new ApexEventException("event \"" + name + ": field \"" + fieldName + "=" + fieldValue + "\" is illegal"); + } + } + + /** + * Check that the key of an event is valid. + * + * @param key the key + * @return the string + * @throws ApexEventException the apex event exception + */ + private String validKey(final String key) throws ApexEventException { + if (key.matches(NAME_REGEXP)) { + return key; + } + else { + LOGGER.warn("event \"" + name + ": key \"" + key + "\" is illegal"); + throw new ApexEventException("event \"" + name + ": key \"" + key + "\" is illegal"); + } + } + + /** + * Gets the name. + * + * @return the name + */ + public String getName() { + return name; + } + + /** + * Gets the version. + * + * @return the version + */ + public String getVersion() { + return version; + } + + /** + * Gets the name space. + * + * @return the name space + */ + public String getNameSpace() { + return nameSpace; + } + + /** + * Gets the source. + * + * @return the source + */ + public String getSource() { + return source; + } + + /** + * Gets the target. + * + * @return the target + */ + public String getTarget() { + return target; + } + + /** + * Gets the pass-thru executionID for this event. + * + * @return the executionID + */ + public long getExecutionID() { + return executionID; + } + + /** + * Sets the pass-thru executionID for this event. The default value for executionID will be be unique in the current JVM. For some applications/deployments + * this executionID may need to globally unique + * + * @param executionID the executionID + */ + 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; + } + + /* + * Map overrides from here + */ + + /* + * (non-Javadoc) + * + * @see java.util.Map#put(java.lang.Object, java.lang.Object) + */ + @Override + public Object put(final String key, final Object value) { + // Check if the key is valid + try { + return super.put(validKey(key), value); + } + catch (final ApexEventException e) { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see java.util.Map#putAll(java.util.Map) + */ + @Override + public void putAll(final Map<? extends String, ? extends Object> incomingMap) { + // Check the keys are valid + try { + for (final String key : incomingMap.keySet()) { + validKey(key); + } + } + catch (final ApexEventException e) { + // One of the keys is invalid + return; + } + + // Go ahead and put everything + super.putAll(incomingMap); + } + + /* + * Object overrides from here + */ + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append("name="); + builder.append(name); + builder.append(",version="); + builder.append(version); + builder.append(",nameSpace="); + builder.append(nameSpace); + builder.append(",source="); + builder.append(source); + builder.append(",target="); + builder.append(target); + builder.append(",executionID="); + builder.append(executionID); + builder.append(",exceptionMessage="); + builder.append(exceptionMessage); + builder.append(","); + builder.append("["); + + boolean firstData = true; + for (final Map.Entry<String, Object> dataEntry : this.entrySet()) { + if (firstData) { + firstData = false; + } + else { + builder.append(','); + } + + builder.append(dataEntry.getKey()); + builder.append('='); + builder.append(dataEntry.getValue()); + } + + builder.append("]"); + return builder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventConsumer.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventConsumer.java new file mode 100644 index 000000000..53f11dd61 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventConsumer.java @@ -0,0 +1,80 @@ +/*- + * ============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.service.engine.event; + +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; + +/** + * This interface is used by technology specific consumers and listeners that are are listening for + * or collecting events for input into Apex. Users specify the consumer technology to use in the + * Apex configuration and Apex uses a factory to start the appropriate consumer plugin that + * implements this interface for its input. The technology specific implementation details are + * hidden behind this interface. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface ApexEventConsumer { + /** + * Initialize the consumer. + * + * @param name a name for this consumer + * @param consumerParameters the parameters to initialize this consumer + * @param apexEventReceiver the apex event receiver that should be used to pass events received + * by the consumer into Apex + * @throws ApexEventException container exception on errors initializing event handling + */ + void init(String name, EventHandlerParameters consumerParameters, ApexEventReceiver apexEventReceiver) + throws ApexEventException; + + /** + * Start the consumer, start input of events into Apex. + */ + void start(); + + /** + * Get the peered reference object for this consumer. + * + * @param peeredMode the peered mode for which to return the reference + * @return the peered reference object for this consumer + */ + PeeredReference getPeeredReference(EventHandlerPeeredMode peeredMode); + + /** + * Set the peered reference object for this consumer. + * + * @param peeredMode the peered mode for which to return the reference + * @param peeredReference the peered reference object for this consumer + */ + void setPeeredReference(EventHandlerPeeredMode peeredMode, PeeredReference peeredReference); + + /** + * Get the name of this event consumer. + * + * @return the event consumer name + */ + String getName(); + + /** + * Stop the event consumer. + */ + void stop(); +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventConverter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventConverter.java new file mode 100644 index 000000000..11f005ddf --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventConverter.java @@ -0,0 +1,55 @@ +/*- + * ============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.service.engine.event; + +import java.util.List; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; + +/** + * The Interface ApexEventConverter is used for applications that want to convert arbitrary event + * types to and from Apex events. Application implement this interface to convert their events to + * and from Apex events.The Apex service can then use this interface to transparently transfer + * events into and out of an Apex system. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface ApexEventConverter { + + /** + * Convert an event of arbitrary type into an Apex event. + * + * @param name the name of the incoming event + * @param eventOfOtherType the event of some other type to convert + * @return the apex event + * @throws ApexException thrown on conversion errors + */ + List<ApexEvent> toApexEvent(String name, Object eventOfOtherType) throws ApexException; + + /** + * Convert an Apex event into an event of arbitrary type {@code OTHER_EVENT_TYPE}. + * + * @param apexEvent the apex event to convert + * @return the event converted into the other type + * @throws ApexException thrown on conversion errors + */ + Object fromApexEvent(ApexEvent apexEvent) throws ApexException; +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventException.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventException.java new file mode 100644 index 000000000..24f57d741 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventException.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.service.engine.event; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; + +/** + * This class will be called if an error occurs in handling Apex events. + * + * @author eeilfn + */ +public class ApexEventException extends ApexException { + private static final long serialVersionUID = -4245694568321686450L; + + /** + * Instantiates a new apex event exception. + * + * @param message the message + */ + public ApexEventException(final String message) { + super(message); + } + + /** + * Instantiates a new apex event exception. + * + * @param message the message + * @param e the e + */ + public ApexEventException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventList.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventList.java new file mode 100644 index 000000000..9fe03ef47 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventList.java @@ -0,0 +1,32 @@ +/*- + * ============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.service.engine.event; + +import java.util.ArrayList; + +/** + * The Class ApexEventList holds a list of APEX events. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexEventList extends ArrayList<ApexEvent> { + private static final long serialVersionUID = -8496211897512202896L; +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventProducer.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventProducer.java new file mode 100644 index 000000000..414fbc9e3 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventProducer.java @@ -0,0 +1,81 @@ +/*- + * ============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.service.engine.event; + +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; + +/** + * This interface is used by technology specific producers and publishers that are handling events + * output by Apex. Users specify the producer technology to use in the Apex configuration and Apex + * uses a factory to start the appropriate producer plugin that implements this interface for its + * output. The technology specific implementation details are hidden behind this interface. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface ApexEventProducer { + + /** + * Initialize the producer. + * + * @param name a name for this producer + * @param producerParameters the parameters to initialise this producer + * @throws ApexEventException exception on errors initializing an event producer + */ + void init(String name, EventHandlerParameters producerParameters) throws ApexEventException; + + /** + * Get the peered reference object for this producer. + * + * @param peeredMode the peered mode for which to return the reference + * @return the peered reference object for this producer + */ + PeeredReference getPeeredReference(EventHandlerPeeredMode peeredMode); + + /** + * Set the peered reference object for this producer. + * + * @param peeredMode the peered mode for which to return the reference + * @param peeredReference the peered reference object for this producer + */ + void setPeeredReference(EventHandlerPeeredMode peeredMode, PeeredReference peeredReference); + + /** + * Send an event to the producer. + * + * @param executionId the unique ID that produced this event + * @param eventName The name of the event + * @param event The converted event as an object + */ + void sendEvent(long executionId, String eventName, Object event); + + /** + * Get the name of this event producer. + * + * @return the event producer name + */ + String getName(); + + /** + * Stop the event producer. + */ + void stop(); +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventProtocolConverter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventProtocolConverter.java new file mode 100644 index 000000000..ec19e65c3 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventProtocolConverter.java @@ -0,0 +1,39 @@ +/*- + * ============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.service.engine.event; + +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; + +/** + * The Interface ApexEventProtocolConverter extends ApexEventConverter to allow + * EventProtocolParameters conversion parameters to be passed to the converter. + * + * @author John Keeney (john.keeney@ericsson.com) + */ +public interface ApexEventProtocolConverter extends ApexEventConverter { + + /** + * Initialise the converter instance with the parameters for the EventProtocol. + * + * @param parameters the parameters for the EventProtocol + */ + void init(EventProtocolParameters parameters); +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventReceiver.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventReceiver.java new file mode 100644 index 000000000..8d7e7bae5 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventReceiver.java @@ -0,0 +1,46 @@ +/*- + * ============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.service.engine.event; + +/** + * This interface is used by an Apex event consumer {@link ApexEventConsumer} consumer to pass a + * received event to Apex. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface ApexEventReceiver { + /** + * Receive an event from a consumer for processing. + * + * @param executionId the unique ID for execution of this event + * @param event the event to receive + * @throws ApexEventException on exceptions receiving an event into Apex + */ + void receiveEvent(long executionId, Object event) throws ApexEventException; + + /** + * Receive an event from a consumer for processing. + * + * @param event the event to receive + * @throws ApexEventException on exceptions receiving an event into Apex + */ + void receiveEvent(Object event) throws ApexEventException; +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventRuntimeException.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventRuntimeException.java new file mode 100644 index 000000000..1a624face --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexEventRuntimeException.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.service.engine.event; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException; + +/** + * This exception will be called if a runtime error occurs in Apex event handling. + * + * @author Liam Fallon + */ +public class ApexEventRuntimeException extends ApexRuntimeException { + private static final long serialVersionUID = -8507246953751956974L; + + /** + * Instantiates a new apex runtime event exception with a message. + * + * @param message the message + */ + public ApexEventRuntimeException(final String message) { + super(message); + } + + /** + * Instantiates a new apex runtime event exception with a message and a caused by exception. + * + * @param message the message + * @param e the exception that caused this exception to be thrown + */ + public ApexEventRuntimeException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexPeriodicEventGenerator.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexPeriodicEventGenerator.java new file mode 100644 index 000000000..62663b9f1 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/ApexPeriodicEventGenerator.java @@ -0,0 +1,176 @@ +/*- + * ============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.service.engine.event; + +import java.util.HashMap; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; + +import org.onap.policy.apex.service.engine.runtime.EngineServiceEventInterface; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class is used to generate periodic events into an Apex engine service. It is used to trigger + * policies that perform housekeeping operations. + * + * @author eeilfn + */ +public class ApexPeriodicEventGenerator extends TimerTask { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexPeriodicEventGenerator.class); + + /** The name of the periodic event. */ + public static final String PERIODIC_EVENT_NAME = "PERIODIC_EVENT"; + + /** The version of the periodic event. */ + public static final String PERIODIC_EVENT_VERSION = "0.0.1"; + + /** The name space of the periodic event. */ + public static final String PERIODIC_EVENT_NAMESPACE = "com.ericsson.apex.service.engine.event"; + + /** The source of the periodic event. */ + public static final String PERIODIC_EVENT_SOURCE = "internal"; + + /** The target of the periodic event. */ + public static final String PERIODIC_EVENT_TARGET = "internal"; + + /** + * The field name in the periodic event for the delay between occurrences of the periodic event. + */ + public static final String PERIODIC_DELAY = "PERIODIC_DELAY"; + + /** + * The field name in the periodic event for the time at which the first periodic event will + * occur. + */ + public static final String PERIODIC_FIRST_TIME = "PERIODIC_FIRST_TIME"; + + /** + * The field name in the periodic event for the time at which the last periodic event will + * occur. + */ + public static final String PERIODIC_LAST_TIME = "PERIODIC_LAST_TIME"; + + /** The field name in the periodic event for the time at which the event was sent. */ + public static final String PERIODIC_CURRENT_TIME = "PERIODIC_CURRENT_TIME"; + + /** + * The field name in the periodic event for the number of occurrences of this event that have + * been sent to date, this is a sequence number for the periodic event. + */ + public static final String PERIODIC_EVENT_COUNT = "PERIODIC_EVENT_COUNT"; + + // The Java timer used to send periodic events + private Timer timer = null; + + // The engine service interface we'll send periodic events to + private final EngineServiceEventInterface engineServiceEventInterface; + + // Timing information + private long period = 0; + private long firstEventTime = 0; + private long lastEventTime = 0; + private long eventCount = 0; + + /** + * Constructor, save a reference to the event stream handler. + * + * @param engineServiceEventInterface the engine service event interface on which to send + * periodic events + * @param period The period in milliseconds between events + */ + public ApexPeriodicEventGenerator(final EngineServiceEventInterface engineServiceEventInterface, + final long period) { + // Save the engine service reference and delay + this.engineServiceEventInterface = engineServiceEventInterface; + this.period = period; + + timer = new Timer(ApexPeriodicEventGenerator.class.getSimpleName(), true); + timer.schedule(this, period, period); + } + + /** + * Output the metrics for stream loading. + */ + @Override + public void run() { + final Map<String, Object> periodicEventMap = new HashMap<>(); + + // Record the current event time + final long currentEventTime = System.currentTimeMillis(); + + // Check if this is the first periodic event + if (firstEventTime == 0) { + firstEventTime = currentEventTime; + lastEventTime = currentEventTime; + } + + // Increment the event counter + eventCount++; + + // Set the fields in the periodic event + periodicEventMap.put(PERIODIC_DELAY, period); + periodicEventMap.put(PERIODIC_FIRST_TIME, firstEventTime); + periodicEventMap.put(PERIODIC_LAST_TIME, lastEventTime); + periodicEventMap.put(PERIODIC_CURRENT_TIME, currentEventTime); + periodicEventMap.put(PERIODIC_EVENT_COUNT, eventCount); + + // Send the periodic event + try { + final ApexEvent periodicEvent = new ApexEvent(PERIODIC_EVENT_NAME, PERIODIC_EVENT_VERSION, + PERIODIC_EVENT_NAMESPACE, PERIODIC_EVENT_SOURCE, PERIODIC_EVENT_TARGET); + periodicEvent.putAll(periodicEventMap); + engineServiceEventInterface.sendEvent(periodicEvent); + } catch (final ApexEventException e) { + LOGGER.warn("could not send Apex periodic event " + PERIODIC_EVENT_NAME + ":" + PERIODIC_EVENT_VERSION, e); + return; + } + + // Save the current time as the last time + lastEventTime = currentEventTime; + } + + /** + * Cancel the timer. + * + * @return true, if cancel + */ + @Override + public boolean cancel() { + // Cancel the timer + if (timer != null) { + timer.cancel(); + } + return true; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "ApexPeriodicEventGenerator [period=" + period + ", firstEventTime=" + firstEventTime + + ", lastEventTime=" + lastEventTime + ", eventCount=" + eventCount + "]"; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/PeeredReference.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/PeeredReference.java new file mode 100644 index 000000000..9560a834c --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/PeeredReference.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.service.engine.event; + +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; + +/** + * This class holds a reference to an event consumer and producer that have been peered. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class PeeredReference { + // The consumer putting events into APEX + private final ApexEventConsumer peeredConsumer; + + // The synchronous producer taking events out of APEX + private final ApexEventProducer peeredProducer; + + /** + * Create a peered consumer/producer reference + * + * @param peeredMode the peered mode for which to return the reference + * @param consumer the consumer that is receiving event + * @param producer the producer that is sending events + */ + public PeeredReference(final EventHandlerPeeredMode peeredMode, final ApexEventConsumer consumer, final ApexEventProducer producer) { + this.peeredConsumer = consumer; + this.peeredProducer = producer; + + // Set the peered reference on the producer and consumer + peeredConsumer.setPeeredReference(peeredMode, this); + peeredProducer.setPeeredReference(peeredMode, this); + } + + /** + * Gets the synchronous consumer putting events into the cache. + * + * @return the source synchronous consumer + */ + public ApexEventConsumer getPeeredConsumer() { + return peeredConsumer; + } + + /** + * Gets the synchronous producer taking events from the cache. + * + * @return the synchronous producer that is taking events from the cache + */ + public ApexEventProducer getPeeredProducer() { + return peeredProducer; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/SynchronousEventCache.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/SynchronousEventCache.java new file mode 100644 index 000000000..25f92d843 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/SynchronousEventCache.java @@ -0,0 +1,294 @@ +/*- + * ============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.service.engine.event; + +import java.util.AbstractMap.SimpleEntry; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class holds a cache of the synchronous events sent into Apex and that have not yet been replied to. It runs a thread to time out events that have not + * been replied to in the specified timeout. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class SynchronousEventCache extends PeeredReference implements Runnable { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(SynchronousEventCache.class); + + // The default amount of time to wait for a synchronous event to be replied to is 1 second + private static final long DEFAULT_SYNCHRONOUS_EVENT_TIMEOUT = 1000; + + // The timeout to wait between event polls in milliseconds and the time to wait for the thread to stop + private static final long OUTSTANDING_EVENT_POLL_TIMEOUT = 50; + private static final long CACHE_STOP_WAIT_INTERVAL = 10; + + // The time in milliseconds to wait for the reply to a sent synchronous event + private long synchronousEventTimeout = DEFAULT_SYNCHRONOUS_EVENT_TIMEOUT; + + // Map holding outstanding synchronous events + private final Map<Long, SimpleEntry<Long, Object>> toApexEventMap = new HashMap<Long, SimpleEntry<Long, Object>>(); + + // Map holding reply events + private final Map<Long, SimpleEntry<Long, Object>> fromApexEventMap = new HashMap<Long, SimpleEntry<Long, Object>>(); + + // The message listener thread and stopping flag + private final Thread synchronousEventCacheThread; + private boolean stopOrderedFlag = false; + + /** + * Create a synchronous event cache that caches outstanding synchronous Apex events. + * + * @param peeredMode the peered mode for which to return the reference + * @param consumer the consumer that is populating the cache + * @param producer the producer that is emptying the cache + * @param synchronousEventTimeout the time in milliseconds to wait for the reply to a sent synchronous event + */ + public SynchronousEventCache(final EventHandlerPeeredMode peeredMode, final ApexEventConsumer consumer, final ApexEventProducer producer, final long synchronousEventTimeout) { + super(peeredMode, consumer, producer); + + if (synchronousEventTimeout != 0) { + this.synchronousEventTimeout = synchronousEventTimeout; + } + else { + this.synchronousEventTimeout = DEFAULT_SYNCHRONOUS_EVENT_TIMEOUT; + } + + // Start scanning the outstanding events + synchronousEventCacheThread = new Thread(this); + synchronousEventCacheThread.setDaemon(true); + synchronousEventCacheThread.start(); + } + + /** + * Gets the timeout value for synchronous events. + * + * @return the synchronous event timeout + */ + public long getSynchronousEventTimeout() { + return synchronousEventTimeout; + } + + /** + * Cache a synchronized event sent into Apex in the event cache. + * + * @param executionId the execution ID that was assigned to the event + * @param event the apex event + */ + public void cacheSynchronizedEventToApex(final long executionId, final Object event) { + // Add the event to the map + synchronized (toApexEventMap) { + cacheSynchronizedEvent(toApexEventMap, executionId, event); + } + } + + /** + * Remove the record of an event sent to Apex if it exists in the cache. + * + * @param executionId the execution ID of the event + * @return The removed event + */ + public Object removeCachedEventToApexIfExists(final long executionId) { + synchronized (toApexEventMap) { + return removeCachedEventIfExists(toApexEventMap, executionId); + } + } + + /** + * Check if an event exists in the to apex cache. + * + * @param executionId the execution ID of the event + * @return true if the event exists, false otherwise + */ + public boolean existsEventToApex(final long executionId) { + synchronized (toApexEventMap) { + return toApexEventMap.containsKey(executionId); + } + } + + /** + * Cache synchronized event received from Apex in the event cache. + * + * @param executionId the execution ID of the event + * @param event the apex event + */ + public void cacheSynchronizedEventFromApex(final long executionId, final Object event) { + // Add the event to the map + synchronized (fromApexEventMap) { + cacheSynchronizedEvent(fromApexEventMap, executionId, event); + } + } + + /** + * Remove the record of an event received from Apex if it exists in the cache. + * + * @param executionId the execution ID of the event + * @return The removed event + */ + public Object removeCachedEventFromApexIfExists(final long executionId) { + synchronized (fromApexEventMap) { + return removeCachedEventIfExists(fromApexEventMap, executionId); + } + } + + /** + * Check if an event exists in the from apex cache. + * + * @param executionId the execution ID of the event + * @return true if the event exists, false otherwise + */ + public boolean existsEventFromApex(final long executionId) { + synchronized (fromApexEventMap) { + return fromApexEventMap.containsKey(executionId); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + LOGGER.entry(); + + // Periodic scan of outstanding events + while (synchronousEventCacheThread.isAlive() && !stopOrderedFlag) { + ThreadUtilities.sleep(OUTSTANDING_EVENT_POLL_TIMEOUT); + + // Check for timeouts on events + synchronized (toApexEventMap) { + timeoutEventsOnCache(toApexEventMap); + } + synchronized (fromApexEventMap) { + timeoutEventsOnCache(fromApexEventMap); + } + } + + LOGGER.exit(); + } + + /** + * Stops the scanning thread and clears the cache. + */ + public synchronized void stop() { + LOGGER.entry(); + stopOrderedFlag = true; + + while (synchronousEventCacheThread.isAlive()) { + ThreadUtilities.sleep(CACHE_STOP_WAIT_INTERVAL); + } + + // Check if there are any unprocessed events + if (!toApexEventMap.isEmpty()) { + LOGGER.warn(toApexEventMap.size() + " synchronous events dropped due to system shutdown"); + } + + toApexEventMap.clear(); + LOGGER.exit(); + } + + /** + * Cache a synchronized event sent in an event cache. + * @param eventCacheMap the map to cache the event on + * @param executionId the execution ID of the event + * @param event the event to cache + */ + private void cacheSynchronizedEvent(final Map<Long, SimpleEntry<Long, Object>> eventCacheMap, final long executionId, final Object event) { + LOGGER.entry("Adding event with execution ID: " + executionId); + + // Check if the event is already in the cache + if (eventCacheMap.containsKey(executionId)) { + // If there was no sent event then the event timed out or some unexpected event was received + final String errorMessage = "an event with ID " + executionId + + " already exists in the synchronous event cache, execution IDs must be unique in the system"; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + + // Add the event to the map + eventCacheMap.put(executionId, new SimpleEntry<Long, Object>(System.currentTimeMillis(), event)); + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("event has been cached:" + event); + } + + LOGGER.exit("Added: " + executionId); + } + + /** + * Remove the record of an event if it exists in the cache. + * + * @param eventCacheMap the map to remove the event from + * @param executionId the execution ID of the event + * @return The removed event + */ + private Object removeCachedEventIfExists(final Map<Long, SimpleEntry<Long, Object>> eventCacheMap, final long executionId) { + LOGGER.entry("Removing: " + executionId); + + final SimpleEntry<Long, Object> removedEventEntry = eventCacheMap.remove(executionId); + + if (removedEventEntry != null) { + LOGGER.exit("Removed: " + executionId); + return removedEventEntry.getValue(); + } + else { + // The event may not be one of the events in our cache, so we just ignore removal failures + return null; + } + } + + /** + * Time out events on an event cache map. Events that have a timeout longer than the configured timeout are timed out. + * @param eventCacheMap the event cache to operate on + */ + private void timeoutEventsOnCache(final Map<Long, SimpleEntry<Long, Object>> eventCacheMap) { + // Use a set to keep track of the events that have timed out + final Set<Long> timedOutEventSet = new HashSet<>(); + + for (final Entry<Long, SimpleEntry<Long, Object>> cachedEventEntry : eventCacheMap.entrySet()) { + // The amount of time we are waiting for the event reply + final long eventWaitTime = System.currentTimeMillis() - cachedEventEntry.getValue().getKey(); + + // Have we a timeout? + if (eventWaitTime > synchronousEventTimeout) { + timedOutEventSet.add(cachedEventEntry.getKey()); + } + } + + // Remove timed out events from the map + for (final long timedoutEventExecutionID : timedOutEventSet) { + // Remove the map entry and issue a warning + final SimpleEntry<Long, Object> timedOutEventEntry = eventCacheMap.remove(timedoutEventExecutionID); + + LOGGER.warn("synchronous event timed out, reply not received in " + synchronousEventTimeout + " milliseconds on event " + + timedOutEventEntry.getValue()); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventConsumerFactory.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventConsumerFactory.java new file mode 100644 index 000000000..8f54c049b --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventConsumerFactory.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.service.engine.event.impl; + +import org.onap.policy.apex.service.engine.event.ApexEventConsumer; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This factory class creates event consumers of various technology types for Apex engines. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventConsumerFactory { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventConsumerFactory.class); + + /** + * Empty constructor with no generic overloading. + */ + public EventConsumerFactory() {} + + /** + * Create an event consumer of the required type for the specified consumer technology. + * + * @param name the name of the consumer + * @param consumerParameters The parameters for the Apex engine, we use the technology type of + * the required consumer + * @return the event consumer + * @throws ApexEventException on errors creating the Apex event consumer + */ + public ApexEventConsumer createConsumer(final String name, final EventHandlerParameters consumerParameters) + throws ApexEventException { + // Get the carrier technology parameters + final CarrierTechnologyParameters technologyParameters = consumerParameters.getCarrierTechnologyParameters(); + + // Get the class for the event consumer using reflection + final String consumerPluginClass = technologyParameters.getEventConsumerPluginClass(); + Object consumerPluginObject = null; + try { + consumerPluginObject = Class.forName(consumerPluginClass).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + final String errorMessage = "could not create an Apex event consumer for \"" + name + + "\" for the carrier technology \"" + technologyParameters.getLabel() + + "\", specified event consumer plugin class \"" + consumerPluginClass + "\" not found"; + LOGGER.error(errorMessage, e); + throw new ApexEventException(errorMessage, e); + } + + // Check the class is an event consumer + if (!(consumerPluginObject instanceof ApexEventConsumer)) { + final String errorMessage = "could not create an Apex event consumer \"" + name + + "\" for the carrier technology \"" + technologyParameters.getLabel() + + "\", specified event consumer plugin class \"" + consumerPluginClass + + "\" is not an instance of \"" + ApexEventConsumer.class.getCanonicalName() + "\""; + LOGGER.error(errorMessage); + throw new ApexEventException(errorMessage); + } + + return (ApexEventConsumer) consumerPluginObject; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventProducerFactory.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventProducerFactory.java new file mode 100644 index 000000000..9bbbad362 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventProducerFactory.java @@ -0,0 +1,82 @@ +/*- + * ============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.service.engine.event.impl; + +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProducer; +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This factory class creates event producers for the defined technology type for Apex engines. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventProducerFactory { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventProducerFactory.class); + + /** + * Empty constructor with no generic overloading. + */ + public EventProducerFactory() {} + + /** + * Create an event producer of the required type for the specified producer technology. + * + * @param name the name of the producer + * @param producerParameters The Apex parameters containing the configuration for the producer + * @return the event producer + * @throws ApexEventException on errors creating the Apex event producer + */ + public ApexEventProducer createProducer(final String name, final EventHandlerParameters producerParameters) + throws ApexEventException { + // Get the carrier technology parameters + final CarrierTechnologyParameters technologyParameters = producerParameters.getCarrierTechnologyParameters(); + + // Get the class for the event producer using reflection + final String producerPluginClass = technologyParameters.getEventProducerPluginClass(); + Object producerPluginObject = null; + try { + producerPluginObject = Class.forName(producerPluginClass).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + final String errorMessage = "could not create an Apex event producer for Producer \"" + name + + "\" for the carrier technology \"" + technologyParameters.getLabel() + + "\", specified event producer plugin class \"" + producerPluginClass + "\" not found"; + LOGGER.error(errorMessage, e); + throw new ApexEventException(errorMessage, e); + } + + // Check the class is an event producer + if (!(producerPluginObject instanceof ApexEventProducer)) { + final String errorMessage = "could not create an Apex event producer for Producer \"" + name + + "\" for the carrier technology \"" + technologyParameters.getLabel() + + "\", specified event producer plugin class \"" + producerPluginClass + + "\" is not an instance of \"" + ApexEventProducer.class.getCanonicalName() + "\""; + LOGGER.error(errorMessage); + throw new ApexEventException(errorMessage); + } + + return (ApexEventProducer) producerPluginObject; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventProtocolFactory.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventProtocolFactory.java new file mode 100644 index 000000000..85c5bf03f --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/EventProtocolFactory.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.service.engine.event.impl; + +import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This factory class uses the Apex event protocol parameters to create and return an instance of + * the correct Apex event protocol converter plugin for the specified event protocol. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventProtocolFactory { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventProtocolFactory.class); + + /** + * Create an event converter that converts between an + * {@link org.onap.policy.apex.service.engine.event.ApexEvent} and the specified event protocol. + * + * @param name the name of the event protocol + * @param eventProtocolParameters the event protocol parameters defining what to convert from + * and to + * @return The event converter for converting events to and from Apex format + */ + public ApexEventProtocolConverter createConverter(final String name, + final EventProtocolParameters eventProtocolParameters) { + // Get the class for the event protocol plugin using reflection + final String eventProtocolPluginClass = eventProtocolParameters.getEventProtocolPluginClass(); + Object eventProtocolPluginObject = null; + try { + eventProtocolPluginObject = Class.forName(eventProtocolPluginClass).newInstance(); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + final String errorMessage = "could not create an Apex event protocol converter for \"" + name + + "\" for the protocol \"" + eventProtocolParameters.getLabel() + + "\", specified event protocol converter plugin class \"" + eventProtocolPluginClass + + "\" not found"; + LOGGER.error(errorMessage, e); + throw new ApexEventRuntimeException(errorMessage, e); + } + + // Check the class is an event consumer + if (!(eventProtocolPluginObject instanceof ApexEventProtocolConverter)) { + final String errorMessage = "could not create an Apex event protocol converter for \"" + name + + "\" for the protocol \"" + eventProtocolParameters.getLabel() + + "\", specified event protocol converter plugin class \"" + eventProtocolPluginClass + + "\" is not an instance of \"" + ApexEventProtocolConverter.class.getCanonicalName() + "\""; + LOGGER.error(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + ((ApexEventProtocolConverter) eventProtocolPluginObject).init(eventProtocolParameters); + return (ApexEventProtocolConverter) eventProtocolPluginObject; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/Apex2ApexEventConverter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/Apex2ApexEventConverter.java new file mode 100644 index 000000000..b73aeb567 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/Apex2ApexEventConverter.java @@ -0,0 +1,140 @@ +/*- + * ============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.service.engine.event.impl.apexprotocolplugin; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventList; +import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * The Class Apex2ApexEventConverter passes through {@link ApexEvent} instances. It is used for + * transferring Apex events directly as POJOs between APEX producers and consumers. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class Apex2ApexEventConverter implements ApexEventProtocolConverter { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(Apex2ApexEventConverter.class); + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter#init(org.onap.policy. + * apex. service.parameters.eventprotocol.EventProtocolParameters) + */ + @Override + public void init(final EventProtocolParameters parameters) { + // Check and get the APEX parameters + if (!(parameters instanceof ApexEventProtocolParameters)) { + final String errorMessage = "specified consumer properties are not applicable to the APEX event protocol"; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String, + * java.lang.Object) + */ + @Override + public List<ApexEvent> toApexEvent(final String eventName, final Object eventObject) throws ApexEventException { + // Check the event eventObject + if (eventObject == null) { + LOGGER.warn("event processing failed, event is null"); + throw new ApexEventException("event processing failed, event is null"); + } + + // The list of events we will return + final List<ApexEvent> eventList = new ArrayList<>(); + + try { + // Check if its a single APEX event + if (!(eventObject instanceof ApexEvent)) { + throw new ApexEventException("incoming event (" + eventObject + ") is not an ApexEvent"); + } + + final ApexEvent event = (ApexEvent) eventObject; + + // Check whether we have any ApexEventList fields, if so this is an event of events and + // all fields should be of type ApexEventList + boolean foundEventListFields = false; + boolean foundOtherFields = false; + for (final Object fieldObject : event.values()) { + if (fieldObject instanceof ApexEventList) { + foundEventListFields = true; + + // Add the events to the event list + eventList.addAll((ApexEventList) fieldObject); + } else { + foundOtherFields = true; + } + } + + // If we found both event list fields and other fields we're in trouble + if (foundEventListFields && foundOtherFields) { + throw new ApexEventException("incoming event (" + eventObject + + ") has both event list fields and other fields, it cannot be processed"); + } + + // Check if the incoming event just has other fields, if so it's just a regular event + // and we add it to the event list as the only event there + if (foundOtherFields) { + eventList.add(event); + } + } catch (final Exception e) { + final String errorString = "Failed to unmarshal APEX event: " + e.getMessage() + ", event=" + eventObject; + LOGGER.warn(errorString, e); + throw new ApexEventException(errorString, e); + } + + // Return the list of events we have unmarshalled + return eventList; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy. + * apex.service.engine.event.ApexEvent) + */ + @Override + public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException { + // Check the Apex event + if (apexEvent == null) { + LOGGER.warn("event processing failed, Apex event is null"); + throw new ApexEventException("event processing failed, Apex event is null"); + } + + return apexEvent; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/ApexEventProtocolParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/ApexEventProtocolParameters.java new file mode 100644 index 000000000..10cd58eb7 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/ApexEventProtocolParameters.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.service.engine.event.impl.apexprotocolplugin; + +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; + +/** + * Event protocol parameters for JSON as an event protocol, there are no user defined parameters. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexEventProtocolParameters extends EventProtocolParameters { + /** The label of this event protocol. */ + public static final String APEX_EVENT_PROTOCOL_LABEL = "APEX"; + + /** + * Constructor to create a JSON event protocol parameter instance and register the instance with + * the parameter service. + */ + public ApexEventProtocolParameters() { + this(ApexEventProtocolParameters.class.getCanonicalName(), APEX_EVENT_PROTOCOL_LABEL); + } + + /** + * Constructor to create an event protocol parameters instance with the name of a sub class of + * this class. + * + * @param parameterClassName the class name of a sub class of this class + * @param eventProtocolLabel the name of the event protocol for this plugin + */ + public ApexEventProtocolParameters(final String parameterClassName, final String eventProtocolLabel) { + super(parameterClassName); + + // Set the event protocol properties for the JSON event protocol + this.setLabel(eventProtocolLabel); + + // Set the event protocol plugin class + this.setEventProtocolPluginClass(Apex2ApexEventConverter.class.getCanonicalName()); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/package-info.java new file mode 100644 index 000000000..a3c7d0d79 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/apexprotocolplugin/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 the implementation of the APEX event protocol converter plugin for events in Json + * format. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.apexprotocolplugin; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/enevent/ApexEvent2EnEventConverter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/enevent/ApexEvent2EnEventConverter.java new file mode 100644 index 000000000..90a19fff2 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/enevent/ApexEvent2EnEventConverter.java @@ -0,0 +1,143 @@ +/*- + * ============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.service.engine.event.impl.enevent; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexEventConverter; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import org.onap.policy.apex.core.engine.engine.ApexEngine; +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.service.ModelService; +import org.onap.policy.apex.model.eventmodel.concepts.AxEvent; +import org.onap.policy.apex.model.eventmodel.concepts.AxEvents; + +/** + * The Class ApexEvent2EnEventConverter converts externally facing {@link ApexEvent} instances to + * and from instances of {@link EnEvent} that are used internally in the Apex engine core. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public final class ApexEvent2EnEventConverter implements ApexEventConverter { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexEvent2EnEventConverter.class); + + // The Apex engine with its event definitions + private final ApexEngine apexEngine; + + /** + * Set up the event converter. + * + * @param apexEngine The engine to use to create events to be converted + */ + public ApexEvent2EnEventConverter(final ApexEngine apexEngine) { + this.apexEngine = apexEngine; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String, + * java.lang.Object) + */ + @Override + public List<ApexEvent> toApexEvent(final String eventName, final Object event) throws ApexException { + // Check the Engine event + if (event == null) { + LOGGER.warn("event processing failed, engine event is null"); + throw new ApexEventException("event processing failed, engine event is null"); + } + + // Cast the event to an Engine event event, if our conversion is correctly configured, this + // cast should always work + EnEvent enEvent = null; + try { + enEvent = (EnEvent) event; + } catch (final Exception e) { + final String errorMessage = "error transferring event \"" + event + "\" to the Apex engine"; + LOGGER.debug(errorMessage, e); + throw new ApexEventRuntimeException(errorMessage, e); + } + + // Create the Apex event + final AxEvent axEvent = enEvent.getAxEvent(); + final ApexEvent apexEvent = new ApexEvent(axEvent.getKey().getName(), axEvent.getKey().getVersion(), + axEvent.getNameSpace(), axEvent.getSource(), axEvent.getTarget()); + + // Copy the ExecutionID from the EnEvent into the ApexEvent + apexEvent.setExecutionID(enEvent.getExecutionID()); + + // Copy he exception message to the Apex event if it is set + if (enEvent.getExceptionMessage() != null) { + apexEvent.setExceptionMessage(enEvent.getExceptionMessage()); + } + + // Set the data on the apex event + apexEvent.putAll(enEvent); + + // Return the event in a single element + final ArrayList<ApexEvent> eventList = new ArrayList<ApexEvent>(); + eventList.add(apexEvent); + return eventList; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy. + * apex.service.engine.event.ApexEvent) + */ + @Override + public EnEvent fromApexEvent(final ApexEvent apexEvent) throws ApexException { + // Check the Apex model + if (apexEngine == null) { + LOGGER.warn("event processing failed, apex engine is null"); + throw new ApexEventException("event processing failed, apex engine is null"); + } + + // Get the event definition + final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName()); + if (eventDefinition == null) { + LOGGER.warn("event processing failed, event \"" + apexEvent.getName() + "\" not found in apex model"); + throw new ApexEventException( + "event processing failed, event \"" + apexEvent.getName() + "\" not found in apex model"); + } + + // Create the internal engine event + final EnEvent enEvent = apexEngine.createEvent(eventDefinition.getKey()); + + // Set the data on the engine event + enEvent.putAll(apexEvent); + + // copy the ExecutionID from the ApexEvent into the EnEvent + enEvent.setExecutionID(apexEvent.getExecutionID()); + + return enEvent; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/enevent/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/enevent/package-info.java new file mode 100644 index 000000000..6bc6bc2b3 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/enevent/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 conversion between externally facing + * {@link org.onap.policy.apex.service.engine.event.ApexEvent} instances and internal + * {@link org.onap.policy.apex.core.engine.event.EnEvent} instances. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.enevent; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorCarrierTechnologyParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorCarrierTechnologyParameters.java new file mode 100644 index 000000000..fb722ea2f --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorCarrierTechnologyParameters.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.service.engine.event.impl.eventrequestor; + +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; + +/** + * This class holds the parameters that allows an output event to to be sent back into APEX as one + * or multiple input events, there are no user defined parameters. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventRequestorCarrierTechnologyParameters extends CarrierTechnologyParameters { + // @formatter:off + /** The label of this carrier technology. */ + public static final String EVENT_REQUESTOR_CARRIER_TECHNOLOGY_LABEL = "EVENT_REQUESTOR"; + + /** The producer plugin class for the EVENT_REQUESTOR carrier technology. */ + public static final String EVENT_REQUESTOR_EVENT_PRODUCER_PLUGIN_CLASS = + EventRequestorProducer.class.getCanonicalName(); + + /** The consumer plugin class for the EVENT_REQUESTOR carrier technology. */ + public static final String EVENT_REQUESTOR_EVENT_CONSUMER_PLUGIN_CLASS = + EventRequestorConsumer.class.getCanonicalName(); + // @formatter:on + + /** + * Constructor to create an event requestor carrier technology parameters instance and register + * the instance with the parameter service. + */ + public EventRequestorCarrierTechnologyParameters() { + super(EventRequestorCarrierTechnologyParameters.class.getCanonicalName()); + + // Set the carrier technology properties for the EVENT_REQUESTOR carrier technology + this.setLabel(EVENT_REQUESTOR_CARRIER_TECHNOLOGY_LABEL); + this.setEventProducerPluginClass(EVENT_REQUESTOR_EVENT_PRODUCER_PLUGIN_CLASS); + this.setEventConsumerPluginClass(EVENT_REQUESTOR_EVENT_CONSUMER_PLUGIN_CLASS); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + return ""; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorConsumer.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorConsumer.java new file mode 100644 index 000000000..b472cc9c7 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorConsumer.java @@ -0,0 +1,218 @@ +/*- + * ============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.service.engine.event.impl.eventrequestor; + +import java.util.EnumMap; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.service.engine.event.ApexEventConsumer; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventReceiver; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.engine.event.PeeredReference; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class implements an Apex event consumer that receives events from its peered event requestor + * producer. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventRequestorConsumer implements ApexEventConsumer, Runnable { + // Get a reference to the logger + private static final Logger LOGGER = LoggerFactory.getLogger(EventRequestorConsumer.class); + + // The amount of time to wait in milliseconds between checks that the consumer thread has + // stopped + private static final long EVENT_REQUESTOR_WAIT_SLEEP_TIME = 50; + + // The event receiver that will receive events from this consumer + private ApexEventReceiver eventReceiver; + + // The name for this consumer + private String name = null; + + // The peer references for this event handler + private final Map<EventHandlerPeeredMode, PeeredReference> peerReferenceMap = + new EnumMap<>(EventHandlerPeeredMode.class); + + // Temporary request holder for incoming event send requests + private final BlockingQueue<Object> incomingEventRequestQueue = new LinkedBlockingQueue<>(); + + // The consumer thread and stopping flag + private Thread consumerThread; + private boolean stopOrderedFlag = false; + + // The number of events received to date + private int eventsReceived = 0; + + @Override + public void init(final String consumerName, final EventHandlerParameters consumerParameters, + final ApexEventReceiver incomingEventReceiver) throws ApexEventException { + this.eventReceiver = incomingEventReceiver; + this.name = consumerName; + + // Check and get the event requestor consumer properties + if (!(consumerParameters + .getCarrierTechnologyParameters() instanceof EventRequestorCarrierTechnologyParameters)) { + final String errorMessage = + "specified consumer properties are not applicable to event Requestor consumer (" + this.name + ")"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + + // Check if we are in peered mode + if (!consumerParameters.isPeeredMode(EventHandlerPeeredMode.REQUESTOR)) { + final String errorMessage = "event Requestor consumer (" + this.name + + ") must run in peered requestor mode with a event Requestor producer"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + + } + + /** + * Receive an incoming event send request from the peered event Requestor producer and queue it + * + * @param eventObject the incoming event to process + * @throws ApexEventRuntimeException on queueing errors + */ + public void processEvent(final Object eventObject) { + // Push the event onto the queue for handling + try { + incomingEventRequestQueue.add(eventObject); + } catch (final Exception e) { + final String errorMessage = + "could not queue request \"" + eventObject + "\" on event Requestor consumer (" + this.name + ")"; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#start() + */ + @Override + public void start() { + // Configure and start the event reception thread + final String threadName = this.getClass().getName() + ":" + this.name; + consumerThread = new ApplicationThreadFactory(threadName).newThread(this); + consumerThread.setDaemon(true); + consumerThread.start(); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#getName() + */ + @Override + public String getName() { + return name; + } + + /** + * Get the number of events received to date + * + * @return the number of events received + */ + public int getEventsReceived() { + return eventsReceived; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#getPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode) + */ + @Override + public PeeredReference getPeeredReference(final EventHandlerPeeredMode peeredMode) { + return peerReferenceMap.get(peeredMode); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#setPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode, + * org.onap.policy.apex.service.engine.event.PeeredReference) + */ + @Override + public void setPeeredReference(final EventHandlerPeeredMode peeredMode, final PeeredReference peeredReference) { + peerReferenceMap.put(peeredMode, peeredReference); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + // The endless loop that receives events using REST calls + while (consumerThread.isAlive() && !stopOrderedFlag) { + try { + // Take the next event from the queue + final Object eventObject = + incomingEventRequestQueue.poll(EVENT_REQUESTOR_WAIT_SLEEP_TIME, TimeUnit.MILLISECONDS); + if (eventObject == null) { + // Poll timed out, wait again + continue; + } + + // Send the event into Apex + eventReceiver.receiveEvent(eventObject); + + eventsReceived++; + } catch (final InterruptedException e) { + LOGGER.debug("Thread interrupted, Reason {}", e.getMessage()); + Thread.currentThread().interrupt(); + } catch (final Exception e) { + LOGGER.warn("error receiving events on thread {}", consumerThread.getName(), e); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.producer.ApexEventConsumer#stop() + */ + @Override + public void stop() { + stopOrderedFlag = true; + + while (consumerThread.isAlive()) { + ThreadUtilities.sleep(EVENT_REQUESTOR_WAIT_SLEEP_TIME); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorProducer.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorProducer.java new file mode 100644 index 000000000..4a972f2ce --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/EventRequestorProducer.java @@ -0,0 +1,178 @@ +/*- + * ============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.service.engine.event.impl.eventrequestor; + +import java.util.EnumMap; +import java.util.Map; + +import org.onap.policy.apex.service.engine.event.ApexEventConsumer; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProducer; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.engine.event.PeeredReference; +import org.onap.policy.apex.service.engine.event.SynchronousEventCache; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Concrete implementation of an Apex event producer that sends one or more events to its peered + * event requestor consumer. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + * + */ +public class EventRequestorProducer implements ApexEventProducer { + private static final Logger LOGGER = LoggerFactory.getLogger(EventRequestorProducer.class); + + // The name for this producer + private String name = null; + + // The peer references for this event handler + private final Map<EventHandlerPeeredMode, PeeredReference> peerReferenceMap = + new EnumMap<>(EventHandlerPeeredMode.class); + + // The number of events sent + private int eventsSent = 0; + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#init(java.lang.String, + * org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters) + */ + @Override + public void init(final String producerName, final EventHandlerParameters producerParameters) + throws ApexEventException { + this.name = producerName; + + // Check and get the producer Properties + if (!(producerParameters + .getCarrierTechnologyParameters() instanceof EventRequestorCarrierTechnologyParameters)) { + final String errorMessage = + "specified consumer properties are not applicable to event requestor producer (" + this.name + ")"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + + // Check if we are in peered mode + if (!producerParameters.isPeeredMode(EventHandlerPeeredMode.REQUESTOR)) { + final String errorMessage = "Event Requestor producer (" + this.name + + ") must run in peered requestor mode with a Event Requestor consumer"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#getName() + */ + @Override + public String getName() { + return name; + } + + /** + * Get the number of events sent to date + * + * @return the number of events received + */ + public int getEventsSent() { + return eventsSent; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#getPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode) + */ + @Override + public PeeredReference getPeeredReference(final EventHandlerPeeredMode peeredMode) { + return peerReferenceMap.get(peeredMode); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#setPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode, + * org.onap.policy.apex.service.engine.event.PeeredReference) + */ + @Override + public void setPeeredReference(final EventHandlerPeeredMode peeredMode, final PeeredReference peeredReference) { + peerReferenceMap.put(peeredMode, peeredReference); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#sendEvent(long, java.lang. + * String, java.lang.Object) + */ + @Override + public void sendEvent(final long executionId, final String eventName, final Object eventObject) { + // Check if this is a synchronized event, if so we have received a reply + final SynchronousEventCache synchronousEventCache = + (SynchronousEventCache) peerReferenceMap.get(EventHandlerPeeredMode.SYNCHRONOUS); + if (synchronousEventCache != null) { + synchronousEventCache.removeCachedEventToApexIfExists(executionId); + } + + // Find the peered consumer for this producer + final PeeredReference peeredRequestorReference = peerReferenceMap.get(EventHandlerPeeredMode.REQUESTOR); + if (peeredRequestorReference != null) { + // Find the event Response Consumer that will handle this request + final ApexEventConsumer consumer = peeredRequestorReference.getPeeredConsumer(); + if (!(consumer instanceof EventRequestorConsumer)) { + final String errorMessage = "send of event to event consumer \"" + + peeredRequestorReference.getPeeredConsumer() + "\" failed," + + " event response consumer is not an instance of EventRequestorConsumer\n" + eventObject; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + + // Use the consumer to handle this event + final EventRequestorConsumer eventRequstConsumer = (EventRequestorConsumer) consumer; + eventRequstConsumer.processEvent(eventObject); + + eventsSent++; + } else { + // No peered consumer defined + final String errorMessage = "send of event failed, event response consumer is not defined\n" + eventObject; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#stop() + */ + @Override + public void stop() { + // For event requestor, all the implementation is in the consumer + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/package-info.java new file mode 100644 index 000000000..3b6da08a4 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/eventrequestor/package-info.java @@ -0,0 +1,26 @@ +/*- + * ============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 the Event Requestor carrier technology for multiple event input from an output event. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.eventrequestor; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/FILECarrierTechnologyParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/FILECarrierTechnologyParameters.java new file mode 100644 index 000000000..f7f25cb9e --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/FILECarrierTechnologyParameters.java @@ -0,0 +1,208 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin; + +import org.onap.policy.apex.model.utilities.ResourceUtils; +import org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.consumer.ApexFileEventConsumer; +import org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.producer.ApexFileEventProducer; +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; + +/** + * This class holds the parameters that allows transport of events into and out of Apex using files + * and standard input and output. + * + * <p> + * The following parameters are defined: + * <ol> + * <li>fileName: The full path to the file from which to read events or to which to write events. + * <li>standardIO: If this flag is set to true, then standard input is used to read events in or + * standard output is used to write events and the fileName parameter is ignored if present + * <li>standardError: If this flag is set to true, then standard error is used to write events + * <li>streamingMode: If this flag is set to true, then streaming mode is set for reading events and + * event handling will wait on the input stream for events until the stream is closed. If streaming + * model is off, then event reading completes when the end of input is detected. + * <li>startDelay: The amount of milliseconds to wait at startup startup before processing the first + * event. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class FILECarrierTechnologyParameters extends CarrierTechnologyParameters { + // @formatter:off + /** The label of this carrier technology. */ + public static final String FILE_CARRIER_TECHNOLOGY_LABEL = "FILE"; + + /** The producer plugin class for the FILE carrier technology. */ + public static final String FILE_EVENT_PRODUCER_PLUGIN_CLASS = ApexFileEventProducer.class.getCanonicalName(); + + /** The consumer plugin class for the FILE carrier technology. */ + public static final String FILE_EVENT_CONSUMER_PLUGIN_CLASS = ApexFileEventConsumer.class.getCanonicalName(); + + private String fileName; + private boolean standardIO = false; + private boolean standardError = false; + private boolean streamingMode = false; + private long startDelay = 0; + // @formatter:on + + /** + * Constructor to create a file carrier technology parameters instance and register the instance + * with the parameter service. + */ + public FILECarrierTechnologyParameters() { + super(FILECarrierTechnologyParameters.class.getCanonicalName()); + + // Set the carrier technology properties for the FILE carrier technology + this.setLabel(FILE_CARRIER_TECHNOLOGY_LABEL); + this.setEventProducerPluginClass(FILE_EVENT_PRODUCER_PLUGIN_CLASS); + this.setEventConsumerPluginClass(FILE_EVENT_CONSUMER_PLUGIN_CLASS); + } + + /** + * Gets the file name from which to read or to which to write events. + * + * @return the file name from which to read or to which to write events + */ + public String getFileName() { + return ResourceUtils.getFilePath4Resource(fileName); + } + + /** + * Checks if is standard IO should be used for input or output. + * + * @return true, if standard IO should be used for input or output + */ + public boolean isStandardIO() { + return standardIO; + } + + /** + * Checks if is standard error should be used for output. + * + * @return true, if standard error should be used for output + */ + public boolean isStandardError() { + return standardError; + } + + /** + * Checks if is streaming mode is on. + * + * @return true, if streaming mode is on + */ + public boolean isStreamingMode() { + return streamingMode; + } + + /** + * Sets the file name from which to read or to which to write events. + * + * @param fileName the file name from which to read or to which to write events + */ + public void setFileName(final String fileName) { + this.fileName = fileName; + } + + /** + * Sets if standard IO should be used for event input or output. + * + * @param standardIO if standard IO should be used for event input or output + */ + public void setStandardIO(final boolean standardIO) { + this.standardIO = standardIO; + } + + /** + * Sets if standard error should be used for event output. + * + * @param standardError if standard error should be used for event output + */ + public void setStandardError(final boolean standardError) { + this.standardError = standardError; + } + + /** + * Sets streaming mode. + * + * @param streamingMode the streaming mode value + */ + public void setStreamingMode(final boolean streamingMode) { + this.streamingMode = streamingMode; + } + + /** + * Gets the delay in milliseconds before the plugin starts processing + * + * @return the delay + */ + public long getStartDelay() { + return startDelay; + } + + /** + * Sets the delay in milliseconds before the plugin starts processing + * + * @param startDelay the delay + */ + public void setStartDelay(final long startDelay) { + this.startDelay = startDelay; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters# + * toString() + */ + @Override + public String toString() { + return "FILECarrierTechnologyParameters [fileName=" + fileName + ", standardIO=" + standardIO + + ", standardError=" + standardError + ", streamingMode=" + streamingMode + ", startDelay=" + startDelay + + "]"; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + errorMessageBuilder.append(super.validate()); + + if (!standardIO && !standardError && (fileName == null || fileName.trim().length() == 0)) { + errorMessageBuilder.append( + " fileName not specified or is blank or null, it must be specified as a valid file location\n"); + } + + if (standardIO || standardError) { + streamingMode = true; + } + + if (startDelay < 0) { + errorMessageBuilder.append(" startDelay must be zero or a positive number of milliseconds\n"); + } + + return errorMessageBuilder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/ApexFileEventConsumer.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/ApexFileEventConsumer.java new file mode 100644 index 000000000..7521c3a08 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/ApexFileEventConsumer.java @@ -0,0 +1,247 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin.consumer; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.EnumMap; +import java.util.Map; +import java.util.concurrent.atomic.AtomicLong; + +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.service.engine.event.ApexEventConsumer; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventReceiver; +import org.onap.policy.apex.service.engine.event.PeeredReference; +import org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.FILECarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Concrete implementation an Apex event consumer that reads events from a file. This consumer also + * implements ApexEventProducer and therefore can be used as a synchronous consumer. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexFileEventConsumer implements ApexEventConsumer, Runnable { + + // Get a reference to the logger + private static final Logger LOGGER = LoggerFactory.getLogger(ApexFileEventConsumer.class); + + // The input stream to read events from + private InputStream eventInputStream; + + // The text block reader that will read text blocks from the contents of the file + private TextBlockReader textBlockReader; + + // The event receiver that will receive asynchronous events from this consumer + private ApexEventReceiver eventReceiver = null; + + // The consumer thread and stopping flag + private Thread consumerThread; + + // The name for this consumer + private String consumerName = null; + + // The specific carrier technology parameters for this consumer + private FILECarrierTechnologyParameters fileCarrierTechnologyParameters; + + // The peer references for this event handler + private final Map<EventHandlerPeeredMode, PeeredReference> peerReferenceMap = + new EnumMap<>(EventHandlerPeeredMode.class); + + // Holds the next identifier for event execution. + private static AtomicLong nextExecutionID = new AtomicLong(0L); + + /** + * Private utility to get the next candidate value for a Execution ID. This value will always be + * unique in a single JVM + * + * @return the next candidate value for a Execution ID + */ + private static synchronized long getNextExecutionID() { + return nextExecutionID.getAndIncrement(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.apps.uservice.consumer.ApexEventConsumer#init(org.onap.policy.apex.apps. + * uservice.consumer.ApexEventReceiver) + */ + @Override + public void init(final String name, final EventHandlerParameters consumerParameters, + final ApexEventReceiver incomingEventReceiver) throws ApexEventException { + this.eventReceiver = incomingEventReceiver; + this.consumerName = name; + + // Get and check the Apex parameters from the parameter service + if (consumerParameters == null) { + final String errorMessage = "Consumer parameters for ApexFileConsumer \"" + consumerName + "\" is null"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + + // Check and get the file Properties + if (!(consumerParameters.getCarrierTechnologyParameters() instanceof FILECarrierTechnologyParameters)) { + final String errorMessage = "specified consumer properties for ApexFileConsumer \"" + consumerName + + "\" are not applicable to a File consumer"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + fileCarrierTechnologyParameters = + (FILECarrierTechnologyParameters) consumerParameters.getCarrierTechnologyParameters(); + + // Open the file producing events + try { + if (fileCarrierTechnologyParameters.isStandardIO()) { + eventInputStream = System.in; + } else { + eventInputStream = new FileInputStream(fileCarrierTechnologyParameters.getFileName()); + } + + // Get an event composer for our event source + textBlockReader = new TextBlockReaderFactory().getTaggedReader(eventInputStream, + consumerParameters.getEventProtocolParameters()); + } catch (final IOException e) { + final String errorMessage = "ApexFileConsumer \"" + consumerName + "\" failed to open file for reading: \"" + + fileCarrierTechnologyParameters.getFileName() + "\""; + LOGGER.warn(errorMessage, e); + throw new ApexEventException(errorMessage, e); + } + + if (fileCarrierTechnologyParameters.getStartDelay() > 0) { + ThreadUtilities.sleep(fileCarrierTechnologyParameters.getStartDelay()); + } + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#getName() + */ + @Override + public String getName() { + return consumerName; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#getPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode) + */ + @Override + public PeeredReference getPeeredReference(final EventHandlerPeeredMode peeredMode) { + return peerReferenceMap.get(peeredMode); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#setPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode, + * org.onap.policy.apex.service.engine.event.PeeredReference) + */ + @Override + public void setPeeredReference(final EventHandlerPeeredMode peeredMode, final PeeredReference peeredReference) { + peerReferenceMap.put(peeredMode, peeredReference); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConsumer#start() + */ + @Override + public void start() { + // Configure and start the event reception thread + final String threadName = this.getClass().getName() + " : " + consumerName; + consumerThread = new ApplicationThreadFactory(threadName).newThread(this); + consumerThread.setDaemon(true); + consumerThread.start(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + // Check that we have been initialized in async or sync mode + if (eventReceiver == null) { + LOGGER.warn("\"{}\" has not been initilaized for either asynchronous or synchronous event handling", + consumerName); + return; + } + + // Read the events from the file while there are still events in the file + try { + // Read all the text blocks + TextBlock textBlock; + do { + // Read the text block + textBlock = textBlockReader.readTextBlock(); + + // Process the event from the text block if there is one there + if (textBlock.getText() != null) { + eventReceiver.receiveEvent(getNextExecutionID(), textBlock.getText()); + } + } while (!textBlock.isEndOfText()); + } catch (final Exception e) { + LOGGER.warn("\"" + consumerName + "\" failed to read event from file: \"" + + fileCarrierTechnologyParameters.getFileName() + "\"", e); + } finally { + try { + eventInputStream.close(); + } catch (final IOException e) { + LOGGER.warn("ApexFileConsumer \"" + consumerName + "\" failed to close file: \"" + + fileCarrierTechnologyParameters.getFileName() + "\"", e); + } + } + + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.producer.ApexEventProducer#stop() + */ + @Override + public void stop() { + try { + eventInputStream.close(); + } catch (final IOException e) { + LOGGER.warn("ApexFileConsumer \"" + consumerName + "\" failed to close file for reading: \"" + + fileCarrierTechnologyParameters.getFileName() + "\"", e); + } + + if (consumerThread.isAlive() && !consumerThread.isInterrupted()) { + consumerThread.interrupt(); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/CharacterDelimitedTextBlockReader.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/CharacterDelimitedTextBlockReader.java new file mode 100644 index 000000000..b286f8afe --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/CharacterDelimitedTextBlockReader.java @@ -0,0 +1,141 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin.consumer; + +import java.io.IOException; +import java.io.InputStream; + +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolTextCharDelimitedParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * The class CharacterDelimitedTextBlockReader reads the next block of text between two character + * tags from an input stream. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class CharacterDelimitedTextBlockReader implements TextBlockReader { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(CharacterDelimitedTextBlockReader.class); + + // The character tags + private final char startTagChar; + private final char endTagChar; + + // The input stream for text + private InputStream inputStream; + + // Flag indicating we have seen EOF on the stream + private boolean eofOnInputStream = false; + + /** + * Constructor, set the delimiters. + * + * @param startTagChar The start tag for text blocks + * @param endTagChar The end tag for text blocks + */ + public CharacterDelimitedTextBlockReader(final char startTagChar, final char endTagChar) { + this.startTagChar = startTagChar; + this.endTagChar = endTagChar; + } + + /** + * Constructor, set the delimiters from a character delimited event protocol parameter class. + * + * @param charDelimitedParameters the character delimited event protocol parameter class + */ + public CharacterDelimitedTextBlockReader(final EventProtocolTextCharDelimitedParameters charDelimitedParameters) { + this.startTagChar = charDelimitedParameters.getStartChar(); + this.endTagChar = charDelimitedParameters.getEndChar(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.consumer.TextBlockReader#init( + * java.io.InputStream) + */ + @Override + public void init(final InputStream incomingInputStream) { + this.inputStream = incomingInputStream; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.consumer.TextBlockReader# + * readTextBlock() + */ + @Override + public TextBlock readTextBlock() throws IOException { + // Check if there was a previous end of a text block with a non-empty text block returned + if (eofOnInputStream) { + return new TextBlock(eofOnInputStream, null); + } + + // The initial nesting level of incoming text blocks is always zero + int nestingLevel = 0; + + // Holder for the text block + final StringBuilder textBlockBuilder = new StringBuilder(); + + // Read the next text block + while (true) { + final char nextChar = (char) inputStream.read(); + + // Check for EOF + if (nextChar == (char) -1) { + eofOnInputStream = true; + break; + } + + if (nextChar == startTagChar) { + nestingLevel++; + } else if (nestingLevel == 0 && !Character.isWhitespace(nextChar)) { + LOGGER.warn("invalid input on consumer: " + nextChar); + continue; + } + + textBlockBuilder.append(nextChar); + + // Check for end of the text block, we have come back to level 0 + if (nextChar == endTagChar) { + if (nestingLevel > 0) { + nestingLevel--; + } + + if (nestingLevel == 0) { + break; + } + } + } + + // Condition the text block and return it + final String textBlock = textBlockBuilder.toString().trim(); + if (textBlock.length() > 0) { + return new TextBlock(eofOnInputStream, textBlock); + } else { + return new TextBlock(eofOnInputStream, null); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/HeaderDelimitedTextBlockReader.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/HeaderDelimitedTextBlockReader.java new file mode 100644 index 000000000..e40bc756c --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/HeaderDelimitedTextBlockReader.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.service.engine.event.impl.filecarrierplugin.consumer; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.Queue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolTextTokenDelimitedParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * The Class TextBlockReader reads the next block of text from an input stream. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class HeaderDelimitedTextBlockReader implements TextBlockReader, Runnable { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(HeaderDelimitedTextBlockReader.class); + + // The amount of time to wait for input on the text block reader + private static final long TEXT_BLOCK_DELAY = 250; + + // Tag for the start of a text block + private final String blockStartToken; + + // The input stream for text + private InputStream inputStream; + + // The lines of input read from the input stream + private final Queue<String> textLineQueue = new LinkedBlockingQueue<>(); + + // The thread used to read text from the input stream + private Thread textConsumputionThread; + + // True while EOF has not been seen on input + private boolean eofOnInputStream = false; + + /** + * Constructor, initialize the text block reader. + * + * @param blockStartToken the block start token for the start of a text block + */ + public HeaderDelimitedTextBlockReader(final String blockStartToken) { + this.blockStartToken = blockStartToken; + } + + /** + * Constructor, initialize the text block reader using token delimited event protocol + * parameters. + * + * @param tokenDelimitedParameters the token delimited event protocol parameters + */ + public HeaderDelimitedTextBlockReader(final EventProtocolTextTokenDelimitedParameters tokenDelimitedParameters) { + this.blockStartToken = tokenDelimitedParameters.getDelimiterToken(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.consumer.TextBlockReader# + * init( java.io.InputStream) + */ + @Override + public void init(final InputStream incomingInputStream) { + this.inputStream = incomingInputStream; + + // Configure and start the text reading thread + textConsumputionThread = new ApplicationThreadFactory(this.getClass().getName()).newThread(this); + textConsumputionThread.setDaemon(true); + textConsumputionThread.start(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.consumer.TextBlockReader# + * readTextBlock() + */ + @Override + public TextBlock readTextBlock() throws IOException { + // Holder for the current text block + final StringBuilder textBlockBuilder = new StringBuilder(); + + // Wait for the timeout period if there is no input + if (!eofOnInputStream && textLineQueue.size() == 0) { + ThreadUtilities.sleep(TEXT_BLOCK_DELAY); + } + + // Scan the lines in the queue + while (textLineQueue.size() > 0) { + // Scroll down in the available lines looking for the start of the text block + if (textLineQueue.peek().startsWith(blockStartToken)) { + // Process the input line header + textBlockBuilder.append(textLineQueue.remove()); + textBlockBuilder.append('\n'); + break; + } else { + LOGGER.warn("invalid input on consumer: " + textLineQueue.remove()); + } + } + + // Get the rest of the text document + while (textLineQueue.size() > 0 && !textLineQueue.peek().startsWith(blockStartToken)) { + textBlockBuilder.append(textLineQueue.remove()); + textBlockBuilder.append('\n'); + } + + // Condition the text block and return it + final String textBlock = textBlockBuilder.toString().trim(); + final boolean endOfText = (eofOnInputStream && textLineQueue.size() == 0 ? true : false); + + if (textBlock.length() > 0) { + return new TextBlock(endOfText, textBlock); + } else { + return new TextBlock(endOfText, null); + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + final BufferedReader textReader = new BufferedReader(new InputStreamReader(inputStream)); + + try { + // Read the input line by line until we see end of file on the stream + String line; + while ((line = textReader.readLine()) != null) { + textLineQueue.add(line); + } + } catch (final IOException e) { + LOGGER.warn("I/O exception on text input on consumer: ", e); + } finally { + eofOnInputStream = true; + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlock.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlock.java new file mode 100644 index 000000000..526d9c318 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlock.java @@ -0,0 +1,78 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin.consumer; + +/** + * This class is a bean that holds a block of text read from an incoming text file. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class TextBlock { + private boolean endOfText = false; + private String text; + + /** + * Constructor to initiate the text block. + * + * @param endOfText the end of text + * @param text the text + */ + public TextBlock(final boolean endOfText, final String text) { + this.endOfText = endOfText; + this.text = text; + } + + /** + * Checks if is end of text. + * + * @return true, if checks if is end of text + */ + public boolean isEndOfText() { + return endOfText; + } + + /** + * Sets whether end of text has been reached. + * + * @param endOfText the end of text flag value + */ + public void setEndOfText(final boolean endOfText) { + this.endOfText = endOfText; + } + + /** + * Gets the text of the text block. + * + * @return the text of the text block + */ + public String getText() { + return text; + } + + /** + * Sets the text of the text block. + * + * @param text the text of the text block + */ + public void setText(final String text) { + this.text = text; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlockReader.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlockReader.java new file mode 100644 index 000000000..627718402 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlockReader.java @@ -0,0 +1,46 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin.consumer; + +import java.io.IOException; +import java.io.InputStream; + +/** + * Implementers of the interface TextBlockReader read the next block of text from an input stream. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface TextBlockReader { + /** + * Initialize the text block reader reader. + * + * @param inputStream The stream to read from + */ + void init(InputStream inputStream); + + /** + * Read a block of text between two delimiters. + * + * @return The text block + * @throws IOException On reading errors + */ + TextBlock readTextBlock() throws IOException; +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlockReaderFactory.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlockReaderFactory.java new file mode 100644 index 000000000..e48266634 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/TextBlockReaderFactory.java @@ -0,0 +1,80 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin.consumer; + +import java.io.InputStream; + +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolTextCharDelimitedParameters; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolTextTokenDelimitedParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This factory creates text block readers for breaking character streams into blocks of text. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class TextBlockReaderFactory { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(TextBlockReaderFactory.class); + + /** + * Get a text block reader for the given event protocol. + * + * @param inputStream the input stream that will be used for reading + * @param eventProtocolParameters the parameters that have been specified for event protocols + * @return the tagged reader + * @throws ApexEventException On an unsupported event protocol + */ + public TextBlockReader getTaggedReader(final InputStream inputStream, + final EventProtocolParameters eventProtocolParameters) throws ApexEventException { + // Check the type of event protocol we have + if (eventProtocolParameters instanceof EventProtocolTextCharDelimitedParameters) { + // We have character delimited textual input + final EventProtocolTextCharDelimitedParameters charDelimitedParameters = + (EventProtocolTextCharDelimitedParameters) eventProtocolParameters; + + // Create the text block reader + final TextBlockReader characterDelimitedTextBlockReader = + new CharacterDelimitedTextBlockReader(charDelimitedParameters); + characterDelimitedTextBlockReader.init(inputStream); + return characterDelimitedTextBlockReader; + } else if (eventProtocolParameters instanceof EventProtocolTextTokenDelimitedParameters) { + // We have token delimited textual input + final EventProtocolTextTokenDelimitedParameters tokenDelimitedParameters = + (EventProtocolTextTokenDelimitedParameters) eventProtocolParameters; + + // Create the text block reader + final HeaderDelimitedTextBlockReader headerDelimitedTextBlockReader = + new HeaderDelimitedTextBlockReader(tokenDelimitedParameters); + headerDelimitedTextBlockReader.init(inputStream); + return headerDelimitedTextBlockReader; + } else { + final String errorMessage = + "could not create text block reader for a textual event protocol, the required type " + + eventProtocolParameters.getLabel() + " is not supported"; + LOGGER.error(errorMessage); + throw new ApexEventException(errorMessage); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/package-info.java new file mode 100644 index 000000000..05833ac7c --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/consumer/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 the FILE carrier technology consumer that sends events to APEX from files, standard IO + * or named pipes. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.consumer; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/package-info.java new file mode 100644 index 000000000..de0b1b56e --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/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 the FILE carrier technology for event input and output to and from Apex using files, + * named pipes, and standard IO. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.filecarrierplugin; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/producer/ApexFileEventProducer.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/producer/ApexFileEventProducer.java new file mode 100644 index 000000000..d5f9ff1b2 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/producer/ApexFileEventProducer.java @@ -0,0 +1,181 @@ +/*- + * ============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.service.engine.event.impl.filecarrierplugin.producer; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.util.EnumMap; +import java.util.Map; + +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProducer; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.engine.event.PeeredReference; +import org.onap.policy.apex.service.engine.event.SynchronousEventCache; +import org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.FILECarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Concrete implementation of an Apex event producer that sends events to a file. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexFileEventProducer implements ApexEventProducer { + // Get a reference to the logger + private static final Logger LOGGER = LoggerFactory.getLogger(ApexFileEventProducer.class); + + // The name for this producer + private String producerName = null; + + // The output stream to write events to + private PrintStream eventOutputStream; + + // The peer references for this event handler + private final Map<EventHandlerPeeredMode, PeeredReference> peerReferenceMap = + new EnumMap<>(EventHandlerPeeredMode.class); + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.producer.ApexEventProducer#init() + */ + @Override + public void init(final String name, final EventHandlerParameters producerParameters) throws ApexEventException { + producerName = name; + + // Get and check the Apex parameters from the parameter service + if (producerParameters == null) { + final String errorMessage = "Producer parameters for ApexFileProducer \"" + producerName + "\" is null"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + + // Check and get the file Properties + if (!(producerParameters.getCarrierTechnologyParameters() instanceof FILECarrierTechnologyParameters)) { + final String errorMessage = "specified producer properties for ApexFileProducer \"" + producerName + + "\" are not applicable to a FILE producer"; + LOGGER.warn(errorMessage); + throw new ApexEventException(errorMessage); + } + final FILECarrierTechnologyParameters fileCarrierTechnologyParameters = + (FILECarrierTechnologyParameters) producerParameters.getCarrierTechnologyParameters(); + + // Now we create a writer for events + try { + if (fileCarrierTechnologyParameters.isStandardError()) { + eventOutputStream = System.err; + } else if (fileCarrierTechnologyParameters.isStandardIO()) { + eventOutputStream = System.out; + } else { + eventOutputStream = + new PrintStream(new FileOutputStream(fileCarrierTechnologyParameters.getFileName()), true); + } + } catch (final IOException e) { + final String errorMessage = "ApexFileProducer \"" + producerName + "\" failed to open file for writing: \"" + + fileCarrierTechnologyParameters.getFileName() + "\""; + LOGGER.warn(errorMessage, e); + throw new ApexEventException(errorMessage, e); + } + + if (fileCarrierTechnologyParameters.getStartDelay() > 0) { + ThreadUtilities.sleep(fileCarrierTechnologyParameters.getStartDelay()); + } + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#getName() + */ + @Override + public String getName() { + return producerName; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#getPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode) + */ + @Override + public PeeredReference getPeeredReference(final EventHandlerPeeredMode peeredMode) { + return peerReferenceMap.get(peeredMode); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#setPeeredReference(org.onap. + * policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode, + * org.onap.policy.apex.service.engine.event.PeeredReference) + */ + @Override + public void setPeeredReference(final EventHandlerPeeredMode peeredMode, final PeeredReference peeredReference) { + peerReferenceMap.put(peeredMode, peeredReference); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProducer#sendEvent(long, + * java.lang.String, java.lang.Object) + */ + @Override + public void sendEvent(final long executionId, final String eventName, final Object event) { + // Check if this is a synchronized event, if so we have received a reply + final SynchronousEventCache synchronousEventCache = + (SynchronousEventCache) peerReferenceMap.get(EventHandlerPeeredMode.SYNCHRONOUS); + if (synchronousEventCache != null) { + synchronousEventCache.removeCachedEventToApexIfExists(executionId); + } + + // Cast the event to a string, if our conversion is correctly configured, this cast should + // always work + String stringEvent = null; + try { + stringEvent = (String) event; + } catch (final Exception e) { + final String errorMessage = "error in ApexFileProducer \"" + producerName + "\" while transferring event \"" + + event + "\" to the output stream"; + LOGGER.debug(errorMessage, e); + throw new ApexEventRuntimeException(errorMessage, e); + } + + eventOutputStream.println(stringEvent); + eventOutputStream.flush(); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.producer.ApexEventProducer#stop() + */ + @Override + public void stop() { + eventOutputStream.close(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/producer/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/producer/package-info.java new file mode 100644 index 000000000..f7d7cbfbd --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/filecarrierplugin/producer/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 the FILE carrier technology producer that outputs events from APEX to files, standard + * IO or named pipes. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.producer; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/Apex2JSONEventConverter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/Apex2JSONEventConverter.java new file mode 100644 index 000000000..3b21a29ca --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/Apex2JSONEventConverter.java @@ -0,0 +1,433 @@ +/*- + * ============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.service.engine.event.impl.jsonprotocolplugin; + +import java.util.ArrayList; +import java.util.List; + +import org.onap.policy.apex.context.SchemaHelper; +import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory; +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.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; + +/** + * The Class Apex2JSONEventConverter converts {@link ApexEvent} instances to and from JSON string + * representations of Apex events. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class Apex2JSONEventConverter implements ApexEventProtocolConverter { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(Apex2JSONEventConverter.class); + + // The parameters for the JSON event protocol + private JSONEventProtocolParameters jsonPars; + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter#init(org.onap.policy. + * apex.service.parameters.eventprotocol.EventProtocolParameters) + */ + @Override + public void init(final EventProtocolParameters parameters) { + // Check and get the JSON parameters + if (!(parameters instanceof JSONEventProtocolParameters)) { + final String errorMessage = "specified consumer properties are not applicable to the JSON event protocol"; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + + jsonPars = (JSONEventProtocolParameters) parameters; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String, + * java.lang.Object) + */ + @Override + public List<ApexEvent> toApexEvent(final String eventName, final Object eventObject) throws ApexEventException { + // Check the event eventObject + if (eventObject == null) { + LOGGER.warn("event processing failed, event is null"); + throw new ApexEventException("event processing failed, event is null"); + } + + // Cast the event to a string, if our conversion is correctly configured, this cast should + // always work + String jsonEventString = null; + try { + jsonEventString = (String) eventObject; + } catch (final Exception e) { + final String errorMessage = "error converting event \"" + eventObject + "\" to a string"; + LOGGER.debug(errorMessage, e); + throw new ApexEventRuntimeException(errorMessage, e); + } + + // The list of events we will return + final List<ApexEvent> eventList = new ArrayList<ApexEvent>(); + + try { + // We may have a single JSON object with a single event or an array of JSON objects + final Object decodedJsonObject = + new GsonBuilder().serializeNulls().create().fromJson(jsonEventString, Object.class); + + // Check if we have a list of objects + if (decodedJsonObject instanceof List) { + // Check if it's a list of JSON objects or a list of strings + @SuppressWarnings("unchecked") + final List<Object> decodedJsonList = (List<Object>) decodedJsonObject; + + // Decode each of the list elements in sequence + for (final Object jsonListObject : decodedJsonList) { + if (jsonListObject instanceof String) { + eventList.add(jsonStringApexEvent(eventName, (String) jsonListObject)); + } else if (jsonListObject instanceof JsonObject) { + eventList.add(jsonObject2ApexEvent(eventName, (JsonObject) jsonListObject)); + } else { + throw new ApexEventException("incoming event (" + jsonEventString + + ") is a JSON object array containing an invalid object " + jsonListObject); + } + } + } else { + eventList.add(jsonStringApexEvent(eventName, jsonEventString)); + } + } catch (final Exception e) { + final String errorString = + "Failed to unmarshal JSON event: " + e.getMessage() + ", event=" + jsonEventString; + LOGGER.warn(errorString, e); + throw new ApexEventException(errorString, e); + } + + // Return the list of events we have unmarshalled + return eventList; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy. + * apex.service.engine.event.ApexEvent) + */ + @Override + public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException { + // Check the Apex event + if (apexEvent == null) { + LOGGER.warn("event processing failed, Apex event is null"); + throw new ApexEventException("event processing failed, Apex event is null"); + } + + // Get the event definition for the event from the model service + final AxEvent eventDefinition = + ModelService.getModel(AxEvents.class).get(apexEvent.getName(), apexEvent.getVersion()); + + // Use a GSON Json object to marshal the Apex event to JSON + final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create(); + final JsonObject jsonObject = new JsonObject(); + + jsonObject.addProperty(ApexEvent.NAME_HEADER_FIELD, apexEvent.getName()); + jsonObject.addProperty(ApexEvent.VERSION_HEADER_FIELD, apexEvent.getVersion()); + jsonObject.addProperty(ApexEvent.NAMESPACE_HEADER_FIELD, apexEvent.getNameSpace()); + jsonObject.addProperty(ApexEvent.SOURCE_HEADER_FIELD, apexEvent.getSource()); + jsonObject.addProperty(ApexEvent.TARGET_HEADER_FIELD, apexEvent.getTarget()); + + if (apexEvent.getExceptionMessage() != null) { + jsonObject.addProperty(ApexEvent.EXCEPTION_MESSAGE_HEADER_FIELD, apexEvent.getExceptionMessage()); + } + + for (final AxField eventField : eventDefinition.getFields()) { + final String fieldName = eventField.getKey().getLocalName(); + + if (!apexEvent.containsKey(fieldName)) { + if (!eventField.getOptional()) { + final String errorMessage = "error parsing " + eventDefinition.getID() + " event to Json. " + + "Field \"" + fieldName + "\" is missing, but is mandatory. Fields: " + apexEvent; + LOGGER.debug(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + continue; + } + + final Object fieldValue = apexEvent.get(fieldName); + + // Get the schema helper + final SchemaHelper fieldSchemaHelper = + new SchemaHelperFactory().createSchemaHelper(eventField.getKey(), eventField.getSchema()); + jsonObject.add(fieldName, fieldSchemaHelper.marshal2JsonElement(fieldValue)); + } + + // Output JSON string in a pretty format + return gson.toJson(jsonObject); + } + + /** + * This method converts a JSON object into an Apex event. + * + * @param eventName the name of the event + * @param jsonEventString the JSON string that holds the event + * @return the apex event that we have converted the JSON object into + * @throws ApexEventException thrown on unmarshaling exceptions + */ + private ApexEvent jsonStringApexEvent(final String eventName, final String jsonEventString) + throws ApexEventException { + // Use GSON to read the event string + final JsonObject jsonObject = + new GsonBuilder().serializeNulls().create().fromJson(jsonEventString, JsonObject.class); + + if (jsonObject == null || !jsonObject.isJsonObject()) { + throw new ApexEventException( + "incoming event (" + jsonEventString + ") is not a JSON object or an JSON object array"); + } + + return jsonObject2ApexEvent(eventName, jsonObject); + } + + /** + * This method converts a JSON object into an Apex event. + * + * @param eventName the name of the event + * @param jsonObject the JSON object that holds the event + * @return the apex event that we have converted the JSON object into + * @throws ApexEventException thrown on unmarshaling exceptions + */ + private ApexEvent jsonObject2ApexEvent(final String eventName, final JsonObject jsonObject) + throws ApexEventException { + // Process the mandatory Apex header + final ApexEvent apexEvent = processApexEventHeader(eventName, jsonObject); + + // Get the event definition for the event from the model service + final AxEvent eventDefinition = + ModelService.getModel(AxEvents.class).get(apexEvent.getName(), apexEvent.getVersion()); + + // Iterate over the input fields in the event + for (final AxField eventField : eventDefinition.getFields()) { + final String fieldName = eventField.getKey().getLocalName(); + if (!hasJSONField(jsonObject, fieldName)) { + if (!eventField.getOptional()) { + final String errorMessage = "error parsing " + eventDefinition.getID() + " event from Json. " + + "Field \"" + fieldName + "\" is missing, but is mandatory."; + LOGGER.debug(errorMessage); + throw new ApexEventException(errorMessage); + } + continue; + } + + final JsonElement fieldValue = getJSONField(jsonObject, fieldName, null, !eventField.getOptional()); + + if (fieldValue != null && !fieldValue.isJsonNull()) { + // Get the schema helper + final SchemaHelper fieldSchemaHelper = + new SchemaHelperFactory().createSchemaHelper(eventField.getKey(), eventField.getSchema()); + apexEvent.put(fieldName, fieldSchemaHelper.createNewInstance(fieldValue)); + } else { + apexEvent.put(fieldName, null); + } + } + return apexEvent; + + } + + /** + * This method processes the event header of an Apex event. + * + * @param eventName the name of the event + * @param jsonObject the JSON object containing the JSON representation of the incoming event + * @return an apex event constructed using the header fields of the event + * @throws ApexEventRuntimeException the apex event runtime exception + * @throws ApexEventException on invalid events with missing header fields + */ + private ApexEvent processApexEventHeader(final String eventName, final JsonObject jsonObject) + throws ApexEventRuntimeException, ApexEventException { + // Get the event header fields + // @formatter:off + String name = getJSONStringField(jsonObject, ApexEvent.NAME_HEADER_FIELD, jsonPars.getNameAlias(), ApexEvent.NAME_REGEXP, false); + String version = getJSONStringField(jsonObject, ApexEvent.VERSION_HEADER_FIELD, jsonPars.getVersionAlias(), ApexEvent.VERSION_REGEXP, false); + String namespace = getJSONStringField(jsonObject, ApexEvent.NAMESPACE_HEADER_FIELD, jsonPars.getNameSpaceAlias(), ApexEvent.NAMESPACE_REGEXP, false); + String source = getJSONStringField(jsonObject, ApexEvent.SOURCE_HEADER_FIELD, jsonPars.getSourceAlias(), ApexEvent.SOURCE_REGEXP, false); + String target = getJSONStringField(jsonObject, ApexEvent.TARGET_HEADER_FIELD, jsonPars.getTargetAlias(), ApexEvent.TARGET_REGEXP, false); + // @formatter:on + + // Check if an event name was specified on the event parameters + if (eventName != null) { + if (name != null && !eventName.equals(name)) { + LOGGER.warn("The incoming event name \"" + name + "\" does not match the configured event name \"" + + eventName + "\", using configured event name"); + } + name = eventName; + } else { + if (name == null) { + throw new ApexEventRuntimeException( + "event received without mandatory parameter \"name\" on configuration or on event"); + } + } + + // Now, find the event definition in the model service. If version is null, the newest event + // definition in the model service is used + final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(name, version); + if (eventDefinition == null) { + if (version == null) { + throw new ApexEventRuntimeException( + "an event definition for an event named \"" + name + "\" not found in Apex model"); + } else { + throw new ApexEventRuntimeException("an event definition for an event named \"" + name + + "\" with version \"" + version + "\" not found in Apex model"); + } + } + + // Use the defined event version if no version is specified on the incoming fields + if (version == null) { + version = eventDefinition.getKey().getVersion(); + } + + // Check the name space is OK if it is defined, if not, use the name space from the model + if (namespace != null) { + if (!namespace.equals(eventDefinition.getNameSpace())) { + throw new ApexEventRuntimeException( + "namespace \"" + namespace + "\" on event \"" + name + "\" does not match namespace \"" + + eventDefinition.getNameSpace() + "\" for that event in the Apex model"); + } + } else { + namespace = eventDefinition.getNameSpace(); + } + + // For source, use the defined source only if the source is not found on the incoming event + if (source == null) { + source = eventDefinition.getSource(); + } + + // For target, use the defined source only if the source is not found on the incoming event + if (target == null) { + target = eventDefinition.getTarget(); + } + + return new ApexEvent(name, version, namespace, source, target); + } + + /** + * This method gets an event string field from a JSON object. + * + * @param jsonObject the JSON object containing the JSON representation of the incoming event + * @param fieldName the field name to find in the event + * @param fieldAlias the alias for the field to find in the event, overrides the field name if + * it is not null + * @param fieldRE the regular expression to check the field against for validity + * @param mandatory true if the field is mandatory + * @return the value of the field in the JSON object or null if the field is optional + * @throws ApexEventRuntimeException the apex event runtime exception + */ + private String getJSONStringField(final JsonObject jsonObject, final String fieldName, final String fieldAlias, + final String fieldRE, final boolean mandatory) throws ApexEventRuntimeException { + // Get the JSON field for the string field + final JsonElement jsonField = getJSONField(jsonObject, fieldName, fieldAlias, mandatory); + + // Null strings are allowed + if (jsonField == null || jsonField.isJsonNull()) { + return null; + } + + // Check if this is a string field + String fieldValueString = null; + try { + fieldValueString = jsonField.getAsString(); + } catch (final Exception e) { + // The element is not a string so throw an error + throw new ApexEventRuntimeException("field \"" + fieldName + "\" with type \"" + + jsonField.getClass().getCanonicalName() + "\" is not a string value"); + } + + // Is regular expression checking required + if (fieldRE == null) { + return fieldValueString; + } + + // Check the event field against its regular expression + if (!fieldValueString.matches(fieldRE)) { + throw new ApexEventRuntimeException( + "field \"" + fieldName + "\" with value \"" + fieldValueString + "\" is invalid"); + } + + return fieldValueString; + } + + /** + * This method gets an event field from a JSON object. + * + * @param jsonObject the JSON object containing the JSON representation of the incoming event + * @param fieldName the field name to find in the event + * @param fieldAlias the alias for the field to find in the event, overrides the field name if + * it is not null + * @param mandatory true if the field is mandatory + * @return the value of the field in the JSON object or null if the field is optional + * @throws ApexEventRuntimeException the apex event runtime exception + */ + private JsonElement getJSONField(final JsonObject jsonObject, final String fieldName, final String fieldAlias, + final boolean mandatory) throws ApexEventRuntimeException { + + // Check if we should use the alias for this field + String fieldToFind = fieldName; + if (fieldAlias != null) { + fieldToFind = fieldAlias; + } + + // Get the event field + final JsonElement eventElement = jsonObject.get(fieldToFind); + if (eventElement == null) { + if (!mandatory) { + return null; + } else { + throw new ApexEventRuntimeException("mandatory field \"" + fieldToFind + "\" is missing"); + } + } + + return eventElement; + } + + /** + * This method if a JSON object has a named field. + * + * @param jsonObject the JSON object containing the JSON representation of the incoming event + * @param fieldName the field name to find in the event + * @return true if the field is present + * @throws ApexEventRuntimeException the apex event runtime exception + */ + private boolean hasJSONField(final JsonObject jsonObject, final String fieldName) throws ApexEventRuntimeException { + // check for the field + return jsonObject.has(fieldName); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/JSONEventProtocolParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/JSONEventProtocolParameters.java new file mode 100644 index 000000000..b4a4055d7 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/JSONEventProtocolParameters.java @@ -0,0 +1,135 @@ +/*- + * ============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.service.engine.event.impl.jsonprotocolplugin; + +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolTextCharDelimitedParameters; + +/** + * Event protocol parameters for JSON as an event protocol. + * + * The parameters for this plugin are: + * <ol> + * <li>nameAlias: The field in a JSON event to use as an alias for the event name. This parameter is + * optional. + * <li>versionAlias: The field in a JSON event to use as an alias for the event version. This + * parameter is optional. + * <li>nameSpaceAlias: The field in a JSON event to use as an alias for the event name space. This + * parameter is optional. + * <li>sourceAlias: The field in a JSON event to use as an alias for the event source. This + * parameter is optional. + * <li>targetAlias: The field in a JSON event to use as an alias for the event target. This + * parameter is optional. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class JSONEventProtocolParameters extends EventProtocolTextCharDelimitedParameters { + /** The label of this event protocol. */ + public static final String JSON_EVENT_PROTOCOL_LABEL = "JSON"; + + // Constants for text block delimiters + private static final char JSON_TEXT_BLOCK_START_DELIMITER = '{'; + private static final char JSON_TEXT_BLOCK_END_DELIMITER = '}'; + + // Aliases for Apex event header fields + // @formatter:off + private final String nameAlias = null; + private final String versionAlias = null; + private final String nameSpaceAlias = null; + private final String sourceAlias = null; + private final String targetAlias = null; + // @formatter:on + + /** + * Constructor to create a JSON event protocol parameter instance and register the instance with + * the parameter service. + */ + public JSONEventProtocolParameters() { + this(JSONEventProtocolParameters.class.getCanonicalName(), JSON_EVENT_PROTOCOL_LABEL); + } + + /** + * Constructor to create an event protocol parameters instance with the name of a sub class of + * this class. + * + * @param parameterClassName the class name of a sub class of this class + * @param eventProtocolLabel the name of the event protocol for this plugin + */ + public JSONEventProtocolParameters(final String parameterClassName, final String eventProtocolLabel) { + super(parameterClassName); + + // Set the event protocol properties for the JSON event protocol + this.setLabel(eventProtocolLabel); + + // Set the starting and ending delimiters for text blocks of JSON events + this.setStartChar(JSON_TEXT_BLOCK_START_DELIMITER); + this.setEndChar(JSON_TEXT_BLOCK_END_DELIMITER); + + // Set the event protocol plugin class + this.setEventProtocolPluginClass(Apex2JSONEventConverter.class.getCanonicalName()); + } + + /** + * Gets the name alias. + * + * @return the name alias + */ + public String getNameAlias() { + return nameAlias; + } + + /** + * Gets the version alias. + * + * @return the version alias + */ + public String getVersionAlias() { + return versionAlias; + } + + /** + * Gets the name space alias. + * + * @return the name space alias + */ + public String getNameSpaceAlias() { + return nameSpaceAlias; + } + + /** + * Gets the source alias. + * + * @return the source alias + */ + public String getSourceAlias() { + return sourceAlias; + } + + /** + * Gets the target alias. + * + * @return the target alias + */ + public String getTargetAlias() { + return targetAlias; + } + +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/package-info.java new file mode 100644 index 000000000..65f4831ec --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/jsonprotocolplugin/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 the implementation of the APEX event protocol cinverter plugin for events in Json + * format. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl.jsonprotocolplugin; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/package-info.java new file mode 100644 index 000000000..ae2d58701 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/impl/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========================================================= + */ + +/** + * Contains implementations for conversion between externally facing + * {@link org.onap.policy.apex.service.engine.event.ApexEvent} instances and internal APEX engine + * {@link org.onap.policy.apex.core.engine.event.EnEvent} instances. It also contains the + * implementation of the default APEX File carrier technology plugin as well as the protocol plugins + * for XML and JSON event protocols. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event.impl; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/package-info.java new file mode 100644 index 000000000..75404137e --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/event/package-info.java @@ -0,0 +1,32 @@ +/*- + * ============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 generic externally-facing {@link ApexEvent} class that can be sent into an APEX engine + * and processed by an APEX engine. It provides the producer {@link ApexEventProducer} producer and + * {@link ApexEventConsumer} consumer interfaces that APEX uses to send events to and receive events + * from other systems. It also provides the {@link ApexEventConverter} interface that can be + * implemented by plugins that wish to convert some external event format into the APEX event + * format. It also provides a periodic event generator that can be used to send periodic events into + * an APEX engine for triggering of policies to carry out housekeeping tasks. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.event; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivator.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivator.java new file mode 100644 index 000000000..20af31496 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivator.java @@ -0,0 +1,193 @@ +/*- + * ============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.service.engine.main; + +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; +import org.onap.policy.apex.model.utilities.TextFileUtils; +import org.onap.policy.apex.service.engine.engdep.EngDepMessagingService; +import org.onap.policy.apex.service.engine.runtime.EngineService; +import org.onap.policy.apex.service.engine.runtime.impl.EngineServiceImpl; +import org.onap.policy.apex.service.parameters.ApexParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class wraps an Apex engine so that it can be activated as a complete service together with + * all its context, executor, and event plugins. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexActivator { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexActivator.class); + + // The parameters of this Apex activator + private final ApexParameters apexParameters; + + // Event unmarshalers are used to receive events asynchronously into Apex + private final Map<String, ApexEventUnmarshaller> unmarshallerMap = new LinkedHashMap<>(); + + // Event marshalers are used to send events asynchronously from Apex + private final Map<String, ApexEventMarshaller> marshallerMap = new LinkedHashMap<>(); + + // The engine service handler holds the references to the engine and its EngDep deployment + // interface. It also acts as a receiver for asynchronous + // and synchronous events from the engine. + private ApexEngineServiceHandler engineServiceHandler = null; + + /** + * Instantiate the activator for the Apex engine as a complete service. + * + * @param parameters the apex parameters for the Apex service + */ + public ApexActivator(final ApexParameters parameters) { + apexParameters = parameters; + } + + /** + * Initialize the Apex engine as a complete service. + * + * @throws ApexActivatorException on errors in initializing the engine + */ + public void initialize() throws ApexActivatorException { + LOGGER.debug("Apex engine starting as a service . . ."); + + try { + // Create engine with specified thread count + LOGGER.debug("starting apex engine service . . ."); + final EngineService apexEngineService = + EngineServiceImpl.create(apexParameters.getEngineServiceParameters()); + + // Instantiate and start the messaging service for Deployment + LOGGER.debug("starting apex deployment service . . ."); + final EngDepMessagingService engDepService = new EngDepMessagingService(apexEngineService, + apexParameters.getEngineServiceParameters().getDeploymentPort()); + engDepService.start(); + + // Create the engine holder to hold the engine's references and act as an event receiver + engineServiceHandler = new ApexEngineServiceHandler(apexEngineService, engDepService); + + // Check if a policy model file has been specified + if (apexParameters.getEngineServiceParameters().getPolicyModelFileName() != null) { + LOGGER.debug("deploying policy model in \"" + + apexParameters.getEngineServiceParameters().getPolicyModelFileName() + + "\" to the apex engines . . ."); + + // Set the policy model in the engine + final String policyModelString = TextFileUtils + .getTextFileAsString(apexParameters.getEngineServiceParameters().getPolicyModelFileName()); + apexEngineService.updateModel(apexParameters.getEngineServiceParameters().getEngineKey(), + policyModelString, true); + apexEngineService.startAll(); + } + + // Producer parameters specify what event marshalers to handle events leaving Apex are + // set up and how they are set up + for (final Entry<String, EventHandlerParameters> outputParameters : apexParameters + .getEventOutputParameters().entrySet()) { + final ApexEventMarshaller marshaller = new ApexEventMarshaller(outputParameters.getKey(), + apexParameters.getEngineServiceParameters(), outputParameters.getValue()); + marshaller.init(); + apexEngineService.registerActionListener(outputParameters.getKey(), marshaller); + marshallerMap.put(outputParameters.getKey(), marshaller); + } + + // Consumer parameters specify what event unmarshalers to handle events coming into Apex + // are set up and how they are set up + for (final Entry<String, EventHandlerParameters> inputParameters : apexParameters.getEventInputParameters() + .entrySet()) { + final ApexEventUnmarshaller unmarshaller = new ApexEventUnmarshaller(inputParameters.getKey(), + apexParameters.getEngineServiceParameters(), inputParameters.getValue()); + unmarshallerMap.put(inputParameters.getKey(), unmarshaller); + unmarshaller.init(engineServiceHandler); + } + + // Set up unmarshaler/marshaler pairing for synchronized event handling. We only need to + // traverse the unmarshalers because the + // unmarshalers and marshalers are paired one to one uniquely so if we find a + // synchronized unmarshaler we'll also find its + // paired marshaler + for (final Entry<String, EventHandlerParameters> inputParameters : apexParameters.getEventInputParameters() + .entrySet()) { + final ApexEventUnmarshaller unmarshaller = unmarshallerMap.get(inputParameters.getKey()); + + // Pair up peered unmarshalers and marshalers + for (final EventHandlerPeeredMode peeredMode : EventHandlerPeeredMode.values()) { + // Check if the unmarshaler is synchronized with a marshaler + if (inputParameters.getValue().isPeeredMode(peeredMode)) { + // Find the unmarshaler and marshaler + final ApexEventMarshaller peeredMarshaler = + marshallerMap.get(inputParameters.getValue().getPeer(peeredMode)); + + // Connect the unmarshaler and marshaler + unmarshaller.connectMarshaler(peeredMode, peeredMarshaler); + } + } + // Now let's get events flowing + unmarshaller.start(); + } + } catch (final Exception e) { + LOGGER.debug("Apex engine failed to start as a service", e); + throw new ApexActivatorException("Apex engine failed to start as a service", e); + } + + LOGGER.debug("Apex engine started as a service"); + } + + /** + * Terminate the Apex engine. + * + * @throws ApexException on termination errors + */ + public void terminate() throws ApexException { + // Shut down all marshalers and unmarshalers + for (final ApexEventMarshaller marshaller : marshallerMap.values()) { + marshaller.stop(); + } + marshallerMap.clear(); + + for (final ApexEventUnmarshaller unmarshaller : unmarshallerMap.values()) { + unmarshaller.stop(); + } + unmarshallerMap.clear(); + + // Check if the engine service handler has been shut down already + if (engineServiceHandler != null) { + engineServiceHandler.terminate(); + engineServiceHandler = null; + } + } + + /** + * Get the parameters used by the adapter. + * + * @return the parameters of the adapter + */ + public ApexParameters getApexParameters() { + return apexParameters; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivatorException.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivatorException.java new file mode 100644 index 000000000..371a3a882 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivatorException.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.service.engine.main; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; + +/** + * This exception will be called if an error occurs when running Apex as a complete service. + * + * @author Liam Fallon + */ +public class ApexActivatorException extends ApexException { + private static final long serialVersionUID = -8507246953751956974L; + + /** + * Instantiates a new apex activator exception with a message. + * + * @param message the message + */ + public ApexActivatorException(final String message) { + super(message); + } + + /** + * Instantiates a new apex activator exception with a message and a caused by exception. + * + * @param message the message + * @param e the exception that caused this exception to be thrown + */ + public ApexActivatorException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivatorRuntimeException.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivatorRuntimeException.java new file mode 100644 index 000000000..cf1842dbe --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexActivatorRuntimeException.java @@ -0,0 +1,52 @@ +/*- + * ============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.service.engine.main; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException; + +/** + * This runtime exception will be called if a runtime error occurs when running Apex as a complete + * service. + * + * @author Liam Fallon + */ +public class ApexActivatorRuntimeException extends ApexRuntimeException { + private static final long serialVersionUID = -8507246953751956974L; + + /** + * Instantiates a new apex activator exception with a message. + * + * @param message the message + */ + public ApexActivatorRuntimeException(final String message) { + super(message); + } + + /** + * Instantiates a new apex activator exception with a message and a caused by exception. + * + * @param message the message + * @param e the exception that caused this exception to be thrown + */ + public ApexActivatorRuntimeException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexCommandLineArguments.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexCommandLineArguments.java new file mode 100644 index 000000000..02dc248b4 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexCommandLineArguments.java @@ -0,0 +1,285 @@ +/*- + * ============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.service.engine.main; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URL; +import java.util.Arrays; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.DefaultParser; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; +import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException; +import org.onap.policy.apex.model.utilities.ResourceUtils; + +/** + * This class reads and handles command line parameters for the Apex main program. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexCommandLineArguments { + private static final int HELP_LINE_LENGTH = 120; + + // Apache Commons CLI options + private final Options options; + + // The command line options + private String modelFilePath = null; + private String configurationFilePath = null; + + /** + * Construct the options for the CLI editor. + */ + public ApexCommandLineArguments() { + //@formatter:off + options = new Options(); + options.addOption(Option.builder("h") + .longOpt("help") + .desc("outputs the usage of this command") + .required(false) + .type(Boolean.class) + .build()); + options.addOption(Option.builder("v") + .longOpt("version") + .desc("outputs the version of Apex") + .required(false) + .type(Boolean.class) + .build()); + options.addOption(Option.builder("c") + .longOpt("config-file") + .desc("the full path to the configuration file to use, the configuration file must be a Json file containing the Apex configuration parameters") + .hasArg() + .argName("CONFIG_FILE") + .required(false) + .type(String.class) + .build()); + options.addOption(Option.builder("m").longOpt("model-file") + .desc("the full path to the model file to use, if set it overrides the model file set in the configuration file").hasArg().argName("MODEL_FILE") + .required(false) + .type(String.class).build()); + //@formatter:on + } + + /** + * Construct the options for the CLI editor and parse in the given arguments. + * + * @param args The command line arguments + */ + public ApexCommandLineArguments(final String[] args) { + // Set up the options with the default constructor + this(); + + // Parse the arguments + try { + parse(args); + } catch (final ApexException e) { + throw new ApexRuntimeException("parse error on Apex parameters"); + } + } + + /** + * Parse the command line options. + * + * @param args The command line arguments + * @return a string with a message for help and version, or null if there is no message + * @throws ApexException on command argument errors + */ + public String parse(final String[] args) throws ApexException { + // Clear all our arguments + setConfigurationFilePath(null); + setModelFilePath(null); + + CommandLine commandLine = null; + try { + commandLine = new DefaultParser().parse(options, args); + } catch (final ParseException e) { + throw new ApexException("invalid command line arguments specified : " + e.getMessage()); + } + + // Arguments left over after Commons CLI does its stuff + final String[] remainingArgs = commandLine.getArgs(); + + if (remainingArgs.length > 0 && commandLine.hasOption('c') || remainingArgs.length > 1) { + throw new ApexException("too many command line arguments specified : " + Arrays.toString(args)); + } + + if (remainingArgs.length == 1) { + configurationFilePath = remainingArgs[0]; + } + + if (commandLine.hasOption('h')) { + return help(ApexMain.class.getCanonicalName()); + } + + if (commandLine.hasOption('v')) { + return version(); + } + + if (commandLine.hasOption('c')) { + setConfigurationFilePath(commandLine.getOptionValue('c')); + } + + if (commandLine.hasOption('m')) { + setModelFilePath(commandLine.getOptionValue('m')); + } + + return null; + } + + /** + * Validate the command line options. + * + * @throws ApexException on command argument validation errors + */ + public void validate() throws ApexException { + validateReadableFile("Apex configuration", configurationFilePath); + + if (checkSetModelFilePath()) { + validateReadableFile("Apex model", modelFilePath); + } + } + + /** + * Print version information for Apex. + * + * @return the version string + */ + public String version() { + return ResourceUtils.getResourceAsString("version.txt"); + } + + /** + * Print help information for Apex. + * + * @param mainClassName the main class name + * @return the help string + */ + public String help(final String mainClassName) { + final HelpFormatter helpFormatter = new HelpFormatter(); + final StringWriter stringWriter = new StringWriter(); + final PrintWriter stringPW = new PrintWriter(stringWriter); + + helpFormatter.printHelp(stringPW, HELP_LINE_LENGTH, mainClassName + " [options...]", "options", options, 0, 0, + ""); + + return stringWriter.toString(); + } + + /** + * Gets the model file path. + * + * @return the model file path + */ + public String getModelFilePath() { + return ResourceUtils.getFilePath4Resource(modelFilePath); + } + + /** + * Sets the model file path. + * + * @param modelFilePath the model file path + */ + public void setModelFilePath(final String modelFilePath) { + this.modelFilePath = modelFilePath; + } + + /** + * Check set model file path. + * + * @return true, if check set model file path + */ + public boolean checkSetModelFilePath() { + return modelFilePath != null && modelFilePath.length() > 0; + } + + /** + * Gets the configuration file path. + * + * @return the configuration file path + */ + public String getConfigurationFilePath() { + return configurationFilePath; + } + + /** + * Gets the full expanded configuration file path. + * + * @return the configuration file path + */ + public String getFullConfigurationFilePath() { + return ResourceUtils.getFilePath4Resource(getConfigurationFilePath()); + } + + /** + * Sets the configuration file path. + * + * @param configurationFilePath the configuration file path + */ + public void setConfigurationFilePath(final String configurationFilePath) { + this.configurationFilePath = configurationFilePath; + + } + + /** + * Check set configuration file path. + * + * @return true, if check set configuration file path + */ + public boolean checkSetConfigurationFilePath() { + return configurationFilePath != null && configurationFilePath.length() > 0; + } + + /** + * Validate readable file. + * + * @param fileTag the file tag + * @param fileName the file name + * @throws ApexException the apex exception + */ + private void validateReadableFile(final String fileTag, final String fileName) throws ApexException { + if (fileName == null || fileName.length() == 0) { + throw new ApexException(fileTag + " file was not specified as an argument"); + } + + // The file name can refer to a resource on the local file system or on the class path + final URL fileURL = ResourceUtils.getURL4Resource(fileName); + if (fileURL == null) { + throw new ApexException(fileTag + " file \"" + fileName + "\" does not exist"); + } + + final File theFile = new File(fileURL.getPath()); + if (!theFile.exists()) { + throw new ApexException(fileTag + " file \"" + fileName + "\" does not exist"); + } + if (!theFile.isFile()) { + throw new ApexException(fileTag + " file \"" + fileName + "\" is not a normal file"); + } + if (!theFile.canRead()) { + throw new ApexException(fileTag + " file \"" + fileName + "\" is ureadable"); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEngineServiceHandler.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEngineServiceHandler.java new file mode 100644 index 000000000..ad7af9449 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEngineServiceHandler.java @@ -0,0 +1,88 @@ +/*- + * ============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.service.engine.main; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; +import org.onap.policy.apex.service.engine.engdep.EngDepMessagingService; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.runtime.EngineService; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * The Class ApexEngineServiceHandler holds the reference to the Apex engine service and the EngDep + * service for that engine. It also acts as an event receiver for asynchronous and synchronous + * events. + */ +public class ApexEngineServiceHandler { + // The logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexEngineServiceHandler.class); + + // The Apex engine service, the Apex engine itself + private final EngineService apexEngineService; + + // The interface between the Apex engine and Apex policy deployment for the Apex engine + private final EngDepMessagingService engDepService; + + /** + * Instantiates a new engine holder with its engine service and EngDep service. + * + * @param apexEngineService the apex engine service + * @param engDepService the EngDep service + */ + ApexEngineServiceHandler(final EngineService apexEngineService, final EngDepMessagingService engDepService) { + this.apexEngineService = apexEngineService; + this.engDepService = engDepService; + } + + /** + * This method forwards an event to the Apex service. + * + * @param apexEvent The event to forward to Apex + */ + public void forwardEvent(final ApexEvent apexEvent) { + try { + // Send the event to the engine runtime + apexEngineService.getEngineServiceEventInterface().sendEvent(apexEvent); + } catch (final Exception e) { + final String errorMessage = "error transferring event \"" + apexEvent.getName() + "\" to the Apex engine"; + LOGGER.debug(errorMessage, e); + throw new ApexActivatorRuntimeException(errorMessage, e); + } + } + + /** + * Terminate the Apex engine. + * + * @throws ApexException on termination errors + */ + public void terminate() throws ApexException { + // Shut down engine management + if (engDepService != null) { + engDepService.stop(); + } + + // Shut down each engine instance + if (apexEngineService != null) { + apexEngineService.stop(); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEventMarshaller.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEventMarshaller.java new file mode 100644 index 000000000..b4ba2ac3e --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEventMarshaller.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.service.engine.main; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProducer; +import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter; +import org.onap.policy.apex.service.engine.event.impl.EventProducerFactory; +import org.onap.policy.apex.service.engine.event.impl.EventProtocolFactory; +import org.onap.policy.apex.service.engine.runtime.ApexEventListener; +import org.onap.policy.apex.service.parameters.engineservice.EngineServiceParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This event marshaler handles events coming out of Apex and sends them on, handles threading, + * event queuing, transformations and sending using the configured sending technology. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexEventMarshaller implements ApexEventListener, Runnable { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexEventMarshaller.class); + + // Interval to wait between thread shutdown checks + private static final int MARSHALLER_SHUTDOWN_WAIT_INTERVAL = 10; + + // The amount of time to wait between polls of the event queue in milliseconds + private static final long EVENT_QUEUE_POLL_INTERVAL = 20; + + // The name of the marshaler + private final String name; + + // The engine service and producer parameters + private final EngineServiceParameters engineServiceParameters; + private final EventHandlerParameters producerParameters; + + // Apex event producer and event converter, all conversions are to and from string + // representation of events + private ApexEventProducer producer; + private ApexEventProtocolConverter converter; + + // Temporary event holder for events coming out of Apex + private final BlockingQueue<ApexEvent> queue = new LinkedBlockingQueue<>(); + + // The marshaler thread and stopping flag + private Thread marshallerThread; + private boolean stopOrderedFlag = false; + + /** + * Create the marshaler. + * + * @param name the name of the marshaler + * @param engineServiceParameters the engine service parameters for this Apex engine + * @param producerParameters the producer parameters for this specific marshaler + */ + public ApexEventMarshaller(final String name, final EngineServiceParameters engineServiceParameters, + final EventHandlerParameters producerParameters) { + this.name = name; + this.engineServiceParameters = engineServiceParameters; + this.producerParameters = producerParameters; + } + + /** + * Configure the marshaler by setting up the producer and event converter and initialize the + * thread for event sending. + * + * @throws ApexActivatorException on errors initializing the producer + * @throws ApexEventException on errors initializing event handling + */ + public void init() throws ApexActivatorException, ApexEventException { + // Create the producer for sending events and the converter for transforming events + producer = new EventProducerFactory().createProducer(name, producerParameters); + + // Initialize the producer + producer.init(this.name, this.producerParameters); + + // Create the converter for transforming events + converter = new EventProtocolFactory().createConverter(name, producerParameters.getEventProtocolParameters()); + + // Configure and start the event sending thread + final String threadName = + engineServiceParameters.getEngineKey().getName() + ':' + this.getClass().getName() + ':' + this.name; + marshallerThread = new ApplicationThreadFactory(threadName).newThread(this); + marshallerThread.setDaemon(true); + marshallerThread.start(); + } + + /** + * Gets the name of the marshaler. + * + * @return the marshaler name + */ + public String getName() { + return name; + } + + /** + * Gets the technology specific producer for this marshaler. + * + * @return the producer + */ + public ApexEventProducer getProducer() { + return producer; + } + + /** + * Gets the event protocol converter for this marshaler. + * + * @return the event protocol converter + */ + public ApexEventProtocolConverter getConverter() { + return converter; + } + + /** + * Callback method called on implementations of this interface when Apex emits an event. + * + * @param apexEvent the apex event emitted by Apex + */ + @Override + public void onApexEvent(final ApexEvent apexEvent) { + // Check if we are filtering events on this marshaler, if so check the event name against + // the filter + if (producerParameters.isSetEventNameFilter() + && !apexEvent.getName().matches(producerParameters.getEventNameFilter())) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("onMessage(): event {} not processed, filtered out by filter", apexEvent, + producerParameters.getEventNameFilter()); + } + + // Ignore this event + return; + } + + // Push the event onto the queue for handling + try { + queue.put(apexEvent); + } catch (final InterruptedException e) { + LOGGER.warn("Failed to queue the event: " + apexEvent, e); + } + } + + /** + * Run a thread that runs forever (well until system termination anyway) and listens for + * outgoing events on the queue. + */ + @Override + public void run() { + // Run until interrupted + while (marshallerThread.isAlive() && !stopOrderedFlag) { + try { + // Take the next event from the queue + final ApexEvent apexEvent = queue.poll(EVENT_QUEUE_POLL_INTERVAL, TimeUnit.MILLISECONDS); + if (apexEvent == null) { + continue; + } + + // Process the next Apex event from the queue + final Object event = converter.fromApexEvent(apexEvent); + + producer.sendEvent(apexEvent.getExecutionID(), apexEvent.getName(), event); + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("event sent : " + apexEvent.toString()); + } + } catch (final InterruptedException e) { + LOGGER.debug("Thread interrupted, Reason {}", e.getMessage()); + break; + } catch (final Exception e) { + LOGGER.warn("Error while forwarding events for " + marshallerThread.getName(), e); + continue; + } + } + + // Stop event production if we are not synchronized,;in the synchronized case, the producer + // takes care of its own cleanup. + producer.stop(); + } + + /** + * Get the marshaler thread. + * + * @return the marshaler thread + */ + public Thread getThread() { + return marshallerThread; + } + + /** + * Stop the Apex event marshaller's event producer using its termination mechanism. + */ + public void stop() { + LOGGER.entry("shutting down Apex event marshaller . . ."); + + // Order the stop + stopOrderedFlag = true; + + // Wait for thread shutdown + while (marshallerThread.isAlive()) { + ThreadUtilities.sleep(MARSHALLER_SHUTDOWN_WAIT_INTERVAL); + } + + LOGGER.exit("shut down Apex event marshaller"); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEventUnmarshaller.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEventUnmarshaller.java new file mode 100644 index 000000000..a9385751e --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexEventUnmarshaller.java @@ -0,0 +1,323 @@ +/*- + * ============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.service.engine.main; + +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; + +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexEventConsumer; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter; +import org.onap.policy.apex.service.engine.event.ApexEventReceiver; +import org.onap.policy.apex.service.engine.event.PeeredReference; +import org.onap.policy.apex.service.engine.event.SynchronousEventCache; +import org.onap.policy.apex.service.engine.event.impl.EventConsumerFactory; +import org.onap.policy.apex.service.engine.event.impl.EventProtocolFactory; +import org.onap.policy.apex.service.parameters.engineservice.EngineServiceParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This event unmarshaler handles events coming into Apex, handles threading, event queuing, + * transformation and receiving using the configured receiving technology. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexEventUnmarshaller implements ApexEventReceiver, Runnable { + // Get a reference to the logger + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexEventUnmarshaller.class); + + // Interval to wait between thread shutdown checks + private static final int UNMARSHALLER_SHUTDOWN_WAIT_INTERVAL = 10; + + // The amount of time to wait between polls of the event queue in milliseconds + private static final long EVENT_QUEUE_POLL_INTERVAL = 20; + + // The name of the unmarshaler + private final String name; + + // The engine service and consumer parameters + private final EngineServiceParameters engineServiceParameters; + private final EventHandlerParameters consumerParameters; + + // The engine service handler to use for forwarding on of unmarshalled events + private ApexEngineServiceHandler engineServiceHandler; + + // Apex event producer and event converter, all events are sent as string representations + private ApexEventConsumer consumer; + private ApexEventProtocolConverter converter; + + // Temporary event holder for events going into Apex + private final BlockingQueue<ApexEvent> queue = new LinkedBlockingQueue<>(); + + // The unmarshaler thread and stopping flag + private Thread unmarshallerThread = null; + private boolean stopOrderedFlag = false; + + /** + * Create the unmarshaler. + * + * @param name the name of the unmarshaler + * @param engineServiceParameters the engine service parameters for this Apex engine + * @param consumerParameters the consumer parameters for this specific unmarshaler + */ + public ApexEventUnmarshaller(final String name, final EngineServiceParameters engineServiceParameters, + final EventHandlerParameters consumerParameters) { + this.name = name; + this.engineServiceParameters = engineServiceParameters; + this.consumerParameters = consumerParameters; + } + + /** + * Configure the consumer and initialize the thread for event sending. + * + * @param incomingEngineServiceHandler the Apex engine service handler for passing events to + * Apex + * @throws ApexEventException on errors initializing event handling + */ + public void init(final ApexEngineServiceHandler incomingEngineServiceHandler) throws ApexEventException { + this.engineServiceHandler = incomingEngineServiceHandler; + + // Create the consumer for sending events and the converter for transforming events + consumer = new EventConsumerFactory().createConsumer(name, consumerParameters); + consumer.init(this.name, this.consumerParameters, this); + + converter = new EventProtocolFactory().createConverter(name, consumerParameters.getEventProtocolParameters()); + } + + /** + * Start the unmarshaler and consumer threads. + */ + public void start() { + // Start the consumer + consumer.start(); + + // Configure and start the event reception thread + final String threadName = + engineServiceParameters.getEngineKey().getName() + ":" + this.getClass().getName() + ":" + name; + unmarshallerThread = new ApplicationThreadFactory(threadName).newThread(this); + unmarshallerThread.setDaemon(true); + unmarshallerThread.start(); + } + + /** + * Gets the name of the unmarshaler. + * + * @return the unmarshaler name + */ + public String getName() { + return name; + } + + /** + * Gets the technology specific consumer for this unmarshaler. + * + * @return the consumer + */ + public ApexEventConsumer getConsumer() { + return consumer; + } + + /** + * Gets the event protocol converter for this unmarshaler. + * + * @return the event protocol converter + */ + public ApexEventProtocolConverter getConverter() { + return converter; + } + + /** + * Connect a synchronous unmarshaler with a synchronous marshaler. + * + * @param peeredMode the peered mode under which the unmarshaler and marshaler are connected + * @param peeredMarshaller the synchronous marshaler to connect with + */ + public void connectMarshaler(final EventHandlerPeeredMode peeredMode, final ApexEventMarshaller peeredMarshaller) { + switch (peeredMode) { + case SYNCHRONOUS: + // To connect a synchronous unmarshaler and marshaler, we create a synchronous event + // cache on the consumer/producer pair + new SynchronousEventCache(peeredMode, consumer, peeredMarshaller.getProducer(), + consumerParameters.getPeerTimeout(EventHandlerPeeredMode.SYNCHRONOUS)); + return; + + case REQUESTOR: + new PeeredReference(peeredMode, consumer, peeredMarshaller.getProducer()); + return; + + default: + return; + } + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.event.ApexEventReceiver#receiveEvent(java.lang.Object) + */ + @Override + public void receiveEvent(final Object event) throws ApexEventException { + receiveEvent(0, event, true); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventReceiver#receiveEvent(long, + * java.lang.Object) + */ + @Override + public void receiveEvent(final long executionId, final Object event) throws ApexEventException { + receiveEvent(executionId, event, false); + } + + /** + * Receive an event from a consumer, convert its protocol and forward it to Apex. + * + * @param executionId the execution id the incoming execution ID + * @param event the event in its native format + * @param generateExecutionId if true, let Apex generate the execution ID, if false, use the + * incoming execution ID + * @throws ApexEventException on unmarshaling errors on events + */ + private void receiveEvent(final long executionId, final Object event, final boolean generateExecutionId) + throws ApexEventException { + // Push the event onto the queue + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("onMessage(): event received: {}", event.toString()); + } + + // Convert the incoming events to Apex events + try { + final List<ApexEvent> apexEventList = converter.toApexEvent(consumerParameters.getEventName(), event); + for (final ApexEvent apexEvent : apexEventList) { + // Check if we are filtering events on this unmarshaler, if so check the event name + // against the filter + if (consumerParameters.isSetEventNameFilter() + && !apexEvent.getName().matches(consumerParameters.getEventNameFilter())) { + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("onMessage(): event {} not processed, filtered out by filter", apexEvent, + consumerParameters.getEventNameFilter()); + } + + // Ignore this event + continue; + } + + if (!generateExecutionId) { + apexEvent.setExecutionID(executionId); + } + + // Enqueue the event + queue.add(apexEvent); + + // Cache synchronized events that are sent + if (consumerParameters.isPeeredMode(EventHandlerPeeredMode.SYNCHRONOUS)) { + final SynchronousEventCache synchronousEventCache = + (SynchronousEventCache) consumer.getPeeredReference(EventHandlerPeeredMode.SYNCHRONOUS); + synchronousEventCache.cacheSynchronizedEventToApex(apexEvent.getExecutionID(), apexEvent); + } + } + } catch (final ApexException e) { + final String errorMessage = "Error while converting event into an ApexEvent for " + name + ": " + + e.getMessage() + ", Event=" + event; + LOGGER.warn(errorMessage, e); + throw new ApexEventException(errorMessage, e); + } + } + + /** + * Run a thread that runs forever (well until system termination anyway) and listens for + * incoming events on the queue. + */ + @Override + public void run() { + // Run until interruption + while (unmarshallerThread.isAlive() && !stopOrderedFlag) { + try { + // Take the next event from the queue + final ApexEvent apexEvent = queue.poll(EVENT_QUEUE_POLL_INTERVAL, TimeUnit.MILLISECONDS); + if (apexEvent == null) { + continue; + } + + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("event received {}", apexEvent.toString()); + } + + // Pass the event to the activator for forwarding to Apex + engineServiceHandler.forwardEvent(apexEvent); + } catch (final InterruptedException e) { + LOGGER.warn("BatchProcessor thread interrupted, Reason {}", e.getMessage()); + break; + } catch (final Exception e) { + LOGGER.warn("Error while forwarding events for " + unmarshallerThread.getName(), e); + continue; + } + } + + // Stop event production + consumer.stop(); + } + + /** + * Get the unmarshaler thread. + * + * @return the unmarshaler thread + */ + public Thread getThread() { + return unmarshallerThread; + } + + /** + * Stop the Apex event unmarshaller's event producer using its termination mechanism. + */ + public void stop() { + LOGGER.entry("shutting down Apex event unmarshaller . . ."); + + // Order the stop + stopOrderedFlag = true; + + // Order a stop on the synchronous cache if one exists + if (consumerParameters != null && consumerParameters.isPeeredMode(EventHandlerPeeredMode.SYNCHRONOUS)) { + if (consumer.getPeeredReference(EventHandlerPeeredMode.SYNCHRONOUS) != null) { + ((SynchronousEventCache) consumer.getPeeredReference(EventHandlerPeeredMode.SYNCHRONOUS)).stop(); + } + } + + // Wait for thread shutdown + while (unmarshallerThread != null && unmarshallerThread.isAlive()) { + ThreadUtilities.sleep(UNMARSHALLER_SHUTDOWN_WAIT_INTERVAL); + } + + LOGGER.exit("shut down Apex event unmarshaller"); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexMain.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexMain.java new file mode 100644 index 000000000..1b5603482 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/ApexMain.java @@ -0,0 +1,169 @@ +/*- + * ============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.service.engine.main; + +import java.util.Arrays; +import java.util.Map.Entry; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; +import org.onap.policy.apex.service.parameters.ApexParameterHandler; +import org.onap.policy.apex.service.parameters.ApexParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * This class initiates Apex as a complete service from the command line. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexMain { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexMain.class); + + // The Apex Activator that activates the Apex engine + private ApexActivator activator; + + // The parameters read in from JSON + private ApexParameters parameters; + + /** + * Instantiates the Apex Apex service. + * + * @param args the commaind line arguments + */ + public ApexMain(final String[] args) { + System.out.println("Starting Apex service with parameters " + Arrays.toString(args) + " . . ."); + LOGGER.entry("Starting Apex service with parameters " + Arrays.toString(args) + " . . ."); + + // Check the arguments + final ApexCommandLineArguments arguments = new ApexCommandLineArguments(); + try { + // The arguments return a string if there is a message to print and we should exit + final String argumentMessage = arguments.parse(args); + if (argumentMessage != null) { + LOGGER.info(argumentMessage); + System.out.println(argumentMessage); + return; + } + + // Validate that the arguments are sane + arguments.validate(); + } catch (final ApexException e) { + System.err.println("start of Apex service failed: " + e.getMessage()); + LOGGER.error("start of Apex service failed", e); + System.err.println(arguments.help(ApexMain.class.getCanonicalName())); + return; + } + + // Read the parameters + try { + parameters = new ApexParameterHandler().getParameters(arguments); + } catch (final Exception e) { + System.err.println("start of Apex service failed\n" + e.getMessage()); + LOGGER.error("start of Apex service failed", e); + return; + } + + // Set the name of the event handler parameters for producers and consumers + for (final Entry<String, EventHandlerParameters> ehParameterEntry : parameters.getEventOutputParameters() + .entrySet()) { + if (!ehParameterEntry.getValue().checkSetName()) { + ehParameterEntry.getValue().setName(ehParameterEntry.getKey()); + } + } + for (final Entry<String, EventHandlerParameters> ehParameterEntry : parameters.getEventInputParameters() + .entrySet()) { + if (!ehParameterEntry.getValue().checkSetName()) { + ehParameterEntry.getValue().setName(ehParameterEntry.getKey()); + } + } + + // Now, create the activator for the Apex service + activator = new ApexActivator(parameters); + + // Start the activator + try { + activator.initialize(); + } catch (final ApexActivatorException e) { + System.err.println("start of Apex service failed, used parameters are " + Arrays.toString(args)); + e.printStackTrace(System.err); + LOGGER.error("start of Apex service failed, used parameters are " + Arrays.toString(args), e); + return; + } + + // Add a shutdown hook to shut everything down in an orderly manner + Runtime.getRuntime().addShutdownHook(new ApexMainShutdownHookClass()); + LOGGER.exit("Started Apex"); + System.out.println("Started Apex service"); + } + + /** + * Get the parameters specified in JSON. + * + * @return the parameters + */ + public ApexParameters getParameters() { + return parameters; + } + + /** + * Shut down Execution. + * + * @throws ApexException on shutdown errors + */ + public void shutdown() throws ApexException { + if (activator != null) { + activator.terminate(); + } + } + + /** + * The Class ApexMainShutdownHookClass terminates the Apex engine for the Apex service when its + * run method is called. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ + private class ApexMainShutdownHookClass extends Thread { + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + try { + // Shutdown the Apex engine and wait for everything to stop + activator.terminate(); + } catch (final ApexException e) { + LOGGER.warn("error occured during shut down of the Apex service", e); + } + } + } + + /** + * The main method. + * + * @param args the arguments + */ + public static void main(final String[] args) { + new ApexMain(args); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/package-info.java new file mode 100644 index 000000000..488d5ab19 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/main/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 APEX as a complete service together with all its context, executor and event + * handling plugins. A main method to allow APEX execution from the command line is also provided. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.main; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/package-info.java new file mode 100644 index 000000000..cdd6c5e16 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/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 Java APIs for Apex. APIs are provided to give access to the APEX engine runtime, to give + * access to APEX event handling, and to give access to the ApexEngDep protocol for engine + * management. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/ApexEventListener.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/ApexEventListener.java new file mode 100644 index 000000000..a498bcff8 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/ApexEventListener.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.service.engine.runtime; + +import org.onap.policy.apex.service.engine.event.ApexEvent; + +/** + * The listener interface for receiving apexEvent events. The class that is interested in processing + * a apexEvent event implements this interface, and the object created with that class is registered + * with a component using the component's {@code addApexEventListener} method. When the apexEvent + * event occurs, that object's appropriate method is invoked. + * + * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com) + */ +public interface ApexEventListener { + + /** + * Callback method called on implementations of this interface when APEX emits an event. + * + * @param apexEvent the apex event emitted by APEX + */ + void onApexEvent(ApexEvent apexEvent); +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/EngineService.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/EngineService.java new file mode 100644 index 000000000..e6fc332f2 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/EngineService.java @@ -0,0 +1,214 @@ +/*- + * ============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.service.engine.runtime; + +import java.util.Collection; + +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.AxEngineState; +import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel; + +/** + * The administration interface for Apex engine users. Apex engine implementations expose this + * interface and external users use it to manage Apex engines. + * + * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com) + */ +public interface EngineService { + /** + * A method to attach a listener to the engine. + * + * @param listenerName a unique name for the listener + * @param listener is a callback interface to the engine. + */ + void registerActionListener(String listenerName, ApexEventListener listener); + + /** + * A method to detach a listener from the engine. + * + * @param listenerName the unique name of the listener to deregister + */ + void deregisterActionListener(String listenerName); + + /** + * This method gets the current runtime information for the running Apex engines. + * + * @return the engine service event interface + */ + EngineServiceEventInterface getEngineServiceEventInterface(); + + /** + * Gets the key of the engine service or worker. + * + * @return the key + */ + AxArtifactKey getKey(); + + /** + * This method gets the keys of the engines on the engine service. + * + * @return the engine keys + */ + Collection<AxArtifactKey> getEngineKeys(); + + /** + * The the key of the Apex model the engine service is running on. + * + * @return the Apex model key + */ + AxArtifactKey getApexModelKey(); + + /** + * This method updates the Apex model on Apex execution engines using a string representation of + * the model. + * + * @param engineServiceKey The key of the engine service on which to update the model + * @param apexModelString the apex model string + * @param forceFlag if true, model updates will be executed even on incompatible models + * (different model names) and versions (different model version) + * @throws ApexException on model update errors + */ + void updateModel(AxArtifactKey engineServiceKey, String apexModelString, boolean forceFlag) throws ApexException; + + /** + * This method updates the Apex model on Apex execution engines using a policy model as input. + * + * @param engineServiceKey The key of the engine service on which to update the model + * @param apexModel is a policy definition model + * @param forceFlag if true, model updates will be executed even on incompatible models + * (different model names) and versions (different model version) + * @throws ApexException on model update errors + */ + void updateModel(AxArtifactKey engineServiceKey, AxPolicyModel apexModel, boolean forceFlag) throws ApexException; + + /** + * This method returns the state of an engine service or engine. + * + * @return The engine service or engine state + */ + AxEngineState getState(); + + /** + * This method starts all Apex engines in the engine service. + * + * @throws ApexException on start errors + */ + void startAll() throws ApexException; + + /** + * This method starts an Apex engine in the engine service. + * + * @param engineKey The key of the Apex engine to start + * @throws ApexException on start errors + */ + void start(AxArtifactKey engineKey) throws ApexException; + + /** + * This method stops all Apex engines in the engine service. + * + * @throws ApexException on stop errors + */ + void stop() throws ApexException; + + /** + * This method stops an Apex engine in the engine service. + * + * @param engineKey The key of the Apex engine to stop + * @throws ApexException on stop errors + */ + void stop(AxArtifactKey engineKey) throws ApexException; + + /** + * This method checks if all Apex engines in the engine service are started. + * <p> + * Note: an engine can be both not stopped and not started, for example, when it is starting or + * stopping + * + * @return true if all Apex engines in the engine service are started. + */ + boolean isStarted(); + + /** + * This method checks if an Apex engine in the engine service is started. + * <p> + * Note: an engine can be both not stopped and not started, for example, when it is starting or + * stopping + * + * @param engineKey The key of the Apex engine to check + * @return true if all Apex engines in the engine service are started. + */ + boolean isStarted(AxArtifactKey engineKey); + + /** + * This method checks if all Apex engines in the engine service are stopped. + * <p> + * Note: an engine can be both not stopped and not started, for example, when it is starting or + * stopping + * + * @return true if all Apex engines in the engine service are stopped. + */ + boolean isStopped(); + + /** + * This method checks if an Apex engine in the engine service is stopped. + * <p> + * Note: an engine can be both not stopped and not started, for example, when it is starting or + * stopping + * + * @param engineKey The key of the Apex engine to check + * @return true if all Apex engines in the engine service are stopped. + */ + boolean isStopped(AxArtifactKey engineKey); + + /** + * This method starts periodic event generation. + * + * @param period The period in milliseconds between periodic events + * @throws ApexException On periodic event start errors + */ + void startPeriodicEvents(long period) throws ApexException; + + /** + * This method stops periodic event generation. + * + * @throws ApexException On periodic event stop errors + */ + void stopPeriodicEvents() throws ApexException; + + /** + * This method gets the status of an Apex engine in the engine service. + * + * @param engineKey the engine key + * @return the engine runtime information + * @throws ApexException on status read errors + */ + String getStatus(AxArtifactKey engineKey) throws ApexException; + + /** + * This method gets the runtime information of all Apex engines in the engine service. + * + * @param engineKey the engine key + * @return the engine runtime information + * @throws ApexException on runtime information read errors + */ + String getRuntimeInfo(AxArtifactKey engineKey) throws ApexException; +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/EngineServiceEventInterface.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/EngineServiceEventInterface.java new file mode 100644 index 000000000..8e4cb82e2 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/EngineServiceEventInterface.java @@ -0,0 +1,39 @@ +/*- + * ============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.service.engine.runtime; + +import org.onap.policy.apex.service.engine.event.ApexEvent; + +/** + * The run time interface for APEX engine users. APEX engine implementations expose this interface + * and external users use it to send events to the engine. + * + * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com), John Keeney + * (john.keeney@ericsson.com) + */ +public interface EngineServiceEventInterface { + /** + * This method forwards an event to the APEX engine. + * + * @param event is an instance {@link ApexEvent} + */ + void sendEvent(ApexEvent event); +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EnEventListenerImpl.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EnEventListenerImpl.java new file mode 100644 index 000000000..abd6db2d9 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EnEventListenerImpl.java @@ -0,0 +1,71 @@ +/*- + * ============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.service.engine.runtime.impl; + +import org.onap.policy.apex.core.engine.engine.EnEventListener; +import org.onap.policy.apex.core.engine.event.EnEvent; +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.impl.enevent.ApexEvent2EnEventConverter; +import org.onap.policy.apex.service.engine.runtime.ApexEventListener; + +/** + * The Class EnEventListenerImpl is used by the Apex engine implementation to listen for events + * coming from the core APEX engine. This listener converts the {@link EnEvent} instances into + * {@link ApexEvent} instances using an {@link ApexEvent2EnEventConverter} instance and forwards the + * events to an {@link ApexEventListener} instance for outputting to listening applications. The + * {@link ApexEventListener} is implemented in the external application communicating with Apex. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public final class EnEventListenerImpl implements EnEventListener { + // Listener for ApexEvents + private ApexEventListener apexEventListener = null; + + // Converter for Engine events to Apex Events + private ApexEvent2EnEventConverter apexEnEventConverter = null; + + /** + * Instantiates a new listener implementation. + * + * @param apexEventListener the apex event listener + * @param apexEnEventConverter the ApexEvent to enEvent converter + */ + public EnEventListenerImpl(final ApexEventListener apexEventListener, + final ApexEvent2EnEventConverter apexEnEventConverter) { + this.apexEventListener = apexEventListener; + this.apexEnEventConverter = apexEnEventConverter; + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.core.engine.engine.EnEventListener#onEnEvent(org.onap.policy.apex.core. + * engine.event.EnEvent) + */ + @Override + public void onEnEvent(final EnEvent enEvent) throws ApexException { + for (final ApexEvent apexEvent : apexEnEventConverter.toApexEvent(enEvent.getName(), enEvent)) { + apexEventListener.onApexEvent(apexEvent); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EngineServiceImpl.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EngineServiceImpl.java new file mode 100644 index 000000000..24d8263f4 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EngineServiceImpl.java @@ -0,0 +1,682 @@ +/*- + * ============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.service.engine.runtime.impl; + +import java.io.ByteArrayInputStream; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.onap.policy.apex.context.ContextException; +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +import org.onap.policy.apex.core.infrastructure.threading.ThreadUtilities; +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.ApexModelException; +import org.onap.policy.apex.model.basicmodel.handling.ApexModelReader; +import org.onap.policy.apex.model.basicmodel.service.ModelService; +import org.onap.policy.apex.model.enginemodel.concepts.AxEngineState; +import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexPeriodicEventGenerator; +import org.onap.policy.apex.service.engine.runtime.ApexEventListener; +import org.onap.policy.apex.service.engine.runtime.EngineService; +import org.onap.policy.apex.service.engine.runtime.EngineServiceEventInterface; +import org.onap.policy.apex.service.parameters.engineservice.EngineServiceParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +/** + * The Class EngineServiceImpl controls a thread pool that runs a set of Apex engine workers, each + * of which is running on an identical Apex model. This class handles the management of the engine + * worker instances, their threads, and event forwarding to and from the engine workers. + * + * @author Sajeevan Achuthan (sajeevan.achuthan@ericsson.com) + * @author Liam Fallon (liam.fallon@ericsson.com) + * @author John Keeney (john.keeney@ericsson.com) + */ +public final class EngineServiceImpl implements EngineService, EngineServiceEventInterface { + // Logging static variables + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EngineServiceImpl.class); + private static final boolean DEBUG_ENABLED = LOGGER.isDebugEnabled(); + + // Constants for timing + private static final long MAX_START_WAIT_TIME = 5000; // 5 seconds + private static final long MAX_STOP_WAIT_TIME = 5000; // 5 seconds + private static final int ENGINE_SERVICE_STOP_START_WAIT_INTERVAL = 200; + + // The ID of this engine + private AxArtifactKey engineServiceKey = null; + + // The Apex engine workers this engine service is handling + private final Map<AxArtifactKey, EngineService> engineWorkerMap = + Collections.synchronizedMap(new LinkedHashMap<AxArtifactKey, EngineService>()); + + // Event queue for events being sent into the Apex engines, it used by all engines within a + // group. + private final BlockingQueue<ApexEvent> queue = new LinkedBlockingQueue<>(); + + // Thread factory for thread management + private final ApplicationThreadFactory tFactory = new ApplicationThreadFactory("apex-engine-service", 512); + + // Periodic event generator and its period in milliseconds + private ApexPeriodicEventGenerator periodicEventGenerator = null; + private long periodicEventPeriod; + + /** + * This constructor instantiates engine workers and adds them to the set of engine workers to be + * managed. The constructor is private to prevent subclassing. + * + * @param engineServiceKey the engine service key + * @param incomingThreadCount the thread count, the number of engine workers to start + * @param periodicEventPeriod the period in milliseconds at which periodic events are generated + * @throws ApexException on worker instantiation errors + */ + private EngineServiceImpl(final AxArtifactKey engineServiceKey, final int incomingThreadCount, + final long periodicEventPeriod) throws ApexException { + LOGGER.entry(engineServiceKey, incomingThreadCount); + + this.engineServiceKey = engineServiceKey; + this.periodicEventPeriod = periodicEventPeriod; + + int threadCount = incomingThreadCount; + if (threadCount <= 0) { + // Just start one engine worker + threadCount = 1; + } + + // Start engine workers + for (int engineCounter = 0; engineCounter < threadCount; engineCounter++) { + final AxArtifactKey engineWorkerKey = + new AxArtifactKey(engineServiceKey.getName() + '-' + engineCounter, engineServiceKey.getVersion()); + engineWorkerMap.put(engineWorkerKey, new EngineWorker(engineWorkerKey, queue, tFactory)); + LOGGER.info("Created apex engine {} .", engineWorkerKey.getID()); + } + + LOGGER.info("APEX service created."); + LOGGER.exit(); + } + + /** + * Create an Apex Engine Service instance. This method is deprecated and will be removed in the + * next version. + * + * @param engineServiceKey the engine service key + * @param threadCount the thread count, the number of engine workers to start + * @return the Engine Service instance + * @throws ApexException on worker instantiation errors + * @deprecated Do not use this version. Use {@link #create(EngineServiceParameters)} + */ + @Deprecated + public static EngineServiceImpl create(final AxArtifactKey engineServiceKey, final int threadCount) + throws ApexException { + // Check if the Apex model specified is sane + if (engineServiceKey == null) { + LOGGER.warn("engine service key is null"); + throw new ApexException("engine service key is null"); + } + return new EngineServiceImpl(engineServiceKey, threadCount, 0); + } + + /** + * Create an Apex Engine Service instance. This method does not load the policy so + * {@link #updateModel(AxArtifactKey, AxPolicyModel, boolean)} or + * {@link #updateModel(AxArtifactKey, AxPolicyModel, boolean)} must be used to load a model. + * This method does not start the Engine Service so {@link #start(AxArtifactKey)} or + * {@link #startAll()} must be used. + * + * @param config the configuration for this Apex Engine Service. + * @return the Engine Service instance + * @throws ApexException on worker instantiation errors + */ + public static EngineServiceImpl create(final EngineServiceParameters config) throws ApexException { + if (config == null) { + LOGGER.warn("Engine service configuration parameters is null"); + throw new ApexException("engine service configuration parameters is null"); + } + final String validation = config.validate(); + if (validation != null && validation.length() > 0) { + LOGGER.warn("Invalid engine service configuration parameters: " + validation); + throw new ApexException("Invalid engine service configuration parameters: " + validation); + } + final AxArtifactKey engineServiceKey = config.getEngineKey(); + final int threadCount = config.getInstanceCount(); + + // Check if the Apex model specified is sane + if (engineServiceKey == null) { + LOGGER.warn("engine service key is null"); + throw new ApexException("engine service key is null"); + } + + return new EngineServiceImpl(engineServiceKey, threadCount, config.getPeriodicEventPeriod()); + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#registerActionListener(java.lang. + * String, com.ericsson.apex.service.engine.runtime.ApexEventListener) + */ + @Override + public void registerActionListener(final String listenerName, final ApexEventListener apexEventListener) { + LOGGER.entry(apexEventListener); + + // Register the Apex event listener on all engine workers, each worker will return Apex + // events to the listening application + for (final EngineService engineWorker : engineWorkerMap.values()) { + engineWorker.registerActionListener(listenerName, apexEventListener); + } + + LOGGER.info("Added the action listener to the engine"); + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#deregisterActionListener(java.lang. + * String) + */ + @Override + public void deregisterActionListener(final String listenerName) { + LOGGER.entry(listenerName); + + // Register the Apex event listener on all engine workers, each worker will return Apex + // events to the listening application + for (final EngineService engineWorker : engineWorkerMap.values()) { + engineWorker.deregisterActionListener(listenerName); + } + + LOGGER.info("Removed the action listener from the engine"); + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#getEngineServiceEventInterface() + */ + @Override + public EngineServiceEventInterface getEngineServiceEventInterface() { + return this; + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#getKey() + */ + @Override + public AxArtifactKey getKey() { + return engineServiceKey; + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#getInfo() + */ + @Override + public Collection<AxArtifactKey> getEngineKeys() { + return engineWorkerMap.keySet(); + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#getApexModelKey() + */ + @Override + public AxArtifactKey getApexModelKey() { + if (engineWorkerMap.size() == 0) { + return null; + } + + return engineWorkerMap.entrySet().iterator().next().getValue().getApexModelKey(); + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#updateModel(com.ericsson.apex.model. + * basicmodel.concepts.AxArtifactKey, java.lang.String, boolean) + */ + @Override + public void updateModel(final AxArtifactKey incomingEngineServiceKey, final String apexModelString, + final boolean forceFlag) throws ApexException { + // Check if the Apex model specified is sane + if (apexModelString == null || apexModelString.trim().length() == 0) { + LOGGER.warn( + "model for updating on engine service with key " + incomingEngineServiceKey.getID() + " is empty"); + throw new ApexException( + "model for updating on engine service with key " + incomingEngineServiceKey.getID() + " is empty"); + } + + // Read the Apex model into memory using the Apex Model Reader + AxPolicyModel apexPolicyModel = null; + try { + final ApexModelReader<AxPolicyModel> modelReader = new ApexModelReader<>(AxPolicyModel.class); + apexPolicyModel = modelReader.read(new ByteArrayInputStream(apexModelString.getBytes())); + } catch (final ApexModelException e) { + LOGGER.error("failed to unmarshal the apex model on engine service " + incomingEngineServiceKey.getID(), e); + throw new ApexException( + "failed to unmarshal the apex model on engine service " + incomingEngineServiceKey.getID(), e); + } + + if (apexPolicyModel == null) { + LOGGER.error("apex model null on engine service " + incomingEngineServiceKey.getID()); + throw new ApexException("apex model null on engine service " + incomingEngineServiceKey.getID()); + } + + // Update the model + updateModel(incomingEngineServiceKey, apexPolicyModel, forceFlag); + + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#updateModel(com.ericsson.apex.model. + * basicmodel.concepts.AxArtifactKey, + * com.ericsson.apex.model.policymodel.concepts.AxPolicyModel, boolean) + */ + @Override + public void updateModel(final AxArtifactKey incomingEngineServiceKey, final AxPolicyModel apexModel, + final boolean forceFlag) throws ApexException { + LOGGER.entry(incomingEngineServiceKey); + + // Check if the Apex model specified is sane + if (apexModel == null) { + LOGGER.warn( + "model for updating on engine service with key " + incomingEngineServiceKey.getID() + " is null"); + throw new ApexException( + "model for updating on engine service with key " + incomingEngineServiceKey.getID() + " is null"); + } + + // Check if the key on the update request is correct + if (!this.engineServiceKey.equals(incomingEngineServiceKey)) { + LOGGER.warn("engine service key " + incomingEngineServiceKey.getID() + " does not match the key" + + engineServiceKey.getID() + " of this engine service"); + throw new ApexException("engine service key " + incomingEngineServiceKey.getID() + " does not match the key" + + engineServiceKey.getID() + " of this engine service"); + } + + // Check model compatibility + if (ModelService.existsModel(AxPolicyModel.class)) { + // The current policy model may or may not be defined + final AxPolicyModel currentModel = ModelService.getModel(AxPolicyModel.class); + if (!currentModel.getKey().isCompatible(apexModel.getKey())) { + if (forceFlag) { + LOGGER.warn("apex model update forced, supplied model with key \"" + apexModel.getKey().getID() + + "\" is not a compatible model update from the existing engine model with key \"" + + currentModel.getKey().getID() + "\""); + } else { + throw new ContextException( + "apex model update failed, supplied model with key \"" + apexModel.getKey().getID() + + "\" is not a compatible model update from the existing engine model with key \"" + + currentModel.getKey().getID() + "\""); + } + } + } + + final boolean wasstopped = isStopped(); + + if (!wasstopped) { + // Stop all engines on this engine service + stop(); + final long stoptime = System.currentTimeMillis(); + while (!isStopped() && System.currentTimeMillis() - stoptime < MAX_STOP_WAIT_TIME) { + ThreadUtilities.sleep(ENGINE_SERVICE_STOP_START_WAIT_INTERVAL); + } + // Check if all engines are stopped + final StringBuilder notStoppedEngineIDBuilder = new StringBuilder(); + for (final Entry<AxArtifactKey, EngineService> engineWorkerEntry : engineWorkerMap.entrySet()) { + if (engineWorkerEntry.getValue().getState() != AxEngineState.STOPPED) { + notStoppedEngineIDBuilder.append(engineWorkerEntry.getKey().getID()); + notStoppedEngineIDBuilder.append('('); + notStoppedEngineIDBuilder.append(engineWorkerEntry.getValue().getState()); + notStoppedEngineIDBuilder.append(") "); + } + } + if (notStoppedEngineIDBuilder.length() > 0) { + final String errorString = "cannot update model on engine service with key " + + incomingEngineServiceKey.getID() + ", engines not stopped after " + MAX_STOP_WAIT_TIME + + "ms are: " + notStoppedEngineIDBuilder.toString().trim(); + LOGGER.warn(errorString); + throw new ApexException(errorString); + } + } + + // Update the engines + for (final Entry<AxArtifactKey, EngineService> engineWorkerEntry : engineWorkerMap.entrySet()) { + LOGGER.info("Registering apex model on engine {}", engineWorkerEntry.getKey().getID()); + engineWorkerEntry.getValue().updateModel(engineWorkerEntry.getKey(), apexModel, forceFlag); + } + + if (!wasstopped) { + // start all engines on this engine service if it was not stopped before the update + startAll(); + final long starttime = System.currentTimeMillis(); + while (!isStarted() && System.currentTimeMillis() - starttime < MAX_START_WAIT_TIME) { + ThreadUtilities.sleep(ENGINE_SERVICE_STOP_START_WAIT_INTERVAL); + } + // Check if all engines are running + final StringBuilder notRunningEngineIDBuilder = new StringBuilder(); + for (final Entry<AxArtifactKey, EngineService> engineWorkerEntry : engineWorkerMap.entrySet()) { + if (engineWorkerEntry.getValue().getState() != AxEngineState.READY + && engineWorkerEntry.getValue().getState() != AxEngineState.EXECUTING) { + notRunningEngineIDBuilder.append(engineWorkerEntry.getKey().getID()); + notRunningEngineIDBuilder.append('('); + notRunningEngineIDBuilder.append(engineWorkerEntry.getValue().getState()); + notRunningEngineIDBuilder.append(") "); + } + } + if (notRunningEngineIDBuilder.length() > 0) { + final String errorString = "engine start error on model update on engine service with key " + + incomingEngineServiceKey.getID() + ", engines not running are: " + + notRunningEngineIDBuilder.toString().trim(); + LOGGER.warn(errorString); + throw new ApexException(errorString); + } + } + + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#getState() + */ + @Override + public AxEngineState getState() { + // If one worker is running then we are running, otherwise we are stopped + for (final EngineService engine : engineWorkerMap.values()) { + if (engine.getState() != AxEngineState.STOPPED) { + return AxEngineState.EXECUTING; + } + } + + return AxEngineState.STOPPED; + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#startAll() + */ + @Override + public void startAll() throws ApexException { + for (final EngineService engine : engineWorkerMap.values()) { + start(engine.getKey()); + } + + // Check if periodic events should be turned on + if (periodicEventPeriod > 0) { + startPeriodicEvents(periodicEventPeriod); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#start(com.ericsson.apex.core.model. + * concepts.AxArtifactKey) + */ + @Override + public void start(final AxArtifactKey engineKey) throws ApexException { + LOGGER.entry(engineKey); + + // Check if we have this key on our map + if (!engineWorkerMap.containsKey(engineKey)) { + LOGGER.warn("engine with key " + engineKey.getID() + " not found in engine service"); + throw new ApexException("engine with key " + engineKey.getID() + " not found in engine service"); + } + + // Start the engine + engineWorkerMap.get(engineKey).start(engineKey); + + LOGGER.exit(engineKey); + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#stop() + */ + @Override + public void stop() throws ApexException { + LOGGER.entry(); + + // Stop each engine + for (final EngineService engine : engineWorkerMap.values()) { + if (engine.getState() != AxEngineState.STOPPED) { + engine.stop(); + } + } + + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#stop(com.ericsson.apex.core.model. + * concepts.AxArtifactKey) + */ + @Override + public void stop(final AxArtifactKey engineKey) throws ApexException { + LOGGER.entry(engineKey); + + // Check if we have this key on our map + if (!engineWorkerMap.containsKey(engineKey)) { + LOGGER.warn("engine with key " + engineKey.getID() + " not found in engine service"); + throw new ApexException("engine with key " + engineKey.getID() + " not found in engine service"); + } + + // Stop the engine + engineWorkerMap.get(engineKey).stop(engineKey); + + LOGGER.exit(engineKey); + } + + /** + * Check all engines are started. + * + * @return true if <i>all</i> engines are started + * @see org.onap.policy.apex.service.engine.runtime.EngineService#isStarted() + */ + @Override + public boolean isStarted() { + for (final EngineService engine : engineWorkerMap.values()) { + if (!engine.isStarted()) { + return false; + } + } + return true; + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#isStarted(com.ericsson.apex.model. + * basicmodel.concepts.AxArtifactKey) + */ + @Override + public boolean isStarted(final AxArtifactKey engineKey) { + // Check if we have this key on our map + if (!engineWorkerMap.containsKey(engineKey)) { + LOGGER.warn("engine with key " + engineKey.getID() + " not found in engine service"); + } + return engineWorkerMap.get(engineKey).isStarted(); + } + + /** + * Check all engines are stopped. + * + * @return true if <i>all</i> engines are stopped + * @see org.onap.policy.apex.service.engine.runtime.EngineService#isStopped() + */ + @Override + public boolean isStopped() { + for (final EngineService engine : engineWorkerMap.values()) { + if (!engine.isStopped()) { + return false; + } + } + return true; + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#isStopped(com.ericsson.apex.model. + * basicmodel.concepts.AxArtifactKey) + */ + @Override + public boolean isStopped(final AxArtifactKey engineKey) { + // Check if we have this key on our map + if (!engineWorkerMap.containsKey(engineKey)) { + LOGGER.warn("engine with key " + engineKey.getID() + " not found in engine service"); + } + return engineWorkerMap.get(engineKey).isStopped(); + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#startPeriodicEvents(long) + */ + @Override + public void startPeriodicEvents(final long period) throws ApexException { + // Check if periodic events are already started + if (periodicEventGenerator != null) { + LOGGER.warn("Peiodic event geneation already running on engine " + engineServiceKey.getID() + ", " + + periodicEventGenerator.toString()); + throw new ApexException("Peiodic event geneation already running on engine " + engineServiceKey.getID() + + ", " + periodicEventGenerator.toString()); + } + + // Set up periodic event execution, its a Java Timer/TimerTask + periodicEventGenerator = new ApexPeriodicEventGenerator(this.getEngineServiceEventInterface(), period); + + // Record the periodic event period because it may have been set over the Web Socket admin + // interface + this.periodicEventPeriod = period; + } + + /* + * (non-Javadoc) + * + * @see com.ericsson.apex.service.engine.runtime.EngineService#stopPeriodicEvents() + */ + @Override + public void stopPeriodicEvents() throws ApexException { + // Check if periodic events are already started + if (periodicEventGenerator == null) { + LOGGER.warn("Peiodic event geneation not running on engine " + engineServiceKey.getID()); + throw new ApexException("Peiodic event geneation not running on engine " + engineServiceKey.getID()); + } + + // Stop periodic events + periodicEventGenerator.cancel(); + periodicEventGenerator = null; + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#getStatus(com.ericsson.apex.core.model + * .concepts.AxArtifactKey) + */ + @Override + public String getStatus(final AxArtifactKey engineKey) throws ApexException { + // Check if we have this key on our map + if (!engineWorkerMap.containsKey(engineKey)) { + LOGGER.warn("engine with key " + engineKey.getID() + " not found in engine service"); + throw new ApexException("engine with key " + engineKey.getID() + " not found in engine service"); + } + + // Return the information for this worker + return engineWorkerMap.get(engineKey).getStatus(engineKey); + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineService#getRuntimeInfo(com.ericsson.apex.core. + * model.concepts.AxArtifactKey) + */ + @Override + public String getRuntimeInfo(final AxArtifactKey engineKey) throws ApexException { + // Check if we have this key on our map + if (!engineWorkerMap.containsKey(engineKey)) { + LOGGER.warn("engine with key " + engineKey.getID() + " not found in engine service"); + throw new ApexException("engine with key " + engineKey.getID() + " not found in engine service"); + } + + // Return the information for this worker + return engineWorkerMap.get(engineKey).getRuntimeInfo(engineKey); + } + + /* + * (non-Javadoc) + * + * @see + * com.ericsson.apex.service.engine.runtime.EngineServiceEventInterface#sendEvent(com.ericsson. + * apex.service.engine.event.ApexEvent) + */ + @Override + public void sendEvent(final ApexEvent event) { + // Check if we have this key on our map + if (getState() == AxEngineState.STOPPED) { + LOGGER.warn("event " + event.getName() + " not processed, no engines on engine service " + + engineServiceKey.getID() + " are running"); + return; + } + + if (event == null) { + LOGGER.warn("Null events cannot be processed, in engine service " + engineServiceKey.getID()); + return; + } + + if (DEBUG_ENABLED) { + LOGGER.debug("Forwarding Apex Event {} to the processing engine", event); + } + + // Add the incoming event to the queue, the next available worker will process it + queue.add(event); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EngineWorker.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EngineWorker.java new file mode 100644 index 000000000..20f8aaf75 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/EngineWorker.java @@ -0,0 +1,674 @@ +/*- + * ============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.service.engine.runtime.impl; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; +import java.util.Collection; +import java.util.Map; +import java.util.Map.Entry; +import java.util.concurrent.BlockingQueue; + +import org.onap.policy.apex.context.ContextException; +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.engine.ApexEngine; +import org.onap.policy.apex.core.engine.engine.impl.ApexEngineFactory; +import org.onap.policy.apex.core.engine.event.EnEvent; +import org.onap.policy.apex.core.infrastructure.threading.ApplicationThreadFactory; +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.ApexModelException; +import org.onap.policy.apex.model.basicmodel.handling.ApexModelReader; +import org.onap.policy.apex.model.basicmodel.handling.ApexModelWriter; +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.enginemodel.concepts.AxEngineModel; +import org.onap.policy.apex.model.enginemodel.concepts.AxEngineState; +import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.impl.enevent.ApexEvent2EnEventConverter; +import org.onap.policy.apex.service.engine.runtime.ApexEventListener; +import org.onap.policy.apex.service.engine.runtime.EngineService; +import org.onap.policy.apex.service.engine.runtime.EngineServiceEventInterface; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; + +/** + * The Class EngineWorker encapsulates a core {@link ApexEngine} instance, which runs policies + * defined in the {@link org.onap.policy.apex.model.basicmodel.concepts.AxModelAxModel}. Each policy + * is triggered by an Apex event, and when the policy is triggered it runs through to completion in + * the ApexEngine. + * + * This class acts as a container for an {@link ApexEngine}, running it in a thread, sending it + * events, and receiving events from it. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +final class EngineWorker implements EngineService { + // Logger for this class + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EngineService.class); + + // The ID of this engine + private final AxArtifactKey engineWorkerKey; + + // The Apex engine which is running the policies in this worker + private final ApexEngine engine; + + // The event processor is an inner class, an instance of which runs as a thread that reads + // incoming events from a queue and forwards them to the Apex engine + private EventProcessor processor = null; + + // Thread handling for the worker + private final ApplicationThreadFactory threadFactory; + private Thread processorThread; + + // Converts ApexEvent instances to and from EnEvent instances + private ApexEvent2EnEventConverter apexEnEventConverter = null; + + /** + * Constructor that creates an Apex engine, an event processor for events to be sent to that + * engine, and an {@link ApexModelReader} instance to read Apex models using JAXB. + * + * @param engineWorkerKey the engine worker key + * @param queue the queue on which events for this Apex worker will come + * @param threadFactory the thread factory to use for creating the event processing thread + * @throws ApexException thrown on errors on worker instantiation + */ + EngineWorker(final AxArtifactKey engineWorkerKey, final BlockingQueue<ApexEvent> queue, + final ApplicationThreadFactory threadFactory) throws ApexException { + LOGGER.entry(engineWorkerKey); + + this.engineWorkerKey = engineWorkerKey; + this.threadFactory = threadFactory; + + // Create the Apex engine + engine = new ApexEngineFactory().createApexEngine(engineWorkerKey); + + // Create and run the event processor + processor = new EventProcessor(queue); + + // Set the Event converter up + apexEnEventConverter = new ApexEvent2EnEventConverter(engine); + + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#registerActionListener(java.lang. + * String, org.onap.policy.apex.service.engine.runtime.ApexEventListener) + */ + @Override + public void registerActionListener(final String listenerName, final ApexEventListener apexEventListener) { + // Sanity checks on the Apex model + if (engine == null) { + LOGGER.warn("listener registration on engine with key " + engineWorkerKey.getID() + + ", failed, listener is null"); + return; + } + + engine.addEventListener(listenerName, new EnEventListenerImpl(apexEventListener, apexEnEventConverter)); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#deregisterActionListener(java.lang. + * String) + */ + @Override + public void deregisterActionListener(final String listenerName) { + // Sanity checks on the Apex model + if (engine == null) { + LOGGER.warn("listener deregistration on engine with key " + engineWorkerKey.getID() + + ", failed, listener is null"); + return; + } + + engine.removeEventListener(listenerName); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#getEngineServiceEventInterface() + */ + @Override + public EngineServiceEventInterface getEngineServiceEventInterface() { + throw new UnsupportedOperationException( + "getEngineServiceEventInterface() call is not allowed on an Apex Engine Worker"); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#getKey() + */ + @Override + public AxArtifactKey getKey() { + return engineWorkerKey; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#getInfo() + */ + @Override + public Collection<AxArtifactKey> getEngineKeys() { + return Arrays.asList(engineWorkerKey); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#getApexModelKey() + */ + @Override + public AxArtifactKey getApexModelKey() { + if (ModelService.existsModel(AxPolicyModel.class)) { + return ModelService.getModel(AxPolicyModel.class).getKey(); + } else { + return null; + } + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#updateModel(org.onap.policy.apex. + * model. basicmodel.concepts.AxArtifactKey, java.lang.String, boolean) + */ + @Override + public void updateModel(final AxArtifactKey engineKey, final String engineModel, final boolean forceFlag) + throws ApexException { + LOGGER.entry(engineKey); + + // Read the Apex model into memory using the Apex Model Reader + AxPolicyModel apexPolicyModel = null; + try { + final ApexModelReader<AxPolicyModel> modelReader = new ApexModelReader<>(AxPolicyModel.class); + apexPolicyModel = modelReader.read(new ByteArrayInputStream(engineModel.getBytes())); + } catch (final ApexModelException e) { + LOGGER.error("failed to unmarshal the apex model on engine " + engineKey.getID(), e); + throw new ApexException("failed to unmarshal the apex model on engine " + engineKey.getID(), e); + } + + if (apexPolicyModel == null) { + LOGGER.error("apex model null on engine " + engineKey.getID()); + throw new ApexException("apex model null on engine " + engineKey.getID()); + } + + // Update the Apex model in the Apex engine + updateModel(engineKey, apexPolicyModel, forceFlag); + + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#updateModel(org.onap.policy.apex. + * model. basicmodel.concepts.AxArtifactKey, + * org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel, boolean) + */ + @Override + public void updateModel(final AxArtifactKey engineKey, final AxPolicyModel apexModel, final boolean forceFlag) + throws ApexException { + LOGGER.entry(engineKey); + + // Check if the key on the update request is correct + if (!engineWorkerKey.equals(engineKey)) { + LOGGER.warn("engine key " + engineKey.getID() + " does not match the key" + engineWorkerKey.getID() + + " of this engine"); + throw new ApexException("engine key " + engineKey.getID() + " does not match the key" + + engineWorkerKey.getID() + " of this engine"); + } + + // Sanity checks on the Apex model + if (engine == null) { + LOGGER.warn("engine with key " + engineKey.getID() + " not initialized"); + throw new ApexException("engine with key " + engineKey.getID() + " not initialized"); + } + + // Check model compatibility + if (ModelService.existsModel(AxPolicyModel.class)) { + // The current policy model may or may not be defined + final AxPolicyModel currentModel = ModelService.getModel(AxPolicyModel.class); + if (!currentModel.getKey().isCompatible(apexModel.getKey())) { + if (forceFlag) { + LOGGER.warn("apex model update forced, supplied model with key \"" + apexModel.getKey().getID() + + "\" is not a compatible model update from the existing engine model with key \"" + + currentModel.getKey().getID() + "\""); + } else { + throw new ContextException( + "apex model update failed, supplied model with key \"" + apexModel.getKey().getID() + + "\" is not a compatible model update from the existing engine model with key \"" + + currentModel.getKey().getID() + "\""); + } + } + } + + // Update the Apex model in the Apex engine + engine.updateModel(apexModel); + + LOGGER.debug("engine model {} added to the engine-{}", apexModel.getKey().getID(), engineWorkerKey); + LOGGER.exit(); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#getState() + */ + @Override + public AxEngineState getState() { + return engine.getState(); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#startAll() + */ + @Override + public void startAll() throws ApexException { + start(this.getKey()); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#start(org.onap.policy.apex.core. + * model. concepts.AxArtifactKey) + */ + @Override + public void start(final AxArtifactKey engineKey) throws ApexException { + LOGGER.entry(engineKey); + + // Check if the key on the start request is correct + if (!engineWorkerKey.equals(engineKey)) { + LOGGER.warn("engine key " + engineKey.getID() + " does not match the key" + engineWorkerKey.getID() + + " of this engine"); + throw new ApexException("engine key " + engineKey.getID() + " does not match the key" + + engineWorkerKey.getID() + " of this engine"); + } + + if (engine == null) { + LOGGER.error("apex engine for engine key" + engineWorkerKey.getID() + " null"); + throw new ApexException("apex engine for engine key" + engineWorkerKey.getID() + " null"); + } + + // Starts the event processing thread that handles incoming events + if (processorThread != null && processorThread.isAlive()) { + LOGGER.error("apex engine for engine key" + engineWorkerKey.getID() + " is already running with state " + + getState()); + throw new ApexException("apex engine for engine key" + engineWorkerKey.getID() + + " is already running with state " + getState()); + } + + // Start the engine + engine.start(); + + // Start a thread to process events for the engine + processorThread = threadFactory.newThread(processor); + processorThread.start(); + + LOGGER.exit(engineKey); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#stop() + */ + @Override + public void stop() throws ApexException { + stop(this.getKey()); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#stop(org.onap.policy.apex.core. + * model. concepts.AxArtifactKey) + */ + @Override + public void stop(final AxArtifactKey engineKey) throws ApexException { + // Check if the key on the start request is correct + if (!engineWorkerKey.equals(engineKey)) { + LOGGER.warn("engine key " + engineKey.getID() + " does not match the key" + engineWorkerKey.getID() + + " of this engine"); + throw new ApexException("engine key " + engineKey.getID() + " does not match the key" + + engineWorkerKey.getID() + " of this engine"); + } + + if (engine == null) { + LOGGER.error("apex engine for engine key" + engineWorkerKey.getID() + " null"); + throw new ApexException("apex engine for engine key" + engineWorkerKey.getID() + " null"); + } + + // Interrupt the worker to stop its thread + if (processorThread == null || !processorThread.isAlive()) { + processorThread = null; + + LOGGER.warn("apex engine for engine key" + engineWorkerKey.getID() + " is already stopped with state " + + getState()); + return; + } + + // Interrupt the thread that is handling events toward the engine + processorThread.interrupt(); + + // Stop the engine + engine.stop(); + + LOGGER.exit(engineKey); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#isStarted() + */ + @Override + public boolean isStarted() { + return isStarted(this.getKey()); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#isStarted(org.onap.policy.apex. + * model. basicmodel.concepts.AxArtifactKey) + */ + @Override + public boolean isStarted(final AxArtifactKey engineKey) { + final AxEngineState engstate = getState(); + switch (engstate) { + case STOPPED: + case STOPPING: + case UNDEFINED: + return false; + case EXECUTING: + case READY: + return processorThread != null && processorThread.isAlive() && !processorThread.isInterrupted(); + default: + break; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#isStopped() + */ + @Override + public boolean isStopped() { + return isStopped(this.getKey()); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#isStopped(org.onap.policy.apex. + * model. basicmodel.concepts.AxArtifactKey) + */ + @Override + public boolean isStopped(final AxArtifactKey engineKey) { + final AxEngineState engstate = getState(); + switch (engstate) { + case STOPPING: + case UNDEFINED: + case EXECUTING: + case READY: + return false; + case STOPPED: + return processorThread == null || !processorThread.isAlive(); + default: + break; + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#startPeriodicEvents(long) + */ + @Override + public void startPeriodicEvents(final long period) { + throw new UnsupportedOperationException("startPeriodicEvents() call is not allowed on an Apex Engine Worker"); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.runtime.EngineService#stopPeriodicEvents() + */ + @Override + public void stopPeriodicEvents() { + throw new UnsupportedOperationException("stopPeriodicEvents() call is not allowed on an Apex Engine Worker"); + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#getStatus(org.onap.policy.apex.core + * .model .concepts.AxArtifactKey) + */ + @Override + public String getStatus(final AxArtifactKey engineKey) { + // Get the information from the engine that we want to return + final AxEngineModel apexEngineModel = engine.getEngineStatus(); + apexEngineModel.getKeyInformation().generateKeyInfo(apexEngineModel); + + // Convert that information into a string + try { + final ByteArrayOutputStream baOutputStream = new ByteArrayOutputStream(); + final ApexModelWriter<AxEngineModel> modelWriter = new ApexModelWriter<>(AxEngineModel.class); + modelWriter.write(apexEngineModel, baOutputStream); + return baOutputStream.toString(); + } catch (final Exception e) { + LOGGER.warn("error outputting runtime information for engine {}", engineWorkerKey, e); + return null; + } + } + + /* + * (non-Javadoc) + * + * @see + * org.onap.policy.apex.service.engine.runtime.EngineService#getRuntimeInfo(org.onap.policy.apex + * .core.model.concepts.AxArtifactKey) + */ + @Override + public String getRuntimeInfo(final AxArtifactKey engineKey) { + // We'll build up the JSON string for runtime information bit by bit + final StringBuilder runtimeJsonStringBuilder = new StringBuilder(); + + // Get the engine information + final AxEngineModel engineModel = engine.getEngineStatus(); + final Map<AxArtifactKey, Map<String, Object>> engineContextAlbums = engine.getEngineContext(); + + // Use GSON to convert our context information into JSON + final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + // Get context into a JSON string + runtimeJsonStringBuilder.append("{\"TimeStamp\":"); + runtimeJsonStringBuilder.append(engineModel.getTimestamp()); + runtimeJsonStringBuilder.append(",\"State\":"); + runtimeJsonStringBuilder.append(engineModel.getState()); + runtimeJsonStringBuilder.append(",\"Stats\":"); + runtimeJsonStringBuilder.append(gson.toJson(engineModel.getStats())); + + // Get context into a JSON string + runtimeJsonStringBuilder.append(",\"ContextAlbums\":["); + + boolean firstAlbum = true; + for (final Entry<AxArtifactKey, Map<String, Object>> contextAlbumEntry : engineContextAlbums.entrySet()) { + if (firstAlbum) { + firstAlbum = false; + } else { + runtimeJsonStringBuilder.append(","); + } + + runtimeJsonStringBuilder.append("{\"AlbumKey\":"); + runtimeJsonStringBuilder.append(gson.toJson(contextAlbumEntry.getKey())); + runtimeJsonStringBuilder.append(",\"AlbumContent\":["); + + + // Get the schema helper to use to marshal context album objects to JSON + final AxContextAlbum axContextAlbum = + ModelService.getModel(AxContextAlbums.class).get(contextAlbumEntry.getKey()); + SchemaHelper schemaHelper = null; + + try { + // Get a schema helper to manage the translations between objects on the album map + // for this album + schemaHelper = new SchemaHelperFactory().createSchemaHelper(axContextAlbum.getKey(), + axContextAlbum.getItemSchema()); + } catch (final ContextRuntimeException e) { + final String resultString = + "could not find schema helper to marshal context album \"" + axContextAlbum + "\" to JSON"; + LOGGER.warn(resultString, e); + + // End of context album entry + runtimeJsonStringBuilder.append(resultString); + runtimeJsonStringBuilder.append("]}"); + + continue; + } + + boolean firstEntry = true; + for (final Entry<String, Object> contextEntry : contextAlbumEntry.getValue().entrySet()) { + if (firstEntry) { + firstEntry = false; + } else { + runtimeJsonStringBuilder.append(","); + } + runtimeJsonStringBuilder.append("{\"EntryName\":"); + runtimeJsonStringBuilder.append(gson.toJson(contextEntry.getKey())); + runtimeJsonStringBuilder.append(",\"EntryContent\":"); + runtimeJsonStringBuilder.append(gson.toJson(schemaHelper.marshal2Json(contextEntry.getValue()))); + + // End of context entry + runtimeJsonStringBuilder.append("}"); + } + + // End of context album entry + runtimeJsonStringBuilder.append("]}"); + } + + runtimeJsonStringBuilder.append("]}"); + + // Tidy up the JSON string + final JsonParser jsonParser = new JsonParser(); + final JsonElement jsonElement = jsonParser.parse(runtimeJsonStringBuilder.toString()); + final String tidiedRuntimeString = gson.toJson(jsonElement); + + LOGGER.debug("runtime information=" + tidiedRuntimeString); + + return tidiedRuntimeString; + } + + /** + * This is an event processor thread, this class decouples the events handling logic from core + * business logic. This class runs its own thread and continuously querying the blocking queue + * for the events that have been sent to the worker for processing by the Apex engine. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ + private class EventProcessor implements Runnable { + private final boolean debugEnabled = LOGGER.isDebugEnabled(); + // the events queue + private BlockingQueue<ApexEvent> eventProcessingQueue = null; + + /** + * Constructor accepts {@link ApexEngine} and {@link BlockingQueue} type objects. + * + * @param eventProcessingQueue is reference of {@link BlockingQueue} which contains trigger + * events. + */ + EventProcessor(final BlockingQueue<ApexEvent> eventProcessingQueue) { + this.eventProcessingQueue = eventProcessingQueue; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + LOGGER.debug("Engine {} processing ... ", engineWorkerKey); + + // Take events from the event processing queue of the worker and pass them to the engine + // for processing + while (!processorThread.isInterrupted()) { + ApexEvent event = null; + try { + event = eventProcessingQueue.take(); + } catch (final InterruptedException e) { + LOGGER.debug("Engine {} processing interrupted ", engineWorkerKey); + break; + } + + try { + if (event != null) { + if (debugEnabled) { + LOGGER.debug("Trigger Event {} forwarded to the Apex engine", event); + } + final EnEvent enevent = apexEnEventConverter.fromApexEvent(event); + engine.handleEvent(enevent); + } + } catch (final ApexException e) { + LOGGER.warn("Engine {} failed to process event {}", engineWorkerKey, event.toString(), e); + } catch (final Exception e) { + LOGGER.warn("Engine {} terminated processing event {}", engineWorkerKey, event.toString(), e); + break; + } + } + LOGGER.debug("Engine {} completed processing", engineWorkerKey); + } + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/impl/package-info.java new file mode 100644 index 000000000..b0acca8d7 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/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 APEX engine runtime interfaces. It uses the APEX core engine + * as its implementation. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.runtime.impl; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/package-info.java new file mode 100644 index 000000000..b54a90349 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/engine/runtime/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 Java APIs for APEX engines at runtime. The {@link EngineService} is used to start, stop + * and manage APEX engines. {@link EngineServiceEventInterface} is used to send events to an APEX + * engine. {@link ApexEventListener} interface is used to receive events from an APEX engine. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.engine.runtime; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterException.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterException.java new file mode 100644 index 000000000..229250bcf --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterException.java @@ -0,0 +1,52 @@ +/*- + * ============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.service.parameters; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexException; + +/** + * This exception will be called if an error occurs in Apex parameter handling. + * + * @author Liam Fallon + */ +public class ApexParameterException extends ApexException { + private static final long serialVersionUID = -8507246953751956974L; + + /** + * Instantiates a new apex parameter handling exception with a message. + * + * @param message the message + */ + public ApexParameterException(final String message) { + super(message); + } + + /** + * Instantiates a new apex parameter handling exception with a message and a caused by + * exception. + * + * @param message the message + * @param e the exception that caused this exception to be thrown + */ + public ApexParameterException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterHandler.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterHandler.java new file mode 100644 index 000000000..79f10bdea --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterHandler.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.service.parameters; + +import java.io.FileReader; + +import org.onap.policy.apex.core.engine.EngineParameters; +import org.onap.policy.apex.service.engine.main.ApexCommandLineArguments; +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParametersJSONAdapter; +import org.onap.policy.apex.service.parameters.engineservice.EngineServiceParametersJSONAdapter; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParametersJSONAdapter; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +/** + * This class handles reading, parsing and validating of Apex parameters from JSON files. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexParameterHandler { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexParameterHandler.class); + + /** + * Read the parameters from the parameter file. + * + * @param arguments the arguments passed to Apex + * @return the parameters read from the configuration file + * @throws ApexParameterException on parameter exceptions + */ + public ApexParameters getParameters(final ApexCommandLineArguments arguments) throws ApexParameterException { + ApexParameters parameters = null; + + // Read the parameters + try { + // Register the adapters for our carrier technologies and event protocols with GSON + // @formatter:off + final Gson gson = new GsonBuilder() + .registerTypeAdapter(EngineParameters .class, new EngineServiceParametersJSONAdapter()) + .registerTypeAdapter(CarrierTechnologyParameters.class, new CarrierTechnologyParametersJSONAdapter()) + .registerTypeAdapter(EventProtocolParameters .class, new EventProtocolParametersJSONAdapter()) + .create(); + // @formatter:on + parameters = gson.fromJson(new FileReader(arguments.getFullConfigurationFilePath()), ApexParameters.class); + } catch (final Exception e) { + final String errorMessage = "error reading parameters from \"" + arguments.getConfigurationFilePath() + + "\"\n" + "(" + e.getClass().getSimpleName() + "):" + e.getMessage(); + LOGGER.error(errorMessage, e); + throw new ApexParameterException(errorMessage, e); + } + + // The JSON processing returns null if there is an empty file + if (parameters == null) { + final String errorMessage = "no parameters found in \"" + arguments.getConfigurationFilePath() + "\""; + LOGGER.error(errorMessage); + throw new ApexParameterException(errorMessage); + } + + // Check if we should override the model file parameter + final String modelFilePath = arguments.getModelFilePath(); + if (modelFilePath != null && modelFilePath.replaceAll("\\s+", "").length() > 0) { + parameters.getEngineServiceParameters().setPolicyModelFileName(modelFilePath); + } + + // validate the parameters + final String validationResult = parameters.validate(); + if (!validationResult.isEmpty()) { + String returnMessage = + "validation error(s) on parameters from \"" + arguments.getConfigurationFilePath() + "\"\n"; + returnMessage += validationResult; + + LOGGER.error(returnMessage); + throw new ApexParameterException(returnMessage); + } + + return parameters; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterRuntimeException.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterRuntimeException.java new file mode 100644 index 000000000..2334a7e60 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterRuntimeException.java @@ -0,0 +1,52 @@ +/*- + * ============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.service.parameters; + +import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException; + +/** + * This exception will be called if an error occurs in Apex parameter handling. + * + * @author Liam Fallon + */ +public class ApexParameterRuntimeException extends ApexRuntimeException { + private static final long serialVersionUID = -8507246953751956974L; + + /** + * Instantiates a new apex parameter handling exception with a message. + * + * @param message the message + */ + public ApexParameterRuntimeException(final String message) { + super(message); + } + + /** + * Instantiates a new apex parameter handling exception with a message and a caused by + * exception. + * + * @param message the message + * @param e the exception that caused this exception to be thrown + */ + public ApexParameterRuntimeException(final String message, final Exception e) { + super(message, e); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterValidator.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterValidator.java new file mode 100644 index 000000000..a8cbe3b46 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameterValidator.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.service.parameters; + +/** + * This interface is implemented by Apex parameter classes so that they can be validated. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public interface ApexParameterValidator { + /** + * Validate a parameter java bean, if the parameter bean is valid, an empty string is returned, + * otherwise the string gives details of the invalid parameters. + * + * @return the string with validation errors + */ + String validate(); +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameters.java new file mode 100644 index 000000000..52c6f4960 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/ApexParameters.java @@ -0,0 +1,357 @@ +/*- + * ============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.service.parameters; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +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; +import org.onap.policy.apex.service.parameters.engineservice.EngineServiceParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters; +import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode; + +/** + * The main container parameter class for an Apex service. + * <p> + * The following parameters are defined: + * <ol> + * <li>engineServiceParameters: The parameters for the Apex engine service itself, such as the + * number of engine threads to run and the deployment port number to use. + * <li>eventOutputParameters: A map of parameters for event outputs that Apex will use to emit + * events. Apex emits events on all outputs + * <li>eventInputParameters: A map or parameters for event inputs from which Apex will consume + * events. Apex reads events from all its event inputs. + * <li>synchronousEventHandlerParameters: A map of parameters for synchronous event handlers That + * Apex receives events from and replies immediately to those events. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class ApexParameters extends AbstractParameters implements ApexParameterValidator { + /** + * Constructor to create an apex parameters instance and register the instance with the + * parameter service. + */ + public ApexParameters() { + super(ContextParameters.class.getCanonicalName()); + ParameterService.registerParameters(ApexParameters.class, this); + } + + // Parameters for the engine service and the engine threads in the engine service + private EngineServiceParameters engineServiceParameters; + + // Parameters for the event outputs that Apex will use to send events on its outputs + private Map<String, EventHandlerParameters> eventOutputParameters = new LinkedHashMap<>(); + + // Parameters for the event inputs that Apex will use to receive events on its inputs + private Map<String, EventHandlerParameters> eventInputParameters = new LinkedHashMap<>(); + + /** + * Gets the parameters for the Apex engine service. + * + * @return the engine service parameters + */ + public EngineServiceParameters getEngineServiceParameters() { + return engineServiceParameters; + } + + /** + * Sets the engine service parameters. + * + * @param engineServiceParameters the engine service parameters + */ + public void setEngineServiceParameters(final EngineServiceParameters engineServiceParameters) { + this.engineServiceParameters = engineServiceParameters; + } + + /** + * Gets the event output parameter map. + * + * @return the parameters for all event outputs + */ + public Map<String, EventHandlerParameters> getEventOutputParameters() { + return eventOutputParameters; + } + + /** + * Sets the event output parameters. + * + * @param eventOutputParameters the event outputs parameters + */ + public void setEventOutputParameters(final Map<String, EventHandlerParameters> eventOutputParameters) { + this.eventOutputParameters = eventOutputParameters; + } + + /** + * Gets the event input parameter map. + * + * @return the parameters for all event inputs + */ + public Map<String, EventHandlerParameters> getEventInputParameters() { + return eventInputParameters; + } + + /** + * Sets the event input parameters. + * + * @param eventInputParameters the event input parameters + */ + public void setEventInputParameters(final Map<String, EventHandlerParameters> eventInputParameters) { + this.eventInputParameters = eventInputParameters; + } + + /** + * This method formats a validation result with a header if the result is not empty. + * + * @param validationResultMessage The incoming message + * @param heading The heading to prepend on the message + * @return the formatted message + */ + private String validationResultFormatter(final String validationResultMessage, final String heading) { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + if (validationResultMessage.length() > 0) { + errorMessageBuilder.append(heading); + errorMessageBuilder.append(validationResultMessage); + } + + return errorMessageBuilder.toString(); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + if (engineServiceParameters == null) { + errorMessageBuilder.append(" engine service parameters are not specified\n"); + } else { + errorMessageBuilder.append(validationResultFormatter(engineServiceParameters.validate(), + " engine service parameters invalid\n")); + } + + // Sanity check, we must have an entry in both output and input maps + if (eventOutputParameters.isEmpty() || eventInputParameters.isEmpty()) { + errorMessageBuilder.append(" at least one event output and one event input must be specified\n"); + } + + // Validate that the values of all parameters are ok + validateEventHandlerMap("event input", errorMessageBuilder, eventInputParameters); + validateEventHandlerMap("event output", errorMessageBuilder, eventOutputParameters); + + // Only do peer mode validate if there are no other errors + if (errorMessageBuilder.length() == 0) { + for (final EventHandlerPeeredMode peeredMode : EventHandlerPeeredMode.values()) { + validatePeeredMode(errorMessageBuilder, peeredMode); + } + } + + // Check if we have any errors + if (errorMessageBuilder.length() > 0) { + errorMessageBuilder.insert(0, "Apex parameters invalid\n"); + } + + return errorMessageBuilder.toString().trim(); + } + + /** + * This method validates the parameters in an event handler map. + * + * @param eventHandlerType the type of the event handler to use on error messages + * @param errorMessageBuilder the builder to use to return validation messages + * @param parsForValidation The event handler parameters to validate (input or output) + */ + // CHECKSTYLE:OFF: checkstyle:finalParameter + private void validateEventHandlerMap(final String eventHandlerType, final StringBuilder errorMessageBuilder, + final Map<String, EventHandlerParameters> parsForValidation) { + // CHECKSTYLE:ON: checkstyle:finalParameter + for (final Entry<String, EventHandlerParameters> parameterEntry : parsForValidation.entrySet()) { + if (parameterEntry.getKey() == null || parameterEntry.getKey().trim().isEmpty()) { + errorMessageBuilder + .append(" invalid " + eventHandlerType + " name \"" + parameterEntry.getKey() + "\" \n"); + } else if (parameterEntry.getValue() == null) { + errorMessageBuilder.append(" invalid/Null event input prameters specified for " + eventHandlerType + + " name \"" + parameterEntry.getKey() + "\" \n"); + } else { + errorMessageBuilder.append(validationResultFormatter(parameterEntry.getValue().validate(), + " " + eventHandlerType + " (" + parameterEntry.getKey() + ") parameters invalid\n")); + } + + parameterEntry.getValue().setName(parameterEntry.getKey()); + + // Validate parameters for peered mode settings + for (final EventHandlerPeeredMode peeredMode : EventHandlerPeeredMode.values()) { + validatePeeredModeParameters(eventHandlerType, errorMessageBuilder, parameterEntry, peeredMode); + } + } + } + + /** + * Validate parameter values for event handlers in a peered mode + * + * @param eventHandlerType The event handler type we are checking + * @param errorMessageBuilder The builder to which to append any error messages + * @param parameterEntry The entry to check the peered mode on + * @param peeredMode The mode to check + */ + private void validatePeeredModeParameters(final String eventHandlerType, final StringBuilder errorMessageBuilder, + final Entry<String, EventHandlerParameters> parameterEntry, final EventHandlerPeeredMode peeredMode) { + final String messagePreamble = " specified peered mode \"" + peeredMode + "\""; + final String peer = parameterEntry.getValue().getPeer(peeredMode); + + if (parameterEntry.getValue().isPeeredMode(peeredMode)) { + if (peer == null || peer.trim().isEmpty()) { + errorMessageBuilder.append(messagePreamble + " mandatory parameter not specified or is null on " + + eventHandlerType + " \"" + parameterEntry.getKey() + "\" \n"); + } + if (parameterEntry.getValue().getPeerTimeout(peeredMode) < 0) { + errorMessageBuilder.append( + messagePreamble + " timeout value \"" + parameterEntry.getValue().getPeerTimeout(peeredMode) + + "\" is illegal on " + eventHandlerType + " \"" + parameterEntry.getKey() + + "\", specify a non-negative timeout value in milliseconds\n"); + } + } else { + if (peer != null) { + errorMessageBuilder.append(messagePreamble + " peer is illegal on non synchronous " + eventHandlerType + + " \"" + parameterEntry.getKey() + "\" \n"); + } + if (parameterEntry.getValue().getPeerTimeout(peeredMode) != 0) { + errorMessageBuilder.append(messagePreamble + " timeout is illegal on non synchronous " + + eventHandlerType + " \"" + parameterEntry.getKey() + "\" \n"); + } + } + } + + /** + * This method validates that the settings are valid for the given peered mode + * + * @param errorMessageBuilder The builder to which to append any error messages + * @param peeredMode The peered mode to check + */ + private void validatePeeredMode(final StringBuilder errorMessageBuilder, final EventHandlerPeeredMode peeredMode) { + // Find the input and output event handlers that use this peered mode + final Map<String, EventHandlerParameters> inputParametersUsingMode = new HashMap<>(); + final Map<String, EventHandlerParameters> outputParametersUsingMode = new HashMap<>(); + + // Find input and output parameters using this mode + for (final Entry<String, EventHandlerParameters> inputParameterEntry : eventInputParameters.entrySet()) { + if (inputParameterEntry.getValue().isPeeredMode(peeredMode)) { + inputParametersUsingMode.put(inputParameterEntry.getKey(), inputParameterEntry.getValue()); + } + } + for (final Entry<String, EventHandlerParameters> outputParameterEntry : eventOutputParameters.entrySet()) { + if (outputParameterEntry.getValue().isPeeredMode(peeredMode)) { + outputParametersUsingMode.put(outputParameterEntry.getKey(), outputParameterEntry.getValue()); + } + } + + // Validate the parameters for each side of the peered mode parameters + validatePeeredModePeers(" event input for peered mode \"" + peeredMode + "\": ", errorMessageBuilder, + peeredMode, inputParametersUsingMode, outputParametersUsingMode); + validatePeeredModePeers(" event output for peered mode \"" + peeredMode + "\": ", errorMessageBuilder, + peeredMode, outputParametersUsingMode, inputParametersUsingMode); + } + + /** + * This method validates that the settings are valid for the event handlers on one + * + * @param messagePreamble the preamble for messages indicating the peered mode side + * @param errorMessageBuilder The builder to which to append any error messages + * @param leftModeParameters The mode parameters being checked + * @param rightModeParameters The mode parameters being referenced by the checked parameters + */ + private void validatePeeredModePeers(final String messagePreamble, final StringBuilder errorMessageBuilder, + final EventHandlerPeeredMode peeredMode, final Map<String, EventHandlerParameters> leftModeParameterMap, + final Map<String, EventHandlerParameters> rightModeParameterMap) { + + // These sets are used to check for duplicate references on the both sides + final Set<String> leftCheckDuplicateSet = new HashSet<>(); + final Set<String> rightCheckDuplicateSet = new HashSet<>(); + + // Check for missing peers, all peers are set because we have checked them previously so no + // need for null checks + for (final Entry<String, EventHandlerParameters> leftModeParameterEntry : leftModeParameterMap.entrySet()) { + final String leftSidePeer = leftModeParameterEntry.getValue().getPeer(peeredMode); + + final EventHandlerParameters leftModeParameters = leftModeParameterEntry.getValue(); + final EventHandlerParameters rightModeParameters = rightModeParameterMap.get(leftSidePeer); + + // Check that the peer reference is OK + if (rightModeParameters == null) { + errorMessageBuilder.append(messagePreamble + "peer \"" + leftModeParameters.getPeer(peeredMode) + + "\" for event handler \"" + leftModeParameterEntry.getKey() + + "\" does not exist or is not defined as being synchronous\n"); + continue; + } + + // Now check that the right side peer is the left side event handler + final String rightSidePeer = rightModeParameters.getPeer(peeredMode); + if (!rightSidePeer.equals(leftModeParameterEntry.getKey())) { + errorMessageBuilder + .append(messagePreamble + "peer value \"" + rightSidePeer + "\" on peer \"" + leftSidePeer + + "\" does not equal event handler \"" + leftModeParameterEntry.getKey() + "\"\n"); + } else { + // Check for duplicates + if (!leftCheckDuplicateSet.add(leftSidePeer)) { + errorMessageBuilder + .append(messagePreamble + "peer value \"" + leftSidePeer + "\" on event handler \"" + + leftModeParameterEntry.getKey() + "\" is used more than once\n"); + } + if (!rightCheckDuplicateSet.add(rightSidePeer)) { + errorMessageBuilder.append(messagePreamble + "peer value \"" + rightSidePeer + "\" on peer \"" + + leftSidePeer + "\" on event handler \"" + leftModeParameterEntry.getKey() + + "\" is used more than once\n"); + } + } + + // Cross-set the timeouts if they are not specified + if (leftModeParameters.getPeerTimeout(peeredMode) != 0) { + if (rightModeParameters.getPeerTimeout(peeredMode) != 0) { + if (leftModeParameters.getPeerTimeout(peeredMode) != rightModeParameters + .getPeerTimeout(peeredMode)) { + errorMessageBuilder.append(messagePreamble + "timeout " + + leftModeParameters.getPeerTimeout(peeredMode) + "on event handler \"" + + leftModeParameters.getName() + "\" does not equal timeout value " + + rightModeParameters.getPeerTimeout(peeredMode) + "on event handler \"" + + rightModeParameters.getName() + "\"\n"); + } + } else { + rightModeParameters.setPeerTimeout(peeredMode, leftModeParameters.getPeerTimeout(peeredMode)); + } + } else { + if (rightModeParameters.getPeerTimeout(peeredMode) != 0) { + leftModeParameters.setPeerTimeout(peeredMode, rightModeParameters.getPeerTimeout(peeredMode)); + } + } + } + + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/CarrierTechnologyParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/CarrierTechnologyParameters.java new file mode 100644 index 000000000..ba0327de1 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/CarrierTechnologyParameters.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.service.parameters.carriertechnology; + +import org.onap.policy.apex.model.basicmodel.service.AbstractParameters; +import org.onap.policy.apex.service.parameters.ApexParameterValidator; + +/** + * The default carrier technology parameter class that may be specialized by carrier technology + * plugins that require plugin specific parameters. + * <p> + * The following parameters are defined: + * <ol> + * <li>label: The label of the carrier technology. + * <li>eventProducerPluginClass: The name of the plugin class that will be used by Apex to produce + * and emit output events for this carrier technology + * <li>eventConsumerPluginClass: The name of the plugin class that will be used by Apex to receive + * and process input events from this carrier technology carrier technology + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public abstract class CarrierTechnologyParameters extends AbstractParameters implements ApexParameterValidator { + + // The carrier technology label + private String label = null; + + // Producer and Consumer plugin classes for the event producer and consumer for this carrier + // technology + private String eventProducerPluginClass = null; + private String eventConsumerPluginClass = null; + + /** + * Constructor to create a carrier technology 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 CarrierTechnologyParameters(final String parameterClassName) { + super(parameterClassName); + } + + /** + * Gets the label of the carrier technology. + * + * @return the label of the carrier technology + */ + public String getLabel() { + return label; + } + + /** + * Sets the label of the carrier technology. + * + * @param label the label of the carrier technology + */ + public void setLabel(final String label) { + if (label != null) { + this.label = label.replaceAll("\\s+", ""); + } else { + this.label = null; + } + } + + /** + * Gets the event producer plugin class. + * + * @return the event producer plugin class + */ + public String getEventProducerPluginClass() { + return eventProducerPluginClass; + } + + /** + * Sets the event producer plugin class. + * + * @param eventProducerPluginClass the new event producer plugin class + */ + public void setEventProducerPluginClass(final String eventProducerPluginClass) { + if (eventProducerPluginClass != null) { + this.eventProducerPluginClass = eventProducerPluginClass.replaceAll("\\s+", ""); + } else { + this.eventProducerPluginClass = null; + } + } + + /** + * Gets the event consumer plugin class. + * + * @return the event consumer plugin class + */ + public String getEventConsumerPluginClass() { + return eventConsumerPluginClass; + } + + /** + * Sets the event consumer plugin class. + * + * @param eventConsumerPluginClass the new event consumer plugin class + */ + public void setEventConsumerPluginClass(final String eventConsumerPluginClass) { + if (eventConsumerPluginClass != null) { + this.eventConsumerPluginClass = eventConsumerPluginClass.replaceAll("\\s+", ""); + } else { + this.eventConsumerPluginClass = null; + } + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "CarrierTechnologyParameters [label=" + label + ", eventProducerPluginClass=" + eventProducerPluginClass + + ", eventConsumerPluginClass=" + eventConsumerPluginClass + "]"; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + if (label == null || label.length() == 0) { + errorMessageBuilder.append(" carrier technology label not specified or is blank\n"); + } + + if (eventProducerPluginClass == null || eventProducerPluginClass.length() == 0) { + errorMessageBuilder.append(" carrier technology eventProducerPluginClass not specified or is blank\n"); + } + + if (eventConsumerPluginClass == null || eventConsumerPluginClass.length() == 0) { + errorMessageBuilder.append(" carrier technology eventConsumerPluginClass not specified or is blank\n"); + } + + return errorMessageBuilder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/CarrierTechnologyParametersJSONAdapter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/CarrierTechnologyParametersJSONAdapter.java new file mode 100644 index 000000000..5aa7d6455 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/CarrierTechnologyParametersJSONAdapter.java @@ -0,0 +1,176 @@ +/*- + * ============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.service.parameters.carriertechnology; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import org.onap.policy.apex.service.engine.event.impl.eventrequestor.EventRequestorCarrierTechnologyParameters; +import org.onap.policy.apex.service.engine.event.impl.filecarrierplugin.FILECarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.ApexParameterRuntimeException; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * This class deserialises various type of carrier technology parameters from JSON. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class CarrierTechnologyParametersJSONAdapter + implements JsonSerializer<CarrierTechnologyParameters>, JsonDeserializer<CarrierTechnologyParameters> { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(CarrierTechnologyParametersJSONAdapter.class); + + private static final String PARAMETER_CLASS_NAME = "parameterClassName"; + + private static final String CARRIER_TECHNOLOGY_TOKEN = "carrierTechnology"; + private static final String CARRIER_TECHNOLOGY_PARAMETERS = "parameters"; + + // Built in technology parameters + private static final Map<String, String> BUILT_IN_CARRIER_TECHNOLOGY_PARMETER_CLASS_MAP = new HashMap<>(); + static { + BUILT_IN_CARRIER_TECHNOLOGY_PARMETER_CLASS_MAP.put("FILE", + FILECarrierTechnologyParameters.class.getCanonicalName()); + BUILT_IN_CARRIER_TECHNOLOGY_PARMETER_CLASS_MAP.put("EVENT_REQUESTOR", + EventRequestorCarrierTechnologyParameters.class.getCanonicalName()); + } + + /* + * (non-Javadoc) + * + * @see com.google.gson.JsonSerializer#serialize(java.lang.Object, java.lang.reflect.Type, + * com.google.gson.JsonSerializationContext) + */ + @Override + public JsonElement serialize(final CarrierTechnologyParameters src, final Type typeOfSrc, + final JsonSerializationContext context) { + final String returnMessage = "serialization of Apex carrier technology parameters to Json is not supported"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + /* + * (non-Javadoc) + * + * @see com.google.gson.JsonDeserializer#deserialize(com.google.gson.JsonElement, + * java.lang.reflect.Type, com.google.gson.JsonDeserializationContext) + */ + @Override + public CarrierTechnologyParameters deserialize(final JsonElement json, final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + + // Get the carrier technology label primitive + final JsonPrimitive labelJsonPrimitive = (JsonPrimitive) jsonObject.get(CARRIER_TECHNOLOGY_TOKEN); + + // Check if we found our carrier technology + if (labelJsonPrimitive == null) { + LOGGER.warn("carrier technology parameter \"" + CARRIER_TECHNOLOGY_TOKEN + "\" not found in JSON file"); + return null; + } + + // Get and check the carrier technology label + final String carrierTechnologyLabel = labelJsonPrimitive.getAsString().replaceAll("\\s+", ""); + if (carrierTechnologyLabel == null || carrierTechnologyLabel.length() == 0) { + final String errorMessage = "carrier technology parameter \"" + CARRIER_TECHNOLOGY_TOKEN + "\" value \"" + + labelJsonPrimitive.getAsString() + "\" invalid in JSON file"; + LOGGER.warn(errorMessage); + throw new ApexParameterRuntimeException(errorMessage); + } + + // We now get the technology carrier parameter class + String carrierTechnologyParameterClassName = null; + + // Get the technology carrier parameter class for the carrier technology plugin class from + // the configuration parameters + final JsonPrimitive classNameJsonPrimitive = (JsonPrimitive) jsonObject.get(PARAMETER_CLASS_NAME); + + // If no technology carrier parameter class was specified, we try to use a built in carrier + // technology + if (classNameJsonPrimitive == null) { + carrierTechnologyParameterClassName = + BUILT_IN_CARRIER_TECHNOLOGY_PARMETER_CLASS_MAP.get(carrierTechnologyLabel); + } else { + // We use the specified one + carrierTechnologyParameterClassName = classNameJsonPrimitive.getAsString().replaceAll("\\s+", ""); + } + + // Check the carrier technology parameter class + if (carrierTechnologyParameterClassName == null || carrierTechnologyParameterClassName.length() == 0) { + final String errorMessage = + "carrier technology \"" + carrierTechnologyLabel + "\" parameter \"" + PARAMETER_CLASS_NAME + + "\" value \"" + classNameJsonPrimitive.getAsString() + "\" invalid in JSON file"; + LOGGER.warn(errorMessage); + throw new ApexParameterRuntimeException(errorMessage); + } + + // Get the class for the carrier technology + Class<?> carrierTechnologyParameterClass = null; + try { + carrierTechnologyParameterClass = Class.forName(carrierTechnologyParameterClassName); + } catch (final ClassNotFoundException e) { + final String errorMessage = + "carrier technology \"" + carrierTechnologyLabel + "\" parameter \"" + PARAMETER_CLASS_NAME + + "\" value \"" + carrierTechnologyParameterClassName + "\", could not find class"; + LOGGER.warn(errorMessage, e); + throw new ApexParameterRuntimeException(errorMessage, e); + } + + // Deserialise the class + CarrierTechnologyParameters carrierTechnologyParameters = + context.deserialize(jsonObject.get(CARRIER_TECHNOLOGY_PARAMETERS), carrierTechnologyParameterClass); + if (carrierTechnologyParameters == null) { + // OK no parameters for the carrier technology have been specified, just instantiate the + // default parameters + try { + carrierTechnologyParameters = + (CarrierTechnologyParameters) carrierTechnologyParameterClass.newInstance(); + } catch (final Exception e) { + final String errorMessage = "could not create default parameters for carrier technology \"" + + carrierTechnologyLabel + "\"\n" + e.getMessage(); + LOGGER.warn(errorMessage, e); + throw new ApexParameterRuntimeException(errorMessage, e); + } + } + + // Check that the carrier technology label matches the label in the carrier technology + // parameters object + if (!carrierTechnologyParameters.getLabel().equals(carrierTechnologyLabel)) { + final String errorMessage = "carrier technology \"" + carrierTechnologyLabel + "\" does not match plugin \"" + + carrierTechnologyParameters.getLabel() + "\" in \"" + carrierTechnologyParameterClassName + + "\", specify correct carrier technology parameter plugin in parameter \"" + PARAMETER_CLASS_NAME + + "\""; + LOGGER.warn(errorMessage); + throw new ApexParameterRuntimeException(errorMessage); + } + + return carrierTechnologyParameters; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/package-info.java new file mode 100644 index 000000000..5912c0129 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/carriertechnology/package-info.java @@ -0,0 +1,26 @@ +/*- + * ============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 structure of carrier technology parameters for APEX. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.parameters.carriertechnology; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/EngineServiceParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/EngineServiceParameters.java new file mode 100644 index 000000000..6b60732c3 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/EngineServiceParameters.java @@ -0,0 +1,329 @@ +/*- + * ============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.service.parameters.engineservice; + +import java.io.File; +import java.net.URL; + +import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey; +import org.onap.policy.apex.model.basicmodel.service.AbstractParameters; +import org.onap.policy.apex.model.basicmodel.service.ParameterService; +import org.onap.policy.apex.model.utilities.ResourceUtils; +import org.onap.policy.apex.service.parameters.ApexParameterValidator; + +import org.onap.policy.apex.core.engine.EngineParameters; + +/** + * This class holds the parameters for an Apex Engine Service with multiple engine threads running + * multiple engines. + * + * <p> + * The following parameters are defined: + * <ol> + * <li>name: The name of the Apex engine service, which can be set to any value that matches the + * regular expression {@link org.onap.policy.apex.model.basicmodel.concepts.AxKey#NAME_REGEXP}. + * <li>version: The name of the Apex engine service, which can be set to any value that matches the + * regular expression {@link org.onap.policy.apex.model.basicmodel.concepts.AxKey#VERSION_REGEXP}. + * <li>id: The ID of the Apex engine service, which can be set to any integer value by a user. + * <li>instanceCount: The number of Apex engines to spawn in this engine service. Each engine + * executes in its own thread. + * <li>deploymentPort: The port that the Apex Engine Service will open so that it can be managed + * using the EngDep protocol. The EngDep protocol allows the engine service to be monitored, to + * start and stop engines in the engine service, and to update the policy model of the engine + * service. + * <li>engineParameters: Parameters (a {@link EngineParameters} instance) that all of the engines in + * the engine service will use. All engine threads use the same parameters and act as a pool of + * engines. Engine parameters specify the executors and context management for the engines. + * <li>policyModelFileName: The full path to the policy model file name to deploy on the engine + * service. + * <li>periodicEventPeriod: The period in milliseconds at which the periodic event PERIOIC_EVENT + * will be generated by APEX, 0 means no periodic event generation, negative values are illegal. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EngineServiceParameters extends AbstractParameters implements ApexParameterValidator { + private static final int MAX_PORT = 65535; + + // @formatter:off + /** The default name of the Apex engine service. */ + public static final String DEFAULT_NAME = "ApexEngineService"; + + /** The default version of the Apex engine service. */ + public static final String DEFAULT_VERSION = "1.0.0"; + + /** The default ID of the Apex engine service. */ + public static final int DEFAULT_ID = -1; + + /** The default instance count for the Apex engine service. */ + public static final int DEFAULT_INSTANCE_COUNT = 1; + + /** The default EngDep deployment port of the Apex engine service. */ + public static final int DEFAULT_DEPLOYMENT_PORT = 34421; + + // Apex engine service parameters + private String name = DEFAULT_NAME; + private String version = DEFAULT_VERSION; + private int id = DEFAULT_ID; + private int instanceCount = DEFAULT_INSTANCE_COUNT; + private int deploymentPort = DEFAULT_DEPLOYMENT_PORT; + private String policyModelFileName = null; + private long periodicEventPeriod = 0; + // @formatter:on + + // Apex engine internal parameters + private EngineParameters engineParameters = new EngineParameters(); + + /** + * Constructor to create an apex engine service parameters instance and register the instance + * with the parameter service. + */ + public EngineServiceParameters() { + super(EngineServiceParameters.class.getCanonicalName()); + ParameterService.registerParameters(EngineServiceParameters.class, this); + } + + /** + * Gets the key of the Apex engine service. + * + * @return the Apex engine service key + */ + public AxArtifactKey getEngineKey() { + return new AxArtifactKey(name, version); + } + + /** + * Sets the key of the Apex engine service. + * + * @param key the the Apex engine service key + */ + public void setEngineKey(final AxArtifactKey key) { + this.setName(key.getName()); + this.setVersion(key.getVersion()); + } + + /** + * Gets the name of the engine service. + * + * @return the name of the engine service + */ + public String getName() { + return name; + } + + /** + * Sets the name of the engine service. + * + * @param name the name of the engine service + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Gets the version of the engine service. + * + * @return the version of the engine service + */ + public String getVersion() { + return version; + } + + /** + * Sets the version of the engine service. + * + * @param version the version of the engine service + */ + public void setVersion(final String version) { + this.version = version; + } + + /** + * Gets the id of the engine service. + * + * @return the id of the engine service + */ + public int getId() { + return id; + } + + /** + * Sets the id of the engine service. + * + * @param id the id of the engine service + */ + public void setId(final int id) { + this.id = id; + } + + /** + * Gets the instance count of the engine service. + * + * @return the instance count of the engine service + */ + public int getInstanceCount() { + return instanceCount; + } + + /** + * Sets the instance count of the engine service. + * + * @param instanceCount the instance count of the engine service + */ + public void setInstanceCount(final int instanceCount) { + this.instanceCount = instanceCount; + } + + /** + * Gets the deployment port of the engine service. + * + * @return the deployment port of the engine service + */ + public int getDeploymentPort() { + return deploymentPort; + } + + /** + * Sets the deployment port of the engine service. + * + * @param deploymentPort the deployment port of the engine service + */ + public void setDeploymentPort(final int deploymentPort) { + this.deploymentPort = deploymentPort; + } + + /** + * Gets the file name of the policy engine for deployment on the engine service. + * + * @return the file name of the policy engine for deployment on the engine service + */ + public String getPolicyModelFileName() { + return ResourceUtils.getFilePath4Resource(policyModelFileName); + } + + /** + * Sets the file name of the policy engine for deployment on the engine service. + * + * @param policyModelFileName the file name of the policy engine for deployment on the engine + * service + */ + public void setPolicyModelFileName(final String policyModelFileName) { + this.policyModelFileName = policyModelFileName; + } + + /** + * Get the period in milliseconds at which periodic events are sent, zero means no periodic + * events are being sent. + * + * @return the periodic period + */ + public long getPeriodicEventPeriod() { + return periodicEventPeriod; + } + + /** + * Set the period in milliseconds at which periodic events are sent, zero means no periodic + * events are to be sent, negative values are illegal. + * + * @param periodicEventPeriod the periodic period + */ + public void setPeriodicEventPeriod(final long periodicEventPeriod) { + this.periodicEventPeriod = periodicEventPeriod; + } + + /** + * Gets the engine parameters for engines in the engine service. + * + * @return the engine parameters for engines in the engine service + */ + public EngineParameters getEngineParameters() { + return engineParameters; + } + + /** + * Sets the engine parameters for engines in the engine service. + * + * @param engineParameters the engine parameters for engines in the engine service + */ + public void setEngineParameters(final EngineParameters engineParameters) { + this.engineParameters = engineParameters; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.apps.uservice.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + try { + new AxArtifactKey(name, version); + } catch (final Exception e) { + errorMessageBuilder.append(" name [" + name + "] and/or version [" + version + "] invalid\n"); + errorMessageBuilder.append(" " + e.getMessage() + "\n"); + } + + if (id < 0) { + errorMessageBuilder.append( + " id not specified or specified value [" + id + "] invalid, must be specified as id >= 0\n"); + } + + if (instanceCount < 1) { + errorMessageBuilder.append( + " instanceCount [" + instanceCount + "] invalid, must be specified as instanceCount >= 1\n"); + } + + if (deploymentPort < 1 || deploymentPort > MAX_PORT) { + errorMessageBuilder.append( + " deploymentPort [" + deploymentPort + "] invalid, must be specified as 1024 <= port <= 65535\n"); + } + + if (policyModelFileName != null) { + if (policyModelFileName.trim().length() == 0) { + errorMessageBuilder.append(" policyModelFileName [" + policyModelFileName + + "] invalid, must be specified as a non-empty string\n"); + } else { + // The file name can refer to a resource on the local file system or on the class + // path + final URL fileURL = ResourceUtils.getURL4Resource(policyModelFileName); + if (fileURL == null) { + errorMessageBuilder.append( + " policyModelFileName [" + policyModelFileName + "] not found or is not a plain file\n"); + } else { + final File policyModelFile = new File(fileURL.getPath()); + if (!policyModelFile.isFile()) { + errorMessageBuilder.append(" policyModelFileName [" + policyModelFileName + + "] not found or is not a plain file\n"); + } + } + } + } + + if (periodicEventPeriod < 0) { + errorMessageBuilder.append(" periodicEventPeriod [" + periodicEventPeriod + + "] invalid, must be specified in milliseconds as >=0\n"); + } + + return errorMessageBuilder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/EngineServiceParametersJSONAdapter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/EngineServiceParametersJSONAdapter.java new file mode 100644 index 000000000..9fca2fd19 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/EngineServiceParametersJSONAdapter.java @@ -0,0 +1,287 @@ +/*- + * ============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.service.parameters.engineservice; + +import java.lang.reflect.Type; +import java.util.Map.Entry; + +import org.onap.policy.apex.context.impl.schema.java.JavaSchemaHelperParameters; +import org.onap.policy.apex.context.parameters.ContextParameters; +import org.onap.policy.apex.context.parameters.DistributorParameters; +import org.onap.policy.apex.context.parameters.LockManagerParameters; +import org.onap.policy.apex.context.parameters.PersistorParameters; +import org.onap.policy.apex.context.parameters.SchemaHelperParameters; +import org.onap.policy.apex.context.parameters.SchemaParameters; +import org.onap.policy.apex.core.engine.EngineParameters; +import org.onap.policy.apex.core.engine.ExecutorParameters; +import org.onap.policy.apex.model.basicmodel.service.AbstractParameters; +import org.onap.policy.apex.service.parameters.ApexParameterRuntimeException; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * This class deserializes engine service parameters from JSON format. The class produces an + * {@link EngineServiceParameters} instance from incoming JSON read from a configuration file in + * JSON format. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EngineServiceParametersJSONAdapter + implements JsonSerializer<EngineParameters>, JsonDeserializer<EngineParameters> { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EngineServiceParametersJSONAdapter.class); + + private static final String PARAMETER_CLASS_NAME = "parameterClassName"; + + // @formatter:off + private static final String CONTEXT_PARAMETERS = "contextParameters"; + private static final String DISTRIBUTOR_PARAMETERS = "distributorParameters"; + private static final String LOCK_MANAGER_PARAMETERS = "lockManagerParameters"; + private static final String PERSISTOR_PARAMETERS = "persistorParameters"; + private static final String SCHEMA_PARAMETERS = "schemaParameters"; + private static final String EXECUTOR_PARAMETERS = "executorParameters"; + // @formatter:on + + /* + * (non-Javadoc) + * + * @see com.google.gson.JsonSerializer#serialize(java.lang.Object, java.lang.reflect.Type, + * com.google.gson.JsonSerializationContext) + */ + @Override + public JsonElement serialize(final EngineParameters src, final Type typeOfSrc, + final JsonSerializationContext context) { + final String returnMessage = "serialization of Apex parameters to Json is not supported"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + /* + * (non-Javadoc) + * + * @see com.google.gson.JsonDeserializer#deserialize(com.google.gson.JsonElement, + * java.lang.reflect.Type, com.google.gson.JsonDeserializationContext) + */ + @Override + public EngineParameters deserialize(final JsonElement json, final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + final JsonObject engineParametersJsonObject = json.getAsJsonObject(); + + final EngineParameters engineParameters = new EngineParameters(); + + // Deserialise context parameters, they may be a subclass of the ContextParameters class + engineParameters.setContextParameters( + (ContextParameters) context.deserialize(engineParametersJsonObject, ContextParameters.class)); + + // Context parameter wrangling + getContextParameters(engineParametersJsonObject, engineParameters, context); + + // Executor parameter wrangling + getExecutorParameters(engineParametersJsonObject, engineParameters, context); + + return engineParameters; + } + + /** + * Get the context parameters for Apex. + * + * @param engineParametersJsonObject The input JSON + * @param engineParameters The output parameters + * @param context the JSON context + */ + private void getContextParameters(final JsonObject engineParametersJsonObject, + final EngineParameters engineParameters, final JsonDeserializationContext context) { + final JsonElement contextParametersElement = engineParametersJsonObject.get(CONTEXT_PARAMETERS); + + // Context parameters are optional so if the element does not exist, just return + if (contextParametersElement == null) { + return; + } + + // We do this because the JSON parameters may be for a subclass of ContextParameters + final ContextParameters contextParameters = + (ContextParameters) deserializeParameters(CONTEXT_PARAMETERS, contextParametersElement, context); + + // We know this will work because if the context parameters was not a Json object, the + // previous deserializeParameters() call would not have worked + final JsonObject contextParametersObject = engineParametersJsonObject.get(CONTEXT_PARAMETERS).getAsJsonObject(); + + // Now get the distributor, lock manager, and persistence parameters + final JsonElement distributorParametersElement = contextParametersObject.get(DISTRIBUTOR_PARAMETERS); + if (distributorParametersElement != null) { + contextParameters + .setDistributorParameters((DistributorParameters) deserializeParameters(DISTRIBUTOR_PARAMETERS, + distributorParametersElement, context)); + } + + final JsonElement lockManagerParametersElement = contextParametersObject.get(LOCK_MANAGER_PARAMETERS); + if (lockManagerParametersElement != null) { + contextParameters + .setLockManagerParameters((LockManagerParameters) deserializeParameters(LOCK_MANAGER_PARAMETERS, + lockManagerParametersElement, context)); + } + + final JsonElement persistorParametersElement = contextParametersObject.get(PERSISTOR_PARAMETERS); + if (persistorParametersElement != null) { + contextParameters.setPersistorParameters((PersistorParameters) deserializeParameters(PERSISTOR_PARAMETERS, + persistorParametersElement, context)); + } + + // Schema Handler parameter wrangling + getSchemaHandlerParameters(contextParametersObject, contextParameters, context); + + // Get the engine plugin parameters + engineParameters.setContextParameters(contextParameters); + } + + /** + * Get the executor parameters for Apex. + * + * @param engineParametersJsonObject The input JSON + * @param engineParameters The output parameters + * @param context the JSON context + */ + private void getExecutorParameters(final JsonObject engineParametersJsonObject, + final EngineParameters engineParameters, final JsonDeserializationContext context) { + final JsonElement executorParametersElement = engineParametersJsonObject.get(EXECUTOR_PARAMETERS); + + // Executor parameters are mandatory so if the element does not exist throw an exception + if (executorParametersElement == null) { + final String returnMessage = "no \"" + EXECUTOR_PARAMETERS + + "\" entry found in parameters, at least one executor parameter entry must be specified"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + // Deserialize the executor parameters + final JsonObject executorParametersJsonObject = + engineParametersJsonObject.get(EXECUTOR_PARAMETERS).getAsJsonObject(); + + for (final Entry<String, JsonElement> executorEntries : executorParametersJsonObject.entrySet()) { + final ExecutorParameters executorParameters = + (ExecutorParameters) deserializeParameters(EXECUTOR_PARAMETERS + ':' + executorEntries.getKey(), + executorEntries.getValue(), context); + engineParameters.getExecutorParameterMap().put(executorEntries.getKey(), executorParameters); + } + } + + /** + * Get the schema parameters for Apex. + * + * @param contextParametersJsonObject The input JSON + * @param contextParameters The output parameters + * @param context the JSON context + */ + private void getSchemaHandlerParameters(final JsonObject contextParametersJsonObject, + final ContextParameters contextParameters, final JsonDeserializationContext context) { + final JsonElement schemaParametersElement = contextParametersJsonObject.get(SCHEMA_PARAMETERS); + + // Insert the default Java schema helper + contextParameters.getSchemaParameters().getSchemaHelperParameterMap() + .put(SchemaParameters.DEFAULT_SCHEMA_FLAVOUR, new JavaSchemaHelperParameters()); + + // Context parameters are optional so if the element does not exist, just return + if (schemaParametersElement == null) { + return; + } + + // Deserialize the executor parameters + final JsonObject schemaHelperParametersJsonObject = + contextParametersJsonObject.get(SCHEMA_PARAMETERS).getAsJsonObject(); + + for (final Entry<String, JsonElement> schemaHelperEntries : schemaHelperParametersJsonObject.entrySet()) { + contextParameters.getSchemaParameters().getSchemaHelperParameterMap().put(schemaHelperEntries.getKey(), + (SchemaHelperParameters) deserializeParameters( + SCHEMA_PARAMETERS + ':' + schemaHelperEntries.getKey(), schemaHelperEntries.getValue(), + context)); + } + } + + /** + * Deserialize a parameter object that's a superclass of the AbstractParameters class. + * + * @param parametersLabel Label to use for error messages + * @param parametersElement The JSON object holding the parameters + * @param context The GSON context + * @return the parameters + * @throws ApexParameterRuntimeException on errors reading the parameters + */ + private AbstractParameters deserializeParameters(final String parametersLabel, final JsonElement parametersElement, + final JsonDeserializationContext context) throws ApexParameterRuntimeException { + JsonObject parametersObject = null; + + // Check that the JSON element is a JSON object + if (parametersElement.isJsonObject()) { + parametersObject = parametersElement.getAsJsonObject(); + } else { + final String returnMessage = "value of \"" + parametersLabel + "\" entry is not a parameter JSON object"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + // Get the parameter class name for instantiation in deserialization + final JsonElement parameterClassNameElement = parametersObject.get(PARAMETER_CLASS_NAME); + if (parameterClassNameElement == null) { + final String returnMessage = + "could not find field \"" + PARAMETER_CLASS_NAME + "\" in \"" + parametersLabel + "\" entry"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + // Check the parameter is a JSON primitive + if (!parameterClassNameElement.isJsonPrimitive()) { + final String returnMessage = "value for field \"" + PARAMETER_CLASS_NAME + "\" in \"" + parametersLabel + + "\" entry is not a plain string"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + // Check the parameter has a value + final String parameterClassName = parameterClassNameElement.getAsString(); + if (parameterClassName == null || parameterClassName.trim().length() == 0) { + final String returnMessage = "value for field \"" + PARAMETER_CLASS_NAME + "\" in \"" + parametersLabel + + "\" entry is not specified or is blank"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + // Deserialize the parameters using GSON + AbstractParameters parameters = null; + try { + parameters = context.deserialize(parametersObject, Class.forName(parameterClassName)); + } catch (JsonParseException | ClassNotFoundException e) { + final String returnMessage = + "failed to deserialize the parameters for \"" + parametersLabel + "\" " + "to parameter class \"" + + parameterClassName + "\"\n" + e.getClass().getCanonicalName() + ": " + e.getMessage(); + LOGGER.error(returnMessage, e); + throw new ApexParameterRuntimeException(returnMessage, e); + } + + return parameters; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/package-info.java new file mode 100644 index 000000000..e724ce41c --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/engineservice/package-info.java @@ -0,0 +1,26 @@ +/*- + * ============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 parameters for the APEX Engine Service and for engines running in the engine service. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.parameters.engineservice; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/EventHandlerParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/EventHandlerParameters.java new file mode 100644 index 000000000..34589f31b --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/EventHandlerParameters.java @@ -0,0 +1,362 @@ +/*- + * ============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.service.parameters.eventhandler; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import org.onap.policy.apex.model.basicmodel.service.AbstractParameters; +import org.onap.policy.apex.service.parameters.ApexParameterValidator; +import org.onap.policy.apex.service.parameters.carriertechnology.CarrierTechnologyParameters; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; + +/** + * The parameters for a single event producer, event consumer or synchronous event handler. + * <p> + * Event producers, consumers, and synchronous event handlers all use a carrier technology and an + * event protocol so the actual parameters for each one are the same. Therefore, we use the same + * class for the parameters of each one. + * <p> + * The following parameters are defined: + * <ol> + * <li>carrierTechnologyParameters: The carrier technology is the type of messaging infrastructure + * used to carry events. Examples are File, Kafka or REST. + * <li>eventProtocolParameters: The format that the events are in when being carried. Examples are + * JSON, XML, or Java Beans. carrier technology + * <li>synchronousMode: true if the event handler is working in synchronous mode, defaults to false + * <li>synchronousPeer: the peer event handler (consumer for producer or producer for consumer) of + * this event handler in synchronous mode + * <li>synchronousTimeout: the amount of time to wait for the reply to synchronous events before + * they are timed out + * <li>requestorMode: true if the event handler is working in requestor mode, defaults to false + * <li>requestorPeer: the peer event handler (consumer for producer or producer for consumer) of + * this event handler in requestor mode + * <li>requestorTimeout: the amount of time to wait for the reply to synchronous events before they + * are timed out + * <li>eventNameFilter: a regular expression to apply to events on this event handler. If specified, + * events not matching the given regular expression are ignored. If it is null, all events are + * handledDefaults to null. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventHandlerParameters extends AbstractParameters implements ApexParameterValidator { + private String name = null; + private CarrierTechnologyParameters carrierTechnologyParameters = null; + private EventProtocolParameters eventProtocolParameters = null; + private boolean synchronousMode = false; + private String synchronousPeer = null; + private long synchronousTimeout = 0; + private boolean requestorMode = false; + private String requestorPeer = null; + private long requestorTimeout = 0; + private String eventName = null; + private String eventNameFilter = null; + + /** + * Constructor to create an event handler parameters instance. + */ + public EventHandlerParameters() { + super(EventHandlerParameters.class.getCanonicalName()); + } + + /** + * Constructor to create an event handler parameters instance with the name of a sub class of + * this class. + * + * @param parameterClassName the class name of a sub class of this class + */ + public EventHandlerParameters(final String parameterClassName) { + super(parameterClassName); + } + + /** + * Gets the name of the event handler. + * + * @return the event handler name + */ + public String getName() { + return name; + } + + /** + * Sets the name of the event handler. + * + * @param name the event handler name + */ + public void setName(final String name) { + this.name = name; + } + + /** + * Checks if the name of the event handler is set. + * + * @return true if the name is set + */ + public boolean checkSetName() { + return !(name == null || name.trim().length() == 0); + } + + /** + * Gets the carrier technology parameters of the event handler. + * + * @return the carrierTechnologyParameters of the event handler + */ + public CarrierTechnologyParameters getCarrierTechnologyParameters() { + return carrierTechnologyParameters; + } + + /** + * Sets the carrier technology parameters of the event handler. + * + * @param carrierTechnologyParameters the carrierTechnologyParameters to set + */ + public void setCarrierTechnologyParameters(final CarrierTechnologyParameters carrierTechnologyParameters) { + this.carrierTechnologyParameters = carrierTechnologyParameters; + } + + /** + * Gets the event protocol parameters of the event handler. + * + * @return the eventProtocolParameters + */ + public EventProtocolParameters getEventProtocolParameters() { + return eventProtocolParameters; + } + + /** + * Sets the event protocol parameters. + * + * @param eventProtocolParameters the eventProtocolParameters to set + */ + public void setEventProtocolParameters(final EventProtocolParameters eventProtocolParameters) { + this.eventProtocolParameters = eventProtocolParameters; + } + + + /** + * Checks if the event handler is in the given peered mode. + * + * @param peeredMode the peer mode + * @return true, if the event handler is in the peered mode + */ + public boolean isPeeredMode(final EventHandlerPeeredMode peeredMode) { + switch (peeredMode) { + case SYNCHRONOUS: + return synchronousMode; + case REQUESTOR: + return requestorMode; + default: + return false; + } + } + + /** + * Sets a peered mode as true or false on the event handler. + * + * @param peeredMode the peered mode to set + * @param peeredModeValue the value to set the peered mode to + */ + public void setPeeredMode(final EventHandlerPeeredMode peeredMode, final boolean peeredModeValue) { + switch (peeredMode) { + case SYNCHRONOUS: + synchronousMode = peeredModeValue; + return; + case REQUESTOR: + requestorMode = peeredModeValue; + return; + default: + return; + } + } + + /** + * Gets the peer for the event handler in this peered mode. + * + * @param peeredMode the peered mode to get the peer for + * @return the peer + */ + public String getPeer(final EventHandlerPeeredMode peeredMode) { + switch (peeredMode) { + case SYNCHRONOUS: + return synchronousPeer; + case REQUESTOR: + return requestorPeer; + default: + return null; + } + } + + /** + * Sets the peer for the event handler in this peered mode. + * + * @param peeredMode the peered mode to set the peer for + * @param peer the peer + */ + public void setPeer(final EventHandlerPeeredMode peeredMode, final String peer) { + switch (peeredMode) { + case SYNCHRONOUS: + synchronousPeer = peer; + return; + case REQUESTOR: + requestorPeer = peer; + return; + default: + return; + } + } + + /** + * Get the timeout value for the event handler in peered mode. + * + * @param peeredMode the peered mode to get the timeout for + * @return the timeout value + */ + public long getPeerTimeout(final EventHandlerPeeredMode peeredMode) { + switch (peeredMode) { + case SYNCHRONOUS: + return synchronousTimeout; + case REQUESTOR: + return requestorTimeout; + default: + return -1; + } + } + + /** + * Set the timeout value for the event handler in peered mode. + * + * @param peeredMode the peered mode to set the timeout for + * @param timeout the timeout value + */ + public void setPeerTimeout(final EventHandlerPeeredMode peeredMode, final long timeout) { + switch (peeredMode) { + case SYNCHRONOUS: + synchronousTimeout = timeout; + return; + case REQUESTOR: + requestorTimeout = timeout; + return; + default: + return; + } + } + + /** + * Check if an event name is being used. + * + * @return true if an event name is being used + */ + public boolean isSetEventName() { + return eventName != null; + } + + /** + * Gets the event name for this event handler. + * + * @return the event name + */ + public String getEventName() { + return eventName; + } + + /** + * Sets the event name for this event handler. + * + * @param eventName the event name + */ + public void setEventName(final String eventName) { + this.eventName = eventName; + } + + /** + * Check if event name filtering is being used. + * + * @return true if event name filtering is being used + */ + public boolean isSetEventNameFilter() { + return eventNameFilter != null; + } + + /** + * Gets the event name filter for this event handler. + * + * @return the event name filter + */ + public String getEventNameFilter() { + return eventNameFilter; + } + + /** + * Sets the event name filter for this event handler. + * + * @param eventNameFilter the event name filter + */ + public void setEventNameFilter(final String eventNameFilter) { + this.eventNameFilter = eventNameFilter; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + if (eventProtocolParameters == null) { + errorMessageBuilder.append(" event handler eventProtocolParameters not specified or blank\n"); + } else { + errorMessageBuilder.append(eventProtocolParameters.validate()); + } + + if (carrierTechnologyParameters == null) { + errorMessageBuilder.append(" event handler carrierTechnologyParameters not specified or blank\n"); + } else { + errorMessageBuilder.append(carrierTechnologyParameters.validate()); + } + + if (eventNameFilter != null) { + try { + Pattern.compile(eventNameFilter); + } catch (final PatternSyntaxException pse) { + errorMessageBuilder.append(" event handler eventNameFilter is not a valid regular expression: " + + pse.getMessage() + "\n"); + } + } + return errorMessageBuilder.toString(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return "EventHandlerParameters [name=" + name + ", carrierTechnologyParameters=" + carrierTechnologyParameters + + ", eventProtocolParameters=" + eventProtocolParameters + ", synchronousMode=" + synchronousMode + + ", synchronousPeer=" + synchronousPeer + ", synchronousTimeout=" + synchronousTimeout + + ", requestorMode=" + requestorMode + ", requestorPeer=" + requestorPeer + ", requestorTimeout=" + + requestorTimeout + ", eventName=" + eventName + ", eventNameFilter=" + eventNameFilter + "]"; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/EventHandlerPeeredMode.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/EventHandlerPeeredMode.java new file mode 100644 index 000000000..b7ee667f3 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/EventHandlerPeeredMode.java @@ -0,0 +1,42 @@ +/*- + * ============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.service.parameters.eventhandler; + +/** + * This enum specifies the peered mode that an event handler may be in. + * + * <p> + * The following values are defined: + * <ol> + * <li>SYNCHRONOUS: The event handler is tied to another event handler for event handling in APEX, + * used for request-response calls where APEX is the receiver. + * <li>REQUESTOR: The event handler is tied another event handler for event handling in APEX, used + * for request-response calls where APEX is the sender. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + * + * @author liam.fallon@ericsson.com + * + */ +public enum EventHandlerPeeredMode { + SYNCHRONOUS, REQUESTOR +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/package-info.java new file mode 100644 index 000000000..2866358ff --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventhandler/package-info.java @@ -0,0 +1,26 @@ +/*- + * ============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 structure of producer parameters for APEX. + * + * @author John Keeney (john.keeney@ericsson.com) + */ +package org.onap.policy.apex.service.parameters.eventhandler; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolParameters.java new file mode 100644 index 000000000..6e66a18e9 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolParameters.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.service.parameters.eventprotocol; + +import org.onap.policy.apex.model.basicmodel.service.AbstractParameters; +import org.onap.policy.apex.service.parameters.ApexParameterValidator; + +/** + * A default event protocol parameter class that may be specialized by event protocol plugins that + * require plugin specific parameters. + * + * <p> + * The following parameters are defined: + * <ol> + * <li>label: The label of the event protocol technology. + * <li>eventProducerPluginClass: The name of the plugin class that will be used by Apex to produce + * and emit output events for this carrier technology + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public abstract class EventProtocolParameters extends AbstractParameters implements ApexParameterValidator { + // The event protocol label + private String label = null; + + // Event protocol converter plugin class for this event protocol + private String eventProtocolPluginClass; + + /** + * Constructor to create an event protocol 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 EventProtocolParameters(final String parameterClassName) { + super(parameterClassName); + } + + /** + * Gets the label of the event protocol. + * + * @return the label of the event protocol + */ + public String getLabel() { + return label; + } + + /** + * Sets the label of the event protocol. + * + * @param label the label of the event protocol + */ + public void setLabel(final String label) { + this.label = label.replaceAll("\\s+", ""); + } + + /** + * Gets the event event protocol plugin class. + * + * @return the event event protocol plugin class + */ + public String getEventProtocolPluginClass() { + return eventProtocolPluginClass; + } + + /** + * Sets the event event protocol plugin class. + * + * @param eventProtocolPluginClass the event event protocol plugin class + */ + public void setEventProtocolPluginClass(final String eventProtocolPluginClass) { + this.eventProtocolPluginClass = eventProtocolPluginClass.replaceAll("\\s+", ""); + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.model.basicmodel.service.AbstractParameters#toString() + */ + @Override + public String toString() { + return "CarrierTechnologyParameters [label=" + label + ", EventProtocolPluginClass=" + eventProtocolPluginClass + + "]"; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + if (label == null || label.length() == 0) { + errorMessageBuilder.append(" event protocol label not specified or is blank\n"); + } + + if (eventProtocolPluginClass == null || eventProtocolPluginClass.length() == 0) { + errorMessageBuilder.append(" event protocol eventProtocolPluginClass not specified or is blank\n"); + } + + return errorMessageBuilder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolParametersJSONAdapter.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolParametersJSONAdapter.java new file mode 100644 index 000000000..c880756b3 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolParametersJSONAdapter.java @@ -0,0 +1,172 @@ +/*- + * ============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.service.parameters.eventprotocol; + +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; + +import org.onap.policy.apex.service.engine.event.impl.apexprotocolplugin.ApexEventProtocolParameters; +import org.onap.policy.apex.service.engine.event.impl.jsonprotocolplugin.JSONEventProtocolParameters; +import org.onap.policy.apex.service.parameters.ApexParameterRuntimeException; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; + +/** + * This class serialises and deserialises various type of event protocol parameters to and from + * JSON. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class EventProtocolParametersJSONAdapter + implements JsonSerializer<EventProtocolParameters>, JsonDeserializer<EventProtocolParameters> { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(EventProtocolParametersJSONAdapter.class); + + private static final String PARAMETER_CLASS_NAME = "parameterClassName"; + + private static final String EVENT_PROTOCOL_TOKEN = "eventProtocol"; + private static final String EVENT_PROTOCOL_PARAMETERS = "parameters"; + + // Built in event protocol parameters + private static final Map<String, String> BUILT_IN_EVENT_RPOTOCOL_PARMETER_CLASS_MAP = new HashMap<>(); + static { + BUILT_IN_EVENT_RPOTOCOL_PARMETER_CLASS_MAP.put("JSON", JSONEventProtocolParameters.class.getCanonicalName()); + BUILT_IN_EVENT_RPOTOCOL_PARMETER_CLASS_MAP.put("APEX", ApexEventProtocolParameters.class.getCanonicalName()); + } + + /* + * (non-Javadoc) + * + * @see com.google.gson.JsonSerializer#serialize(java.lang.Object, java.lang.reflect.Type, + * com.google.gson.JsonSerializationContext) + */ + @Override + public JsonElement serialize(final EventProtocolParameters src, final Type typeOfSrc, + final JsonSerializationContext context) { + final String returnMessage = "serialization of Apex event protocol parameters to Json is not supported"; + LOGGER.error(returnMessage); + throw new ApexParameterRuntimeException(returnMessage); + } + + /* + * (non-Javadoc) + * + * @see com.google.gson.JsonDeserializer#deserialize(com.google.gson.JsonElement, + * java.lang.reflect.Type, com.google.gson.JsonDeserializationContext) + */ + @Override + public EventProtocolParameters deserialize(final JsonElement json, final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + final JsonObject jsonObject = json.getAsJsonObject(); + + // Get the event protocol label primitive + final JsonPrimitive labelJsonPrimitive = (JsonPrimitive) jsonObject.get(EVENT_PROTOCOL_TOKEN); + + // Check if we found our event protocol + if (labelJsonPrimitive == null) { + LOGGER.warn("event protocol parameter \"" + EVENT_PROTOCOL_TOKEN + "\" not found in JSON file"); + return null; + } + + // Get and check the event protocol label + final String eventProtocolLabel = labelJsonPrimitive.getAsString().replaceAll("\\s+", ""); + if (eventProtocolLabel == null || eventProtocolLabel.length() == 0) { + final String errorMessage = "event protocol parameter \"" + EVENT_PROTOCOL_TOKEN + "\" value \"" + + labelJsonPrimitive.getAsString() + "\" invalid in JSON file"; + LOGGER.warn(errorMessage); + throw new ApexParameterRuntimeException(errorMessage); + } + + // We now get the event protocol parameter class + String eventProtocolParameterClassName = null; + + // Get the event protocol parameter class for the event protocol plugin class from the + // configuration parameters + final JsonPrimitive classNameJsonPrimitive = (JsonPrimitive) jsonObject.get(PARAMETER_CLASS_NAME); + + // If no event protocol parameter class was specified, we use the default + if (classNameJsonPrimitive == null) { + eventProtocolParameterClassName = BUILT_IN_EVENT_RPOTOCOL_PARMETER_CLASS_MAP.get(eventProtocolLabel); + } else { + // We use the specified one + eventProtocolParameterClassName = classNameJsonPrimitive.getAsString().replaceAll("\\s+", ""); + } + + // Check the event protocol parameter class + if (eventProtocolParameterClassName == null || eventProtocolParameterClassName.length() == 0) { + final String errorMessage = + "event protocol \"" + eventProtocolLabel + "\" parameter \"" + PARAMETER_CLASS_NAME + "\" value \"" + + classNameJsonPrimitive.getAsString() + "\" invalid in JSON file"; + LOGGER.warn(errorMessage); + throw new ApexParameterRuntimeException(errorMessage); + } + + // Get the class for the event protocol + Class<?> eventProtocolParameterClass = null; + try { + eventProtocolParameterClass = Class.forName(eventProtocolParameterClassName); + } catch (final ClassNotFoundException e) { + final String errorMessage = + "event protocol \"" + eventProtocolLabel + "\" parameter \"" + PARAMETER_CLASS_NAME + "\" value \"" + + eventProtocolParameterClassName + "\", could not find class"; + LOGGER.warn(errorMessage, e); + throw new ApexParameterRuntimeException(errorMessage, e); + } + + // Deserialise the class + EventProtocolParameters eventProtocolParameters = + context.deserialize(jsonObject.get(EVENT_PROTOCOL_PARAMETERS), eventProtocolParameterClass); + if (eventProtocolParameters == null) { + // OK no parameters for the event protocol have been specified, just instantiate the + // default parameters + try { + eventProtocolParameters = (EventProtocolParameters) eventProtocolParameterClass.newInstance(); + } catch (final Exception e) { + final String errorMessage = "could not create default parameters for event protocol \"" + + eventProtocolLabel + "\"\n" + e.getMessage(); + LOGGER.warn(errorMessage, e); + throw new ApexParameterRuntimeException(errorMessage, e); + } + } + + // Check that the event protocol label matches the label in the event protocol parameters + // object + if (!eventProtocolParameters.getLabel().equals(eventProtocolLabel)) { + final String errorMessage = "event protocol \"" + eventProtocolLabel + "\" does not match plugin \"" + + eventProtocolParameters.getLabel() + "\" in \"" + eventProtocolParameterClassName + + "\", specify correct event protocol parameter plugin in parameter \"" + PARAMETER_CLASS_NAME + + "\""; + LOGGER.warn(errorMessage); + throw new ApexParameterRuntimeException(errorMessage); + } + + return eventProtocolParameters; + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolTextCharDelimitedParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolTextCharDelimitedParameters.java new file mode 100644 index 000000000..f8873ada4 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolTextCharDelimitedParameters.java @@ -0,0 +1,122 @@ +/*- + * ============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.service.parameters.eventprotocol; + +import org.onap.policy.apex.service.parameters.ApexParameterValidator; + +/** + * An event protocol parameter class for character delimited textual event protocols that may be + * specialized by event protocol plugins that require plugin specific parameters. + * + * <p> + * The following parameters are defined: + * <ol> + * <li>startChar: starting character delimiter for text blocks containing an event. + * <li>endChar: ending character delimiter for text blocks containing an event. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public abstract class EventProtocolTextCharDelimitedParameters extends EventProtocolParameters + implements ApexParameterValidator { + // The starting and ending character delimiter + private char startChar = '\0'; + private char endChar = '\0'; + + /** + * Constructor to create an event protocol parameters instance with the name of a sub class of + * this class. + * + * @param parameterClassName the class name of a sub class of this class + */ + public EventProtocolTextCharDelimitedParameters(final String parameterClassName) { + super(parameterClassName); + } + + /** + * Gets the start character that delimits the start of text blocks. + * + * @return the start char + */ + public char getStartChar() { + return startChar; + } + + /** + * Sets the start character that delimits the start of text blocks. + * + * @param startChar the start character + */ + public void setStartChar(final char startChar) { + this.startChar = startChar; + } + + /** + * Gets the end character that delimits the end of text blocks. + * + * @return the end character + */ + public char getEndChar() { + return endChar; + } + + /** + * Sets the end character that delimits the end of text blocks. + * + * @param endChar the end character + */ + public void setEndChar(final char endChar) { + this.endChar = endChar; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters#toString() + */ + @Override + public String toString() { + return "EventProtocolTextCharDelimitedParameters {" + super.toString() + "} [startChar=" + startChar + + ", endChar=" + endChar + "]"; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + errorMessageBuilder.append(super.validate()); + + if (startChar == '\0') { + errorMessageBuilder.append(" text character delimited start character has not been specified\n"); + } + + if (endChar == '\0') { + errorMessageBuilder.append(" text character delimited end character has not been specified\n"); + } + + return errorMessageBuilder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolTextTokenDelimitedParameters.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolTextTokenDelimitedParameters.java new file mode 100644 index 000000000..37fbd32bf --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/EventProtocolTextTokenDelimitedParameters.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.service.parameters.eventprotocol; + +import org.onap.policy.apex.service.parameters.ApexParameterValidator; + +/** + * An event protocol parameter class for token delimited textual event protocols that may be + * specialized by event protocol plugins that require plugin specific parameters. + * + * <p> + * The following parameters are defined: + * <ol> + * <li>delimiterToken: the token string that delimits text blocks that contain events. + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public abstract class EventProtocolTextTokenDelimitedParameters extends EventProtocolParameters + implements ApexParameterValidator { + // The delimiter token for text blocks + private String delimiterToken = null; + + /** + * Constructor to create an event protocol parameters instance with the name of a sub class of + * this class. + * + * @param parameterClassName the class name of a sub class of this class + */ + public EventProtocolTextTokenDelimitedParameters(final String parameterClassName) { + super(parameterClassName); + } + + /** + * Gets the delimiter token that delimits events in the text. + * + * @return the delimiter token + */ + public String getDelimiterToken() { + return delimiterToken; + } + + + /** + * Sets the delimiter token that delimits events in the text. + * + * @param delimiterToken the delimiter token + */ + public void setDelimiterToken(final String delimiterToken) { + this.delimiterToken = delimiterToken; + } + + + @Override + public String toString() { + return "EventProtocolTextCharDelimitedParameters {" + super.toString() + "} [delimiterToken=" + delimiterToken + + "]"; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.parameters.ApexParameterValidator#validate() + */ + @Override + public String validate() { + final StringBuilder errorMessageBuilder = new StringBuilder(); + + errorMessageBuilder.append(super.validate()); + + if (delimiterToken == null || delimiterToken.length() == 0) { + errorMessageBuilder.append(" text delimiter token not specified or is blank\n"); + } + + return errorMessageBuilder.toString(); + } +} diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/package-info.java new file mode 100644 index 000000000..967198a62 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/eventprotocol/package-info.java @@ -0,0 +1,26 @@ +/*- + * ============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 structure of event protocol parameters for APEX. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.parameters.eventprotocol; diff --git a/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/package-info.java b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/package-info.java new file mode 100644 index 000000000..2c6473776 --- /dev/null +++ b/services/services-engine/src/main/java/org/onap/policy/apex/service/parameters/package-info.java @@ -0,0 +1,29 @@ +/*- + * ============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 parameter handling for all parameters in APEX. It uses specializations of (@link + * {@link org.onap.policy.apex.model.basicmodel.service.AbstractParameters} for all parameters and + * works with {@link org.onap.policy.apex.model.basicmodel.service.ParameterService} for storing and + * finding parameters. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +package org.onap.policy.apex.service.parameters; |