Add support for Prepare, Review and Migrate pre-check in intermediary 66/138566/2
authorFrancescoFioraEst <francesco.fiora@est.tech>
Fri, 19 Jan 2024 14:29:25 +0000 (14:29 +0000)
committerFrancescoFioraEst <francesco.fiora@est.tech>
Wed, 24 Jul 2024 15:19:31 +0000 (16:19 +0100)
Add support for Prepare, Review and Migrate pre-check
in ACM intermediary.

Issue-ID: POLICY-5080
Change-Id: I08045a8eb01dcea6492aac12b7a8021a47ae19e8
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
13 files changed:
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPrepareListener.java [new file with mode: 0644]
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcLockHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java [new file with mode: 0644]
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProvider.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandler.java
participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandler.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java [new file with mode: 0644]
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionOutHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/CacheProviderTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ParticipantHandlerTest.java
participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/ThreadHandlerTest.java

diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPrepareListener.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/comm/AcPrepareListener.java
new file mode 100644 (file)
index 0000000..0f6d2c0
--- /dev/null
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.intermediary.comm;
+
+import org.onap.policy.clamp.acm.participant.intermediary.handler.ParticipantHandler;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
+import org.springframework.stereotype.Component;
+
+@Component
+public class AcPrepareListener extends ParticipantListener<AutomationCompositionPrepare> {
+
+    /**
+     * Constructs the object.
+     *
+     * @param participantHandler the handler for managing the state of the participant
+     */
+    public AcPrepareListener(final ParticipantHandler participantHandler) {
+        super(AutomationCompositionPrepare.class, participantHandler,
+            participantHandler::handleAutomationCompositionPrepare);
+    }
+
+
+    @Override
+    public String getType() {
+        return ParticipantMessageType.AUTOMATION_COMPOSITION_PREPARE.name();
+    }
+}
index 89daa5f..95613cc 100644 (file)
@@ -27,6 +27,7 @@ import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -79,6 +80,7 @@ public class AcLockHandler {
             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
             if (startPhaseMsg.equals(startPhase)) {
                 element.setLockState(LockState.LOCKING);
+                element.setSubState(SubState.NONE);
                 var compositionElement = cacheProvider.createCompositionElementDto(
                         automationComposition.getCompositionId(), element, compositionInProperties);
                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
@@ -99,6 +101,7 @@ public class AcLockHandler {
             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
             if (startPhaseMsg.equals(startPhase)) {
                 element.setLockState(LockState.UNLOCKING);
+                element.setSubState(SubState.NONE);
                 var compositionElement = cacheProvider.createCompositionElementDto(
                         automationComposition.getCompositionId(), element, compositionInProperties);
                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandler.java
new file mode 100644 (file)
index 0000000..3044080
--- /dev/null
@@ -0,0 +1,152 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.intermediary.handler;
+
+import java.util.List;
+import java.util.UUID;
+import lombok.RequiredArgsConstructor;
+import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
+import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
+import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
+import org.onap.policy.clamp.models.acm.concepts.DeployState;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare;
+import org.onap.policy.clamp.models.acm.utils.AcmUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class AcSubStateHandler {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(AcSubStateHandler.class);
+
+    private final CacheProvider cacheProvider;
+    private final ThreadHandler listener;
+
+    /**
+     * Handles AutomationComposition Migration Precheck.
+     *
+     * @param migrationMsg the AutomationCompositionMigration
+     */
+    public void handleAcMigrationPrecheck(AutomationCompositionMigration migrationMsg) {
+        if (migrationMsg.getAutomationCompositionId() == null || migrationMsg.getCompositionTargetId() == null) {
+            return;
+        }
+
+        var automationComposition = cacheProvider.getAutomationComposition(migrationMsg.getAutomationCompositionId());
+        if (automationComposition == null) {
+            LOGGER.debug("Automation composition {} does not use this participant",
+                    migrationMsg.getAutomationCompositionId());
+            return;
+        }
+        automationComposition.setSubState(SubState.MIGRATION_PRECHECKING);
+        for (var participantDeploy : migrationMsg.getParticipantUpdatesList()) {
+            if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
+
+                callParticipantMigratePrecheck(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
+                    automationComposition, migrationMsg.getCompositionTargetId());
+            }
+        }
+    }
+
+    private void callParticipantMigratePrecheck(UUID messageId, List<AcElementDeploy> acElements,
+            AutomationComposition automationComposition, UUID compositionTargetId) {
+        var compositionElementMap = cacheProvider.getCompositionElementDtoMap(automationComposition);
+        var instanceElementMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
+        var acElementList = automationComposition.getElements();
+        for (var acElement : acElements) {
+            var element = acElementList.get(acElement.getId());
+            element.setSubState(SubState.MIGRATION_PRECHECKING);
+        }
+        var acCopyMigrateTo = new AutomationComposition(automationComposition);
+        acElementList = acCopyMigrateTo.getElements();
+        for (var acElement : acElements) {
+            var element = acElementList.get(acElement.getId());
+            AcmUtils.recursiveMerge(element.getProperties(), acElement.getProperties());
+            element.setDefinition(acElement.getDefinition());
+        }
+
+        var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(acCopyMigrateTo,
+            compositionTargetId);
+        var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(acCopyMigrateTo);
+
+        for (var acElement : acElements) {
+            listener.migratePrecheck(messageId, compositionElementMap.get(acElement.getId()),
+                compositionElementTargetMap.get(acElement.getId()),
+                instanceElementMap.get(acElement.getId()), instanceElementMigrateMap.get(acElement.getId()));
+        }
+    }
+
+    /**
+     * Handle AutomationComposition Prepare message.
+     *
+     * @param acPrepareMsg the AutomationCompositionPrepare message
+     */
+    public void handleAcPrepare(AutomationCompositionPrepare acPrepareMsg) {
+        if (acPrepareMsg.isPreDeploy()) {
+            for (var participantPrepare : acPrepareMsg.getParticipantList()) {
+                if (cacheProvider.getParticipantId().equals(participantPrepare.getParticipantId())) {
+                    cacheProvider.initializeAutomationComposition(acPrepareMsg.getCompositionId(),
+                        acPrepareMsg.getAutomationCompositionId(), participantPrepare, DeployState.UNDEPLOYED,
+                        SubState.PREPARING);
+                    callParticipanPrepare(acPrepareMsg.getMessageId(), participantPrepare.getAcElementList(),
+                        acPrepareMsg.getAutomationCompositionId());
+                }
+            }
+        } else {
+            var automationComposition =
+                cacheProvider.getAutomationComposition(acPrepareMsg.getAutomationCompositionId());
+            automationComposition.setSubState(SubState.REVIEWING);
+            callParticipanReview(acPrepareMsg.getMessageId(), automationComposition);
+        }
+    }
+
+    private void callParticipanPrepare(UUID messageId, List<AcElementDeploy> acElementList, UUID instanceId) {
+        var automationComposition = cacheProvider.getAutomationComposition(instanceId);
+        for (var elementDeploy : acElementList) {
+            var element = automationComposition.getElements().get(elementDeploy.getId());
+            var compositionInProperties = cacheProvider
+                .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
+            var compositionElement = cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(),
+                element, compositionInProperties);
+            var instanceElement = new InstanceElementDto(instanceId, elementDeploy.getId(),
+                elementDeploy.getToscaServiceTemplateFragment(),
+                elementDeploy.getProperties(), element.getOutProperties());
+            listener.prepare(messageId, compositionElement, instanceElement);
+        }
+    }
+
+    private void callParticipanReview(UUID messageId, AutomationComposition automationComposition) {
+        for (var element : automationComposition.getElements().values()) {
+            var compositionInProperties = cacheProvider
+                .getCommonProperties(automationComposition.getCompositionId(), element.getDefinition());
+            element.setSubState(SubState.REVIEWING);
+            var compositionElement = cacheProvider.createCompositionElementDto(automationComposition.getCompositionId(),
+                element, compositionInProperties);
+            var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
+                null, element.getProperties(), element.getOutProperties());
+            listener.review(messageId, compositionElement, instanceElement);
+        }
+    }
+}
index b60d637..0f230c0 100644 (file)
 
 package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
-import java.util.HashMap;
 import java.util.List;
-import java.util.Map;
 import java.util.UUID;
 import lombok.RequiredArgsConstructor;
-import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
 import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
 import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessagePublisher;
 import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
@@ -35,6 +32,7 @@ import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantUtils;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
@@ -171,40 +169,12 @@ public class AutomationCompositionHandler {
         }
     }
 
