Add participant properties capability to acm/participants

Add statusProperties support in acm acm/participants.

Issue-ID: POLICY-4652
Change-Id: I6dd37c9be58fce36def022364abe46c791e98a77
Signed-off-by: FrancescoFioraEst <francesco.fiora@est.tech>
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AcElementDeployAck.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AcElementDeployAck.java
index 6113360..39bbdd1 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AcElementDeployAck.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AcElementDeployAck.java
@@ -20,6 +20,8 @@
 
 package org.onap.policy.clamp.models.acm.concepts;
 
+import java.util.LinkedHashMap;
+import java.util.Map;
 import lombok.AllArgsConstructor;
 import lombok.Getter;
 import lombok.Setter;
@@ -41,6 +43,8 @@
 
     private String useState;
 
+    private Map<String, Object> statusProperties = new LinkedHashMap<>();
+
     // Result: Success/Fail.
     private Boolean result;
 
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationCompositionElement.java b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationCompositionElement.java
index edc9e1c..208766e 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationCompositionElement.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/concepts/AutomationCompositionElement.java
@@ -64,6 +64,8 @@
     // which can be deserialized into an instance of the type of the property.
     private Map<String, Object> properties = new LinkedHashMap<>();
 
+    private Map<String, Object> statusProperties = new LinkedHashMap<>();
+
     /**
      * Copy constructor, does a deep copy but as all fields here are immutable, it's just a regular copy.
      *
@@ -75,6 +77,7 @@
         this.participantId = otherElement.participantId;
         this.description = otherElement.description;
         this.properties = PfUtils.mapMap(otherElement.properties, UnaryOperator.identity());
+        this.statusProperties = PfUtils.mapMap(otherElement.statusProperties, UnaryOperator.identity());
         this.deployState = otherElement.deployState;
         this.lockState = otherElement.lockState;
         this.operationalState = otherElement.operationalState;
diff --git a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionElement.java b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionElement.java
index 4ba336e..9956a47 100644
--- a/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionElement.java
+++ b/models/src/main/java/org/onap/policy/clamp/models/acm/persistence/concepts/JpaAutomationCompositionElement.java
@@ -107,6 +107,12 @@
     @Convert(converter = StringToMapConverter.class)
     private Map<String, Object> properties;
 
+    @Lob
+    @NotNull
+    @Valid
+    @Convert(converter = StringToMapConverter.class)
+    private Map<String, Object> statusProperties;
+
     /**
      * The Default Constructor creates a {@link JpaAutomationCompositionElement} object with a null key.
      */
@@ -156,6 +162,9 @@
         this.participantId = copyConcept.participantId;
         this.description = copyConcept.description;
         this.properties = (copyConcept.properties != null ? new LinkedHashMap<>(copyConcept.properties) : null);
+        this.statusProperties =
+                (copyConcept.statusProperties != null ? new LinkedHashMap<>(copyConcept.statusProperties)
+                        : null);
         this.deployState = copyConcept.deployState;
         this.lockState = copyConcept.lockState;
         this.operationalState = copyConcept.operationalState;
@@ -180,6 +189,7 @@
         element.setParticipantId(UUID.fromString(participantId));
         element.setDescription(description);
         element.setProperties(PfUtils.mapMap(properties, UnaryOperator.identity()));
+        element.setStatusProperties(PfUtils.mapMap(statusProperties, UnaryOperator.identity()));
         element.setDeployState(deployState);
         element.setLockState(lockState);
         element.setOperationalState(operationalState);
@@ -194,6 +204,7 @@
         this.participantId = element.getParticipantId().toString();
         this.description = element.getDescription();
         this.properties = PfUtils.mapMap(element.getProperties(), UnaryOperator.identity());
+        this.statusProperties = PfUtils.mapMap(element.getStatusProperties(), UnaryOperator.identity());
         this.deployState = element.getDeployState();
         this.lockState = element.getLockState();
         this.operationalState = element.getOperationalState();
diff --git a/models/src/test/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/AutomationCompositionDeployAckTest.java b/models/src/test/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/AutomationCompositionDeployAckTest.java
index 95a1699..b74b949 100644
--- a/models/src/test/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/AutomationCompositionDeployAckTest.java
+++ b/models/src/test/java/org/onap/policy/clamp/models/acm/messages/dmaap/participant/AutomationCompositionDeployAckTest.java
@@ -50,7 +50,7 @@
         // verify with all values
         orig.setAutomationCompositionId(UUID.randomUUID());
         orig.setParticipantId(CommonTestData.getParticipantId());
