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