-    private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
-        UUID compositionId) {
-        Map<UUID, CompositionElementDto> map = new HashMap<>();
-        for (var element : automationComposition.getElements().values()) {
-            var compositionInProperties = cacheProvider.getCommonProperties(compositionId, element.getDefinition());
-            var compositionElement = cacheProvider
-                    .createCompositionElementDto(compositionId, element, compositionInProperties);
-            map.put(element.getId(), compositionElement);
-        }
-        return map;
-    }
-
-    private Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
-        return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
-    }
-
-    private Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
-        Map<UUID, InstanceElementDto> map = new HashMap<>();
-        var serviceTemplateFragment = cacheProvider
-                .getServiceTemplateFragmentMap().get(automationComposition.getCompositionId());
-        for (var element : automationComposition.getElements().values()) {
-            var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
-                    serviceTemplateFragment, element.getProperties(), element.getOutProperties());
-            map.put(element.getId(), instanceElement);
-        }
-        return map;
-    }
-
     private void callParticipantUpdateProperty(UUID messageId, List<AcElementDeploy> acElements,
         AutomationComposition acCopy) {
-        var instanceElementDtoMap = getInstanceElementDtoMap(acCopy);
-        var instanceElementDtoMapUpdated = getInstanceElementDtoMap(
+        var instanceElementDtoMap = cacheProvider.getInstanceElementDtoMap(acCopy);
+        var instanceElementDtoMapUpdated = cacheProvider.getInstanceElementDtoMap(
             cacheProvider.getAutomationComposition(acCopy.getInstanceId()));
-        var compositionElementDtoMap = getCompositionElementDtoMap(acCopy);
+        var compositionElementDtoMap = cacheProvider.getCompositionElementDtoMap(acCopy);
         for (var acElement : acElements) {
             listener.update(messageId, compositionElementDtoMap.get(acElement.getId()),
                 instanceElementDtoMap.get(acElement.getId()), instanceElementDtoMapUpdated.get(acElement.getId()));
@@ -218,6 +188,7 @@ public class AutomationCompositionHandler {
             var acElement = acElementList.get(element.getId());
             AcmUtils.recursiveMerge(acElement.getProperties(), element.getProperties());
             acElement.setDeployState(deployState);
+            acElement.setSubState(SubState.NONE);
             acElement.setDefinition(element.getDefinition());
         }
     }
@@ -261,6 +232,7 @@ public class AutomationCompositionHandler {
             int startPhase = ParticipantUtils.findStartPhase(compositionInProperties);
             if (startPhaseMsg.equals(startPhase)) {
                 element.setDeployState(DeployState.DELETING);
+                element.setSubState(SubState.NONE);
                 var compositionElement = cacheProvider.createCompositionElementDto(
                         automationComposition.getCompositionId(), element, compositionInProperties);
                 var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
@@ -293,7 +265,7 @@ public class AutomationCompositionHandler {
             if (cacheProvider.getParticipantId().equals(participantDeploy.getParticipantId())) {
 
                 updateExistingElementsOnThisParticipant(migrationMsg.getAutomationCompositionId(), participantDeploy,
-                        DeployState.MIGRATING);
+                    DeployState.MIGRATING);
 
                 callParticipantMigrate(migrationMsg.getMessageId(), participantDeploy.getAcElementList(),
                     acCopy, migrationMsg.getCompositionTargetId());
@@ -303,11 +275,12 @@ public class AutomationCompositionHandler {
 
     private void callParticipantMigrate(UUID messageId, List<AcElementDeploy> acElements,
             AutomationComposition acCopy, UUID compositionTargetId) {
-        var compositionElementMap = getCompositionElementDtoMap(acCopy);
-        var instanceElementMap = getInstanceElementDtoMap(acCopy);
+        var compositionElementMap = cacheProvider.getCompositionElementDtoMap(acCopy);
+        var instanceElementMap = cacheProvider.getInstanceElementDtoMap(acCopy);
         var automationComposition = cacheProvider.getAutomationComposition(acCopy.getInstanceId());
-        var compositionElementTargetMap = getCompositionElementDtoMap(automationComposition, compositionTargetId);
-        var instanceElementMigrateMap = getInstanceElementDtoMap(automationComposition);
+        var compositionElementTargetMap = cacheProvider.getCompositionElementDtoMap(automationComposition,
+            compositionTargetId);
+        var instanceElementMigrateMap = cacheProvider.getInstanceElementDtoMap(automationComposition);
 
         for (var acElement : acElements) {
             listener.migrate(messageId, compositionElementMap.get(acElement.getId()),
index 1f4c036..06b11bb 100644 (file)
@@ -37,6 +37,7 @@ import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDefinition;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantMessageType;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantPrimeAck;
@@ -93,7 +94,13 @@ public class AutomationCompositionOutHandler {
         }
         element.setRestarting(null);
 
-        if (deployState != null) {
+        if (deployState != null && !SubState.NONE.equals(element.getSubState())) {
+            handleSubState(automationComposition, element);
+            if (!StateChangeResult.NO_ERROR.equals(stateChangeResult)) {
+                stateChangeResult = StateChangeResult.NO_ERROR;
+                LOGGER.warn("SubState has always NO_ERROR result!");
+            }
+        } else if (deployState != null) {
             handleDeployState(automationComposition, element, deployState);
         }
         if (lockState != null) {
@@ -132,6 +139,7 @@ public class AutomationCompositionOutHandler {
             }
             automationComposition.setDeployState(deployState);
             automationComposition.setLockState(element.getLockState());
+            automationComposition.setSubState(SubState.NONE);
 
             if (DeployState.DELETED.equals(deployState)) {
                 cacheProvider.removeAutomationComposition(automationComposition.getInstanceId());
@@ -146,6 +154,16 @@ public class AutomationCompositionOutHandler {
                 .filter(acElement -> !lockState.equals(acElement.getLockState())).findAny();
         if (checkOpt.isEmpty()) {
             automationComposition.setLockState(lockState);
+            automationComposition.setSubState(SubState.NONE);
+        }
+    }
+
+    private void handleSubState(AutomationComposition automationComposition, AutomationCompositionElement element) {
+        element.setSubState(SubState.NONE);
+        var checkOpt = automationComposition.getElements().values().stream()
+                .filter(acElement -> !SubState.NONE.equals(acElement.getSubState())).findAny();
+        if (checkOpt.isEmpty()) {
+            automationComposition.setSubState(SubState.NONE);
         }
     }
 
index 29b77fc..f56fabf 100644 (file)
@@ -30,6 +30,7 @@ import lombok.Getter;
 import lombok.NonNull;
 import lombok.Setter;
 import org.onap.policy.clamp.acm.participant.intermediary.api.CompositionElementDto;
+import org.onap.policy.clamp.acm.participant.intermediary.api.InstanceElementDto;
 import org.onap.policy.clamp.acm.participant.intermediary.parameters.ParticipantParameters;
 import org.onap.policy.clamp.models.acm.concepts.AutomationComposition;
 import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElement;
@@ -39,6 +40,7 @@ import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantRestartAc;
 import org.onap.policy.clamp.models.acm.concepts.ParticipantSupportedElementType;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.models.base.PfUtils;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
@@ -161,6 +163,21 @@ public class CacheProvider {
      */
     public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId,
             ParticipantDeploy participantDeploy) {
+        initializeAutomationComposition(compositionId, instanceId, participantDeploy,
+            DeployState.DEPLOYING, SubState.NONE);
+    }
+
+    /**
+     * Initialize an AutomationComposition from a ParticipantDeploy.
+     *
+     * @param compositionId the composition Id
+     * @param instanceId the Automation Composition Id
+     * @param participantDeploy the ParticipantDeploy
+     * @param deployState the DeployState
+     * @param subState the SubState
+     */
+    public void initializeAutomationComposition(@NonNull UUID compositionId, @NonNull UUID instanceId,
+            ParticipantDeploy participantDeploy, DeployState deployState, SubState subState) {
         var acLast = automationCompositions.get(instanceId);
         Map<UUID, AutomationCompositionElement> acElementMap = new LinkedHashMap<>();
         for (var element : participantDeploy.getAcElementList()) {
@@ -168,8 +185,9 @@ public class CacheProvider {
             acElement.setId(element.getId());
             acElement.setParticipantId(getParticipantId());
             acElement.setDefinition(element.getDefinition());
-            acElement.setDeployState(DeployState.DEPLOYING);
+            acElement.setDeployState(deployState);
             acElement.setLockState(LockState.NONE);
+            acElement.setSubState(subState);
             acElement.setProperties(element.getProperties());
             var acElementLast = acLast != null ? acLast.getElements().get(element.getId()) : null;
             if (acElementLast != null) {
@@ -186,6 +204,8 @@ public class CacheProvider {
         automationComposition.setCompositionId(compositionId);
         automationComposition.setInstanceId(instanceId);
         automationComposition.setElements(acElementMap);
+        automationComposition.setDeployState(deployState);
+        automationComposition.setSubState(subState);
         automationCompositions.put(instanceId, automationComposition);
     }
 
@@ -208,6 +228,7 @@ public class CacheProvider {
             acElement.setDefinition(element.getDefinition());
             acElement.setDeployState(element.getDeployState());
             acElement.setLockState(element.getLockState());
+            acElement.setSubState(SubState.NONE);
             acElement.setOperationalState(element.getOperationalState());
             acElement.setUseState(element.getUseState());
             acElement.setProperties(element.getProperties());
@@ -236,10 +257,49 @@ public class CacheProvider {
      * @return the CompositionElementDto
      */
     public CompositionElementDto createCompositionElementDto(UUID compositionId, AutomationCompositionElement element,
-                                                              Map<String, Object> compositionInProperties) {
+            Map<String, Object> compositionInProperties) {
         var compositionOutProperties = getAcElementsDefinitions()
                 .get(compositionId).get(element.getDefinition()).getOutProperties();
         return new CompositionElementDto(compositionId,
                 element.getDefinition(), compositionInProperties, compositionOutProperties);
     }
+
+    /**
+     * Get a Map of CompositionElementDto by elementId from the elements of an AutomationComposition.
+     *
+     * @param automationComposition the AutomationComposition
+     * @param compositionId the compositionId
+     * @return the Map of CompositionElementDto
+     */
+    public Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition,
+            UUID compositionId) {
+        Map<UUID, CompositionElementDto> map = new HashMap<>();
+        for (var element : automationComposition.getElements().values()) {
+            var compositionInProperties = getCommonProperties(compositionId, element.getDefinition());
+            var compositionElement = createCompositionElementDto(compositionId, element, compositionInProperties);
+            map.put(element.getId(), compositionElement);
+        }
+        return map;
+    }
+
+    public Map<UUID, CompositionElementDto> getCompositionElementDtoMap(AutomationComposition automationComposition) {
+        return getCompositionElementDtoMap(automationComposition, automationComposition.getCompositionId());
+    }
+
+    /**
+     * Get a Map of InstanceElementDto by elementId from the elements of an AutomationComposition.
+     *
+     * @param automationComposition the AutomationComposition
+     * @return the Map of InstanceElementDto
+     */
+    public Map<UUID, InstanceElementDto> getInstanceElementDtoMap(AutomationComposition automationComposition) {
+        Map<UUID, InstanceElementDto> map = new HashMap<>();
+        var serviceTemplateFragment = serviceTemplateFragmentMap.get(automationComposition.getCompositionId());
+        for (var element : automationComposition.getElements().values()) {
+            var instanceElement = new InstanceElementDto(automationComposition.getInstanceId(), element.getId(),
+                    serviceTemplateFragment, element.getProperties(), element.getOutProperties());
+            map.put(element.getId(), instanceElement);
+        }
+        return map;
+    }
 }
index 5ae8f04..6615ae1 100644 (file)
@@ -28,6 +28,7 @@ import org.onap.policy.clamp.acm.participant.intermediary.comm.ParticipantMessag
 import org.onap.policy.clamp.models.acm.concepts.ParticipantState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeploy;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionStateChange;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantAckMessage;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantDeregister;
@@ -55,6 +56,7 @@ public class ParticipantHandler {
 
     private final AutomationCompositionHandler automationCompositionHandler;
     private final AcLockHandler acLockHandler;
+    private final AcSubStateHandler acSubStateHandler;
     private final AcDefinitionHandler acDefinitionHandler;
     private final ParticipantMessagePublisher publisher;
     private final CacheProvider cacheProvider;
@@ -106,7 +108,11 @@ public class ParticipantHandler {
             value = "listener.automation_composition_migration",
             description = "AUTOMATION_COMPOSITION_MIGRATION messages received")
     public void handleAutomationCompositionMigration(AutomationCompositionMigration migrationMsg) {
-        automationCompositionHandler.handleAutomationCompositionMigration(migrationMsg);
+        if (Boolean.TRUE.equals(migrationMsg.getPrecheck())) {
+            acSubStateHandler.handleAcMigrationPrecheck(migrationMsg);
+        } else {
+            automationCompositionHandler.handleAutomationCompositionMigration(migrationMsg);
+        }
     }
 
     /**
@@ -119,6 +125,11 @@ public class ParticipantHandler {
         automationCompositionHandler.handleAcPropertyUpdate(propertyUpdateMsg);
     }
 
+    @Timed(value = "listener.prepare", description = "AUTOMATION_COMPOSITION_PREPARE message received")
+    public void handleAutomationCompositionPrepare(AutomationCompositionPrepare acPrepareMsg) {
+        acSubStateHandler.handleAcPrepare(acPrepareMsg);
+    }
+
     /**
      * Check if a participant message applies to this participant handler.
      *
index 00e0044..e6c2d37 100644 (file)
@@ -319,4 +319,93 @@ public class ThreadHandler implements Closeable {
         }
         executionMap.remove(instanceElement.elementId());
     }
+
+    /**
+     * Handles AutomationComposition Migration Precheck.
+     *
+     * @param messageId the messageId
+     * @param compositionElement the information of the Automation Composition Definition Element
+     * @param compositionElementTarget the information of the Automation Composition Definition Element Target
+     * @param instanceElement the information of the Automation Composition Instance Element
+     * @param instanceElementMigrate the information of the Automation Composition Instance Element updated
+     */
+    public void migratePrecheck(UUID messageId, CompositionElementDto compositionElement,
+        CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
+        InstanceElementDto instanceElementMigrate) {
+        cleanExecution(instanceElement.elementId(), messageId);
+        var result = executor.submit(() ->
+            this.migratePrecheckProcess(compositionElement, compositionElementTarget, instanceElement,
+                instanceElementMigrate));
+        executionMap.put(instanceElement.elementId(), result);
+    }
+
+    private void migratePrecheckProcess(CompositionElementDto compositionElement,
+        CompositionElementDto compositionElementTarget, InstanceElementDto instanceElement,
+        InstanceElementDto instanceElementMigrate) {
+        try {
+            listener.migratePrecheck(compositionElement, compositionElementTarget, instanceElement,
+                instanceElementMigrate);
+        } catch (PfModelException e) {
+            LOGGER.error("Automation composition element migrate precheck failed {} {}",
+                instanceElement.elementId(), e.getMessage());
+            intermediaryApi.updateAutomationCompositionElementState(
+                instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED,
+                null, StateChangeResult.FAILED, "Automation composition element migrate precheck failed");
+        }
+        executionMap.remove(instanceElement.elementId());
+    }
+
+    /**
+     * Handles AutomationComposition Prepare Post Deploy.
+     *
+     * @param messageId the messageId
+     * @param compositionElement the information of the Automation Composition Definition Element
+     * @param instanceElement the information of the Automation Composition Instance Element
+     */
+    public void review(UUID messageId, CompositionElementDto compositionElement,
+        InstanceElementDto instanceElement) {
+        cleanExecution(instanceElement.elementId(), messageId);
+        var result = executor.submit(() -> this.reviewProcess(compositionElement, instanceElement));
+        executionMap.put(instanceElement.elementId(), result);
+    }
+
+    private void reviewProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
+        try {
+            listener.review(compositionElement, instanceElement);
+        } catch (PfModelException e) {
+            LOGGER.error("Automation composition element Review failed {} {}",
+                instanceElement.elementId(), e.getMessage());
+            intermediaryApi.updateAutomationCompositionElementState(
+                instanceElement.instanceId(), instanceElement.elementId(), DeployState.DEPLOYED,
+                null, StateChangeResult.FAILED, "Automation composition element Review failed");
+        }
+        executionMap.remove(instanceElement.elementId());
+    }
+
+    /**
+     * Handles AutomationComposition Prepare Pre Deploy.
+     *
+     * @param messageId the messageId
+     * @param compositionElement the information of the Automation Composition Definition Element
+     * @param instanceElement the information of the Automation Composition Instance Element
+     */
+    public void prepare(UUID messageId, CompositionElementDto compositionElement,
+        InstanceElementDto instanceElement) {
+        cleanExecution(instanceElement.elementId(), messageId);
+        var result = executor.submit(() -> this.prepareProcess(compositionElement, instanceElement));
+        executionMap.put(instanceElement.elementId(), result);
+    }
+
+    private void prepareProcess(CompositionElementDto compositionElement, InstanceElementDto instanceElement) {
+        try {
+            listener.prepare(compositionElement, instanceElement);
+        } catch (PfModelException e) {
+            LOGGER.error("Automation composition element prepare Pre Deploy failed {} {}",
+                instanceElement.elementId(), e.getMessage());
+            intermediaryApi.updateAutomationCompositionElementState(
+                instanceElement.instanceId(), instanceElement.elementId(), DeployState.UNDEPLOYED,
+                null, StateChangeResult.FAILED, "Automation composition element prepare Pre Deploy failed");
+        }
+        executionMap.remove(instanceElement.elementId());
+    }
 }
diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AcSubStateHandlerTest.java
new file mode 100644 (file)
index 0000000..293a441
--- /dev/null
@@ -0,0 +1,164 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2024 Nordix Foundation.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.acm.participant.intermediary.handler;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+import org.onap.policy.clamp.acm.participant.intermediary.main.parameters.CommonTestData;
+import org.onap.policy.clamp.models.acm.concepts.AcElementDeploy;
+import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDefinition;
+import org.onap.policy.clamp.models.acm.concepts.ParticipantDeploy;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionMigration;
+import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionPrepare;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+class AcSubStateHandlerTest {
+
+    @Test
+    void handleAcStateChangeNullTest() {
+        var cacheProvider = mock(CacheProvider.class);
+        var ach = new AcSubStateHandler(cacheProvider, mock(ThreadHandler.class));
+
+        var acMigration = new AutomationCompositionMigration();
+        acMigration.setPrecheck(true);
+        assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(acMigration));
+
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        acMigration.setAutomationCompositionId(automationComposition.getInstanceId());
+        acMigration.setCompositionTargetId(UUID.randomUUID());
+        assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(acMigration));
+
+        var acPrepare = new AutomationCompositionPrepare();
+        assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepare));
+
+        acPrepare.setAutomationCompositionId(automationComposition.getInstanceId());
+        assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepare));
+    }
+
+    @Test
+    void handleAcMigrationPrecheckTest() {
+        var listener = mock(ThreadHandler.class);
+        var cacheProvider = mock(CacheProvider.class);
+        var ach = new AcSubStateHandler(cacheProvider, listener);
+        var migrationMsg = new AutomationCompositionMigration();
+        migrationMsg.setPrecheck(true);
+        assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(migrationMsg));
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        migrationMsg.setCompositionTargetId(UUID.randomUUID());
+        migrationMsg.setAutomationCompositionId(automationComposition.getInstanceId());
+        assertDoesNotThrow(() -> ach.handleAcMigrationPrecheck(migrationMsg));
+        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(automationComposition);
+        var participantDeploy = new ParticipantDeploy();
+        participantDeploy.setParticipantId(CommonTestData.getParticipantId());
+        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
+        migrationMsg.getParticipantUpdatesList().add(participantDeploy);
+        Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
+        for (var element : automationComposition.getElements().values()) {
+            var acElementDeploy = new AcElementDeploy();
+            acElementDeploy.setProperties(Map.of());
+            acElementDeploy.setId(element.getId());
+            acElementDeploy.setDefinition(element.getDefinition());
+            participantDeploy.getAcElementList().add(acElementDeploy);
+            map.put(element.getDefinition(), new AutomationCompositionElementDefinition());
+        }
+        when(cacheProvider.getAcElementsDefinitions())
+                .thenReturn(Map.of(automationComposition.getCompositionId(), map,
+                        migrationMsg.getCompositionTargetId(), map));
+
+        ach.handleAcMigrationPrecheck(migrationMsg);
+        verify(listener, times(automationComposition.getElements().size()))
+                .migratePrecheck(any(), any(), any(), any(), any());
+    }
+
+    @Test
+    void handlePrepareTest() {
+        var listener = mock(ThreadHandler.class);
+        var cacheProvider = mock(CacheProvider.class);
+        var ach = new AcSubStateHandler(cacheProvider, listener);
+
+        var acPrepareMsg = new AutomationCompositionPrepare();
+        acPrepareMsg.setPreDeploy(true);
+        assertDoesNotThrow(() -> ach.handleAcPrepare(acPrepareMsg));
+
+        acPrepareMsg.setParticipantId(CommonTestData.getParticipantId());
+        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
+        var participantDeploy = new ParticipantDeploy();
+        participantDeploy.setParticipantId(CommonTestData.getParticipantId());
+        acPrepareMsg.getParticipantList().add(participantDeploy);
+
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        acPrepareMsg.setAutomationCompositionId(automationComposition.getInstanceId());
+        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(automationComposition);
+        Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
+        for (var element : automationComposition.getElements().values()) {
+            var acElementDeploy = new AcElementDeploy();
+            acElementDeploy.setProperties(Map.of());
+            acElementDeploy.setId(element.getId());
+            participantDeploy.getAcElementList().add(acElementDeploy);
+            map.put(element.getDefinition(), new AutomationCompositionElementDefinition());
+        }
+        when(cacheProvider.getAcElementsDefinitions())
+            .thenReturn(Map.of(automationComposition.getCompositionId(), map));
+
+        ach.handleAcPrepare(acPrepareMsg);
+        verify(listener, times(automationComposition.getElements().size())).prepare(any(), any(), any());
+    }
+
+    @Test
+    void handleReviewTest() {
+        var cacheProvider = mock(CacheProvider.class);
+        when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
+
+        var acPrepareMsg = new AutomationCompositionPrepare();
+        acPrepareMsg.setPreDeploy(false);
+        acPrepareMsg.setParticipantId(CommonTestData.getParticipantId());
+
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        acPrepareMsg.setAutomationCompositionId(automationComposition.getInstanceId());
+        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
+            .thenReturn(automationComposition);
+        Map<ToscaConceptIdentifier, AutomationCompositionElementDefinition> map = new HashMap<>();
+        for (var element : automationComposition.getElements().values()) {
+            var acElementDeploy = new AcElementDeploy();
+            acElementDeploy.setProperties(Map.of());
+            acElementDeploy.setId(element.getId());
+            map.put(element.getDefinition(), new AutomationCompositionElementDefinition());
+        }
+        when(cacheProvider.getAcElementsDefinitions())
+            .thenReturn(Map.of(automationComposition.getCompositionId(), map));
+
+        var listener = mock(ThreadHandler.class);
+        var ach = new AcSubStateHandler(cacheProvider, listener);
+        ach.handleAcPrepare(acPrepareMsg);
+        verify(listener, times(automationComposition.getElements().size())).review(any(), any(), any());
+    }
+}
index eed5319..2781398 100644 (file)
@@ -39,6 +39,7 @@ import org.onap.policy.clamp.models.acm.concepts.AutomationCompositionElementDef
 import org.onap.policy.clamp.models.acm.concepts.DeployState;
 import org.onap.policy.clamp.models.acm.concepts.LockState;
 import org.onap.policy.clamp.models.acm.concepts.StateChangeResult;
+import org.onap.policy.clamp.models.acm.concepts.SubState;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.AutomationCompositionDeployAck;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantPrimeAck;
 import org.onap.policy.clamp.models.acm.messages.kafka.participant.ParticipantStatus;
@@ -86,6 +87,23 @@ class AutomationCompositionOutHandlerTest {
         verify(publisher).sendAutomationCompositionAck(any(AutomationCompositionDeployAck.class));
     }
 
+    @Test
+    void updateAutomationCompositionElementStatePrepareTest() {
+        var automationComposition = CommonTestData.getTestAutomationCompositionMap().values().iterator().next();
+        automationComposition.setSubState(SubState.PREPARING);
+        var cacheProvider = mock(CacheProvider.class);
+        when(cacheProvider.getAutomationComposition(automationComposition.getInstanceId()))
+                .thenReturn(automationComposition);
+        var element = automationComposition.getElements().values().iterator().next();
+        element.setSubState(SubState.PREPARING);
+        var elementId = element.getId();
+        var publisher = mock(ParticipantMessagePublisher.class);
+        var acOutHandler = new AutomationCompositionOutHandler(publisher, cacheProvider);
+        acOutHandler.updateAutomationCompositionElementState(automationComposition.getInstanceId(), elementId,
+                DeployState.DEPLOYED, null, StateChangeResult.NO_ERROR, "Prepare completed");
+        verify(publisher).sendAutomationCompositionAck(any(AutomationCompositionDeployAck.class));
+    }
+
     @Test
     void updateAutomationCompositionElementStateLockTest() {
         var publisher = mock(ParticipantMessagePublisher.class);
index 9451f01..ced2d81 100644 (file)
@@ -186,4 +186,36 @@ class CacheProviderTest {
             assertEquals(element.getDefinition(), result.elementDefinitionId());
         }
     }
+
+    @Test
+    void testGetCompositionElementDtoMap() {
+        var parameter = CommonTestData.getParticipantParameters();
+        var cacheProvider = new CacheProvider(parameter);
+        var compositionId = UUID.randomUUID();
+        var automationComposition =
+                CommonTestData.getTestAutomationCompositions().getAutomationCompositionList().get(0);
+        automationComposition.setCompositionId(compositionId);
+        cacheProvider.addElementDefinition(compositionId,
+                CommonTestData.createAutomationCompositionElementDefinitionList(automationComposition));
+        var result = cacheProvider.getCompositionElementDtoMap(automationComposition);
+        for (var element : automationComposition.getElements().values()) {
+            var compositionElementDto = result.get(element.getId());
+            assertEquals(element.getDefinition(), compositionElementDto.elementDefinitionId());
+        }
+    }
+
+    @Test
+    void testGetInstanceElementDtoMap() {
+        var parameter = CommonTestData.getParticipantParameters();
+        var cacheProvider = new CacheProvider(parameter);
+        var compositionId = UUID.randomUUID();
+        var automationComposition =
+                CommonTestData.getTestAutomationCompositions().getAutomationCompositionList().get(0);
+        automationComposition.setCompositionId(compositionId);
+        var result = cacheProvider.getInstanceElementDtoMap(automationComposition);
+        for (var element : automationComposition.getElements().values()) {
+            var compositionElementDto = result.get(element.getId());
+            assertEquals(element.getId(), compositionElementDto.elementId());
+        }
+    }
 }
index 8c2b247..1fb7281 100644 (file)
@@ -62,7 +62,8 @@ class ParticipantHandlerTest {
         when(publisher.isActive()).thenReturn(true);
         var cacheProvider = mock(CacheProvider.class);
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class),
+            publisher, cacheProvider);
         participantHandler.handleParticipantStatusReq(new ParticipantStatusReq());
         verify(publisher).sendParticipantRegister(any(ParticipantRegister.class));
 
@@ -76,7 +77,8 @@ class ParticipantHandlerTest {
     void handleAutomationCompositionDeployTest() {
         var acHandler = mock(AutomationCompositionHandler.class);
         var participantHandler = new ParticipantHandler(acHandler, mock(AcLockHandler.class),
-                mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
+            mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class),
+            mock(CacheProvider.class));
         var automationCompositionDeploy = new AutomationCompositionDeploy();
         participantHandler.handleAutomationCompositionDeploy(automationCompositionDeploy);
         verify(acHandler).handleAutomationCompositionDeploy(automationCompositionDeploy);
@@ -86,8 +88,8 @@ class ParticipantHandlerTest {
     void handleAutomationCompositionStateChangeTest() {
         var acHandler = mock(AutomationCompositionHandler.class);
         var acLockHandler = mock(AcLockHandler.class);
-        var participantHandler = new ParticipantHandler(acHandler, acLockHandler, mock(AcDefinitionHandler.class),
-                mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
+        var participantHandler = new ParticipantHandler(acHandler, acLockHandler, mock(AcSubStateHandler.class),
+            mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
         var acStateChange = new AutomationCompositionStateChange();
 
         acStateChange.setDeployOrderedState(DeployOrder.DEPLOY);
@@ -104,18 +106,25 @@ class ParticipantHandlerTest {
     @Test
     void handleAutomationCompositionMigrationTest() {
         var acHandler = mock(AutomationCompositionHandler.class);
+        var acSubStateHandler = mock(AcSubStateHandler.class);
         var participantHandler = new ParticipantHandler(acHandler, mock(AcLockHandler.class),
-                mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
+            acSubStateHandler, mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class),
+            mock(CacheProvider.class));
         var migrationMsg = new AutomationCompositionMigration();
         participantHandler.handleAutomationCompositionMigration(migrationMsg);
         verify(acHandler).handleAutomationCompositionMigration(migrationMsg);
+
+        migrationMsg.setPrecheck(true);
+        participantHandler.handleAutomationCompositionMigration(migrationMsg);
+        verify(acSubStateHandler).handleAcMigrationPrecheck(migrationMsg);
     }
 
     @Test
     void handleAcPropertyUpdateTest() {
         var acHandler = mock(AutomationCompositionHandler.class);
         var participantHandler = new ParticipantHandler(acHandler, mock(AcLockHandler.class),
-                mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
+            mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class),
+            mock(CacheProvider.class));
         var propertyUpdateMsg = new PropertiesUpdate();
         participantHandler.handleAcPropertyUpdate(propertyUpdateMsg);
         verify(acHandler).handleAcPropertyUpdate(propertyUpdateMsg);
@@ -127,8 +136,8 @@ class ParticipantHandlerTest {
         when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
         when(cacheProvider.getReplicaId()).thenReturn(CommonTestData.getReplicaId());
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class),
-                cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class),
+            mock(ParticipantMessagePublisher.class), cacheProvider);
 
         var participantAckMsg = new ParticipantAckMessage(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY);
         assertTrue(participantHandler.appliesTo(participantAckMsg));
@@ -147,7 +156,8 @@ class ParticipantHandlerTest {
         when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
         when(cacheProvider.getSupportedAcElementTypes()).thenReturn(List.of(new ParticipantSupportedElementType()));
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), publisher,
+            cacheProvider);
 
         participantHandler.sendParticipantRegister();
         verify(publisher).sendParticipantRegister(any(ParticipantRegister.class));
@@ -159,7 +169,8 @@ class ParticipantHandlerTest {
         var cacheProvider = mock(CacheProvider.class);
         when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), publisher,
+            cacheProvider);
 
         participantHandler.handleParticipantRegisterAck(new ParticipantRegisterAck());
         verify(publisher).sendParticipantStatus(any(ParticipantStatus.class));
@@ -171,7 +182,8 @@ class ParticipantHandlerTest {
         var cacheProvider = mock(CacheProvider.class);
         when(cacheProvider.getParticipantId()).thenReturn(CommonTestData.getParticipantId());
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), mock(AcDefinitionHandler.class), publisher, cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class), publisher,
+            cacheProvider);
 
         participantHandler.sendParticipantDeregister();
         verify(publisher).sendParticipantDeregister(any(ParticipantDeregister.class));
@@ -180,8 +192,8 @@ class ParticipantHandlerTest {
     @Test
     void handleParticipantDeregisterAckTest() {
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), mock(AcDefinitionHandler.class), mock(ParticipantMessagePublisher.class),
-                mock(CacheProvider.class));
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), mock(AcDefinitionHandler.class),
+            mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
         var participantDeregisterAck = new ParticipantDeregisterAck();
         assertDoesNotThrow(() -> participantHandler.handleParticipantDeregisterAck(participantDeregisterAck));
     }
@@ -194,8 +206,8 @@ class ParticipantHandlerTest {
 
         var acHandler = mock(AcDefinitionHandler.class);
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), acHandler, mock(ParticipantMessagePublisher.class),
-                mock(CacheProvider.class));
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), acHandler,
+            mock(ParticipantMessagePublisher.class), mock(CacheProvider.class));
 
         participantHandler.handleParticipantPrime(participantPrime);
         verify(acHandler).handlePrime(participantPrime);
