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;
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.AcElementDeploy;
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.DeployState;
40 import org.onap.policy.clamp.models.acm.concepts.LockState;
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.clamp.models.acm.messages.rest.instantiation.DeployOrder;
48 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
49 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
50 import org.onap.policy.models.base.PfModelException;
51 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
52 import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
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 UUID participantId;
65 private final ParticipantMessagePublisher publisher;
66 private final AcInstanceStateResolver acInstanceStateResolver;
69 private final Map<UUID, AutomationComposition> automationCompositionMap = new LinkedHashMap<>();
72 private final Map<UUID, AutomationCompositionElement> elementsOnThisParticipant = new LinkedHashMap<>();
75 private final 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.participantId = parameters.getIntermediaryParameters().getParticipantId();
85 this.publisher = publisher;
86 this.acInstanceStateResolver = new AcInstanceStateResolver();
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 deployState the DeployState state
100 public void updateAutomationCompositionElementState(UUID automationCompositionId, UUID id, DeployState deployState,
101 LockState lockState) {
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.setDeployState(deployState);
113 element.setLockState(lockState);
114 element.setUseState(getUseState(automationCompositionId, id));
115 element.setOperationalState(getOperationalState(automationCompositionId, id));
117 var checkOpt = automationComposition.getElements().values().stream()
118 .filter(acElement -> !deployState.equals(acElement.getDeployState())).findAny();
119 if (checkOpt.isEmpty()) {
120 automationComposition.setDeployState(deployState);
122 checkOpt = automationComposition.getElements().values().stream()
123 .filter(acElement -> !lockState.equals(acElement.getLockState())).findAny();
124 if (checkOpt.isEmpty()) {
125 automationComposition.setLockState(lockState);
129 // Update states of AutomationCompositionElement in elementsOnThisParticipant
130 var acElement = elementsOnThisParticipant.get(id);
131 if (acElement != null) {
132 var automationCompositionStateChangeAck =
133 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
134 automationCompositionStateChangeAck.setParticipantId(participantId);
135 automationCompositionStateChangeAck.setAutomationCompositionId(automationCompositionId);
136 acElement.setDeployState(deployState);
137 acElement.setLockState(lockState);
138 acElement.setUseState(getUseState(automationCompositionId, id));
139 acElement.setOperationalState(getOperationalState(automationCompositionId, id));
140 automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
141 new AcElementDeployAck(deployState, lockState,
142 acElement.getOperationalState(), acElement.getUseState(), true,
143 "Automation composition element {} state changed to {}\", id, newState)"));
144 LOGGER.debug("Automation composition element {} state changed to {}", id, deployState);
145 automationCompositionStateChangeAck
146 .setMessage("AutomationCompositionElement state changed to {} " + deployState);
147 automationCompositionStateChangeAck.setResult(true);
148 publisher.sendAutomationCompositionAck(automationCompositionStateChangeAck);
153 * Handle a automation composition state change message.
155 * @param stateChangeMsg the state change message
156 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
158 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg,
159 List<AutomationCompositionElementDefinition> acElementDefinitions) {
160 if (stateChangeMsg.getAutomationCompositionId() == null) {
164 var automationComposition = automationCompositionMap.get(stateChangeMsg.getAutomationCompositionId());
166 if (automationComposition == null) {
167 var automationCompositionAck =
168 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
169 automationCompositionAck.setParticipantId(participantId);
170 automationCompositionAck.setMessage("Automation composition " + stateChangeMsg.getAutomationCompositionId()
171 + " does not use this participant " + participantId);
172 automationCompositionAck.setResult(false);
173 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
174 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
175 publisher.sendAutomationCompositionAck(automationCompositionAck);
176 LOGGER.debug("Automation composition {} does not use this participant",
177 stateChangeMsg.getAutomationCompositionId());
181 if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
182 stateChangeMsg.getLockOrderedState())) {
183 var automationCompositionAck =
184 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
185 automationCompositionAck.setParticipantId(participantId);
186 automationCompositionAck.setMessage("Automation composition is already in state "
187 + stateChangeMsg.getDeployOrderedState() + " and " + stateChangeMsg.getLockOrderedState());
188 automationCompositionAck.setResult(false);
189 automationCompositionAck.setAutomationCompositionId(automationComposition.getInstanceId());
190 publisher.sendAutomationCompositionAck(automationCompositionAck);
194 if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
195 handleLockOrderState(automationComposition, stateChangeMsg.getLockOrderedState(),
196 stateChangeMsg.getStartPhase(), acElementDefinitions);
198 handleDeployOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
199 stateChangeMsg.getStartPhase(), acElementDefinitions);
203 private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
204 LockOrder lockOrder) {
205 return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
206 automationComposition.getLockState()) != null;
210 * Method to handle state changes.
212 * @param automationComposition participant response
213 * @param orderedState automation composition ordered state
214 * @param startPhaseMsg startPhase from message
215 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
217 private void handleDeployOrderState(final AutomationComposition automationComposition, DeployOrder orderedState,
218 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
220 if (DeployOrder.UNDEPLOY.equals(orderedState)) {
221 handleUndeployState(automationComposition, startPhaseMsg, acElementDefinitions);
223 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
228 * Method to handle state changes.
230 * @param automationComposition participant response
231 * @param orderedState automation composition ordered state
232 * @param startPhaseMsg startPhase from message
233 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
235 private void handleLockOrderState(final AutomationComposition automationComposition, LockOrder orderedState,
236 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
238 switch (orderedState) {
240 handleLockState(automationComposition, startPhaseMsg, acElementDefinitions);
243 handleUnlockState(automationComposition, startPhaseMsg, acElementDefinitions);
246 LOGGER.debug("StateChange message has no state, state is null {}", automationComposition.getKey());
252 * Handle a automation composition Deploy message.
254 * @param updateMsg the Deploy message
255 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
257 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy updateMsg,
258 List<AutomationCompositionElementDefinition> acElementDefinitions) {
260 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
261 LOGGER.warn("No AutomationCompositionElement updates in message {}",
262 updateMsg.getAutomationCompositionId());
266 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
267 if (participantId.equals(participantDeploy.getParticipantId())) {
268 if (updateMsg.isFirstStartPhase()) {
269 initializeDeploy(updateMsg.getMessageId(), updateMsg.getAutomationCompositionId(),
272 callParticipanDeploy(participantDeploy.getAcElementList(), acElementDefinitions,
273 updateMsg.getStartPhase(), updateMsg.getAutomationCompositionId());
278 private void initializeDeploy(UUID messageId, UUID instanceId, ParticipantDeploy participantDeploy) {
279 var automationComposition = automationCompositionMap.get(instanceId);
281 if (automationComposition != null) {
282 var automationCompositionUpdateAck =
283 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
284 automationCompositionUpdateAck.setParticipantId(participantId);
286 automationCompositionUpdateAck.setMessage(
287 "Automation composition " + instanceId + " already defined on participant " + participantId);
288 automationCompositionUpdateAck.setResult(false);
289 automationCompositionUpdateAck.setResponseTo(messageId);
290 automationCompositionUpdateAck.setAutomationCompositionId(instanceId);
291 publisher.sendAutomationCompositionAck(automationCompositionUpdateAck);
295 automationComposition = new AutomationComposition();
296 automationComposition.setInstanceId(instanceId);
297 var acElements = storeElementsOnThisParticipant(participantDeploy);
298 automationComposition.setElements(prepareAcElementMap(acElements));
299 automationCompositionMap.put(instanceId, automationComposition);
302 private void callParticipanDeploy(List<AcElementDeploy> acElements,
303 List<AutomationCompositionElementDefinition> acElementDefinitions, Integer startPhaseMsg,
304 UUID automationCompositionId) {
306 for (var element : acElements) {
307 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, element.getDefinition());
308 if (acElementNodeTemplate != null) {
309 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
310 if (startPhaseMsg.equals(startPhase)) {
311 for (var acElementListener : listeners) {
312 var map = new HashMap<>(acElementNodeTemplate.getProperties());
313 map.putAll(element.getProperties());
314 acElementListener.deploy(automationCompositionId, element, map);
319 } catch (PfModelException e) {
320 LOGGER.debug("Automation composition element update failed {}", automationCompositionId);
325 private ToscaNodeTemplate getAcElementNodeTemplate(
326 List<AutomationCompositionElementDefinition> acElementDefinitions, ToscaConceptIdentifier acElementDefId) {
328 for (var acElementDefinition : acElementDefinitions) {
329 if (acElementDefId.getName().contains(acElementDefinition.getAcElementDefinitionId().getName())) {
330 return acElementDefinition.getAutomationCompositionElementToscaNodeTemplate();
336 private List<AutomationCompositionElement> storeElementsOnThisParticipant(ParticipantDeploy participantDeploy) {
337 List<AutomationCompositionElement> acElementList = new ArrayList<>();
338 for (var element : participantDeploy.getAcElementList()) {
339 var acElement = new AutomationCompositionElement();
340 acElement.setId(element.getId());
341 acElement.setParticipantId(participantDeploy.getParticipantId());
342 acElement.setDefinition(element.getDefinition());
343 acElement.setDeployState(DeployState.DEPLOYING);
344 acElement.setLockState(LockState.NONE);
345 elementsOnThisParticipant.put(element.getId(), acElement);
346 acElementList.add(acElement);
348 return acElementList;
351 private Map<UUID, AutomationCompositionElement> prepareAcElementMap(List<AutomationCompositionElement> acElements) {
352 Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
353 for (var element : acElements) {
354 acElementMap.put(element.getId(), element);
360 * Method to handle when the new state from participant is UNINITIALISED state.
362 * @param automationComposition participant response
363 * @param startPhaseMsg startPhase from message
364 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
366 private void handleUndeployState(final AutomationComposition automationComposition, Integer startPhaseMsg,
367 List<AutomationCompositionElementDefinition> acElementDefinitions) {
369 automationComposition.getElements().values().stream()
370 .forEach(acElement -> automationCompositionElementUndeploy(automationComposition.getInstanceId(),
371 acElement, startPhaseMsg, acElementDefinitions));
373 boolean isAllUninitialised = automationComposition.getElements().values().stream()
374 .filter(element -> !DeployState.UNDEPLOYED.equals(element.getDeployState())).findAny().isEmpty();
375 if (isAllUninitialised) {
376 automationCompositionMap.remove(automationComposition.getInstanceId());
377 automationComposition.getElements().values()
378 .forEach(element -> elementsOnThisParticipant.remove(element.getId()));
383 * Method to handle when the new state from participant is PASSIVE state.
385 * @param automationComposition participant response
386 * @param startPhaseMsg startPhase from message
387 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
389 private void handleLockState(final AutomationComposition automationComposition, Integer startPhaseMsg,
390 List<AutomationCompositionElementDefinition> acElementDefinitions) {
391 automationComposition.getElements().values().stream()
392 .forEach(acElement -> automationCompositionElementLock(automationComposition.getInstanceId(), acElement,
393 startPhaseMsg, acElementDefinitions));
397 * Method to handle when the new state from participant is RUNNING state.
399 * @param automationComposition participant response
400 * @param startPhaseMsg startPhase from message
401 * @param acElementDefinitions the list of AutomationCompositionElementDefinition
403 private void handleUnlockState(final AutomationComposition automationComposition, Integer startPhaseMsg,
404 List<AutomationCompositionElementDefinition> acElementDefinitions) {
405 automationComposition.getElements().values().stream()
406 .forEach(acElement -> automationCompositionElementUnlock(automationComposition.getInstanceId(),
407 acElement, startPhaseMsg, acElementDefinitions));
410 private void automationCompositionElementLock(UUID instanceId, 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.lock(instanceId, acElement.getId());
419 updateAutomationCompositionElementState(instanceId, acElement.getId(), DeployState.DEPLOYED,
421 } catch (PfModelException e) {
422 LOGGER.error("Automation composition element lock failed {}", instanceId);
429 private void automationCompositionElementUnlock(UUID instanceId, AutomationCompositionElement acElement,
430 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
431 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
432 if (acElementNodeTemplate != null) {
433 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
434 if (startPhaseMsg.equals(startPhase)) {
435 for (var acElementListener : listeners) {
437 acElementListener.unlock(instanceId, acElement.getId());
438 updateAutomationCompositionElementState(instanceId, acElement.getId(), DeployState.DEPLOYED,
440 } catch (PfModelException e) {
441 LOGGER.error("Automation composition element unlock failed {}", instanceId);
448 private void automationCompositionElementUndeploy(UUID instanceId, AutomationCompositionElement acElement,
449 Integer startPhaseMsg, List<AutomationCompositionElementDefinition> acElementDefinitions) {
450 var acElementNodeTemplate = getAcElementNodeTemplate(acElementDefinitions, acElement.getDefinition());
451 if (acElementNodeTemplate != null) {
452 int startPhase = ParticipantUtils.findStartPhase(acElementNodeTemplate.getProperties());
453 if (startPhaseMsg.equals(startPhase)) {
454 for (var acElementListener : listeners) {
456 acElementListener.undeploy(instanceId, acElement.getId());
457 } catch (PfModelException e) {
458 LOGGER.error("Automation composition element update failed {}", instanceId);
468 * @param instanceId the instance Id
469 * @param acElementId the Automation Composition Element Id
470 * @return the UseState of the Automation Composition Element
472 public String getUseState(UUID instanceId, UUID acElementId) {
473 for (var acElementListener : listeners) {
475 return acElementListener.getUseState(instanceId, acElementId);
476 } catch (PfModelException e) {
477 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
484 * Get OperationalState.
486 * @param instanceId the instance Id
487 * @param acElementId the Automation Composition Element Id
488 * @return the OperationalState of the Automation Composition Element
490 public String getOperationalState(UUID instanceId, UUID acElementId) {
491 for (var acElementListener : listeners) {
493 return acElementListener.getOperationalState(instanceId, acElementId);
494 } catch (PfModelException e) {
495 LOGGER.error("Automation composition element get Use State failed {}", acElementId);