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.LinkedHashMap;
26 import java.util.List;
28 import java.util.UUID;
29 import java.util.stream.Collectors;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.AutomationCompositionElementListener;
32 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
33 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
34 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
35 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
36 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementAck;
37 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
38 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState;
39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
40 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
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<ToscaConceptIdentifier, 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(
100 ToscaConceptIdentifier automationCompositionId, UUID id, AutomationCompositionOrderedState orderedState,
101 AutomationCompositionState newState) {
104 LOGGER.warn("Cannot update Automation composition element state, id is null");
108 // Update states of AutomationCompositionElement in automationCompositionMap
109 for (var automationComposition : automationCompositionMap.values()) {
110 var element = automationComposition.getElements().get(id);
111 if (element != null) {
112 element.setOrderedState(orderedState);
113 element.setState(newState);
115 var checkOpt = automationComposition.getElements().values().stream()
116 .filter(acElement -> !newState.equals(acElement.getState())).findAny();
117 if (checkOpt.isEmpty()) {
118 automationComposition.setState(newState);
119 automationComposition.setOrderedState(orderedState);
123 // Update states of AutomationCompositionElement in elementsOnThisParticipant
124 var acElement = elementsOnThisParticipant.get(id);
125 if (acElement != null) {
126 var automationCompositionStateChangeAck =
127 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
128 automationCompositionStateChangeAck.setParticipantId(participantId);
129 automationCompositionStateChangeAck.setParticipantType(participantType);
130 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
131 acElement.setOrderedState(orderedState);
132 acElement.setState(newState);
133 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
134 new AutomationCompositionElementAck(newState, true,
135 "Automation composition element {} state changed to {}\", id, newState)"));
136 LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
137 automationCompositionStateChangeAck
138 .setMessage("AutomationCompositionElement state changed to {} " + newState);
139 automationCompositionStateChangeAck.setResult(true);
140 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
147 * Handle a automation composition state change message.
149 * @param stateChangeMsg the state change message
150 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
152 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
153 List<AutomationCompositionElementDefinition> acElementDefinitions) {
154 if (stateChangeMsg.getAutomationCompositionId() == null) {
158 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
160 if (automationComposition == null) {
161 var automationCompositionAck =
162 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
163 automationCompositionAck.setParticipantId(participantId);
164 automationCompositionAck.setParticipantType(participantType);
165 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
166 + " does not use this participant " + participantId);
167 automationCompositionAck.setResult(false);
168 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
169 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
170 publisher.sendAutomationCompositionAck(automationCompositionAck);
171 LOGGER.debug("Automation composition {} does not use this participant",
172 stateChangeMsg.getAutomationCompositionId());
176 handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
177 acElementDefinitions);
181 * Method to handle state changes.
183 * @param automationComposition participant response
184 * @param orderedState automation composition ordered state
185 * @param startPhaseMsg startPhase from message
186 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
188 private void handleState(final AutomationComposition automationComposition,
189 AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
190 List<AutomationCompositionElementDefinition> acElementDefinitions) {
191 switch (orderedState) {
193 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
196 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
199 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
202 LOGGER.debug("StateChange message has no state, state is null {}",
203 automationComposition.getKey());
209 * Handle a automation composition update message.
211 * @param updateMsg the update message
212 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
214 public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg,
215 List<AutomationCompositionElementDefinition> acElementDefinitions) {
217 if (!updateMsg.appliesTo(participantType, participantId)) {
221 if (0 == updateMsg.getStartPhase()) {
222 handleAcUpdatePhase0(updateMsg, acElementDefinitions);
224 handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
228 private void handleAcUpdatePhase0(AutomationCompositionUpdate updateMsg,
229 List<AutomationCompositionElementDefinition> acElementDefinitions) {
230 var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
232 // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
233 // AutomationComposition
234 // elements to existing AutomationComposition has to be supported).
235 if (automationComposition != null) {
236 var automationCompositionUpdateAck =
237 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_UPDATE_ACK);
238 automationCompositionUpdateAck.setParticipantId(participantId);
239 automationCompositionUpdateAck.setParticipantType(participantType);
241 automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
242 + " already defined on participant " + participantId);
243 automationCompositionUpdateAck.setResult(false);
244 automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
245 automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
246 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
250 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
251 LOGGER.warn("No AutomationCompositionElement updates in message {}",
252 updateMsg.getAutomationCompositionId());
256 automationComposition = new AutomationComposition();
257 automationComposition.setName(updateMsg.getAutomationCompositionId().getName());
258 automationComposition.setVersion(updateMsg.getAutomationCompositionId().getVersion());
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 ToscaConceptIdentifier 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 acElementListener.automationCompositionElementUpdate(automationCompositionId, element,
290 acElementNodeTemplate);
295 } catch (PfModelException e) {
296 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
301 private ToscaNodeTemplate getAcElementNodeTemplate(
302 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
304 for (var acElementDefinition : acElementDefinitions) {
305 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
306 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
312 private List<AutomationCompositionElement> storeElementsOnThisParticipant(
313 List<ParticipantUpdates> participantUpdates) {
314 var acElementList = participantUpdates.stream()
315 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
316 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
318 for (var element : acElementList) {
319 elementsOnThisParticipant.put(element.getId(), element);
321 return acElementList;
324 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
325 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
326 for (var element : acElements) {
327 acElementMap.put(element.getId(), element);
333 * Method to handle when the new state from participant is UNINITIALISED state.
335 * @param automationComposition participant response
336 * @param orderedState orderedState
337 * @param startPhaseMsg startPhase from message
338 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
340 private void handleUninitialisedState(final AutomationComposition automationComposition,
341 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
342 List<AutomationCompositionElementDefinition> acElementDefinitions) {
343 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
344 boolean isAllUninitialised = automationComposition.getElements().values().stream()
345 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
347 if (isAllUninitialised) {
348 automationCompositionMap.remove(automationComposition.getKey().asIdentifier());
349 automationComposition.getElements().values()
350 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
355 * Method to handle when the new state from participant is PASSIVE state.
357 * @param automationComposition participant response
358 * @param orderedState orderedState
359 * @param startPhaseMsg startPhase from message
360 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
362 private void handlePassiveState(final AutomationComposition automationComposition,
363 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
364 List<AutomationCompositionElementDefinition> acElementDefinitions) {
365 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
369 * Method to handle when the new state from participant is RUNNING state.
371 * @param automationComposition participant response
372 * @param orderedState orderedState
373 * @param startPhaseMsg startPhase from message
374 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
376 private void handleRunningState(final AutomationComposition automationComposition,
377 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
378 List<AutomationCompositionElementDefinition> acElementDefinitions) {
379 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
383 * Method to update the state of automation composition elements.
385 * @param automationComposition participant status in memory
386 * @param orderedState orderedState the new ordered state the participant should have
387 * @param startPhaseMsg startPhase from message
388 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
390 private void handleStateChange(AutomationComposition automationComposition,
391 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
392 List<AutomationCompositionElementDefinition> acElementDefinitions) {
394 if (orderedState.equals(automationComposition.getOrderedState())) {
395 var automationCompositionAck =
396 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
397 automationCompositionAck.setParticipantId(participantId);
398 automationCompositionAck.setParticipantType(participantType);
399 automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
400 automationCompositionAck.setResult(false);
401 automationCompositionAck.setAutomationCompositionId(automationComposition.getKey().asIdentifier());
402 publisher.sendAutomationCompositionAck(automationCompositionAck);
406 automationComposition.getElements().values().stream()
407 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
408 acElement, startPhaseMsg, acElementDefinitions));
411 private void automationCompositionElementStateChange(AutomationComposition automationComposition,
412 AutomationCompositionOrderedState orderedState, AutomationCompositionElement acElement, Integer startPhaseMsg,
413 List<AutomationCompositionElementDefinition> acElementDefinitions) {
414 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
415 if (acElementNodeTemplate != null) {
416 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
417 if (startPhaseMsg.equals(startPhase)) {
418 for (var acElementListener : listeners) {
420 acElementListener.automationCompositionElementStateChange(
421 automationComposition.getKey().asIdentifier(), acElement.getId(), acElement.getState(),
423 } catch (PfModelException e) {
424 LOGGER.debug("Automation composition element update failed {}",
425 automationComposition.getKey().asIdentifier());
433 * Get automation compositions as a {@link ConrolLoops} class.
435 * @return the automation compositions
437 public AutomationCompositions getAutomationCompositions() {
438 var automationCompositions = new AutomationCompositions();
439 automationCompositions.setAutomationCompositionList(new ArrayList<>(automationCompositionMap.values()));
440 return automationCompositions;