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.ParticipantUpdates;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
43 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionAck;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionUpdate;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
47 import org.onap.policy.models.base.PfModelException;
48 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
49 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
50 import org.slf4j.Logger;
51 import org.slf4j.LoggerFactory;
52 import org.springframework.stereotype.Component;
55 * This class is responsible for managing the state of all automation compositions in the participant.
58 public class AutomationCompositionHandler {
59 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
61 private final ToscaConceptIdentifier participantType;
62 private final ToscaConceptIdentifier participantId;
63 private final ParticipantMessagePublisher publisher;
66 private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
69 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
72 private List<AutomationCompositionElementListener> listeners = new ArrayList<>();
75 * Constructor, set the participant ID and messageSender.
77 * @param parameters the parameters of the participant
78 * @param publisher the ParticipantMessage Publisher
80 public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
81 this.participantType = parameters.getIntermediaryParameters().getParticipantType();
82 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
83 this.publisher = publisher;
86 public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
87 listeners.add(listener);
91 * Handle a automation composition element state change message.
93 * @param automationCompositionId the automationComposition Id
94 * @param id the automationComposition UUID
95 * @param orderedState the current state
96 * @param newState the ordered state
97 * @return automationCompositionElement the updated automation composition element
99 public AutomationCompositionElement updateAutomationCompositionElementState(UUID automationCompositionId, UUID id,
100 AutomationCompositionOrderedState orderedState, AutomationCompositionState newState) {
103 LOGGER.warn("Cannot update Automation composition element state, id is null");
107 // Update states of AutomationCompositionElement in automationCompositionMap
108 for (var automationComposition : automationCompositionMap.values()) {
109 var element = automationComposition.getElements().get(id);
110 if (element != null) {
111 element.setOrderedState(orderedState);
112 element.setState(newState);
114 var checkOpt = automationComposition.getElements().values().stream()
115 .filter(acElement -> !newState.equals(acElement.getState())).findAny();
116 if (checkOpt.isEmpty()) {
117 automationComposition.setState(newState);
118 automationComposition.setOrderedState(orderedState);
122 // Update states of AutomationCompositionElement in elementsOnThisParticipant
123 var acElement = elementsOnThisParticipant.get(id);
124 if (acElement != null) {
125 var automationCompositionStateChangeAck =
126 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
127 automationCompositionStateChangeAck.setParticipantId(participantId);
128 automationCompositionStateChangeAck.setParticipantType(participantType);
129 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
130 acElement.setOrderedState(orderedState);
131 acElement.setState(newState);
132 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
133 new AutomationCompositionElementAck(newState, true,
134 "Automation composition element {} state changed to {}\", id, newState)"));
135 LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
136 automationCompositionStateChangeAck
137 .setMessage("AutomationCompositionElement state changed to {} " + newState);
138 automationCompositionStateChangeAck.setResult(true);
139 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
146 * Handle a automation composition state change message.
148 * @param stateChangeMsg the state change message
149 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
151 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
152 List<AutomationCompositionElementDefinition> acElementDefinitions) {
153 if (stateChangeMsg.getAutomationCompositionId() == null) {
157 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
159 if (automationComposition == null) {
160 var automationCompositionAck =
161 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
162 automationCompositionAck.setParticipantId(participantId);
163 automationCompositionAck.setParticipantType(participantType);
164 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
165 + " does not use this participant " + participantId);
166 automationCompositionAck.setResult(false);
167 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
168 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
169 publisher.sendAutomationCompositionAck(automationCompositionAck);
170 LOGGER.debug("Automation composition {} does not use this participant",
171 stateChangeMsg.getAutomationCompositionId());
175 handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
176 acElementDefinitions);
180 * Method to handle state changes.
182 * @param automationComposition participant response
183 * @param orderedState automation composition ordered state
184 * @param startPhaseMsg startPhase from message
185 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
187 private void handleState(final AutomationComposition automationComposition,
188 AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
189 List<AutomationCompositionElementDefinition> acElementDefinitions) {
190 switch (orderedState) {
192 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
195 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
198 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
201 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
207 * Handle a automation composition update message.
209 * @param updateMsg the update message
210 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
212 public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg,
213 List<AutomationCompositionElementDefinition> acElementDefinitions) {
215 if (!updateMsg.appliesTo(participantType, participantId)) {
219 if (0 == updateMsg.getStartPhase()) {
220 handleAcUpdatePhase0(updateMsg, acElementDefinitions);
222 handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
226 private void handleAcUpdatePhase0(AutomationCompositionUpdate updateMsg,
227 List<AutomationCompositionElementDefinition> acElementDefinitions) {
228 var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
230 // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
231 // AutomationComposition
232 // elements to existing AutomationComposition has to be supported).
233 if (automationComposition != null) {
234 var automationCompositionUpdateAck =
235 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_UPDATE_ACK);
236 automationCompositionUpdateAck.setParticipantId(participantId);
237 automationCompositionUpdateAck.setParticipantType(participantType);
239 automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
240 + " already defined on participant " + participantId);
241 automationCompositionUpdateAck.setResult(false);
242 automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
243 automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
244 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
248 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
249 LOGGER.warn("No AutomationCompositionElement updates in message {}",
250 updateMsg.getAutomationCompositionId());
254 automationComposition = new AutomationComposition();
255 automationComposition.setInstanceId(updateMsg.getAutomationCompositionId());
256 var acElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
257 var acElementMap = prepareAcElementMap(acElements);
258 automationComposition.setElements(acElementMap);
259 automationCompositionMap.put(updateMsg.getAutomationCompositionId(), automationComposition);
261 handleAutomationCompositionElementUpdate(acElements, acElementDefinitions, updateMsg.getStartPhase(),
262 updateMsg.getAutomationCompositionId());
265 private void handleAcUpdatePhaseN(AutomationCompositionUpdate updateMsg,
266 List<AutomationCompositionElementDefinition> acElementDefinitions) {
268 var acElementList = updateMsg.getParticipantUpdatesList().stream()
269 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
270 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
272 handleAutomationCompositionElementUpdate(acElementList, acElementDefinitions, updateMsg.getStartPhase(),
273 updateMsg.getAutomationCompositionId());
276 private void handleAutomationCompositionElementUpdate(List<AutomationCompositionElement> acElements,
277 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
278 UUID automationCompositionId) {
280 for (var element : acElements) {
281 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
282 if (acElementNodeTemplate != null) {
283 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
284 if (startPhaseMsg.equals(startPhase)) {
285 for (var acElementListener : listeners) {
286 var map = new HashMap<>(acElementNodeTemplate.getProperties());
287 map.putAll(element.getProperties());
288 acElementListener.automationCompositionElementUpdate(automationCompositionId, element, map);
293 } catch (PfModelException e) {
294 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
299 private ToscaNodeTemplate getAcElementNodeTemplate(
300 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
302 for (var acElementDefinition : acElementDefinitions) {
303 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
304 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
310 private List<AutomationCompositionElement> storeElementsOnThisParticipant(
311 List<ParticipantUpdates> participantUpdates) {
312 var acElementList = participantUpdates.stream()
313 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
314 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
316 for (var element : acElementList) {
317 elementsOnThisParticipant.put(element.getId(), element);
319 return acElementList;
322 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
323 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
324 for (var element : acElements) {
325 acElementMap.put(element.getId(), element);
331 * Method to handle when the new state from participant is UNINITIALISED state.
333 * @param automationComposition participant response
334 * @param orderedState orderedState
335 * @param startPhaseMsg startPhase from message
336 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
338 private void handleUninitialisedState(final AutomationComposition automationComposition,
339 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
340 List<AutomationCompositionElementDefinition> acElementDefinitions) {
341 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
342 boolean isAllUninitialised = automationComposition.getElements().values().stream()
343 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
345 if (isAllUninitialised) {
346 automationCompositionMap.remove(automationComposition.getInstanceId());
347 automationComposition.getElements().values()
348 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
353 * Method to handle when the new state from participant is PASSIVE state.
355 * @param automationComposition participant response
356 * @param orderedState orderedState
357 * @param startPhaseMsg startPhase from message
358 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
360 private void handlePassiveState(final AutomationComposition automationComposition,
361 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
362 List<AutomationCompositionElementDefinition> acElementDefinitions) {
363 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
367 * Method to handle when the new state from participant is RUNNING state.
369 * @param automationComposition participant response
370 * @param orderedState orderedState
371 * @param startPhaseMsg startPhase from message
372 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
374 private void handleRunningState(final AutomationComposition automationComposition,
375 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
376 List<AutomationCompositionElementDefinition> acElementDefinitions) {
377 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
381 * Method to update the state of automation composition elements.
383 * @param automationComposition participant status in memory
384 * @param orderedState orderedState the new ordered state the participant should have
385 * @param startPhaseMsg startPhase from message
386 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
388 private void handleStateChange(AutomationComposition automationComposition,
389 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
390 List<AutomationCompositionElementDefinition> acElementDefinitions) {
392 if (orderedState.equals(automationComposition.getOrderedState())) {
393 var automationCompositionAck =
394 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
395 automationCompositionAck.setParticipantId(participantId);
396 automationCompositionAck.setParticipantType(participantType);
397 automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
398 automationCompositionAck.setResult(false);
399 automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
400 publisher.sendAutomationCompositionAck(automationCompositionAck);
404 automationComposition.getElements().values().stream()
405 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
406 acElement, startPhaseMsg, acElementDefinitions));
409 private void automationCompositionElementStateChange(AutomationComposition automationComposition,
410 AutomationCompositionOrderedState orderedState, AutomationCompositionElement acElement,
411 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
412 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
413 if (acElementNodeTemplate != null) {
414 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
415 if (startPhaseMsg.equals(startPhase)) {
416 for (var acElementListener : listeners) {
418 acElementListener.automationCompositionElementStateChange(automationComposition.getInstanceId(),
419 acElement.getId(), acElement.getState(), orderedState);
420 } catch (PfModelException e) {
421 LOGGER.debug("Automation composition element update failed {}",
422 automationComposition.getInstanceId());