2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021 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.AcElementStatistics;
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.AutomationCompositionElementAck;
39 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
40 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionOrderedState;
41 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionState;
42 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositions;
43 import org.onap.policy.clamp.models.acm.concepts.ParticipantUpdates;
44 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
45 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionAck;
46 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionStateChange;
47 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.AutomationCompositionUpdate;
48 import org.onap.policy.clamp.models.acm.messages.dmaap.participant.ParticipantMessageType;
49 import org.onap.policy.models.base.PfModelException;
50 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaProperty;
53 import org.slf4j.Logger;
54 import org.slf4j.LoggerFactory;
55 import org.springframework.stereotype.Component;
58 * This class is responsible for managing the state of all automation compositions in the participant.
61 public class AutomationCompositionHandler {
62 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
64 private final ToscaConceptIdentifier participantType;
65 private final ToscaConceptIdentifier participantId;
66 private final ParticipantMessagePublisher publisher;
69 private final Map<ToscaConceptIdentifier, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
72 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
75 private List<AutomationCompositionElementListener> listeners = new ArrayList<>();
78 * Constructor, set the participant ID and messageSender.
80 * @param parameters the parameters of the participant
81 * @param publisher the ParticipantMessage Publisher
83 public AutomationCompositionHandler(ParticipantParameters parameters, ParticipantMessagePublisher publisher) {
84 this.participantType = parameters.getIntermediaryParameters().getParticipantType();
85 this.participantId = parameters.getIntermediaryParameters().getParticipantId();
86 this.publisher = publisher;
89 public void registerAutomationCompositionElementListener(AutomationCompositionElementListener listener) {
90 listeners.add(listener);
94 * Handle a automation composition element state change message.
96 * @param automationCompositionId the automationComposition Id
97 * @param id the automationComposition UUID
98 * @param orderedState the current state
99 * @param newState the ordered state
100 * @return automationCompositionElement the updated automation composition element
102 public AutomationCompositionElement updateAutomationCompositionElementState(
103 ToscaConceptIdentifier automationCompositionId, UUID id, AutomationCompositionOrderedState orderedState,
104 AutomationCompositionState newState) {
107 LOGGER.warn("Cannot update Automation composition element state, id is null");
111 // Update states of AutomationCompositionElement in automationCompositionMap
112 for (var automationComposition : automationCompositionMap.values()) {
113 var element = automationComposition.getElements().get(id);
114 if (element != null) {
115 element.setOrderedState(orderedState);
116 element.setState(newState);
118 var checkOpt = automationComposition.getElements().values().stream()
119 .filter(acElement -> !newState.equals(acElement.getState())).findAny();
120 if (checkOpt.isEmpty()) {
121 automationComposition.setState(newState);
122 automationComposition.setOrderedState(orderedState);
126 // Update states of AutomationCompositionElement in elementsOnThisParticipant
127 var acElement = elementsOnThisParticipant.get(id);
128 if (acElement != null) {
129 var automationCompositionStateChangeAck =
130 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
131 automationCompositionStateChangeAck.setParticipantId(participantId);
132 automationCompositionStateChangeAck.setParticipantType(participantType);
133 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
134 acElement.setOrderedState(orderedState);
135 acElement.setState(newState);
136 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
137 new AutomationCompositionElementAck(newState, true,
138 "Automation composition element {} state changed to {}\", id, newState)"));
139 LOGGER.debug("Automation composition element {} state changed to {}", id, newState);
140 automationCompositionStateChangeAck
141 .setMessage("AutomationCompositionElement state changed to {} " + newState);
142 automationCompositionStateChangeAck.setResult(true);
143 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
150 * Handle a automation composition element statistics.
152 * @param id automation composition element id
153 * @param elementStatistics automation composition element Statistics
155 public void updateAutomationCompositionElementStatistics(UUID id, AcElementStatistics elementStatistics) {
156 var acElement = elementsOnThisParticipant.get(id);
157 if (acElement != null) {
158 elementStatistics.setParticipantId(participantId);
159 elementStatistics.setId(id);
160 acElement.setAcElementStatistics(elementStatistics);
165 * Handle a automation composition state change message.
167 * @param stateChangeMsg the state change message
168 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
170 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
171 List<AutomationCompositionElementDefinition> acElementDefinitions) {
172 if (stateChangeMsg.getAutomationCompositionId() == null) {
176 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
178 if (automationComposition == null) {
179 var automationCompositionAck =
180 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
181 automationCompositionAck.setParticipantId(participantId);
182 automationCompositionAck.setParticipantType(participantType);
183 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
184 + " does not use this participant " + participantId);
185 automationCompositionAck.setResult(false);
186 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
187 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
188 publisher.sendAutomationCompositionAck(automationCompositionAck);
189 LOGGER.debug("Automation composition {} does not use this participant",
190 stateChangeMsg.getAutomationCompositionId());
194 handleState(automationComposition, stateChangeMsg.getOrderedState(), stateChangeMsg.getStartPhase(),
195 acElementDefinitions);
199 * Method to handle state changes.
201 * @param automationComposition participant response
202 * @param orderedState automation composition ordered state
203 * @param startPhaseMsg startPhase from message
204 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
206 private void handleState(final AutomationComposition automationComposition,
207 AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
208 List<AutomationCompositionElementDefinition> acElementDefinitions) {
209 switch (orderedState) {
211 handleUninitialisedState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
214 handlePassiveState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
217 handleRunningState(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
220 LOGGER.debug("StateChange message has no state, state is null {}",
221 automationComposition.getDefinition());
227 * Handle a automation composition update message.
229 * @param updateMsg the update message
230 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
232 public void handleAutomationCompositionUpdate(AutomationCompositionUpdate updateMsg,
233 List<AutomationCompositionElementDefinition> acElementDefinitions) {
235 if (!updateMsg.appliesTo(participantType, participantId)) {
239 if (0 == updateMsg.getStartPhase()) {
240 handleAcUpdatePhase0(updateMsg, acElementDefinitions);
242 handleAcUpdatePhaseN(updateMsg, acElementDefinitions);
246 private void handleAcUpdatePhase0(AutomationCompositionUpdate updateMsg,
247 List<AutomationCompositionElementDefinition> acElementDefinitions) {
248 var automationComposition = automationCompositionMap.get(updateMsg.getAutomationCompositionId());
250 // TODO: Updates to existing AutomationCompositions are not supported yet (Addition/Removal of
251 // AutomationComposition
252 // elements to existing AutomationComposition has to be supported).
253 if (automationComposition != null) {
254 var automationCompositionUpdateAck =
255 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_UPDATE_ACK);
256 automationCompositionUpdateAck.setParticipantId(participantId);
257 automationCompositionUpdateAck.setParticipantType(participantType);
259 automationCompositionUpdateAck.setMessage("Automation composition " + updateMsg.getAutomationCompositionId()
260 + " already defined on participant " + participantId);
261 automationCompositionUpdateAck.setResult(false);
262 automationCompositionUpdateAck.setResponseTo(updateMsg.getMessageId());
263 automationCompositionUpdateAck.setAutomationCompositionId(updateMsg.getAutomationCompositionId());
264 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
268 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
269 LOGGER.warn("No AutomationCompositionElement updates in message {}",
270 updateMsg.getAutomationCompositionId());
274 var acElements = storeElementsOnThisParticipant(updateMsg.getParticipantUpdatesList());
276 var acElementMap = prepareAcElementMap(acElements);
277 automationComposition = new AutomationComposition();
278 automationComposition.setDefinition(updateMsg.getAutomationCompositionId());
279 automationComposition.setElements(acElementMap);
280 automationCompositionMap.put(updateMsg.getAutomationCompositionId(), automationComposition);
282 handleAutomationCompositionElementUpdate(acElements, acElementDefinitions, updateMsg.getStartPhase(),
283 updateMsg.getAutomationCompositionId());
286 private void handleAcUpdatePhaseN(AutomationCompositionUpdate updateMsg,
287 List<AutomationCompositionElementDefinition> acElementDefinitions) {
289 var acElementList = updateMsg.getParticipantUpdatesList().stream()
290 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
291 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
293 handleAutomationCompositionElementUpdate(acElementList, acElementDefinitions, updateMsg.getStartPhase(),
294 updateMsg.getAutomationCompositionId());
297 private void handleAutomationCompositionElementUpdate(List<AutomationCompositionElement> acElements,
298 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
299 ToscaConceptIdentifier automationCompositionId) {
301 for (var element : acElements) {
302 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
303 if (acElementNodeTemplate != null) {
304 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
305 if (startPhaseMsg.equals(startPhase)) {
306 for (var acElementListener : listeners) {
307 acElementListener.automationCompositionElementUpdate(automationCompositionId, element,
308 acElementNodeTemplate);
313 } catch (PfModelException e) {
314 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
319 private ToscaNodeTemplate getAcElementNodeTemplate(
320 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
322 for (var acElementDefinition : acElementDefinitions) {
323 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
324 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
330 private List<AutomationCompositionElement> storeElementsOnThisParticipant(
331 List<ParticipantUpdates> participantUpdates) {
332 var acElementList = participantUpdates.stream()
333 .flatMap(participantUpdate -> participantUpdate.getAutomationCompositionElementList().stream())
334 .filter(element -> participantType.equals(element.getParticipantType())).collect(Collectors.toList());
336 for (var element : acElementList) {
337 elementsOnThisParticipant.put(element.getId(), element);
339 return acElementList;
342 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
343 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
344 for (var element : acElements) {
345 acElementMap.put(element.getId(), element);
351 * Method to handle when the new state from participant is UNINITIALISED state.
353 * @param automationComposition participant response
354 * @param orderedState orderedState
355 * @param startPhaseMsg startPhase from message
356 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
358 private void handleUninitialisedState(final AutomationComposition automationComposition,
359 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
360 List<AutomationCompositionElementDefinition> acElementDefinitions) {
361 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
362 boolean isAllUninitialised = automationComposition.getElements().values().stream()
363 .filter(element -> !AutomationCompositionState.UNINITIALISED.equals(element.getState())).findAny()
365 if (isAllUninitialised) {
366 automationCompositionMap.remove(automationComposition.getDefinition());
367 automationComposition.getElements().values()
368 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
373 * Method to handle when the new state from participant is PASSIVE state.
375 * @param automationComposition participant response
376 * @param orderedState orderedState
377 * @param startPhaseMsg startPhase from message
378 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
380 private void handlePassiveState(final AutomationComposition automationComposition,
381 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
382 List<AutomationCompositionElementDefinition> acElementDefinitions) {
383 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
387 * Method to handle when the new state from participant is RUNNING state.
389 * @param automationComposition participant response
390 * @param orderedState orderedState
391 * @param startPhaseMsg startPhase from message
392 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
394 private void handleRunningState(final AutomationComposition automationComposition,
395 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
396 List<AutomationCompositionElementDefinition> acElementDefinitions) {
397 handleStateChange(automationComposition, orderedState, startPhaseMsg, acElementDefinitions);
401 * Method to update the state of automation composition elements.
403 * @param automationComposition participant status in memory
404 * @param orderedState orderedState the new ordered state the participant should have
405 * @param startPhaseMsg startPhase from message
406 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
408 private void handleStateChange(AutomationComposition automationComposition,
409 final AutomationCompositionOrderedState orderedState, Integer startPhaseMsg,
410 List<AutomationCompositionElementDefinition> acElementDefinitions) {
412 if (orderedState.equals(automationComposition.getOrderedState())) {
413 var automationCompositionAck =
414 new AutomationCompositionAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
415 automationCompositionAck.setParticipantId(participantId);
416 automationCompositionAck.setParticipantType(participantType);
417 automationCompositionAck.setMessage("Automation composition is already in state " + orderedState);
418 automationCompositionAck.setResult(false);
419 automationCompositionAck.setAutomationCompositionId(automationComposition.getDefinition());
420 publisher.sendAutomationCompositionAck(automationCompositionAck);
424 automationComposition.getElements().values().stream()
425 .forEach(acElement -> automationCompositionElementStateChange(automationComposition, orderedState,
426 acElement, startPhaseMsg, acElementDefinitions));
429 private void automationCompositionElementStateChange(AutomationComposition automationComposition,
430 AutomationCompositionOrderedState orderedState, AutomationCompositionElement acElement, Integer startPhaseMsg,
431 List<AutomationCompositionElementDefinition> acElementDefinitions) {
432 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
433 if (acElementNodeTemplate != null) {
434 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
435 if (startPhaseMsg.equals(startPhase)) {
436 for (var acElementListener : listeners) {
438 acElementListener.automationCompositionElementStateChange(automationComposition.getDefinition(),
439 acElement.getId(), acElement.getState(), orderedState);
440 } catch (PfModelException e) {
441 LOGGER.debug("Automation composition element update failed {}",
442 automationComposition.getDefinition());
450 * Get automation compositions as a {@link ConrolLoops} class.
452 * @return the automation compositions
454 public AutomationCompositions getAutomationCompositions() {
455 var automationCompositions = new AutomationCompositions();
456 automationCompositions.setAutomationCompositionList(new ArrayList<>(automationCompositionMap.values()));
457 return automationCompositions;
461 * Get properties of a automation composition element.
463 * @param id the automation composition element id
464 * @return the instance properties
466 public Map<String, ToscaProperty> getAcElementInstanceProperties(UUID id) {
467 Map<String, ToscaProperty> propertiesMap = new HashMap<>();
468 for (var automationComposition : automationCompositionMap.values()) {
469 var element = automationComposition.getElements().get(id);
470 if (element != null) {
471 propertiesMap.putAll(element.getPropertiesMap());
474 return propertiesMap;