-        var acElementResult = new AcElementDeployAck(DeployState.DEPLOYED, LockState.LOCKED, "", "",
+        var acElementResult = new AcElementDeployAck(DeployState.DEPLOYED, LockState.LOCKED, "", "", Map.of(),
             true, "AutomationCompositionElement result");
         final var automationCompositionResultMap = Map.of(UUID.randomUUID(), acElementResult);
         orig.setAutomationCompositionResultMap(automationCompositionResultMap);
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java
index c99241f..a61a667 100644
--- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java
+++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/api/AutomationCompositionElementListener.java
@@ -69,4 +69,10 @@
         // default Operational State
         return "";
     }
+
+    public default Map<String, Object> getStatusProperties(UUID automationCompositionId,
+            UUID automationCompositionElementId) throws PfModelException {
+        // default StatusProperties
+        return Map.of();
+    }
 }
diff --git a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java
index 1eaf63d..f918ed1 100644
--- a/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java
+++ b/participant/participant-intermediary/src/main/java/org/onap/policy/clamp/acm/participant/intermediary/handler/AutomationCompositionHandler.java
@@ -21,6 +21,7 @@
 
 package org.onap.policy.clamp.acm.participant.intermediary.handler;
 
+import com.att.aft.dme2.internal.apache.commons.lang.StringUtils;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -96,6 +97,7 @@
      * @param automationCompositionId the automationComposition Id
      * @param id the automationComposition UUID
      * @param deployState the DeployState state
+     * @param lockState the LockState state
      */
     public void updateAutomationCompositionElementState(UUID automationCompositionId, UUID id, DeployState deployState,
             LockState lockState) {
@@ -113,6 +115,7 @@
                 element.setLockState(lockState);
                 element.setUseState(getUseState(automationCompositionId, id));
                 element.setOperationalState(getOperationalState(automationCompositionId, id));
+                element.setStatusProperties(getStatusProperties(automationCompositionId, id));
             }
             var checkOpt = automationComposition.getElements().values().stream()
                     .filter(acElement -> !deployState.equals(acElement.getDeployState())).findAny();
@@ -137,9 +140,10 @@
             acElement.setLockState(lockState);
             acElement.setUseState(getUseState(automationCompositionId, id));
             acElement.setOperationalState(getOperationalState(automationCompositionId, id));
+            acElement.setStatusProperties(getStatusProperties(automationCompositionId, id));
             automationCompositionStateChangeAck.getAutomationCompositionResultMap().put(acElement.getId(),
-                    new AcElementDeployAck(deployState, lockState,
-                            acElement.getOperationalState(), acElement.getUseState(), true,
+                    new AcElementDeployAck(deployState, lockState, acElement.getOperationalState(),
+                            acElement.getUseState(), acElement.getStatusProperties(), true,
                             "Automation composition element {} state changed to {}\", id, newState)"));
             LOGGER.debug("Automation composition element {} state changed to {}", id, deployState);
             automationCompositionStateChangeAck
@@ -470,14 +474,18 @@
      * @return the UseState of the Automation Composition Element
      */
     public String getUseState(UUID instanceId, UUID acElementId) {
+        var result = new StringBuilder();
         for (var acElementListener : listeners) {
             try {
-                return acElementListener.getUseState(instanceId, acElementId);
+                var state = acElementListener.getUseState(instanceId, acElementId);
+                if (!StringUtils.isBlank(state)) {
+                    result.append(state);
+                }
             } catch (PfModelException e) {
                 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
             }
         }
-        return null;
+        return result.toString();
     }
 
     /**
@@ -488,13 +496,36 @@
      * @return the OperationalState of the Automation Composition Element
      */
     public String getOperationalState(UUID instanceId, UUID acElementId) {
+        var result = new StringBuilder();
         for (var acElementListener : listeners) {
             try {
-                return acElementListener.getOperationalState(instanceId, acElementId);
+                var state = acElementListener.getOperationalState(instanceId, acElementId);
+                if (!StringUtils.isBlank(state)) {
+                    result.append(state);
+                }
             } catch (PfModelException e) {
                 LOGGER.error("Automation composition element get Use State failed {}", acElementId);
             }
         }
-        return null;
+        return result.toString();
+    }
+
+    /**
+     * Get StatusProperties.
+     *
+     * @param instanceId the instance Id
+     * @param acElementId the Automation Composition Element Id
+     * @return the Status Properties Map
+     */
+    public Map<String, Object> getStatusProperties(UUID instanceId, UUID acElementId) {
+        Map<String, Object> result = new HashMap<>();
+        for (var acElementListener : listeners) {
+            try {
+                result.putAll(acElementListener.getStatusProperties(instanceId, acElementId));
+            } catch (PfModelException e) {
+                LOGGER.error("Automation composition element get Status Properties failed {}", acElementId);
+            }
+        }
+        return result;
     }
 }
