2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2023 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.AcElementDeployAck;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
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.ParticipantDeploy;
42 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
43 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeploy;
44 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionDeployAck;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
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 UUID participantId;
62 private final ParticipantMessagePublisher publisher;
65 private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
68 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
71 private List<AutomationCompositionElementListener> listeners = new ArrayList<>();
74 * Constructor, set the participant ID and messageSender.
76 * @param parameters the parameters of the participant
77 * @param publisher the ParticipantMessage Publisher
79 public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
80 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
81 this.publisher = publisher;
84 public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
85 listeners.add(listener);
89 * Handle a automation composition element state change message.
91 * @param automationCompositionId the automationComposition Id
92 * @param id the automationComposition UUID
93 * @param orderedState the current state
94 * @param newState the ordered state
95 * @return automationCompositionElement the updated automation composition element
97 public AutomationCompositionElement updateAutomationCompositionElementState(UUID automationCompositionId, UUID id,
98 AutomationCompositionOrderedState orderedState, AutomationCompositionState newState) {
101 LOGGER.warn("Cannot update Automation composition element state, id is null");
105 // Update states of AutomationCompositionElement in automationCompositionMap
106 for (var automationComposition : automationCompositionMap.values()) {
107 var element = automationComposition.getElements().get(id);
108 if (element != null) {
109 element.setOrderedState(orderedState);
110 element.setState(newState);
112 var checkOpt = automationComposition.getElements().values().stream()
113 .filter(acElement -> !newState.equals(acElement.getState())).findAny();
114 if (checkOpt.isEmpty()) {
115 automationComposition.setState(newState);
116 automationComposition.setOrderedState(orderedState);
120 // Update states of AutomationCompositionElement in elementsOnThisParticipant
121 var acElement = elementsOnThisParticipant.get(id);
122 if (acElement != null) {
123 var automationCompositionStateChangeAck =
124 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
125 automationCompositionStateChangeAck.setParticipantId(participantId);
126 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
127 acElement.setOrderedState(orderedState);
128 acElement.setState(newState);
129 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
130 new AcElementDeployAck(newState, null, true,
131 "Automation composition element {} state changed to {}\", id, newState)"));
132 LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
133 automationCompositionStateChangeAck
134 .setMessage("AutomationCompositionElement state changed to {} " + newState);
135 automationCompositionStateChangeAck.setResult(true);
136 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
143 * Handle a automation composition state change message.
145 * @param stateChangeMsg the state change message
146 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
148 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
149 List<AutomationCompositionElementDefinition> acElementDefinitions) {
150 if (stateChangeMsg.getAutomationCompositionId() == null) {
154 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
156 if (automationComposition == null) {
157 var automationCompositionAck =
158 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
159 automationCompositionAck.setParticipantId(participantId);
160 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
161 + " does not use this participant " + participantId);
162 automationCompositionAck.setResult(false);
163 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
164 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
165 publisher.sendAutomationCompositionAck(automationCompositionAck);
166 LOGGER.debug("Automation composition {} does not use this participant",
167 stateChangeMsg.getAutomationCompositionId());
171 handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
172 acElementDefinitions);
176 * Method to handle state changes.
178 * @param automationComposition participant response
179 * @param orderedState automation composition ordered state
180 * @param startPhaseMsg startPhase from message
181 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
183 private void handleState(final AutomationComposition automationComposition,
184 AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
185 List<AutomationCompositionElementDefinition> acElementDefinitions) {
186 switch (orderedState) {
188 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
191 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
194 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
197 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
203 * Handle a automation composition Deploy message.
205 * @param updateMsg the Deploy message
206 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
208 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy updateMsg,
209 List<AutomationCompositionElementDefinition> acElementDefinitions) {
211 if (!updateMsg.appliesTo(participantId)) {
215 if (0 == updateMsg.getStartPhase()) {
216 handleAcUpdatePhase0(updateMsg, acElementDefinitions);
218 handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
222 private void handleAcUpdatePhase0(AutomationCompositionDeploy updateMsg,
223 List<AutomationCompositionElementDefinition> acElementDefinitions) {
224 var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
226 // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
227 // AutomationComposition
228 // elements to existing AutomationComposition has to be supported).
229 if (automationComposition != null) {
230 var automationCompositionUpdateAck =
231 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
232 automationCompositionUpdateAck.setParticipantId(participantId);
234 automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
235 + " already defined on participant " + participantId);
236 automationCompositionUpdateAck.setResult(false);
237 automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
238 automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
239 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
243 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
244 LOGGER.warn("No AutomationCompositionElement updates in message {}",
245 updateMsg.getAutomationCompositionId());
249 automationComposition = new AutomationComposition();
250 automationComposition.setInstanceId(updateMsg.getAutomationCompositionId());
251 var acElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
252 var acElementMap = prepareAcElementMap(acElements);
253 automationComposition.setElements(acElementMap);
254 automationCompositionMap.put(updateMsg.getAutomationCompositionId(), automationComposition);
256 handleAutomationCompositionElementUpdate(acElements, acElementDefinitions, updateMsg.getStartPhase(),
257 updateMsg.getAutomationCompositionId());
260 private void handleAcUpdatePhaseN(AutomationCompositionDeploy updateMsg,
261 List<AutomationCompositionElementDefinition> acElementDefinitions) {
263 var acElementList = updateMsg.getParticipantUpdatesList().stream()
264 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
265 .filter(element -> participantId.equals(element.getParticipantId())).collect(Collectors.toList());
267 handleAutomationCompositionElementUpdate(acElementList, acElementDefinitions, updateMsg.getStartPhase(),
268 updateMsg.getAutomationCompositionId());
271 private void handleAutomationCompositionElementUpdate(List<AutomationCompositionElement> acElements,
272 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
273 UUID automationCompositionId) {
275 for (var element : acElements) {
276 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
277 if (acElementNodeTemplate != null) {
278 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
279 if (startPhaseMsg.equals(startPhase)) {
280 for (var acElementListener : listeners) {
281 var map = new HashMap<>(acElementNodeTemplate.getProperties());
282 map.putAll(element.getProperties());
283 acElementListener.automationCompositionElementUpdate(automationCompositionId, element, map);
288 } catch (PfModelException e) {
289 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
294 private ToscaNodeTemplate getAcElementNodeTemplate(
295 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
297 for (var acElementDefinition : acElementDefinitions) {
298 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
299 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
305 private List<AutomationCompositionElement> storeElementsOnThisParticipant(
306 List<ParticipantDeploy> participantUpdates) {
307 var acElementList = participantUpdates.stream()
308 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
309 .filter(element -> participantId.equals(element.getParticipantId())).collect(Collectors.toList());
311 for (var element : acElementList) {
312 elementsOnThisParticipant.put(element.getId(), element);
314 return acElementList;
317 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
318 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
319 for (var element : acElements) {
320 acElementMap.put(element.getId(), element);
326 * Method to handle when the new state from participant is UNINITIALISED state.
328 * @param automationComposition participant response
329 * @param orderedState orderedState
330 * @param startPhaseMsg startPhase from message
331 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
333 private void handleUninitialisedState(final AutomationComposition automationComposition,
334 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
335 List<AutomationCompositionElementDefinition> acElementDefinitions) {
336 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
337 boolean isAllUninitialised = automationComposition.getElements().values().stream()
338 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
340 if (isAllUninitialised) {
341 automationCompositionMap.remove(automationComposition.getInstanceId());
342 automationComposition.getElements().values()
343 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
348 * Method to handle when the new state from participant is PASSIVE state.
350 * @param automationComposition participant response
351 * @param orderedState orderedState
352 * @param startPhaseMsg startPhase from message
353 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
355 private void handlePassiveState(final AutomationComposition automationComposition,
356 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
357 List<AutomationCompositionElementDefinition> acElementDefinitions) {
358 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
362 * Method to handle when the new state from participant is RUNNING state.
364 * @param automationComposition participant response
365 * @param orderedState orderedState
366 * @param startPhaseMsg startPhase from message
367 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
369 private void handleRunningState(final AutomationComposition automationComposition,
370 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
371 List<AutomationCompositionElementDefinition> acElementDefinitions) {
372 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
376 * Method to update the state of automation composition elements.
378 * @param automationComposition participant status in memory
379 * @param orderedState orderedState the new ordered state the participant should have
380 * @param startPhaseMsg startPhase from message
381 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
383 private void handleStateChange(AutomationComposition automationComposition,
384 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
385 List<AutomationCompositionElementDefinition> acElementDefinitions) {
387 if (orderedState.equals(automationComposition.getOrderedState())) {
388 var automationCompositionAck =
389 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
390 automationCompositionAck.setParticipantId(participantId);
391 automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
392 automationCompositionAck.setResult(false);
393 automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
394 publisher.sendAutomationCompositionAck(automationCompositionAck);
398 automationComposition.getElements().values().stream()
399 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
400 acElement, startPhaseMsg, acElementDefinitions));
403 private void automationCompositionElementStateChange(AutomationComposition automationComposition,
404 AutomationCompositionOrderedState orderedState, AutomationCompositionElement acElement,
405 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
406 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
407 if (acElementNodeTemplate != null) {
408 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
409 if (startPhaseMsg.equals(startPhase)) {
410 for (var acElementListener : listeners) {
412 acElementListener.automationCompositionElementStateChange(automationComposition.getInstanceId(),
413 acElement.getId(), acElement.getState(), orderedState);
414 } catch (PfModelException e) {
415 LOGGER.debug("Automation composition element update failed {}",
416 automationComposition.getInstanceId());