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