@@ -213,7 +225,7 @@ class ParticipantHandlerTest {
         var publisher = mock(ParticipantMessagePublisher.class);
         var acHandler = mock(AcDefinitionHandler.class);
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), acHandler, publisher, cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), acHandler, publisher, cacheProvider);
 
         participantHandler.handleParticipantSync(participantSyncMsg);
         verify(acHandler).handleParticipantSync(participantSyncMsg);
@@ -229,7 +241,7 @@ class ParticipantHandlerTest {
         when(publisher.isActive()).thenReturn(true);
         var acHandler = mock(AcDefinitionHandler.class);
         var participantHandler = new ParticipantHandler(mock(AutomationCompositionHandler.class),
-                mock(AcLockHandler.class), acHandler, publisher, cacheProvider);
+            mock(AcLockHandler.class), mock(AcSubStateHandler.class), acHandler, publisher, cacheProvider);
         participantHandler.sendHeartbeat();
         verify(publisher).sendParticipantRegister(any(ParticipantRegister.class));
 
index f3471e6..57b0659 100644 (file)
@@ -49,7 +49,7 @@ class ThreadHandlerTest {
     private static final int TIMEOUT = 400;
 
     @Test
-    void test() throws PfModelException, IOException {
+    void testPrime() throws PfModelException, IOException {
         var listener = mock(AutomationCompositionElementListener.class);
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
@@ -61,11 +61,45 @@ class ThreadHandlerTest {
             verify(listener, timeout(TIMEOUT)).prime(composition);
 
             clearInvocations(listener);
+            threadHandler.deprime(messageId, composition);
+            verify(listener, timeout(TIMEOUT)).deprime(composition);
+        }
+    }
+
+    @Test
+    void testPrimeException() throws PfModelException, IOException {
+        var listener = mock(AutomationCompositionElementListener.class);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
+
+            var compositionId = UUID.randomUUID();
+            var composition = new CompositionDto(compositionId, Map.of(), Map.of());
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).prime(composition);
+            var messageId = UUID.randomUUID();
+            threadHandler.prime(messageId, composition);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED,
+                StateChangeResult.FAILED, "Composition Defintion prime failed");
+
+            clearInvocations(listener);
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition);
+            threadHandler.deprime(messageId, composition);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED,
+                StateChangeResult.FAILED, "Composition Defintion deprime failed");
+        }
+    }
+
+    @Test
+    void testDeploy() throws PfModelException, IOException {
+        var listener = mock(AutomationCompositionElementListener.class);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
+
             Map<String, Object> properties = Map.of("key", "value");
             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
                 properties, properties);
             var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
