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;
29 import org.onap.policy.apex.context.ContextException;
30 import org.onap.policy.apex.core.engine.ExecutorParameters;
31 import org.onap.policy.apex.core.engine.context.ApexInternalContext;
32 import org.onap.policy.apex.core.engine.event.EnEvent;
33 import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
34 import org.onap.policy.apex.core.engine.executor.exception.StateMachineRuntimeException;
35 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
36 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
37 import org.onap.policy.apex.model.basicmodel.service.ModelService;
38 import org.onap.policy.apex.model.policymodel.concepts.AxState;
39 import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
40 import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
41 import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskOutputType;
42 import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskReference;
43 import org.onap.policy.apex.model.policymodel.concepts.AxTask;
44 import org.onap.policy.apex.model.policymodel.concepts.AxTasks;
45 import org.slf4j.ext.XLogger;
46 import org.slf4j.ext.XLoggerFactory;
49 * This class is the executor for a state of a policy.
51 * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
52 * @author Liam Fallon (liam.fallon@ericsson.com)
54 public class StateExecutor implements Executor<EnEvent, StateOutput, AxState, ApexInternalContext> {
55 // Logger for this class
56 private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateExecutor.class);
58 // Hold the state and context definitions for this state
59 private AxState axState = null;
60 private Executor<?, ?, ?, ?> parent = null;
61 private ApexInternalContext context = null;
63 // Holds the incoming event and the state output for this state
64 private EnEvent lastIncomingEvent = null;
65 private StateOutput lastStateOutput = null;
67 // The task selection logic executor
68 private TaskSelectExecutor taskSelectExecutor = null;
70 // The map of task executors for this state
71 private final Map<AxArtifactKey, TaskExecutor> taskExecutorMap = new HashMap<>();
73 // The map of state outputs used directly by tasks
74 private final Map<AxArtifactKey, String> directStateOutputMap = new HashMap<>();
76 // The map of state finalizer logic executors used by tasks
77 private final Map<AxArtifactKey, StateFinalizerExecutor> task2StateFinalizerMap = new HashMap<>();
79 // The next state executor
80 private Executor<EnEvent, StateOutput, AxState, ApexInternalContext> nextExecutor = null;
82 // The executor factory
83 private ExecutorFactory executorFactory = null;
86 * Constructor, save the executor factory.
88 * @param executorFactory the executor factory to use for getting executors for task selection logic
90 public StateExecutor(final ExecutorFactory executorFactory) {
91 this.executorFactory = executorFactory;
98 public void setContext(final Executor<?, ?, ?, ?> incomingParent, final AxState incomingAxState,
99 final ApexInternalContext incomingContext) {
100 // Save the state and context definition
101 this.parent = incomingParent;
102 this.axState = incomingAxState;
103 this.context = incomingContext;
105 // Set the task selection executor
106 taskSelectExecutor = executorFactory.getTaskSelectionExecutor(this, axState, context);
108 // Set a task executor for each task
109 for (final Entry<AxArtifactKey, AxStateTaskReference> stateTaskReferenceEntry : axState.getTaskReferences()
111 final AxArtifactKey taskKey = stateTaskReferenceEntry.getKey();
112 final AxStateTaskReference taskReference = stateTaskReferenceEntry.getValue();
115 final AxTask task = ModelService.getModel(AxTasks.class).get(taskKey);
117 // Create a task executor for the task
118 taskExecutorMap.put(taskKey, executorFactory.getTaskExecutor(this, task, context));
120 // Check what type of output is specified for the task on this sate
121 if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
122 // Create a task state output reference for this task
123 directStateOutputMap.put(taskKey, taskReference.getOutput().getLocalName());
124 } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
125 // Get the state finalizer logic for this task
126 final AxStateFinalizerLogic finalizerLogic =
127 axState.getStateFinalizerLogicMap().get(taskReference.getOutput().getLocalName());
128 if (finalizerLogic == null) {
129 // Finalizer logic for the task does not exist
130 throw new StateMachineRuntimeException("state finalizer logic on task reference \"" + taskReference
131 + "\" on state \"" + axState.getId() + "\" does not exist");
134 // Create a state finalizer executor for the task
135 task2StateFinalizerMap.put(taskKey,
136 executorFactory.getStateFinalizerExecutor(this, finalizerLogic, context));
138 // This should never happen but.....
139 throw new StateMachineRuntimeException("invalid state output type on task reference \"" + taskReference
140 + "\" on state \"" + axState.getId() + "\"");
149 public void prepare() throws StateMachineException {
150 // There may be no task selection logic
151 if (taskSelectExecutor != null) {
152 // Prepare the task selector
153 taskSelectExecutor.prepare();
157 for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
158 taskExecutor.prepare();
161 for (final StateFinalizerExecutor stateFinalizer : task2StateFinalizerMap.values()) {
162 stateFinalizer.prepare();
170 public StateOutput execute(final long executionId, final Properties executionProperties,
171 final EnEvent incomingEvent) throws StateMachineException, ContextException {
172 this.lastIncomingEvent = incomingEvent;
174 // Check that the incoming event matches the trigger for this state
175 if (!incomingEvent.getAxEvent().getKey().equals(axState.getTrigger())) {
176 throw new StateMachineException("incoming event \"" + incomingEvent.getId() + "\" does not match trigger \""
177 + axState.getTrigger().getId() + "\" of state \"" + axState.getId() + "\"");
180 // The key of the task to execute
181 AxArtifactKey taskKey = null;
184 // There may be no task selection logic, in which case just return the default task
185 if (taskSelectExecutor != null) {
186 // Fire the task selector to find the task to run
187 taskKey = taskSelectExecutor.execute(executionId, executionProperties, incomingEvent);
190 // If there's no task selection logic or the TSL returned no task, just use the default
192 if (taskKey == null) {
193 taskKey = axState.getDefaultTask();
197 final TreeMap<String, Object> incomingValues = new TreeMap<>();
198 incomingValues.putAll(incomingEvent);
199 final Map<String, Object> taskExecutionResultMap =
200 taskExecutorMap.get(taskKey).execute(executionId, executionProperties, incomingValues);
201 final AxTask task = taskExecutorMap.get(taskKey).getSubject();
203 // Check if this task has direct output
204 String stateOutputName = directStateOutputMap.get(taskKey);
206 // If a direct state output name was not found, state finalizer logic should be defined
208 if (stateOutputName == null) {
209 // State finalizer logic should exist for the task
210 final StateFinalizerExecutor finalizerLogicExecutor = task2StateFinalizerMap.get(taskKey);
211 if (finalizerLogicExecutor == null) {
212 throw new StateMachineException("state finalizer logic for task \"" + taskKey.getId()
213 + "\" not found for state \"" + axState.getId() + "\"");
216 // Execute the state finalizer logic to select a state output and to adjust the
217 // taskExecutionResultMap
219 finalizerLogicExecutor.execute(executionId, executionProperties, taskExecutionResultMap);
222 // Now look up the the actual state output
223 final AxStateOutput stateOutputDefinition = axState.getStateOutputs().get(stateOutputName);
224 if (stateOutputDefinition == null) {
225 throw new StateMachineException("state output definition for state output \"" + stateOutputName
226 + "\" not found for state \"" + axState.getId() + "\"");
229 // Create the state output and transfer all the fields across to its event
230 final StateOutput stateOutput = new StateOutput(stateOutputDefinition);
231 this.lastStateOutput = stateOutput;
233 stateOutput.setEventFields(task.getRawOutputFields(), taskExecutionResultMap);
235 // Copy across fields from the incoming event that are not set on the outgoing event
236 stateOutput.copyUnsetFields(incomingEvent);
238 // Set the ExecutionID for the outgoing event to the value in the incoming event.
239 if (stateOutput.getOutputEvent() != null) {
240 stateOutput.getOutputEvent().setExecutionId(incomingEvent.getExecutionId());
241 stateOutput.getOutputEvent().setExecutionProperties(incomingEvent.getExecutionProperties());
244 // That's it, the state execution is complete
246 } catch (final Exception e) {
247 final String errorMessage = "State execution of state \"" + axState.getId() + "\" on task \""
248 + (taskKey != null ? taskKey.getId() : "null") + "\" failed: " + e.getMessage();
250 LOGGER.warn(errorMessage);
251 throw new StateMachineException(errorMessage, e);
259 public final void executePre(final long executionId, final Properties executionProperties,
260 final EnEvent incomingEntity) throws StateMachineException {
261 throw new StateMachineException("execution pre work not implemented on class");
265 public final void executePost(final boolean returnValue) throws StateMachineException {
266 throw new StateMachineException("execution post work not implemented on class");
273 public void cleanUp() throws StateMachineException {
275 for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
276 taskExecutor.cleanUp();
279 if (taskSelectExecutor != null) {
280 // Clean the task selector
281 taskSelectExecutor.cleanUp();
284 for (final StateFinalizerExecutor stateFinalizer : task2StateFinalizerMap.values()) {
285 stateFinalizer.cleanUp();
293 public AxReferenceKey getKey() {
294 return axState.getKey();
301 public Executor<?, ?, ?, ?> getParent() {
309 public AxState getSubject() {
317 public final ApexInternalContext getContext() {
325 public final EnEvent getIncoming() {
326 return lastIncomingEvent;
333 public final StateOutput getOutgoing() {
334 return lastStateOutput;
341 public final void setNext(final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> incomingNextExecutor) {
342 this.nextExecutor = incomingNextExecutor;
349 public final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> getNext() {
357 public void setParameters(final ExecutorParameters parameters) {
358 // Not implemented in this class