2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2021-2024 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.List;
28 import java.util.UUID;
29 import java.util.stream.Collectors;
30 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionDto;
31 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
32 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
33 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
34 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
35 import org.onap.policy.clamp.models.acm.concepts.AcTypeState;
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.ParticipantRestartAc;
43 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
44 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
45 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
46 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
47 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
48 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
49 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
50 import org.onap.policy.clamp.models.acm.messages.kafka.participant.PropertiesUpdate;
51 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.DeployOrder;
52 import org.onap.policy.clamp.models.acm.messages.rest.instantiation.LockOrder;
53 import org.onap.policy.clamp.models.acm.persistence.provider.AcInstanceStateResolver;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56 import org.springframework.stereotype.Component;
59 * This class is responsible for managing the state of all automation compositions in the participant.
62 public class AutomationCompositionHandler {
63 private static final Logger LOGGER = LoggerFactory.getLogger(AutomationCompositionHandler.class);
65 private final CacheProvider cacheProvider;
66 private final ParticipantMessagePublisher publisher;
67 private final ThreadHandler listener;
68 private final AcInstanceStateResolver acInstanceStateResolver;
71 * Constructor, set the participant ID and messageSender.
73 * @param cacheProvider the Cache Provider
74 * @param publisher the ParticipantMessage Publisher
75 * @param listener the ThreadHandler Listener
77 public AutomationCompositionHandler(CacheProvider cacheProvider, ParticipantMessagePublisher publisher,
78 ThreadHandler listener) {
79 this.cacheProvider = cacheProvider;
80 this.publisher = publisher;
81 this.listener = listener;
82 this.acInstanceStateResolver = new AcInstanceStateResolver();
86 * Handle a automation composition state change message.
88 * @param stateChangeMsg the state change message
90 public void handleAutomationCompositionStateChange(AutomationCompositionStateChange stateChangeMsg) {
91 if (stateChangeMsg.getAutomationCompositionId() == null) {
95 var automationComposition = cacheProvider.getAutomationComposition(stateChangeMsg.getAutomationCompositionId());
97 if (automationComposition == null) {
98 if (DeployOrder.DELETE.equals(stateChangeMsg.getDeployOrderedState())) {
99 var automationCompositionAck = new AutomationCompositionDeployAck(
100 ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
101 automationCompositionAck.setParticipantId(cacheProvider.getParticipantId());
102 automationCompositionAck.setMessage("Already deleted or never used");
103 automationCompositionAck.setResult(true);
104 automationCompositionAck.setStateChangeResult(StateChangeResult.NO_ERROR);
105 automationCompositionAck.setResponseTo(stateChangeMsg.getMessageId());
106 automationCompositionAck.setAutomationCompositionId(stateChangeMsg.getAutomationCompositionId());
107 publisher.sendAutomationCompositionAck(automationCompositionAck);
109 LOGGER.debug("Automation composition {} does not use this participant",
110 stateChangeMsg.getAutomationCompositionId());
115 if (!checkConsistantOrderState(automationComposition, stateChangeMsg.getDeployOrderedState(),
116 stateChangeMsg.getLockOrderedState())) {
117 LOGGER.warn("Not Consistant OrderState Automation composition {}",
118 stateChangeMsg.getAutomationCompositionId());
122 if (DeployOrder.NONE.equals(stateChangeMsg.getDeployOrderedState())) {
123 handleLockOrderState(stateChangeMsg.getMessageId(), automationComposition,
124 stateChangeMsg.getLockOrderedState(), stateChangeMsg.getStartPhase());
126 handleDeployOrderState(stateChangeMsg.getMessageId(), automationComposition,
127 stateChangeMsg.getDeployOrderedState(), stateChangeMsg.getStartPhase());
131 private boolean checkConsistantOrderState(AutomationComposition automationComposition, DeployOrder deployOrder,
132 LockOrder lockOrder) {
133 if (DeployOrder.UPDATE.equals(deployOrder)) {
136 return acInstanceStateResolver.resolve(deployOrder, lockOrder, automationComposition.getDeployState(),
137 automationComposition.getLockState(), automationComposition.getStateChangeResult()) != null;
141 * Method to handle state changes.
143 * @param messageId the messageId
144 * @param automationComposition participant response
145 * @param orderedState automation composition ordered state
146 * @param startPhaseMsg startPhase from message
148 private void handleDeployOrderState(UUID messageId, final AutomationComposition automationComposition,
149 DeployOrder orderedState, Integer startPhaseMsg) {
151 switch (orderedState) {
153 handleUndeployState(messageId, automationComposition, startPhaseMsg);
156 handleDeleteState(messageId, automationComposition, startPhaseMsg);
160 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
166 * Method to handle state changes.
168 * @param messageId the messageId
169 * @param automationComposition participant response
170 * @param orderedState automation composition ordered state
171 * @param startPhaseMsg startPhase from message
173 private void handleLockOrderState(UUID messageId, final AutomationComposition automationComposition,
174 LockOrder orderedState, Integer startPhaseMsg) {
176 switch (orderedState) {
178 handleLockState(messageId, automationComposition, startPhaseMsg);
181 handleUnlockState(messageId, automationComposition, startPhaseMsg);
184 LOGGER.error("StateChange message has no state, state is null {}", automationComposition.getKey());
190 * Handle a automation composition properties update message.
192 * @param updateMsg the properties update message
194 public void handleAcPropertyUpdate(PropertiesUpdate updateMsg) {
196 if (updateMsg.getParticipantUpdatesList().isEmpty()) {
197 LOGGER.warn("No AutomationCompositionElement updates in message {}",
198 updateMsg.getAutomationCompositionId());
202 for (var participantDeploy : updateMsg.getParticipantUpdatesList()) {
203 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
205 var acCopy = new AutomationComposition(cacheProvider.getAutomationComposition(
206 updateMsg.getAutomationCompositionId()));
207 updateExistingElementsOnThisParticipant(updateMsg.getAutomationCompositionId(), participantDeploy,
208 DeployState.UPDATING);
210 callParticipantUpdateProperty(updateMsg.getMessageId(), participantDeploy.getAcElementList(), acCopy);
216 * Handle a automation composition Deploy message.
218 * @param deployMsg the Deploy message
220 public void handleAutomationCompositionDeploy(AutomationCompositionDeploy deployMsg) {
222 if (deployMsg.getParticipantUpdatesList().isEmpty()) {
223 LOGGER.warn("No AutomationCompositionElement deploy in message {}", deployMsg.getAutomationCompositionId());
227 for (var participantDeploy : deployMsg.getParticipantUpdatesList()) {
228 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
229 if (deployMsg.isFirstStartPhase()) {
230 cacheProvider.initializeAutomationComposition(deployMsg.getCompositionId(),
231 deployMsg.getAutomationCompositionId(), participantDeploy);
233 callParticipanDeploy(deployMsg.getMessageId(), participantDeploy.getAcElementList(),
234 deployMsg.getStartPhase(), deployMsg.getAutomationCompositionId());
239 private void callParticipanDeploy(UUID messageId, List<AcElementDeploy> acElementDeployList,
240 Integer startPhaseMsg, UUID instanceId) {
241 var automationComposition = cacheProvider.getAutomationComposition(instanceId);
242 for (var elementDeploy : acElementDeployList) {
243 var element = automationComposition.getElements().get(elementDeploy.getId());
244 var compositionInProperties = cacheProvider
245 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
246 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
247 if (startPhaseMsg.equals(startPhase)) {
248 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
249 element, compositionInProperties);
250 var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
251 elementDeploy.getToscaServiceTemplateFragment(),
252 elementDeploy.getProperties(), element.getOutProperties());
253 listener.deploy(messageId, compositionElement, instanceElement);
258 private CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element,
259 Map<String, Object> compositionInProperties) {
260 var compositionOutProperties = cacheProvider.getAcElementsDefinitions()
261 .get(compositionId).get(element.getDefinition()).getOutProperties();
262 return new CompositionElementDto(compositionId,
263 element.getDefinition(), compositionInProperties, compositionOutProperties);
266 private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
267 UUID compositionId) {
268 Map<UUID, CompositionElementDto> map = new HashMap<>();
269 for (var element : automationComposition.getElements().values()) {
270 var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
271 var compositionElement = createCompositionElementDto(compositionId, element, compositionInProperties);
272 map.put(element.getId(), compositionElement);
277 private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
278 return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
281 private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
282 Map<UUID, InstanceElementDto> map = new HashMap<>();
283 for (var element : automationComposition.getElements().values()) {
284 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
285 null, element.getProperties(), element.getOutProperties());
286 map.put(element.getId(), instanceElement);
291 private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
292 AutomationComposition acCopy) {
293 var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
294 var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
295 cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
296 var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
297 for (var acElement : acElements) {
298 listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
299 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
303 private void updateExistingElementsOnThisParticipant(UUID instanceId, ParticipantDeploy participantDeploy,
304 DeployState deployState) {
305 var acElementList = cacheProvider.getAutomationComposition(instanceId).getElements();
306 for (var element : participantDeploy.getAcElementList()) {
307 var acElement = acElementList.get(element.getId());
308 acElement.getProperties().putAll(element.getProperties());
309 acElement.setDeployState(deployState);
310 acElement.setDefinition(element.getDefinition());
315 * Method to handle when the new state from participant is UNINITIALISED state.
317 * @param messageId the messageId
318 * @param automationComposition participant response
319 * @param startPhaseMsg startPhase from message
321 private void handleUndeployState(UUID messageId, final AutomationComposition automationComposition,
322 Integer startPhaseMsg) {
323 automationComposition.setCompositionTargetId(null);
324 for (var element : automationComposition.getElements().values()) {
325 var compositionInProperties = cacheProvider
326 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
327 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
328 if (startPhaseMsg.equals(startPhase)) {
329 element.setDeployState(DeployState.UNDEPLOYING);
330 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
331 element, compositionInProperties);
332 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
333 null, element.getProperties(), element.getOutProperties());
334 listener.undeploy(messageId, compositionElement, instanceElement);
339 private void handleDeleteState(UUID messageId, final AutomationComposition automationComposition,
340 Integer startPhaseMsg) {
341 for (var element : automationComposition.getElements().values()) {
342 var compositionInProperties = cacheProvider
343 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
344 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
345 if (startPhaseMsg.equals(startPhase)) {
346 element.setDeployState(DeployState.DELETING);
347 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
348 element, compositionInProperties);
349 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
350 null, element.getProperties(), element.getOutProperties());
351 listener.delete(messageId, compositionElement, instanceElement);
357 * Method to handle when the new state from participant is PASSIVE state.
359 * @param messageId the messageId
360 * @param automationComposition participant response
361 * @param startPhaseMsg startPhase from message
363 private void handleLockState(UUID messageId, final AutomationComposition automationComposition,
364 Integer startPhaseMsg) {
365 for (var element : automationComposition.getElements().values()) {
366 var compositionInProperties = cacheProvider
367 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
368 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
369 if (startPhaseMsg.equals(startPhase)) {
370 element.setLockState(LockState.LOCKING);
371 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
372 element, compositionInProperties);
373 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
374 null, element.getProperties(), element.getOutProperties());
375 listener.lock(messageId, compositionElement, instanceElement);
381 * Method to handle when the new state from participant is RUNNING state.
383 * @param messageId the messageId
384 * @param automationComposition participant response
385 * @param startPhaseMsg startPhase from message
387 private void handleUnlockState(UUID messageId, final AutomationComposition automationComposition,
388 Integer startPhaseMsg) {
389 for (var element : automationComposition.getElements().values()) {
390 var compositionInProperties = cacheProvider
391 .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
392 int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
393 if (startPhaseMsg.equals(startPhase)) {
394 element.setLockState(LockState.UNLOCKING);
395 var compositionElement = createCompositionElementDto(automationComposition.getCompositionId(),
396 element, compositionInProperties);
397 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
398 null, element.getProperties(), element.getOutProperties());
399 listener.unlock(messageId, compositionElement, instanceElement);
405 * Handles prime a Composition Definition.
407 * @param messageId the messageId
408 * @param compositionId the compositionId
409 * @param list the list of AutomationCompositionElementDefinition
411 public void prime(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list) {
412 var inPropertiesMap = list.stream().collect(Collectors.toMap(
413 AutomationCompositionElementDefinition::getAcElementDefinitionId,
414 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
415 var outPropertiesMap = list.stream().collect(Collectors.toMap(
416 AutomationCompositionElementDefinition::getAcElementDefinitionId,
417 AutomationCompositionElementDefinition::getOutProperties));
418 listener.prime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
422 * Handles deprime a Composition Definition.
424 * @param messageId the messageId
425 * @param compositionId the compositionId
427 public void deprime(UUID messageId, UUID compositionId) {
428 var acElementsDefinitions = cacheProvider.getAcElementsDefinitions().get(compositionId);
429 if (acElementsDefinitions == null) {
430 // this participant does not handle this composition
433 var list = new ArrayList<>(acElementsDefinitions.values());
434 var inPropertiesMap = list.stream().collect(Collectors.toMap(
435 AutomationCompositionElementDefinition::getAcElementDefinitionId,
436 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
437 var outPropertiesMap = list.stream().collect(Collectors.toMap(
438 AutomationCompositionElementDefinition::getAcElementDefinitionId,
439 AutomationCompositionElementDefinition::getOutProperties));
440 listener.deprime(messageId, new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap));
444 * Handles restarted scenario.
446 * @param messageId the messageId
447 * @param compositionId the compositionId
448 * @param list the list of AutomationCompositionElementDefinition
449 * @param state the state of the composition
450 * @param automationCompositionList list of ParticipantRestartAc
452 public void restarted(UUID messageId, UUID compositionId, List<AutomationCompositionElementDefinition> list,
453 AcTypeState state, List<ParticipantRestartAc> automationCompositionList) {
455 for (var automationcomposition : automationCompositionList) {
456 cacheProvider.initializeAutomationComposition(compositionId, automationcomposition);
458 var inPropertiesMap = list.stream().collect(Collectors.toMap(
459 AutomationCompositionElementDefinition::getAcElementDefinitionId,
460 el -> el.getAutomationCompositionElementToscaNodeTemplate().getProperties()));
461 var outPropertiesMap = list.stream().collect(Collectors.toMap(
462 AutomationCompositionElementDefinition::getAcElementDefinitionId,
463 AutomationCompositionElementDefinition::getOutProperties));
464 var composition = new CompositionDto(compositionId, inPropertiesMap, outPropertiesMap);
465 listener.restarted(messageId, composition, state, automationCompositionList);
469 * Handles AutomationComposition Migration.
471 * @param migrationMsg the AutomationCompositionMigration
473 public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
474 if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
478 var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
479 if (automationComposition == null) {
480 LOGGER.debug("Automation composition {} does not use this participant",
481 migrationMsg.getAutomationCompositionId());
484 var acCopy = new AutomationComposition(automationComposition);
485 automationComposition.setCompositionTargetId(migrationMsg.getCompositionTargetId());
486 for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
487 if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
489 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
490 DeployState.MIGRATING);
492 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
493 acCopy, migrationMsg.getCompositionTargetId());
498 private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
499 AutomationComposition acCopy, UUID compositionTargetId) {
500 var compositionElementMap = getCompositionElementDtoMap(acCopy);
501 var instanceElementMap = getInstanceElementDtoMap(acCopy);
502 var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
503 var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
504 var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
506 for (var acElement : acElements) {
507 listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
508 compositionElementTargetMap.get(acElement.getId()),
509 instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));