-                null, properties, properties);
+                    null, properties, properties);
+            var messageId = UUID.randomUUID();
             threadHandler.deploy(messageId, compositionElement, instanceElement);
             verify(listener, timeout(TIMEOUT)).deploy(compositionElement, instanceElement);
 
@@ -87,14 +121,6 @@ class ThreadHandlerTest {
             verify(listener, timeout(TIMEOUT)).migrate(compositionElement, compositionElementTarget,
                 instanceElement, instanceElementUpdated);
 
-            clearInvocations(listener);
-            threadHandler.lock(messageId, compositionElement, instanceElement);
-            verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement);
-
-            clearInvocations(listener);
-            threadHandler.unlock(messageId, compositionElement, instanceElement);
-            verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement);
-
             clearInvocations(listener);
             threadHandler.undeploy(messageId, compositionElement, instanceElement);
             verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
@@ -102,29 +128,15 @@ class ThreadHandlerTest {
             clearInvocations(listener);
             threadHandler.delete(messageId, compositionElement, instanceElement);
             verify(listener, timeout(TIMEOUT)).delete(compositionElement, instanceElement);
-
-            clearInvocations(listener);
-            threadHandler.deprime(messageId, composition);
-            verify(listener, timeout(TIMEOUT)).deprime(composition);
         }
     }
 
     @Test
