c173d1f094e7ff219b91e54921d840d50583cd97
[policy/apex-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
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.engine.impl;
23
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.Set;
27 import org.onap.policy.apex.core.engine.context.ApexInternalContext;
28 import org.onap.policy.apex.core.engine.event.EnEvent;
29 import org.onap.policy.apex.core.engine.executor.ExecutorFactory;
30 import org.onap.policy.apex.core.engine.executor.StateMachineExecutor;
31 import org.onap.policy.apex.core.engine.executor.exception.StateMachineException;
32 import org.onap.policy.apex.core.engine.executor.impl.ExecutorFactoryImpl;
33 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
34 import org.onap.policy.apex.model.basicmodel.service.ModelService;
35 import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
36 import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
37 import org.onap.policy.apex.model.policymodel.concepts.AxPolicies;
38 import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
39 import org.slf4j.ext.XLogger;
40 import org.slf4j.ext.XLoggerFactory;
41
42 /**
43  * This handler holds and manages state machines for each policy in an Apex engine. When the class is instantiated, an
44  * executor {@link StateMachineExecutor} is created for each policy in the policy model the state machine handler will
45  * execute. The executors for each policy are held in a map indexed by event.
46  *
47  * <p>When an event is received on the policy, the state machine executor to execute that event is looked up on the
48  * executor map and the event is passed to the executor for execution.
49  *
50  * @author Liam Fallon
51  *
52  */
53 public class StateMachineHandler {
54     // Logger for this class
55     private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateMachineHandler.class);
56
57     // The key of the Apex model we are executing
58     private final AxArtifactKey key;
59
60     // The state machines in this engine
61     private final HashMap<AxEvent, StateMachineExecutor> stateMachineExecutorMap = new HashMap<>();
62
63     // The executor factory is used to get logic executors for the particular type of executor we
64     // need for task
65     // selection logic or task logic
66     private final ExecutorFactory executorFactory;
67
68     /**
69      * This constructor builds the state machines for the policies in the apex model.
70      *
71      * @param internalContext The internal context we are using
72      * @throws StateMachineException On state machine initiation errors
73      */
74     protected StateMachineHandler(final ApexInternalContext internalContext) throws StateMachineException {
75         LOGGER.entry("StateMachineHandler()->" + internalContext.getKey().getId());
76
77         key = internalContext.getKey();
78
79         // Create the executor factory to generate executors as the engine runs policies
80         executorFactory = new ExecutorFactoryImpl();
81
82         // Iterate over the policies in the policy model and create a state machine for each one
83         for (final AxPolicy policy : ModelService.getModel(AxPolicies.class).getPolicyMap().values()) {
84             // Create a state machine for this policy
85             final var thisStateMachineExecutor = new StateMachineExecutor(executorFactory, policy.getKey());
86
87             // This executor is the top executor so has no parent
88             thisStateMachineExecutor.setContext(null, policy, internalContext);
89
90             // Get the incoming trigger event
91             final AxEvent triggerEvent = ModelService.getModel(AxEvents.class)
92                     .get(policy.getStateMap().get(policy.getFirstState()).getTrigger());
93
94             // Put the state machine executor on the map for this trigger
95             final var lastStateMachineExecutor = stateMachineExecutorMap.put(triggerEvent, thisStateMachineExecutor);
96             if (lastStateMachineExecutor != null
97                     && lastStateMachineExecutor.getSubject() != thisStateMachineExecutor.getSubject()) {
98                 LOGGER.error("No more than one policy in a model can have the same trigger event. In model "
99                         + internalContext.getKey().getId() + " Policy ("
100                         + lastStateMachineExecutor.getSubject().getKey().getId() + ") and Policy ("
101                         + thisStateMachineExecutor.getSubject().getKey().getId() + ") have the same Trigger event ("
102                         + triggerEvent.getKey().getId() + ") ");
103                 LOGGER.error(" Policy (" + lastStateMachineExecutor.getSubject().getKey() + ") has overwritten Policy ("
104                         + thisStateMachineExecutor.getSubject().getKey().getId()
105                         + " so this overwritten policy will never be triggered in this engine.");
106             }
107         }
108
109         LOGGER.exit("StateMachineHandler()<-" + internalContext.getKey().getId());
110     }
111
112     /**
113      * This constructor starts the state machines for each policy, carrying out whatever initialization executors need.
114      *
115      * @throws StateMachineException On state machine initiation errors
116      */
117     protected void start() throws StateMachineException {
118         LOGGER.entry("start()->" + key.getId());
119
120         // Iterate over the state machines
121         for (final StateMachineExecutor smExecutor : stateMachineExecutorMap.values()) {
122             try {
123                 smExecutor.prepare();
124             } catch (final StateMachineException e) {
125                 final String stateMachineId = smExecutor.getContext().getKey().getId();
126                 String message = "start()<-" + key.getId() + ", start failed, state machine \"" + stateMachineId + "\"";
127                 LOGGER.warn(message, e);
128                 throw new StateMachineException(message, e);
129             }
130         }
131
132         LOGGER.exit("start()<-" + key.getId());
133     }
134
135     /**
136      * This method is called to execute an event on the state machines in an engine.
137      *
138      * @param event The trigger event for the state machine
139      * @return The result of the state machine execution run
140      * @throws StateMachineException On execution errors in a state machine
141      */
142     protected Collection<EnEvent> execute(final EnEvent event) throws StateMachineException {
143         LOGGER.entry("execute()->" + event.getName());
144
145         // Try to execute the state machine for the trigger
146         final var stateMachineExecutor = stateMachineExecutorMap.get(event.getAxEvent());
147         if (stateMachineExecutor == null) {
148             final String exceptionMessage =
149                 "state machine execution not possible, policy not found for trigger event " + event.getName();
150             LOGGER.warn(exceptionMessage);
151
152             event.setExceptionMessage(exceptionMessage);
153             return Set.of(event);
154         }
155
156         // Run the state machine
157         try {
158             LOGGER.debug("execute(): state machine \"{}\" execution starting  . . .", stateMachineExecutor);
159             final Collection<EnEvent> outputEvents =
160                 stateMachineExecutor.execute(event.getExecutionId(), event.getExecutionProperties(), event);
161
162             LOGGER.debug("execute()<-: state machine \"{}\" execution completed", stateMachineExecutor);
163             return outputEvents;
164         } catch (final Exception e) {
165             LOGGER.warn("execute()<-: state machine \"" + stateMachineExecutor + "\" execution failed", e);
166             throw new StateMachineException("execute()<-: execution failed on state machine " + stateMachineExecutor,
167                 e);
168         }
169     }
170
171     /**
172      * Closes down the state machines of an engine.
173      */
174     protected void stop() {
175         LOGGER.entry("stop()->");
176
177         // Iterate through all state machines and clean them
178         for (final StateMachineExecutor smExecutor : stateMachineExecutorMap.values()) {
179             try {
180                 smExecutor.cleanUp();
181             } catch (final StateMachineException e) {
182                 final String smId = smExecutor.getContext().getKey().getId();
183                 LOGGER.warn("stop()<-clean up failed, state machine \"" + smId + "\" cleanup failed", e);
184             }
185         }
186         LOGGER.exit("stop()<-");
187     }
188 }