diff --git a/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AutomationCompositionElementListenerTest.java b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AutomationCompositionElementListenerTest.java
new file mode 100644
index 0000000..6255b88
--- /dev/null
+++ b/participant/participant-intermediary/src/test/java/org/onap/policy/clamp/acm/participant/intermediary/api/impl/AutomationCompositionElementListenerTest.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2023 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.api.impl;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+
+import java.util.UUID;
+import org.junit.jupiter.api.Test;
+import org.onap.policy.clamp.acm.participant.intermediary.handler.DummyAcElementListener;
+import org.onap.policy.models.base.PfModelException;
+
+class AutomationCompositionElementListenerTest {
+
+    @Test
+    void defaultTest() throws PfModelException {
+        var listener = new DummyAcElementListener();
+        assertThat(listener.getStatusProperties(UUID.randomUUID(), UUID.randomUUID())).isNotNull().isEmpty();
+        assertThat(listener.getOperationalState(UUID.randomUUID(), UUID.randomUUID())).isNotNull().isEmpty();
+        assertThat(listener.getUseState(UUID.randomUUID(), UUID.randomUUID())).isNotNull().isEmpty();
+        assertThatCode(() -> listener.lock(UUID.randomUUID(), UUID.randomUUID())).doesNotThrowAnyException();
+        assertThatCode(() -> listener.unlock(UUID.randomUUID(), UUID.randomUUID())).doesNotThrowAnyException();
+    }
+}
diff --git a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
index 85beb76..0eb4ecb 100644
--- a/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
+++ b/runtime-acm/src/main/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandler.java
@@ -166,9 +166,11 @@
                 if (DeployState.DEPLOYED.equals(element.getDeployState())) {
                     element.setOperationalState(acElementAck.getValue().getOperationalState());
                     element.setUseState(acElementAck.getValue().getUseState());
+                    element.setStatusProperties(acElementAck.getValue().getStatusProperties());
                 } else {
                     element.setOperationalState(null);
                     element.setUseState(null);
+                    element.setStatusProperties(Map.of());
                 }
                 updated = true;
             }
diff --git a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java
index c44fa1d..c61984b 100644
--- a/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java
+++ b/runtime-acm/src/test/java/org/onap/policy/clamp/acm/runtime/supervision/SupervisionAcHandlerTest.java
@@ -64,7 +64,8 @@
         var automationCompositionAckMessage =
                 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_STATECHANGE_ACK);
         for (var elementEntry : automationComposition.getElements().entrySet()) {
-            var acElementDeployAck = new AcElementDeployAck(DeployState.DEPLOYED, LockState.UNLOCKED, "", "", true, "");
+            var acElementDeployAck =
+                    new AcElementDeployAck(DeployState.DEPLOYED, LockState.UNLOCKED, "", "", Map.of(), true, "");
             automationCompositionAckMessage.getAutomationCompositionResultMap().put(elementEntry.getKey(),
                     acElementDeployAck);
         }
@@ -86,7 +87,8 @@
         var automationCompositionAckMessage =
                 new AutomationCompositionDeployAck(ParticipantMessageType.AUTOMATION_COMPOSITION_DEPLOY_ACK);
         for (var elementEntry : automationComposition.getElements().entrySet()) {
-            var acElementDeployAck = new AcElementDeployAck(DeployState.DEPLOYED, LockState.LOCKED, "", "", true, "");
+            var acElementDeployAck =
+                    new AcElementDeployAck(DeployState.DEPLOYED, LockState.LOCKED, "", "", Map.of(), true, "");
             automationCompositionAckMessage
                     .setAutomationCompositionResultMap(Map.of(elementEntry.getKey(), acElementDeployAck));
         }