2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * Modifications Copyright (C) 2019-2020 Nordix Foundation.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.apex.core.engine.executor;
24 import java.util.HashMap;
26 import java.util.Map.Entry;
27 import java.util.Properties;
28 import java.util.TreeMap;
30 import org.onap.policy.apex.context.ContextException;
31 import org.onap.policy.apex.core.engine.ExecutorParameters;
32 import org.onap.policy.apex.core.engine.context.ApexInternalContext;
33 import org.onap.policy.apex.core.engine.event.EnEvent;
34 import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
35 import org.onap.policy.apex.core.engine.executor.exception.StateMachineRuntimeException;
36 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
37 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
38 import org.onap.policy.apex.model.basicmodel.service.ModelService;
39 import org.onap.policy.apex.model.policymodel.concepts.AxState;
40 import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
41 import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
42 import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskOutputType;
43 import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskReference;
44 import org.onap.policy.apex.model.policymodel.concepts.AxTask;
45 import org.onap.policy.apex.model.policymodel.concepts.AxTasks;
46 import org.slf4j.ext.XLogger;
47 import org.slf4j.ext.XLoggerFactory;
50 * This class is the executor for a state of a policy.
52 * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
53 * @author Liam Fallon (liam.fallon@ericsson.com)
55 public class StateExecutor implements Executor<EnEvent, StateOutput, AxState, ApexInternalContext> {
56 // Logger for this class
57 private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateExecutor.class);
59 // Hold the state and context definitions for this state
60 private AxState axState = null;
61 private Executor<?, ?, ?, ?> parent = null;
62 private ApexInternalContext context = null;
64 // Holds the incoming event and the state output for this state
65 private EnEvent lastIncomingEvent = null;
66 private StateOutput lastStateOutput = null;
68 // The task selection logic executor
69 private TaskSelectExecutor taskSelectExecutor = null;
71 // The map of task executors for this state
72 private final Map<AxArtifactKey, TaskExecutor> taskExecutorMap = new HashMap<>();
74 // The map of state outputs used directly by tasks
75 private final Map<AxArtifactKey, String> directStateOutputMap = new HashMap<>();
77 // The map of state finalizer logic executors used by tasks
78 private final Map<AxArtifactKey, StateFinalizerExecutor> task2StateFinalizerMap = new HashMap<>();
80 // The next state executor
81 private Executor<EnEvent, StateOutput, AxState, ApexInternalContext> nextExecutor = null;
83 // The executor factory
84 private ExecutorFactory executorFactory = null;
87 * Constructor, save the executor factory.
89 * @param executorFactory the executor factory to use for getting executors for task selection logic
91 public StateExecutor(final ExecutorFactory executorFactory) {
92 this.executorFactory = executorFactory;
99 public void setContext(final Executor<?, ?, ?, ?> incomingParent, final AxState incomingAxState,
100 final ApexInternalContext incomingContext) {
101 // Save the state and context definition
102 this.parent = incomingParent;
103 this.axState = incomingAxState;
104 this.context = incomingContext;
106 // Set the task selection executor
107 taskSelectExecutor = executorFactory.getTaskSelectionExecutor(this, axState, context);
109 // Set a task executor for each task
110 for (final Entry<AxArtifactKey, AxStateTaskReference> stateTaskReferenceEntry : axState.getTaskReferences()
112 final AxArtifactKey taskKey = stateTaskReferenceEntry.getKey();
113 final AxStateTaskReference taskReference = stateTaskReferenceEntry.getValue();
116 final AxTask task = ModelService.getModel(AxTasks.class).get(taskKey);
118 // Create a task executor for the task
119 taskExecutorMap.put(taskKey, executorFactory.getTaskExecutor(this, task, context));
121 // Check what type of output is specified for the task on this sate
122 if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
123 // Create a task state output reference for this task
124 directStateOutputMap.put(taskKey, taskReference.getOutput().getLocalName());
125 } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
126 // Get the state finalizer logic for this task
127 final AxStateFinalizerLogic finalizerLogic =
128 axState.getStateFinalizerLogicMap().get(taskReference.getOutput().getLocalName());
129 if (finalizerLogic == null) {
130 // Finalizer logic for the task does not exist
131 throw new StateMachineRuntimeException("state finalizer logic on task reference \"" + taskReference
132 + "\" on state \"" + axState.getId() + "\" does not exist");
135 // Create a state finalizer executor for the task
136 task2StateFinalizerMap.put(taskKey,
137 executorFactory.getStateFinalizerExecutor(this, finalizerLogic, context));
139 // This should never happen but.....
140 throw new StateMachineRuntimeException("invalid state output type on task reference \"" + taskReference
141 + "\" on state \"" + axState.getId() + "\"");
150 public void prepare() throws StateMachineException {
151 // There may be no task selection logic
152 if (taskSelectExecutor != null) {
153 // Prepare the task selector
154 taskSelectExecutor.prepare();
158 for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
159 taskExecutor.prepare();
162 for (final StateFinalizerExecutor stateFinalizer : task2StateFinalizerMap.values()) {
163 stateFinalizer.prepare();
171 public StateOutput execute(final long executionId, final Properties executionProperties,
172 final EnEvent incomingEvent) throws StateMachineException, ContextException {
173 this.lastIncomingEvent = incomingEvent;
175 // Check that the incoming event matches the trigger for this state
176 if (!incomingEvent.getAxEvent().getKey().equals(axState.getTrigger())) {
177 throw new StateMachineException("incoming event \"" + incomingEvent.getId() + "\" does not match trigger \""
178 + axState.getTrigger().getId() + "\" of state \"" + axState.getId() + "\"");
181 // The key of the task to execute
182 AxArtifactKey taskKey = null;
185 // There may be no task selection logic, in which case just return the default task
186 if (taskSelectExecutor != null) {
187 // Fire the task selector to find the task to run
188 taskKey = taskSelectExecutor.execute(executionId, executionProperties, incomingEvent);
191 // If there's no task selection logic or the TSL returned no task, just use the default
193 if (taskKey == null) {
194 taskKey = axState.getDefaultTask();
198 final TreeMap<String, Object> incomingValues = new TreeMap<>();
199 incomingValues.putAll(incomingEvent);
200 final Map<String, Object> taskExecutionResultMap =
201 taskExecutorMap.get(taskKey).execute(executionId, executionProperties, incomingValues);
202 final AxTask task = taskExecutorMap.get(taskKey).getSubject();
204 // Check if this task has direct output
205 String stateOutputName = directStateOutputMap.get(taskKey);
207 // If a direct state output name was not found, state finalizer logic should be defined
209 if (stateOutputName == null) {
210 // State finalizer logic should exist for the task
211 final StateFinalizerExecutor finalizerLogicExecutor = task2StateFinalizerMap.get(taskKey);
212 if (finalizerLogicExecutor == null) {
213 throw new StateMachineException("state finalizer logic for task \"" + taskKey.getId()
214 + "\" not found for state \"" + axState.getId() + "\"");
217 // Execute the state finalizer logic to select a state output and to adjust the
218 // taskExecutionResultMap
220 finalizerLogicExecutor.execute(executionId, executionProperties, taskExecutionResultMap);
223 // Now look up the the actual state output
224 final AxStateOutput stateOutputDefinition = axState.getStateOutputs().get(stateOutputName);
225 if (stateOutputDefinition == null) {
226 throw new StateMachineException("state output definition for state output \"" + stateOutputName
227 + "\" not found for state \"" + axState.getId() + "\"");
230 // Create the state output and transfer all the fields across to its event
231 final StateOutput stateOutput = new StateOutput(stateOutputDefinition);
232 this.lastStateOutput = stateOutput;
234 stateOutput.setEventFields(task.getRawOutputFields(), taskExecutionResultMap);
236 // Copy across fields from the incoming event that are not set on the outgoing event
237 stateOutput.copyUnsetFields(incomingEvent);
239 // Set the ExecutionID for the outgoing event to the value in the incoming event.
240 if (stateOutput.getOutputEvent() != null) {
241 stateOutput.getOutputEvent().setExecutionId(incomingEvent.getExecutionId());
242 stateOutput.getOutputEvent().setExecutionProperties(incomingEvent.getExecutionProperties());
245 // That's it, the state execution is complete
247 } catch (final Exception e) {
248 final String errorMessage = "State execution of state \"" + axState.getId() + "\" on task \""
249 + (taskKey != null ? taskKey.getId() : "null") + "\" failed: " + e.getMessage();
251 LOGGER.warn(errorMessage);
252 throw new StateMachineException(errorMessage, e);
260 public final void executePre(final long executionId, final Properties executionProperties,
261 final EnEvent incomingEntity) throws StateMachineException {
262 throw new StateMachineException("execution pre work not implemented on class");
266 public final void executePost(final boolean returnValue) throws StateMachineException {
267 throw new StateMachineException("execution post work not implemented on class");
274 public void cleanUp() throws StateMachineException {
276 for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
277 taskExecutor.cleanUp();
280 if (taskSelectExecutor != null) {
281 // Clean the task selector
282 taskSelectExecutor.cleanUp();
285 for (final StateFinalizerExecutor stateFinalizer : task2StateFinalizerMap.values()) {
286 stateFinalizer.cleanUp();
294 public AxReferenceKey getKey() {
295 return axState.getKey();
302 public Executor<?, ?, ?, ?> getParent() {
310 public AxState getSubject() {
318 public final ApexInternalContext getContext() {
326 public final EnEvent getIncoming() {
327 return lastIncomingEvent;
334 public final StateOutput getOutgoing() {
335 return lastStateOutput;
342 public final void setNext(final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> incomingNextExecutor) {
343 this.nextExecutor = incomingNextExecutor;
350 public final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> getNext() {
358 public void setParameters(final ExecutorParameters parameters) {
359 // Not implemented in this class