-    void testException() throws PfModelException, IOException {
+    void testDeployException() throws PfModelException, IOException {
         var listener = mock(AutomationCompositionElementListener.class);
         var intermediaryApi = mock(ParticipantIntermediaryApi.class);
         try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
 
-            var compositionId = UUID.randomUUID();
-            var composition = new CompositionDto(compositionId, Map.of(), Map.of());
-            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
-                .prime(composition);
-            var messageId = UUID.randomUUID();
-            threadHandler.prime(messageId, composition);
-            verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.COMMISSIONED,
-                    StateChangeResult.FAILED, "Composition Defintion prime failed");
-
-            clearInvocations(intermediaryApi);
             Map<String, Object> properties = Map.of("key", "value");
             var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
                 properties, properties);
@@ -135,9 +147,10 @@ class ThreadHandlerTest {
             element.setId(elementId);
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
                 .deploy(compositionElement, instanceElement);
+            var messageId = UUID.randomUUID();
             threadHandler.deploy(messageId, compositionElement, instanceElement);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
-                    DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
+                DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
                     "Automation composition element deploy failed");
 
             clearInvocations(listener);
@@ -147,44 +160,169 @@ class ThreadHandlerTest {
                 .update(compositionElement, instanceElement, instanceElementUpdated);
             threadHandler.update(messageId, compositionElement, instanceElement, instanceElementUpdated);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
-                    DeployState.DEPLOYED, null, StateChangeResult.FAILED,
-                    "Automation composition element update failed");
+                DeployState.DEPLOYED, null, StateChangeResult.FAILED,
+                "Automation composition element update failed");
+
+            clearInvocations(listener);
+            var compositionTargetId = UUID.randomUUID();
+            var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
+                properties, properties);
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
+                .migrate(compositionElement, compositionElementTarget, instanceElement, instanceElementUpdated);
+            threadHandler.migrate(messageId, compositionElement, compositionElementTarget,
+                instanceElement, instanceElementUpdated);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
+                DeployState.DEPLOYED, null, StateChangeResult.FAILED,
+                "Automation composition element migrate failed");
 
             clearInvocations(listener);
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
+                .undeploy(compositionElement, instanceElement);
+            threadHandler.undeploy(messageId, compositionElement, instanceElement);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
+                DeployState.DEPLOYED, null, StateChangeResult.FAILED,
+                "Automation composition element undeploy failed");
+
+            clearInvocations(listener);
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
+                .delete(compositionElement, instanceElement);
+            threadHandler.delete(messageId, compositionElement, instanceElement);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
+                    DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
+                    "Automation composition element delete failed");
+        }
+    }
+
+    @Test
+    void testLock() throws PfModelException, IOException {
+        var listener = mock(AutomationCompositionElementListener.class);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
+
+            Map<String, Object> properties = Map.of("key", "value");
+            var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
+                properties, properties);
+            var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
+                null, properties, properties);
+            var messageId = UUID.randomUUID();
+            threadHandler.lock(messageId, compositionElement, instanceElement);
+            verify(listener, timeout(TIMEOUT)).lock(compositionElement, instanceElement);
+
+            clearInvocations(listener);
+            threadHandler.unlock(messageId, compositionElement, instanceElement);
+            verify(listener, timeout(TIMEOUT)).unlock(compositionElement, instanceElement);
+
+            clearInvocations(listener);
+            threadHandler.undeploy(messageId, compositionElement, instanceElement);
+            verify(listener, timeout(TIMEOUT)).undeploy(compositionElement, instanceElement);
+        }
+    }
+
+    @Test
+    void testLockException() throws PfModelException, IOException {
+        var listener = mock(AutomationCompositionElementListener.class);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
+
+            Map<String, Object> properties = Map.of("key", "value");
+            var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
+                properties, properties);
+            var instanceId = UUID.randomUUID();
+            var elementId = UUID.randomUUID();
+            var instanceElement = new InstanceElementDto(instanceId, elementId, null, properties, properties);
+            var element = new AcElementDeploy();
+            element.setId(elementId);
+            var messageId = UUID.randomUUID();
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
                 .lock(compositionElement, instanceElement);
             threadHandler.lock(messageId, compositionElement, instanceElement);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
