2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2022 Nordix Foundation.
4 * Modifications Copyright (C) 2021 AT&T Intellectual Property. 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
10 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.clamp.acm.participant.intermediary.handler;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.LinkedHashMap;
27 import java.util.List;
29 import java.util.UUID;
30 import java.util.stream.Collectors;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
33 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
34 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementAck;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState;
40 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
41 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantUpdates;
43 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionAck;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionUpdate;
47 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
48 import org.onap.policy.models.base.PfModelException;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53 import org.springframework.stereotype.Component;
56 * This class is responsible for managing the state of all automation compositions in the participant.
59 public class AutomationCompositionHandler {
60 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
62 private final ToscaConceptIdentifier participantType;
63 private final ToscaConceptIdentifier participantId;
64 private final ParticipantMessagePublisher publisher;
67 private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
70 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
73 private List<AutomationCompositionElementListener> listeners = new ArrayList<>();
76 * Constructor, set the participant ID and messageSender.
78 * @param parameters the parameters of the participant
79 * @param publisher the ParticipantMessage Publisher
81 public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
82 this.participantType = parameters.getIntermediaryParameters().getParticipantType();
83 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
84 this.publisher = publisher;
87 public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
88 listeners.add(listener);
92 * Handle a automation composition element state change message.
94 * @param automationCompositionId the automationComposition Id
95 * @param id the automationComposition UUID
96 * @param orderedState the current state
97 * @param newState the ordered state
98 * @return automationCompositionElement the updated automation composition element
100 public AutomationCompositionElement updateAutomationCompositionElementState(
101 UUID automationCompositionId, UUID id, AutomationCompositionOrderedState orderedState,
102 AutomationCompositionState newState) {
105 LOGGER.warn("Cannot update Automation composition element state, id is null");
109 // Update states of AutomationCompositionElement in automationCompositionMap
110 for (var automationComposition : automationCompositionMap.values()) {
111 var element = automationComposition.getElements().get(id);
112 if (element != null) {
113 element.setOrderedState(orderedState);
114 element.setState(newState);
116 var checkOpt = automationComposition.getElements().values().stream()
117 .filter(acElement -> !newState.equals(acElement.getState())).findAny();
118 if (checkOpt.isEmpty()) {
119 automationComposition.setState(newState);
120 automationComposition.setOrderedState(orderedState);
124 // Update states of AutomationCompositionElement in elementsOnThisParticipant
125 var acElement = elementsOnThisParticipant.get(id);
126 if (acElement != null) {
127 var automationCompositionStateChangeAck =
128 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
129 automationCompositionStateChangeAck.setParticipantId(participantId);
130 automationCompositionStateChangeAck.setParticipantType(participantType);
131 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
132 acElement.setOrderedState(orderedState);
133 acElement.setState(newState);
134 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
135 new AutomationCompositionElementAck(newState, true,
136 "Automation composition element {} state changed to {}\", id, newState)"));
137 LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
138 automationCompositionStateChangeAck
139 .setMessage("AutomationCompositionElement state changed to {} " + newState);
140 automationCompositionStateChangeAck.setResult(true);
141 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
148 * Handle a automation composition state change message.
150 * @param stateChangeMsg the state change message
151 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
153 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
154 List<AutomationCompositionElementDefinition> acElementDefinitions) {
155 if (stateChangeMsg.getAutomationCompositionId() == null) {
159 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
161 if (automationComposition == null) {
162 var automationCompositionAck =
163 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
164 automationCompositionAck.setParticipantId(participantId);
165 automationCompositionAck.setParticipantType(participantType);
166 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
167 + " does not use this participant " + participantId);
168 automationCompositionAck.setResult(false);
169 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
170 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
171 publisher.sendAutomationCompositionAck(automationCompositionAck);
172 LOGGER.debug("Automation composition {} does not use this participant",
173 stateChangeMsg.getAutomationCompositionId());
177 handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
178 acElementDefinitions);
182 * Method to handle state changes.
184 * @param automationComposition participant response
185 * @param orderedState automation composition ordered state
186 * @param startPhaseMsg startPhase from message
187 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
189 private void handleState(final AutomationComposition automationComposition,
190 AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
191 List<AutomationCompositionElementDefinition> acElementDefinitions) {
192 switch (orderedState) {
194 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
197 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
200 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
203 LOGGER.debug("StateChange message has no state, state is null {}",
204 automationComposition.getKey());
210 * Handle a automation composition update message.
212 * @param updateMsg the update message
213 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
215 public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg,
216 List<AutomationCompositionElementDefinition> acElementDefinitions) {
218 if (!updateMsg.appliesTo(participantType, participantId)) {
222 if (0 == updateMsg.getStartPhase()) {
223 handleAcUpdatePhase0(updateMsg, acElementDefinitions);
225 handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
229 private void handleAcUpdatePhase0(AutomationCompositionUpdate updateMsg,
230 List<AutomationCompositionElementDefinition> acElementDefinitions) {
231 var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
233 // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
234 // AutomationComposition
235 // elements to existing AutomationComposition has to be supported).
236 if (automationComposition != null) {
237 var automationCompositionUpdateAck =
238 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_UPDATE_ACK);
239 automationCompositionUpdateAck.setParticipantId(participantId);
240 automationCompositionUpdateAck.setParticipantType(participantType);
242 automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
243 + " already defined on participant " + participantId);
244 automationCompositionUpdateAck.setResult(false);
245 automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
246 automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
247 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
251 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
252 LOGGER.warn("No AutomationCompositionElement updates in message {}",
253 updateMsg.getAutomationCompositionId());
257 automationComposition = new AutomationComposition();
258 automationComposition.setInstanceId(updateMsg.getAutomationCompositionId());
259 var acElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
260 var acElementMap = prepareAcElementMap(acElements);
261 automationComposition.setElements(acElementMap);
262 automationCompositionMap.put(updateMsg.getAutomationCompositionId(), automationComposition);
264 handleAutomationCompositionElementUpdate(acElements, acElementDefinitions, updateMsg.getStartPhase(),
265 updateMsg.getAutomationCompositionId());
268 private void handleAcUpdatePhaseN(AutomationCompositionUpdate updateMsg,
269 List<AutomationCompositionElementDefinition> acElementDefinitions) {
271 var acElementList = updateMsg.getParticipantUpdatesList().stream()
272 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
273 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
275 handleAutomationCompositionElementUpdate(acElementList, acElementDefinitions, updateMsg.getStartPhase(),
276 updateMsg.getAutomationCompositionId());
279 private void handleAutomationCompositionElementUpdate(List<AutomationCompositionElement> acElements,
280 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
281 UUID automationCompositionId) {
283 for (var element : acElements) {
284 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
285 if (acElementNodeTemplate != null) {
286 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
287 if (startPhaseMsg.equals(startPhase)) {
288 for (var acElementListener : listeners) {
289 var map = new HashMap<>(acElementNodeTemplate.getProperties());
290 map.putAll(element.getProperties());
291 acElementListener.automationCompositionElementUpdate(automationCompositionId, element, map);
296 } catch (PfModelException e) {
297 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
302 private ToscaNodeTemplate getAcElementNodeTemplate(
303 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
305 for (var acElementDefinition : acElementDefinitions) {
306 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
307 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
313 private List<AutomationCompositionElement> storeElementsOnThisParticipant(
314 List<ParticipantUpdates> participantUpdates) {
315 var acElementList = participantUpdates.stream()
316 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
317 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
319 for (var element : acElementList) {
320 elementsOnThisParticipant.put(element.getId(), element);
322 return acElementList;
325 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
326 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
327 for (var element : acElements) {
328 acElementMap.put(element.getId(), element);
334 * Method to handle when the new state from participant is UNINITIALISED state.
336 * @param automationComposition participant response
337 * @param orderedState orderedState
338 * @param startPhaseMsg startPhase from message
339 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
341 private void handleUninitialisedState(final AutomationComposition automationComposition,
342 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
343 List<AutomationCompositionElementDefinition> acElementDefinitions) {
344 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
345 boolean isAllUninitialised = automationComposition.getElements().values().stream()
346 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
348 if (isAllUninitialised) {
349 automationCompositionMap.remove(automationComposition.getInstanceId());
350 automationComposition.getElements().values()
351 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
356 * Method to handle when the new state from participant is PASSIVE state.
358 * @param automationComposition participant response
359 * @param orderedState orderedState
360 * @param startPhaseMsg startPhase from message
361 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
363 private void handlePassiveState(final AutomationComposition automationComposition,
364 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
365 List<AutomationCompositionElementDefinition> acElementDefinitions) {
366 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
370 * Method to handle when the new state from participant is RUNNING state.
372 * @param automationComposition participant response
373 * @param orderedState orderedState
374 * @param startPhaseMsg startPhase from message
375 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
377 private void handleRunningState(final AutomationComposition automationComposition,
378 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
379 List<AutomationCompositionElementDefinition> acElementDefinitions) {
380 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
384 * Method to update the state of automation composition elements.
386 * @param automationComposition participant status in memory
387 * @param orderedState orderedState the new ordered state the participant should have
388 * @param startPhaseMsg startPhase from message
389 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
391 private void handleStateChange(AutomationComposition automationComposition,
392 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
393 List<AutomationCompositionElementDefinition> acElementDefinitions) {
395 if (orderedState.equals(automationComposition.getOrderedState())) {
396 var automationCompositionAck =
397 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
398 automationCompositionAck.setParticipantId(participantId);
399 automationCompositionAck.setParticipantType(participantType);
400 automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
401 automationCompositionAck.setResult(false);
402 automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
403 publisher.sendAutomationCompositionAck(automationCompositionAck);
407 automationComposition.getElements().values().stream()
408 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
409 acElement, startPhaseMsg, acElementDefinitions));
412 private void automationCompositionElementStateChange(AutomationComposition automationComposition,
413 AutomationCompositionOrderedState orderedState, AutomationCompositionElement acElement, Integer startPhaseMsg,
414 List<AutomationCompositionElementDefinition> acElementDefinitions) {
415 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
416 if (acElementNodeTemplate != null) {
417 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
418 if (startPhaseMsg.equals(startPhase)) {
419 for (var acElementListener : listeners) {
421 acElementListener.automationCompositionElementStateChange(
422 automationComposition.getInstanceId(), acElement.getId(), acElement.getState(),
424 } catch (PfModelException e) {
425 LOGGER.debug("Automation composition element update failed {}",
426 automationComposition.getInstanceId());
434 * Get automation compositions as a {@link ConrolLoops} class.
436 * @return the automation compositions
438 public AutomationCompositions getAutomationCompositions() {
439 var automationCompositions = new AutomationCompositions();
440 automationCompositions.setAutomationCompositionList(new ArrayList<>(automationCompositionMap.values()));
441 return automationCompositions;