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.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
52 import org.slf4j.Logger;
53 import org.slf4j.LoggerFactory;
54 import org.springframework.stereotype.Component;
57 * This class is responsible for managing the state of all automation compositions in the participant.
60 public class AutomationCompositionHandler {
61 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
63 private final ToscaConceptIdentifier participantType;
64 private final ToscaConceptIdentifier participantId;
65 private final ParticipantMessagePublisher publisher;
68 private final Map<ToscaConceptIdentifier, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
71 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
74 private List<AutomationCompositionElementListener> listeners = new ArrayList<>();
77 * Constructor, set the participant ID and messageSender.
79 * @param parameters the parameters of the participant
80 * @param publisher the ParticipantMessage Publisher
82 public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
83 this.participantType = parameters.getIntermediaryParameters().getParticipantType();
84 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
85 this.publisher = publisher;
88 public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
89 listeners.add(listener);
93 * Handle a automation composition element state change message.
95 * @param automationCompositionId the automationComposition Id
96 * @param id the automationComposition UUID
97 * @param orderedState the current state
98 * @param newState the ordered state
99 * @return automationCompositionElement the updated automation composition element
101 public AutomationCompositionElement updateAutomationCompositionElementState(
102 ToscaConceptIdentifier automationCompositionId, UUID id, AutomationCompositionOrderedState orderedState,
103 AutomationCompositionState newState) {
106 LOGGER.warn("Cannot update Automation composition element state, id is null");
110 // Update states of AutomationCompositionElement in automationCompositionMap
111 for (var automationComposition : automationCompositionMap.values()) {
112 var element = automationComposition.getElements().get(id);
113 if (element != null) {
114 element.setOrderedState(orderedState);
115 element.setState(newState);
117 var checkOpt = automationComposition.getElements().values().stream()
118 .filter(acElement -> !newState.equals(acElement.getState())).findAny();
119 if (checkOpt.isEmpty()) {
120 automationComposition.setState(newState);
121 automationComposition.setOrderedState(orderedState);
125 // Update states of AutomationCompositionElement in elementsOnThisParticipant
126 var acElement = elementsOnThisParticipant.get(id);
127 if (acElement != null) {
128 var automationCompositionStateChangeAck =
129 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
130 automationCompositionStateChangeAck.setParticipantId(participantId);
131 automationCompositionStateChangeAck.setParticipantType(participantType);
132 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
133 acElement.setOrderedState(orderedState);
134 acElement.setState(newState);
135 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
136 new AutomationCompositionElementAck(newState, true,
137 "Automation composition element {} state changed to {}\", id, newState)"));
138 LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
139 automationCompositionStateChangeAck
140 .setMessage("AutomationCompositionElement state changed to {} " + newState);
141 automationCompositionStateChangeAck.setResult(true);
142 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
149 * Handle a automation composition state change message.
151 * @param stateChangeMsg the state change message
152 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
154 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
155 List<AutomationCompositionElementDefinition> acElementDefinitions) {
156 if (stateChangeMsg.getAutomationCompositionId() == null) {
160 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
162 if (automationComposition == null) {
163 var automationCompositionAck =
164 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
165 automationCompositionAck.setParticipantId(participantId);
166 automationCompositionAck.setParticipantType(participantType);
167 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
168 + " does not use this participant " + participantId);
169 automationCompositionAck.setResult(false);
170 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
171 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
172 publisher.sendAutomationCompositionAck(automationCompositionAck);
173 LOGGER.debug("Automation composition {} does not use this participant",
174 stateChangeMsg.getAutomationCompositionId());
178 handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
179 acElementDefinitions);
183 * Method to handle state changes.
185 * @param automationComposition participant response
186 * @param orderedState automation composition ordered state
187 * @param startPhaseMsg startPhase from message
188 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
190 private void handleState(final AutomationComposition automationComposition,
191 AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
192 List<AutomationCompositionElementDefinition> acElementDefinitions) {
193 switch (orderedState) {
195 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
198 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
201 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
204 LOGGER.debug("StateChange message has no state, state is null {}",
205 automationComposition.getDefinition());
211 * Handle a automation composition update message.
213 * @param updateMsg the update message
214 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
216 public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg,
217 List<AutomationCompositionElementDefinition> acElementDefinitions) {
219 if (!updateMsg.appliesTo(participantType, participantId)) {
223 if (0 == updateMsg.getStartPhase()) {
224 handleAcUpdatePhase0(updateMsg, acElementDefinitions);
226 handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
230 private void handleAcUpdatePhase0(AutomationCompositionUpdate updateMsg,
231 List<AutomationCompositionElementDefinition> acElementDefinitions) {
232 var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
234 // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
235 // AutomationComposition
236 // elements to existing AutomationComposition has to be supported).
237 if (automationComposition != null) {
238 var automationCompositionUpdateAck =
239 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_UPDATE_ACK);
240 automationCompositionUpdateAck.setParticipantId(participantId);
241 automationCompositionUpdateAck.setParticipantType(participantType);
243 automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
244 + " already defined on participant " + participantId);
245 automationCompositionUpdateAck.setResult(false);
246 automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
247 automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
248 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
252 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
253 LOGGER.warn("No AutomationCompositionElement updates in message {}",
254 updateMsg.getAutomationCompositionId());
258 var acElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
260 var acElementMap = prepareAcElementMap(acElements);
261 automationComposition = new AutomationComposition();
262 automationComposition.setDefinition(updateMsg.getAutomationCompositionId());
263 automationComposition.setElements(acElementMap);
264 automationCompositionMap.put(updateMsg.getAutomationCompositionId(), automationComposition);
266 handleAutomationCompositionElementUpdate(acElements, acElementDefinitions, updateMsg.getStartPhase(),
267 updateMsg.getAutomationCompositionId());
270 private void handleAcUpdatePhaseN(AutomationCompositionUpdate updateMsg,
271 List<AutomationCompositionElementDefinition> acElementDefinitions) {
273 var acElementList = updateMsg.getParticipantUpdatesList().stream()
274 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
275 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
277 handleAutomationCompositionElementUpdate(acElementList, acElementDefinitions, updateMsg.getStartPhase(),
278 updateMsg.getAutomationCompositionId());
281 private void handleAutomationCompositionElementUpdate(List<AutomationCompositionElement> acElements,
282 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
283 ToscaConceptIdentifier automationCompositionId) {
285 for (var element : acElements) {
286 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
287 if (acElementNodeTemplate != null) {
288 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
289 if (startPhaseMsg.equals(startPhase)) {
290 for (var acElementListener : listeners) {
291 acElementListener.automationCompositionElementUpdate(automationCompositionId, element,
292 acElementNodeTemplate);
297 } catch (PfModelException e) {
298 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
303 private ToscaNodeTemplate getAcElementNodeTemplate(
304 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
306 for (var acElementDefinition : acElementDefinitions) {
307 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
308 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
314 private List<AutomationCompositionElement> storeElementsOnThisParticipant(
315 List<ParticipantUpdates> participantUpdates) {
316 var acElementList = participantUpdates.stream()
317 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
318 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
320 for (var element : acElementList) {
321 elementsOnThisParticipant.put(element.getId(), element);
323 return acElementList;
326 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
327 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
328 for (var element : acElements) {
329 acElementMap.put(element.getId(), element);
335 * Method to handle when the new state from participant is UNINITIALISED state.
337 * @param automationComposition participant response
338 * @param orderedState orderedState
339 * @param startPhaseMsg startPhase from message
340 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
342 private void handleUninitialisedState(final AutomationComposition automationComposition,
343 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
344 List<AutomationCompositionElementDefinition> acElementDefinitions) {
345 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
346 boolean isAllUninitialised = automationComposition.getElements().values().stream()
347 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
349 if (isAllUninitialised) {
350 automationCompositionMap.remove(automationComposition.getDefinition());
351 automationComposition.getElements().values()
352 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
357 * Method to handle when the new state from participant is PASSIVE state.
359 * @param automationComposition participant response
360 * @param orderedState orderedState
361 * @param startPhaseMsg startPhase from message
362 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
364 private void handlePassiveState(final AutomationComposition automationComposition,
365 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
366 List<AutomationCompositionElementDefinition> acElementDefinitions) {
367 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
371 * Method to handle when the new state from participant is RUNNING state.
373 * @param automationComposition participant response
374 * @param orderedState orderedState
375 * @param startPhaseMsg startPhase from message
376 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
378 private void handleRunningState(final AutomationComposition automationComposition,
379 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
380 List<AutomationCompositionElementDefinition> acElementDefinitions) {
381 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
385 * Method to update the state of automation composition elements.
387 * @param automationComposition participant status in memory
388 * @param orderedState orderedState the new ordered state the participant should have
389 * @param startPhaseMsg startPhase from message
390 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
392 private void handleStateChange(AutomationComposition automationComposition,
393 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
394 List<AutomationCompositionElementDefinition> acElementDefinitions) {
396 if (orderedState.equals(automationComposition.getOrderedState())) {
397 var automationCompositionAck =
398 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
399 automationCompositionAck.setParticipantId(participantId);
400 automationCompositionAck.setParticipantType(participantType);
401 automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
402 automationCompositionAck.setResult(false);
403 automationCompositionAck.setAutomationCompositionId(automationComposition.getDefinition());
404 publisher.sendAutomationCompositionAck(automationCompositionAck);
408 automationComposition.getElements().values().stream()
409 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
410 acElement, startPhaseMsg, acElementDefinitions));
413 private void automationCompositionElementStateChange(AutomationComposition automationComposition,
414 AutomationCompositionOrderedState orderedState, AutomationCompositionElement acElement, Integer startPhaseMsg,
415 List<AutomationCompositionElementDefinition> acElementDefinitions) {
416 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
417 if (acElementNodeTemplate != null) {
418 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
419 if (startPhaseMsg.equals(startPhase)) {
420 for (var acElementListener : listeners) {
422 acElementListener.automationCompositionElementStateChange(automationComposition.getDefinition(),
423 acElement.getId(), acElement.getState(), orderedState);
424 } catch (PfModelException e) {
425 LOGGER.debug("Automation composition element update failed {}",
426 automationComposition.getDefinition());
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;
445 * Get properties of a automation composition element.
447 * @param id the automation composition element id
448 * @return the instance properties
450 public Map<String, ToscaProperty> getAcElementInstanceProperties(UUID id) {
451 Map<String, ToscaProperty> propertiesMap = new HashMap<>();
452 for (var automationComposition : automationCompositionMap.values()) {
453 var element = automationComposition.getElements().get(id);
454 if (element != null) {
455 propertiesMap.putAll(element.getPropertiesMap());
458 return propertiesMap;