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
 
   9  *      http://www.apache.org/licenses/LICENSE-2.0
 
  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.
 
  17  * SPDX-License-Identifier: Apache-2.0
 
  18  * ============LICENSE_END=========================================================
 
  21 package org.onap.policy.apex.core.engine.engine.impl;
 
  23 import java.util.HashMap;
 
  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;
 
  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.
 
  45  * <p>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.
 
  51 public class StateMachineHandler {
 
  52     // Logger for this class
 
  53     private static final XLogger LOGGER = XLoggerFactory.getXLogger(StateMachineHandler.class);
 
  55     // The key of the Apex model we are executing
 
  56     private final AxArtifactKey key;
 
  58     // The state machines in this engine
 
  59     private final HashMap<AxEvent, StateMachineExecutor> stateMachineExecutorMap = new HashMap<>();
 
  61     // The executor factory is used to get logic executors for the particular type of executor we
 
  63     // selection logic or task logic
 
  64     private final ExecutorFactory executorFactory;
 
  67      * This constructor builds the state machines for the policies in the apex model.
 
  69      * @param internalContext The internal context we are using
 
  70      * @throws StateMachineException On state machine initiation errors
 
  72     protected StateMachineHandler(final ApexInternalContext internalContext) throws StateMachineException {
 
  73         LOGGER.entry("StateMachineHandler()->" + internalContext.getKey().getId());
 
  75         key = internalContext.getKey();
 
  77         // Create the executor factory to generate executors as the engine runs policies
 
  78         executorFactory = new ExecutorFactoryImpl();
 
  80         // Iterate over the policies in the policy model and create a state machine for each one
 
  81         for (final AxPolicy policy : ModelService.getModel(AxPolicies.class).getPolicyMap().values()) {
 
  82             // Create a state machine for this policy
 
  83             final StateMachineExecutor thisStateMachineExecutor =
 
  84                     new StateMachineExecutor(executorFactory, policy.getKey());
 
  86             // This executor is the top executor so has no parent
 
  87             thisStateMachineExecutor.setContext(null, policy, internalContext);
 
  89             // Get the incoming trigger event
 
  90             final AxEvent triggerEvent = ModelService.getModel(AxEvents.class)
 
  91                     .get(policy.getStateMap().get(policy.getFirstState()).getTrigger());
 
  93             // Put the state machine executor on the map for this trigger
 
  94             final StateMachineExecutor lastStateMachineExecutor =
 
  95                     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.");
 
 109         LOGGER.exit("StateMachineHandler()<-" + internalContext.getKey().getId());
 
 113      * This constructor starts the state machines for each policy, carrying out whatever initialization executors need.
 
 115      * @throws StateMachineException On state machine initiation errors
 
 117     protected void start() throws StateMachineException {
 
 118         LOGGER.entry("start()->" + key.getId());
 
 120         // Iterate over the state machines
 
 121         for (final StateMachineExecutor smExecutor : stateMachineExecutorMap.values()) {
 
 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);
 
 132         LOGGER.exit("start()<-" + key.getId());
 
 136      * This method is called to execute an event on the state machines in an engine.
 
 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
 
 142     protected EnEvent execute(final EnEvent event) throws StateMachineException {
 
 143         LOGGER.entry("execute()->" + event.getName());
 
 145         // Try to execute the state machine for the trigger
 
 146         final StateMachineExecutor 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);
 
 152             event.setExceptionMessage(exceptionMessage);
 
 156         // Run the state machine
 
 158             LOGGER.debug("execute(): state machine \"{}\" execution starting  . . .", stateMachineExecutor);
 
 159             final EnEvent outputObject =
 
 160                     stateMachineExecutor.execute(event.getExecutionId(), event.getExecutionProperties(), event);
 
 162             LOGGER.debug("execute()<-: state machine \"{}\" execution completed", stateMachineExecutor);
 
 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,
 
 172      * Closes down the state machines of an engine.
 
 174     protected void stop() {
 
 175         LOGGER.entry("stop()->");
 
 177         // Iterate through all state machines and clean them
 
 178         for (final StateMachineExecutor smExecutor : stateMachineExecutorMap.values()) {
 
 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);
 
 186         LOGGER.exit("stop()<-");