Changes for checkstyle 8.32
[policy/apex-pdp.git] / core / core-engine / src / main / java / org / onap / policy / apex / core / engine / executor / StateExecutor.java
1 /*-
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.core.engine.executor;
23
24 import java.util.HashMap;
25 import java.util.Map;
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;
47
48 /**
49  * This class is the executor for a state of a policy.
50  *
51  * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
52  * @author Liam Fallon (liam.fallon@ericsson.com)
53  */
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);
57
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;
62
63     // Holds the incoming event and the state output for this state
64     private EnEvent lastIncomingEvent = null;
65     private StateOutput lastStateOutput = null;
66
67     // The task selection logic executor
68     private TaskSelectExecutor taskSelectExecutor = null;
69
70     // The map of task executors for this state
71     private final Map<AxArtifactKey, TaskExecutor> taskExecutorMap = new HashMap<>();
72
73     // The map of state outputs used directly by tasks
74     private final Map<AxArtifactKey, String> directStateOutputMap = new HashMap<>();
75
76     // The map of state finalizer logic executors used by tasks
77     private final Map<AxArtifactKey, StateFinalizerExecutor> task2StateFinalizerMap = new HashMap<>();
78
79     // The next state executor
80     private Executor<EnEvent, StateOutput, AxState, ApexInternalContext> nextExecutor = null;
81
82     // The executor factory
83     private ExecutorFactory executorFactory = null;
84
85     /**
86      * Constructor, save the executor factory.
87      *
88      * @param executorFactory the executor factory to use for getting executors for task selection logic
89      */
90     public StateExecutor(final ExecutorFactory executorFactory) {
91         this.executorFactory = executorFactory;
92     }
93
94     /**
95      * {@inheritDoc}.
96      */
97     @Override
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;
104
105         // Set the task selection executor
106         taskSelectExecutor = executorFactory.getTaskSelectionExecutor(this, axState, context);
107
108         // Set a task executor for each task
109         for (final Entry<AxArtifactKey, AxStateTaskReference> stateTaskReferenceEntry : axState.getTaskReferences()
110             .entrySet()) {
111             final AxArtifactKey taskKey = stateTaskReferenceEntry.getKey();
112             final AxStateTaskReference taskReference = stateTaskReferenceEntry.getValue();
113
114             // Get the task
115             final AxTask task = ModelService.getModel(AxTasks.class).get(taskKey);
116
117             // Create a task executor for the task
118             taskExecutorMap.put(taskKey, executorFactory.getTaskExecutor(this, task, context));
119
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");
132                 }
133
134                 // Create a state finalizer executor for the task
135                 task2StateFinalizerMap.put(taskKey,
136                     executorFactory.getStateFinalizerExecutor(this, finalizerLogic, context));
137             } else {
138                 // This should never happen but.....
139                 throw new StateMachineRuntimeException("invalid state output type on task reference \"" + taskReference
140                     + "\" on state \"" + axState.getId() + "\"");
141             }
142         }
143     }
144
145     /**
146      * {@inheritDoc}.
147      */
148     @Override
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();
154         }
155
156         // Prepare the tasks
157         for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
158             taskExecutor.prepare();
159         }
160
161         for (final StateFinalizerExecutor stateFinalizer : task2StateFinalizerMap.values()) {
162             stateFinalizer.prepare();
163         }
164     }
165
166     /**
167      * {@inheritDoc}.
168      */
169     @Override
170     public StateOutput execute(final long executionId, final Properties executionProperties,
171         final EnEvent incomingEvent) throws StateMachineException, ContextException {
172         this.lastIncomingEvent = incomingEvent;
173
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() + "\"");
178         }
179
180         // The key of the task to execute
181         AxArtifactKey taskKey = null;
182
183         try {
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);
188             }
189
190             // If there's no task selection logic or the TSL returned no task, just use the default
191             // task
192             if (taskKey == null) {
193                 taskKey = axState.getDefaultTask();
194             }
195
196             // Execute the task
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();
202
203             // Check if this task has direct output
204             String stateOutputName = directStateOutputMap.get(taskKey);
205
206             // If a direct state output name was not found, state finalizer logic should be defined
207             // for the task
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() + "\"");
214                 }
215
216                 // Execute the state finalizer logic to select a state output and to adjust the
217                 // taskExecutionResultMap
218                 stateOutputName =
219                     finalizerLogicExecutor.execute(executionId, executionProperties, taskExecutionResultMap);
220             }
221
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() + "\"");
227             }
228
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;
232
233             stateOutput.setEventFields(task.getRawOutputFields(), taskExecutionResultMap);
234
235             // Copy across fields from the incoming event that are not set on the outgoing event
236             stateOutput.copyUnsetFields(incomingEvent);
237
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());
242             }
243
244             // That's it, the state execution is complete
245             return stateOutput;
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();
249
250             LOGGER.warn(errorMessage);
251             throw new StateMachineException(errorMessage, e);
252         }
253     }
254
255     /**
256      * {@inheritDoc}.
257      */
258     @Override
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");
262     }
263
264     @Override
265     public final void executePost(final boolean returnValue) throws StateMachineException {
266         throw new StateMachineException("execution post work not implemented on class");
267     }
268
269     /**
270      * {@inheritDoc}.
271      */
272     @Override
273     public void cleanUp() throws StateMachineException {
274         // Clean the tasks
275         for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
276             taskExecutor.cleanUp();
277         }
278
279         if (taskSelectExecutor != null) {
280             // Clean the task selector
281             taskSelectExecutor.cleanUp();
282         }
283
284         for (final StateFinalizerExecutor stateFinalizer : task2StateFinalizerMap.values()) {
285             stateFinalizer.cleanUp();
286         }
287     }
288
289     /**
290      * {@inheritDoc}.
291      */
292     @Override
293     public AxReferenceKey getKey() {
294         return axState.getKey();
295     }
296
297     /**
298      * {@inheritDoc}.
299      */
300     @Override
301     public Executor<?, ?, ?, ?> getParent() {
302         return parent;
303     }
304
305     /**
306      * {@inheritDoc}.
307      */
308     @Override
309     public AxState getSubject() {
310         return axState;
311     }
312
313     /**
314      * {@inheritDoc}.
315      */
316     @Override
317     public final ApexInternalContext getContext() {
318         return context;
319     }
320
321     /**
322      * {@inheritDoc}.
323      */
324     @Override
325     public final EnEvent getIncoming() {
326         return lastIncomingEvent;
327     }
328
329     /**
330      * {@inheritDoc}.
331      */
332     @Override
333     public final StateOutput getOutgoing() {
334         return lastStateOutput;
335     }
336
337     /**
338      * {@inheritDoc}.
339      */
340     @Override
341     public final void setNext(final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> incomingNextExecutor) {
342         this.nextExecutor = incomingNextExecutor;
343     }
344
345     /**
346      * {@inheritDoc}.
347      */
348     @Override
349     public final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> getNext() {
350         return nextExecutor;
351     }
352
353     /**
354      * {@inheritDoc}.
355      */
356     @Override
357     public void setParameters(final ExecutorParameters parameters) {
358         // Not implemented in this class
359     }
360 }