summaryrefslogtreecommitdiffstats
path: root/core/core-engine/src/main/java/org/onap/policy/apex/core/engine/executor/StateExecutor.java
blob: 9cf7d90e3ee08bb93d1eacbc7e3e5a44fcc26981 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
/*-
 * ============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.core.engine.executor;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.onap.policy.apex.context.ContextException;
import org.onap.policy.apex.core.engine.ExecutorParameters;
import org.onap.policy.apex.core.engine.context.ApexInternalContext;
import org.onap.policy.apex.core.engine.event.EnEvent;
import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
import org.onap.policy.apex.core.engine.executor.exception.StateMachineRuntimeException;
import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
import org.onap.policy.apex.model.basicmodel.service.ModelService;
import org.onap.policy.apex.model.policymodel.concepts.AxState;
import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskOutputType;
import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskReference;
import org.onap.policy.apex.model.policymodel.concepts.AxTask;
import org.onap.policy.apex.model.policymodel.concepts.AxTasks;
import org.slf4j.ext.XLogger;
import org.slf4j.ext.XLoggerFactory;

/**
 * This class is the executor for a state of a policy.
 *
 * @author Sven van der Meer (sven.van.der.meer@ericsson.com)
 * @author Liam Fallon (liam.fallon@ericsson.com)
 */