-                    null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed");
+                null, LockState.UNLOCKED, StateChangeResult.FAILED, "Automation composition element lock failed");
 
             clearInvocations(listener);
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
                 .unlock(compositionElement, instanceElement);
             threadHandler.unlock(messageId, compositionElement, instanceElement);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
-                    null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed");
+                null, LockState.LOCKED, StateChangeResult.FAILED, "Automation composition element unlock failed");
+        }
+    }
+
+    @Test
+    void testSubState() throws PfModelException, IOException {
+        var listener = mock(AutomationCompositionElementListener.class);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
+
+            Map<String, Object> properties = Map.of("key", "value");
+            var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
+                properties, properties);
+            var instanceElement = new InstanceElementDto(UUID.randomUUID(), UUID.randomUUID(),
+                null, properties, properties);
+            var messageId = UUID.randomUUID();
+            threadHandler.prepare(messageId, compositionElement, instanceElement);
+            verify(listener, timeout(TIMEOUT)).prepare(compositionElement, instanceElement);
 
             clearInvocations(listener);
+            threadHandler.review(messageId, compositionElement, instanceElement);
+            verify(listener, timeout(TIMEOUT)).review(compositionElement, instanceElement);
+
+            clearInvocations(listener);
+            var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
+                instanceElement.elementId(), null, properties, properties);
+            var compositionTargetId = UUID.randomUUID();
+            var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
+                properties, properties);
+            threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
+                instanceElement, instanceElementMigrate);
+            verify(listener, timeout(TIMEOUT)).migratePrecheck(compositionElement, compositionElementTarget,
+                instanceElement, instanceElementMigrate);
+        }
+    }
+
+    @Test
+    void testSubStateException() throws PfModelException, IOException {
+        var listener = mock(AutomationCompositionElementListener.class);
+        var intermediaryApi = mock(ParticipantIntermediaryApi.class);
+        try (var threadHandler = new ThreadHandler(listener, intermediaryApi, mock(CacheProvider.class))) {
+
+            Map<String, Object> properties = Map.of("key", "value");
+            var compositionElement = new CompositionElementDto(UUID.randomUUID(), new ToscaConceptIdentifier(),
+                properties, properties);
+            var instanceId = UUID.randomUUID();
+            var elementId = UUID.randomUUID();
+            var instanceElement = new InstanceElementDto(instanceId, elementId, null, properties, properties);
+            var element = new AcElementDeploy();
+            element.setId(elementId);
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
-                .undeploy(compositionElement, instanceElement);
-            threadHandler.undeploy(messageId, compositionElement, instanceElement);
+                .prepare(compositionElement, instanceElement);
+            var messageId = UUID.randomUUID();
+            threadHandler.prepare(messageId, compositionElement, instanceElement);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
-                    DeployState.DEPLOYED, null, StateChangeResult.FAILED,
-                    "Automation composition element undeploy failed");
+                DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
+                "Automation composition element prepare Pre Deploy failed");
 
             clearInvocations(listener);
             doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
-                .delete(compositionElement, instanceElement);
-            threadHandler.delete(messageId, compositionElement, instanceElement);
+                .review(compositionElement, instanceElement);
+            threadHandler.review(messageId, compositionElement, instanceElement);
             verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
-                    DeployState.UNDEPLOYED, null, StateChangeResult.FAILED,
-                    "Automation composition element delete failed");
+                DeployState.DEPLOYED, null, StateChangeResult.FAILED,
+                "Automation composition element Review failed");
 
             clearInvocations(listener);
-            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener).deprime(composition);
-            threadHandler.deprime(messageId, composition);
-            verify(intermediaryApi, timeout(TIMEOUT)).updateCompositionState(compositionId, AcTypeState.PRIMED,
-                    StateChangeResult.FAILED, "Composition Defintion deprime failed");
+            var compositionTargetId = UUID.randomUUID();
+            var compositionElementTarget = new CompositionElementDto(compositionTargetId, new ToscaConceptIdentifier(),
+                properties, properties);
+            var instanceElementMigrate = new InstanceElementDto(instanceElement.instanceId(),
+                instanceElement.elementId(), null, properties, properties);
+            doThrow(new PfModelException(Status.INTERNAL_SERVER_ERROR, "Error")).when(listener)
+                .migratePrecheck(compositionElement, compositionElementTarget, instanceElement, instanceElementMigrate);
+            threadHandler.migratePrecheck(messageId, compositionElement, compositionElementTarget,
+                instanceElement, instanceElementMigrate);
+            verify(intermediaryApi, timeout(TIMEOUT)).updateAutomationCompositionElementState(instanceId, elementId,
+                DeployState.DEPLOYED, null, StateChangeResult.FAILED,
+                "Automation composition element migrate precheck failed");
         }
     }
 }