public class StateExecutor implements Executor<EnEvent, StateOutput, AxState, ApexInternalContext> {
    // Logger for this class
    private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateExecutor.class);

    // Hold the state and context definitions for this state
    private AxState axState = null;
    private Executor<?, ?, ?, ?> parent = null;
    private ApexInternalContext context = null;

    // Holds the incoming event and the state output for this state
    private EnEvent lastIncomingEvent = null;
    private StateOutput lastStateOutput = null;

    // The task selection logic executor
    private TaskSelectExecutor taskSelectExecutor = null;

    // The map of task executors for this state
    private final Map<AxArtifactKey, TaskExecutor> taskExecutorMap = new HashMap<>();

    // The map of state outputs used directly by tasks
    private final Map<AxArtifactKey, String> directStateOutputMap = new HashMap<>();

    // The map of state finalizer logic executors used by tasks
    private final Map<AxArtifactKey, StateFinalizerExecutor> task2StateFinalizerMap = new HashMap<>();

    // The next state executor
    private Executor<EnEvent, StateOutput, AxState, ApexInternalContext> nextExecutor = null;

    // The executor factory
    private ExecutorFactory executorFactory = null;

    /**
     * Constructor, save the executor factory.
     *
     * @param executorFactory the executor factory to use for getting executors for task selection
     *        logic
     */
    public StateExecutor(final ExecutorFactory executorFactory) {
        this.executorFactory = executorFactory;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#setContext(org.onap.policy.apex.core.
     * engine.executor.Executor, java.lang.Object, java.lang.Object)
     */
    @Override
    public void setContext(final Executor<?, ?, ?, ?> incomingParent, final AxState incomingAxState,
            final ApexInternalContext incomingContext) {
        // Save the state and context definition
        this.parent = incomingParent;
        this.axState = incomingAxState;
        this.context = incomingContext;

        // Set the task selection executor
        taskSelectExecutor = executorFactory.getTaskSelectionExecutor(this, axState, context);

        // Set a task executor for each task
        for (final Entry<AxArtifactKey, AxStateTaskReference> stateTaskReferenceEntry : axState.getTaskReferences()
                .entrySet()) {
            final AxArtifactKey taskKey = stateTaskReferenceEntry.getKey();
            final AxStateTaskReference taskReference = stateTaskReferenceEntry.getValue();

            // Get the task
            final AxTask task = ModelService.getModel(AxTasks.class).get(taskKey);

            // Create a task executor for the task
            taskExecutorMap.put(taskKey, executorFactory.getTaskExecutor(this, task, context));

            // Check what type of output is specified for the task on this sate
            if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
                // Create a task state output reference for this task
                directStateOutputMap.put(taskKey, taskReference.getOutput().getLocalName());
            } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
                // Get the state finalizer logic for this task
                final AxStateFinalizerLogic finalizerLogic =
                        axState.getStateFinalizerLogicMap().get(taskReference.getOutput().getLocalName());
                if (finalizerLogic == null) {
                    // Finalizer logic for the task does not exist
                    throw new StateMachineRuntimeException("state finalizer logic on task reference \"" + taskReference
                            + "\" on state \"" + axState.getId() + "\" does not exist");
                }

                // Create a state finalizer executor for the task
                task2StateFinalizerMap.put(taskKey,
                        executorFactory.getStateFinalizerExecutor(this, finalizerLogic, context));
            } else {
                // This should never happen but.....
                throw new StateMachineRuntimeException("invalid state output type on task reference \"" + taskReference
                        + "\" on state \"" + axState.getId() + "\"");
            }
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#prepare()
     */
    @Override
    public void prepare() throws StateMachineException {
        // There may be no task selection logic
        if (taskSelectExecutor != null) {
            // Prepare the task selector
            taskSelectExecutor.prepare();
        }

        // Prepare the tasks
        for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
            taskExecutor.prepare();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#execute(java.lang.long,
     * java.lang.Object)
     */
    @Override
    public StateOutput execute(final long executionID, final EnEvent incomingEvent)
            throws StateMachineException, ContextException {
        this.lastIncomingEvent = incomingEvent;

        // Check that the incoming event matches the trigger for this state
        if (!incomingEvent.getAxEvent().getKey().equals(axState.getTrigger())) {
            throw new StateMachineException("incoming event \"" + incomingEvent.getID() + "\" does not match trigger \""
                    + axState.getTrigger().getId() + "\" of state \"" + axState.getId() + "\"");
        }

        // The key of the task to execute
        AxArtifactKey taskKey = null;

        try {
            // There may be no task selection logic, in which case just return the default task
            if (taskSelectExecutor != null) {
                // Fire the task selector to find the task to run
                taskKey = taskSelectExecutor.execute(executionID, incomingEvent);
            }

            // If there's no task selection logic or the TSL returned no task, just use the default
            // task
            if (taskKey == null) {
                taskKey = axState.getDefaultTask();
            }

            // Execute the task
            final TreeMap<String, Object> incomingValues = new TreeMap<>();
            incomingValues.putAll(incomingEvent);
            final Map<String, Object> taskExecutionResultMap =
                    taskExecutorMap.get(taskKey).execute(executionID, incomingValues);
            final AxTask task = taskExecutorMap.get(taskKey).getSubject();

            // Check if this task has direct output
            String stateOutputName = directStateOutputMap.get(taskKey);

            // If a direct state output name was not found, state finalizer logic should be defined
            // for the task
            if (stateOutputName == null) {
                // State finalizer logic should exist for the task
                final StateFinalizerExecutor finalizerLogicExecutor = task2StateFinalizerMap.get(taskKey);
                if (finalizerLogicExecutor == null) {
                    throw new StateMachineException("state finalizer logic for task \"" + taskKey.getId()
                            + "\" not found for state \"" + axState.getId() + "\"");
                }

                // Execute the state finalizer logic to select a state output and to adjust the
                // taskExecutionResultMap
                stateOutputName =
                        finalizerLogicExecutor.execute(incomingEvent.getExecutionID(), taskExecutionResultMap);
            }

            // Now look up the the actual state output
            final AxStateOutput stateOutputDefinition = axState.getStateOutputs().get(stateOutputName);
            if (stateOutputDefinition == null) {
                throw new StateMachineException("state output definition for state output \"" + stateOutputName
                        + "\" not found for state \"" + axState.getId() + "\"");
            }

            // Create the state output and transfer all the fields across to its event
            final StateOutput stateOutput = new StateOutput(stateOutputDefinition);
            this.lastStateOutput = stateOutput;

            stateOutput.setEventFields(task.getRawOutputFields(), taskExecutionResultMap);

            // Copy across fields from the incoming event that are not set on the outgoing event
            stateOutput.copyUnsetFields(incomingEvent);

            // Set the ExecutionID for the outgoing event to the value in the incoming event.
            if (stateOutput != null && stateOutput.getOutputEvent() != null) {
                stateOutput.getOutputEvent().setExecutionID(incomingEvent.getExecutionID());
            }

            // That's it, the state execution is complete
            return stateOutput;
        } catch (final Exception e) {
            final String errorMessage = "State execution of state \"" + axState.getId() + "\" on task \""
                    + (taskKey != null ? taskKey.getId() : "null") + "\" failed: " + e.getMessage();

            LOGGER.warn(errorMessage);
            throw new StateMachineException(errorMessage, e);
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#executePre(java.lang.long,
     * java.lang.Object)
     */
    @Override
    public final void executePre(final long executionID, final EnEvent incomingEntity) throws StateMachineException {
        throw new StateMachineException("execution pre work not implemented on class");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#executePost(boolean)
     */
    @Override
    public final void executePost(final boolean returnValue) throws StateMachineException {
        throw new StateMachineException("execution post work not implemented on class");
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#cleanUp()
     */
    @Override
    public void cleanUp() throws StateMachineException {
        // Clean the tasks
        for (final TaskExecutor taskExecutor : taskExecutorMap.values()) {
            taskExecutor.cleanUp();
        }

        if (taskSelectExecutor != null) {
            // Clean the task selector
            taskSelectExecutor.cleanUp();
        }
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getKey()
     */
    @Override
    public AxReferenceKey getKey() {
        return axState.getKey();
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getParent()
     */
    @Override
    public Executor<?, ?, ?, ?> getParent() {
        return parent;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getSubject()
     */
    @Override
    public AxState getSubject() {
        return axState;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getContext()
     */
    @Override
    public final ApexInternalContext getContext() {
        return context;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getIncoming()
     */
    @Override
    public final EnEvent getIncoming() {
        return lastIncomingEvent;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getOutgoing()
     */
    @Override
    public final StateOutput getOutgoing() {
        return lastStateOutput;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.onap.policy.apex.core.engine.executor.Executor#setNext(org.onap.policy.apex.core.engine.
     * executor.Executor)
     */
    @Override
    public final void setNext(final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> incomingNextExecutor) {
        this.nextExecutor = incomingNextExecutor;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.onap.policy.apex.core.engine.executor.Executor#getNext()
     */
    @Override
    public final Executor<EnEvent, StateOutput, AxState, ApexInternalContext> getNext() {
        return nextExecutor;
    }

    /*
     * (non-Javadoc)
     *
     * @see
     * org.onap.policy.apex.core.engine.executor.Executor#setParameters(org.onap.policy.apex.core.
     * engine. ExecutorParameters)
     */
    @Override
    public void setParameters(final ExecutorParameters parameters) {}
}