aboutsummaryrefslogtreecommitdiffstats
path: root/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop
diff options
context:
space:
mode:
Diffstat (limited to 'runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop')
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java81
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java208
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java360
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java276
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java82
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java416
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java55
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java77
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java59
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java53
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java54
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java38
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java115
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java186
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java151
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java156
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java84
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java273
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java371
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java450
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java116
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java75
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java75
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java74
-rw-r--r--runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java53
25 files changed, 3938 insertions, 0 deletions
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java
new file mode 100644
index 000000000..88e8b1df9
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningHandler.java
@@ -0,0 +1,81 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.commissioning;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import lombok.Getter;
+import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.rest.CommissioningController;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
+import org.onap.policy.common.utils.services.Registry;
+import org.onap.policy.models.base.PfModelRuntimeException;
+
+/**
+ * This class handles commissioning of control loop definitions.
+ */
+public final class CommissioningHandler extends ControlLoopHandler {
+
+ @Getter
+ private CommissioningProvider provider;
+
+ /**
+ * Gets the CommissioningHandler.
+ *
+ * @return CommissioningHandler
+ */
+ public static CommissioningHandler getInstance() {
+ return Registry.get(CommissioningHandler.class.getName());
+ }
+
+ /**
+ * Create a handler.
+ *
+ * @param controlLoopParameters the parameters for access to the database
+ */
+ public CommissioningHandler(ClRuntimeParameterGroup controlLoopParameters) {
+ super(controlLoopParameters.getDatabaseProviderParameters());
+ }
+
+ @Override
+ public Set<Class<?>> getProviderClasses() {
+ return Set.of(CommissioningController.class);
+ }
+
+ @Override
+ public void startProviders() {
+ provider = new CommissioningProvider(getDatabaseProviderParameters());
+ }
+
+ @Override
+ public void stopProviders() {
+ try {
+ provider.close();
+ } catch (IOException e) {
+ throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR,
+ "an error has occured while stopping commissioning providers", e);
+ }
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java
new file mode 100644
index 000000000..50f6787b9
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/CommissioningProvider.java
@@ -0,0 +1,208 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.commissioning;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.collections4.MapUtils;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
+import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.provider.PolicyModelsProvider;
+import org.onap.policy.models.provider.PolicyModelsProviderFactory;
+import org.onap.policy.models.provider.PolicyModelsProviderParameters;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplates;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaTypedEntityFilter;
+
+/**
+ * This class provides the create, read and delete actions on Commissioning of Control Loop concepts in the database to
+ * the callers.
+ */
+public class CommissioningProvider implements Closeable {
+ public static final String CONTROL_LOOP_NODE_TYPE = "org.onap.policy.clamp.controlloop.ControlLoop";
+
+ private final PolicyModelsProvider modelsProvider;
+ private final ControlLoopProvider clProvider;
+
+ private static final Object lockit = new Object();
+
+ /**
+ * Create a commissioning provider.
+ *
+ * @throws ControlLoopRuntimeException on errors creating the provider
+ */
+ public CommissioningProvider(PolicyModelsProviderParameters databaseProviderParameters)
+ throws ControlLoopRuntimeException {
+ try {
+ modelsProvider = new PolicyModelsProviderFactory()
+ .createPolicyModelsProvider(databaseProviderParameters);
+ } catch (PfModelException e) {
+ throw new PfModelRuntimeException(e);
+ }
+
+ try {
+ clProvider = new ControlLoopProvider(databaseProviderParameters);
+ } catch (PfModelException e) {
+ throw new PfModelRuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ try {
+ modelsProvider.close();
+ } catch (PfModelException e) {
+ throw new IOException("error closing modelsProvider", e);
+ }
+ }
+
+ /**
+ * Create control loops from a service template.
+ *
+ * @param serviceTemplate the service template
+ * @return the result of the commissioning operation
+ * @throws PfModelException on creation errors
+ */
+ public CommissioningResponse createControlLoopDefinitions(ToscaServiceTemplate serviceTemplate)
+ throws PfModelException {
+ synchronized (lockit) {
+ modelsProvider.createServiceTemplate(serviceTemplate);
+ }
+
+ CommissioningResponse response = new CommissioningResponse();
+ // @formatter:off
+ response.setAffectedControlLoopDefinitions(serviceTemplate.getToscaTopologyTemplate().getNodeTemplates()
+ .values()
+ .stream()
+ .map(template -> template.getKey().asIdentifier())
+ .collect(Collectors.toList()));
+ // @formatter:on
+
+ return response;
+ }
+
+ /**
+ * Delete the control loop definition with the given name and version.
+ *
+ * @param name the name of the control loop definition to delete
+ * @param version the version of the control loop to delete
+ * @return the result of the deletion
+ * @throws PfModelException on deletion errors
+ */
+ public CommissioningResponse deleteControlLoopDefinition(String name, String version) throws PfModelException {
+ synchronized (lockit) {
+ modelsProvider.deleteServiceTemplate(name, version);
+ }
+
+ CommissioningResponse response = new CommissioningResponse();
+ response.setAffectedControlLoopDefinitions(
+ Collections.singletonList(new ToscaConceptIdentifier(name, version)));
+
+ return response;
+ }
+
+ /**
+ * Get control loop node templates.
+ *
+ * @param clName the name of the control loop, null for all
+ * @param clVersion the version of the control loop, null for all
+ * @return list of control loop node templates
+ * @throws PfModelException on errors getting control loop definitions
+ */
+ public List<ToscaNodeTemplate> getControlLoopDefinitions(String clName, String clVersion) throws PfModelException {
+
+ // @formatter:off
+ ToscaTypedEntityFilter<ToscaNodeTemplate> nodeTemplateFilter = ToscaTypedEntityFilter
+ .<ToscaNodeTemplate>builder()
+ .name(clName)
+ .version(clVersion)
+ .type(CONTROL_LOOP_NODE_TYPE)
+ .build();
+ // @formatter:on
+
+ return clProvider.getFilteredNodeTemplates(nodeTemplateFilter);
+ }
+
+ /**
+ * Get the control loop elements from a control loop node template.
+ *
+ * @param controlLoopNodeTemplate the control loop node template
+ * @return a list of the control loop element node templates in a control loop node template
+ * @throws PfModelException on errors get control loop element node templates
+ */
+ public List<ToscaNodeTemplate> getControlLoopElementDefinitions(ToscaNodeTemplate controlLoopNodeTemplate)
+ throws PfModelException {
+ if (!CONTROL_LOOP_NODE_TYPE.equals(controlLoopNodeTemplate.getType())) {
+ return Collections.emptyList();
+ }
+
+ if (MapUtils.isEmpty(controlLoopNodeTemplate.getProperties())) {
+ return Collections.emptyList();
+ }
+
+ @SuppressWarnings("unchecked")
+ List<Map<String, String>> controlLoopElements =
+ (List<Map<String, String>>) controlLoopNodeTemplate.getProperties().get("elements");
+
+ if (CollectionUtils.isEmpty(controlLoopElements)) {
+ return Collections.emptyList();
+ }
+
+ List<ToscaNodeTemplate> controlLoopElementList = new ArrayList<>();
+ // @formatter:off
+ controlLoopElementList.addAll(
+ controlLoopElements
+ .stream()
+ .map(elementMap -> clProvider.getNodeTemplates(elementMap.get("name"),
+ elementMap.get("version")))
+ .flatMap(List::stream)
+ .collect(Collectors.toList())
+ );
+ // @formatter:on
+
+ return controlLoopElementList;
+ }
+
+ /**
+ * Get the requested control loop definitions.
+ *
+ * @param name the name of the definition to get, null for all definitions
+ * @param version the version of the definition to get, null for all definitions
+ * @return the control loop definitions
+ * @throws PfModelException on errors getting control loop definitions
+ */
+ public ToscaServiceTemplate getToscaServiceTemplate(String name, String version) throws PfModelException {
+ ToscaServiceTemplates serviceTemplates = new ToscaServiceTemplates();
+ serviceTemplates.setServiceTemplates(modelsProvider.getServiceTemplateList(name, version));
+ return serviceTemplates.getServiceTemplates().get(0);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java
new file mode 100644
index 000000000..18e1f7787
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/commissioning/rest/CommissioningController.java
@@ -0,0 +1,360 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.commissioning.rest;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
+import io.swagger.annotations.ResponseHeader;
+import java.util.List;
+import java.util.UUID;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.onap.policy.clamp.controlloop.models.messages.rest.commissioning.CommissioningResponse;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningHandler;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider;
+import org.onap.policy.clamp.controlloop.runtime.main.rest.RestController;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.errors.concepts.ErrorResponse;
+import org.onap.policy.models.errors.concepts.ErrorResponseInfo;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class to provide REST end points for creating, deleting, querying commissioned control loops.
+ */
+public class CommissioningController extends RestController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CommissioningController.class);
+
+ private final CommissioningProvider provider;
+
+ /**
+ * create Commissioning Controller.
+ */
+ public CommissioningController() {
+ this.provider = CommissioningHandler.getInstance().getProvider();
+ }
+
+ /**
+ * Creates a control loop definition.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param body the body of control loop following TOSCA definition
+ * @return a response
+ */
+ // @formatter:off
+ @POST
+ @Path("/commission")
+ @ApiOperation(
+ value = "Commissions control loop definitions",
+ notes = "Commissions control loop definitions, returning the commissioned control loop definition IDs",
+ response = CommissioningResponse.class,
+ tags = {
+ "Control Loop Commissioning API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME,
+ description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_PATCH_NAME,
+ description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_LATEST_NAME,
+ description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = REQUEST_ID_NAME,
+ description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)
+ },
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response create(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Entity Body of Control Loop", required = true) ToscaServiceTemplate body) {
+
+ try {
+ CommissioningResponse response = provider.createControlLoopDefinitions(body);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId)
+ .entity(response).build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Commissioning of the control loops failed", e);
+ CommissioningResponse resp = new CommissioningResponse();
+ resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp);
+ }
+
+ }
+
+ /**
+ * Deletes a control loop definition.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop definition to delete
+ * @param version the version of the control loop definition to delete
+ * @return a response
+ */
+ // @formatter:off
+ @DELETE
+ @Path("/commission")
+ @ApiOperation(value = "Delete a commissioned control loop",
+ notes = "Deletes a Commissioned Control Loop, returning optional error details",
+ response = CommissioningResponse.class,
+ tags = {
+ "Clamp Control Loop Commissioning API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME,
+ description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_PATCH_NAME,
+ description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_LATEST_NAME,
+ description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = REQUEST_ID_NAME,
+ description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response delete(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop definition name", required = true) @QueryParam("name") String name,
+ @ApiParam(value = "Control Loop definition version", required = true)
+ @QueryParam("version") String version) {
+
+ try {
+ CommissioningResponse response = provider.deleteControlLoopDefinition(name, version);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId)
+ .entity(response).build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Decommisssioning of control loop failed", e);
+ CommissioningResponse resp = new CommissioningResponse();
+ resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp);
+ }
+
+ }
+
+ /**
+ * Queries details of all or specific control loop definitions.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop definition to get, null for all definitions
+ * @param version the version of the control loop definition to get, null for all definitions
+ * @return the control loop definitions
+ */
+ // @formatter:off
+ @GET
+ @Path("/commission")
+ @ApiOperation(value = "Query details of the requested commissioned control loop definitions",
+ notes = "Queries details of the requested commissioned control loop definitions, "
+ + "returning all control loop details",
+ response = ToscaNodeTemplate.class,
+ tags = {
+ "Clamp Control Loop Commissioning API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response query(@HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop definition name", required = true)
+ @QueryParam("name") String name,
+ @ApiParam(value = "Control Loop definition version", required = true)
+ @QueryParam("version") String version) {
+
+ try {
+ List<ToscaNodeTemplate> response = provider.getControlLoopDefinitions(name, version);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Get of control loop definitions failed", e);
+ CommissioningResponse resp = new CommissioningResponse();
+ resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp);
+ }
+
+ }
+
+ /**
+ * Queries the elements of a specific control loop.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop definition to get
+ * @param version the version of the control loop definition to get
+ * @return the control loop element definitions
+ */
+ // @formatter:off
+ @GET
+ @Path("/commission/elements")
+ @ApiOperation(value = "Query details of the requested commissioned control loop element definitions",
+ notes = "Queries details of the requested commissioned control loop element definitions, "
+ + "returning all control loop elements' details",
+ response = ToscaNodeTemplate.class,
+ tags = {
+ "Clamp Control Loop Commissioning API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response queryElements(@HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop definition name", required = true)
+ @QueryParam("name") String name,
+ @ApiParam(value = "Control Loop definition version", required = true)
+ @QueryParam("version") String version) throws Exception {
+
+ try {
+ List<ToscaNodeTemplate> nodeTemplate = provider.getControlLoopDefinitions(name, version);
+ //Prevent ambiguous queries with multiple returns
+ if (nodeTemplate.size() > 1) {
+ CommissioningResponse resp = new CommissioningResponse();
+ resp.setErrorDetails("Multiple ControlLoops are not supported");
+ return returnResponse(Response.Status.NOT_ACCEPTABLE, requestId, resp);
+ }
+
+ List<ToscaNodeTemplate> response = provider.getControlLoopElementDefinitions(nodeTemplate.get(0));
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Get of control loop element definitions failed", e);
+ CommissioningResponse resp = new CommissioningResponse();
+ resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ return returnResponse(e.getErrorResponse().getResponseCode(), requestId, resp);
+ }
+
+ }
+
+ private Response returnResponse(Response.Status status, UUID requestId, CommissioningResponse resp) {
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(status)),
+ requestId).entity(resp).build();
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java
new file mode 100644
index 000000000..eb72d9219
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/ControlLoopInstantiationProvider.java
@@ -0,0 +1,276 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.instantiation;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
+import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationCommand;
+import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationResponse;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider;
+import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler;
+import org.onap.policy.common.parameters.BeanValidationResult;
+import org.onap.policy.common.parameters.ObjectValidationResult;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.common.parameters.ValidationStatus;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.provider.PolicyModelsProviderParameters;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaNodeTemplate;
+
+/**
+ * This class is dedicated to the Instantiation of Commissioned control loop.
+ */
+public class ControlLoopInstantiationProvider implements Closeable {
+ private final ControlLoopProvider controlLoopProvider;
+ private final CommissioningProvider commissioningProvider;
+
+ private static final Object lockit = new Object();
+
+ /**
+ * Create a instantiation provider.
+ *
+ * @param databaseProviderParameters the parameters for database access
+ */
+ public ControlLoopInstantiationProvider(PolicyModelsProviderParameters databaseProviderParameters) {
+ try {
+ controlLoopProvider = new ControlLoopProvider(databaseProviderParameters);
+ commissioningProvider = new CommissioningProvider(databaseProviderParameters);
+ } catch (PfModelException e) {
+ throw new PfModelRuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ controlLoopProvider.close();
+ }
+
+ /**
+ * Create control loops.
+ *
+ * @param controlLoops the control loop
+ * @return the result of the instantiation operation
+ * @throws PfModelException on creation errors
+ */
+ public InstantiationResponse createControlLoops(ControlLoops controlLoops) throws PfModelException {
+
+ synchronized (lockit) {
+ for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {
+ ControlLoop checkControlLoop = controlLoopProvider.getControlLoop(controlLoop.getKey().asIdentifier());
+ if (checkControlLoop != null) {
+ throw new PfModelException(Response.Status.BAD_REQUEST,
+ controlLoop.getKey().asIdentifier() + " already defined");
+ }
+ }
+ BeanValidationResult validationResult = validateControlLoops(controlLoops);
+ if (!validationResult.isValid()) {
+ throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
+ }
+ controlLoopProvider.createControlLoops(controlLoops.getControlLoopList());
+ }
+
+ InstantiationResponse response = new InstantiationResponse();
+ response.setAffectedControlLoops(controlLoops.getControlLoopList().stream()
+ .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList()));
+
+ return response;
+ }
+
+ /**
+ * Update control loops.
+ *
+ * @param controlLoops the control loop
+ * @return the result of the instantiation operation
+ * @throws PfModelException on update errors
+ */
+ public InstantiationResponse updateControlLoops(ControlLoops controlLoops) throws PfModelException {
+ synchronized (lockit) {
+ BeanValidationResult validationResult = validateControlLoops(controlLoops);
+ if (!validationResult.isValid()) {
+ throw new PfModelException(Response.Status.BAD_REQUEST, validationResult.getResult());
+ }
+ controlLoopProvider.updateControlLoops(controlLoops.getControlLoopList());
+ }
+
+ InstantiationResponse response = new InstantiationResponse();
+ response.setAffectedControlLoops(controlLoops.getControlLoopList().stream()
+ .map(cl -> cl.getKey().asIdentifier()).collect(Collectors.toList()));
+
+ return response;
+ }
+
+ /**
+ * Validate ControlLoops.
+ *
+ * @param controlLoops ControlLoops to validate
+ * @result the result of validation
+ * @throws PfModelException if controlLoops is not valid
+ */
+ private BeanValidationResult validateControlLoops(ControlLoops controlLoops) throws PfModelException {
+
+ BeanValidationResult result = new BeanValidationResult("ControlLoops", controlLoops);
+
+ for (ControlLoop controlLoop : controlLoops.getControlLoopList()) {
+ BeanValidationResult subResult = new BeanValidationResult(
+ "entry " + controlLoop.getDefinition().getName(), controlLoop);
+
+ List<ToscaNodeTemplate> toscaNodeTemplates = commissioningProvider.getControlLoopDefinitions(
+ controlLoop.getDefinition().getName(), controlLoop.getDefinition().getVersion());
+
+ if (toscaNodeTemplates.isEmpty()) {
+ subResult
+ .addResult(new ObjectValidationResult("ControlLoop", controlLoop.getDefinition().getName(),
+ ValidationStatus.INVALID, "Commissioned control loop definition not FOUND"));
+ } else if (toscaNodeTemplates.size() > 1) {
+ subResult
+ .addResult(new ObjectValidationResult("ControlLoop", controlLoop.getDefinition().getName(),
+ ValidationStatus.INVALID, "Commissioned control loop definition not VALID"));
+ } else {
+
+ List<ToscaNodeTemplate> clElementDefinitions =
+ commissioningProvider.getControlLoopElementDefinitions(toscaNodeTemplates.get(0));
+
+ // @formatter:off
+ Map<String, ToscaConceptIdentifier> definitions = clElementDefinitions
+ .stream()
+ .map(nodeTemplate -> nodeTemplate.getKey().asIdentifier())
+ .collect(Collectors.toMap(ToscaConceptIdentifier::getName, UnaryOperator.identity()));
+ // @formatter:on
+
+ for (ControlLoopElement element : controlLoop.getElements().values()) {
+ subResult.addResult(validateDefinition(definitions, element.getDefinition()));
+ }
+ }
+ result.addResult(subResult);
+ }
+ return result;
+ }
+
+ /**
+ * Validate ToscaConceptIdentifier, checking if exist in ToscaConceptIdentifiers map.
+ *
+ * @param definitions map of all ToscaConceptIdentifiers
+ * @param definition ToscaConceptIdentifier to validate
+ * @result result the validation result
+ */
+ private ValidationResult validateDefinition(Map<String, ToscaConceptIdentifier> definitions,
+ ToscaConceptIdentifier definition) {
+ BeanValidationResult result = new BeanValidationResult("entry " + definition.getName(), definition);
+ ToscaConceptIdentifier identifier = definitions.get(definition.getName());
+ if (identifier == null) {
+ result.setResult(ValidationStatus.INVALID, "Not FOUND");
+ } else if (!identifier.equals(definition)) {
+ result.setResult(ValidationStatus.INVALID, "Version not matching");
+ }
+ return (result.isClean() ? null : result);
+ }
+
+ /**
+ * Delete the control loop with the given name and version.
+ *
+ * @param name the name of the control loop to delete
+ * @param version the version of the control loop to delete
+ * @return the result of the deletion
+ * @throws PfModelException on deletion errors
+ */
+ public InstantiationResponse deleteControlLoop(String name, String version) throws PfModelException {
+ InstantiationResponse response = new InstantiationResponse();
+ synchronized (lockit) {
+ List<ControlLoop> controlLoops = controlLoopProvider.getControlLoops(name, version);
+ if (controlLoops.isEmpty()) {
+ throw new PfModelException(Response.Status.NOT_FOUND, "Control Loop not found");
+ }
+ for (ControlLoop controlLoop : controlLoops) {
+ if (!ControlLoopState.UNINITIALISED.equals(controlLoop.getState())) {
+ throw new PfModelException(Response.Status.BAD_REQUEST,
+ "Control Loop State is still " + controlLoop.getState());
+ }
+ }
+
+ response.setAffectedControlLoops(Collections
+ .singletonList(controlLoopProvider.deleteControlLoop(name, version).getKey().asIdentifier()));
+ }
+ return response;
+ }
+
+ /**
+ * Get the requested control loops.
+ *
+ * @param name the name of the control loop to get, null for all control loops
+ * @param version the version of the control loop to get, null for all control loops
+ * @return the control loops
+ * @throws PfModelException on errors getting control loops
+ */
+ public ControlLoops getControlLoops(String name, String version) throws PfModelException {
+ ControlLoops controlLoops = new ControlLoops();
+ controlLoops.setControlLoopList(controlLoopProvider.getControlLoops(name, version));
+
+ return controlLoops;
+ }
+
+ /**
+ * Issue a command to control loops, setting their ordered state.
+ *
+ * @param command the command to issue to control loops
+ * @return the result of the initiation command
+ * @throws PfModelException on errors setting the ordered state on the control loops
+ * @throws ControlLoopException on ordered state invalid
+ */
+ public InstantiationResponse issueControlLoopCommand(InstantiationCommand command)
+ throws ControlLoopException, PfModelException {
+
+ if (command.getOrderedState() == null) {
+ throw new ControlLoopException(Status.BAD_REQUEST, "ordered state invalid or not specified on command");
+ }
+
+ synchronized (lockit) {
+ List<ControlLoop> controlLoops = new ArrayList<>(command.getControlLoopIdentifierList().size());
+ for (ToscaConceptIdentifier id : command.getControlLoopIdentifierList()) {
+ ControlLoop controlLoop = controlLoopProvider.getControlLoop(id);
+ controlLoop.setCascadedOrderedState(command.getOrderedState());
+ controlLoops.add(controlLoop);
+ }
+ controlLoopProvider.updateControlLoops(controlLoops);
+ }
+
+ SupervisionHandler supervisionHandler = SupervisionHandler.getInstance();
+ supervisionHandler.triggerControlLoopSupervision(command.getControlLoopIdentifierList());
+ InstantiationResponse response = new InstantiationResponse();
+ response.setAffectedControlLoops(command.getControlLoopIdentifierList());
+
+ return response;
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java
new file mode 100644
index 000000000..d81e54ccf
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/InstantiationHandler.java
@@ -0,0 +1,82 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.instantiation;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import lombok.Getter;
+import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler;
+import org.onap.policy.clamp.controlloop.runtime.instantiation.rest.InstantiationController;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
+import org.onap.policy.common.utils.services.Registry;
+import org.onap.policy.models.base.PfModelRuntimeException;
+
+/**
+ * This class handles instantiation of control loop instances.
+ *
+ * <p/>It is effectively a singleton that is started at system start
+ */
+public final class InstantiationHandler extends ControlLoopHandler {
+
+ @Getter
+ private ControlLoopInstantiationProvider controlLoopInstantiationProvider;
+
+ /**
+ * Gets the InstantiationHandler.
+ *
+ * @return InstantiationHandler
+ */
+ public static InstantiationHandler getInstance() {
+ return Registry.get(InstantiationHandler.class.getName());
+ }
+
+ /**
+ * Create a handler.
+ *
+ * @param controlLoopParameters the parameters for access to the database
+ */
+ public InstantiationHandler(ClRuntimeParameterGroup controlLoopParameters) {
+ super(controlLoopParameters.getDatabaseProviderParameters());
+ }
+
+ @Override
+ public Set<Class<?>> getProviderClasses() {
+ return Set.of(InstantiationController.class);
+ }
+
+ @Override
+ public void startProviders() {
+ controlLoopInstantiationProvider = new ControlLoopInstantiationProvider(getDatabaseProviderParameters());
+ }
+
+ @Override
+ public void stopProviders() {
+ try {
+ controlLoopInstantiationProvider.close();
+ } catch (IOException e) {
+ throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, e.getMessage());
+ }
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java
new file mode 100644
index 000000000..7581aaf74
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/instantiation/rest/InstantiationController.java
@@ -0,0 +1,416 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.instantiation.rest;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
+import io.swagger.annotations.ResponseHeader;
+import java.util.UUID;
+import javax.ws.rs.DELETE;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoops;
+import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationCommand;
+import org.onap.policy.clamp.controlloop.models.messages.rest.instantiation.InstantiationResponse;
+import org.onap.policy.clamp.controlloop.runtime.instantiation.ControlLoopInstantiationProvider;
+import org.onap.policy.clamp.controlloop.runtime.instantiation.InstantiationHandler;
+import org.onap.policy.clamp.controlloop.runtime.main.rest.RestController;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.errors.concepts.ErrorResponseInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Class to provide REST end points for creating, deleting, query and commanding a control loop definition.
+ */
+public class InstantiationController extends RestController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(InstantiationController.class);
+
+ // The CL provider for instantiation requests
+ private final ControlLoopInstantiationProvider provider;
+
+ /**
+ * create Instantiation Controller.
+ */
+ public InstantiationController() {
+ this.provider = InstantiationHandler.getInstance().getControlLoopInstantiationProvider();
+ }
+
+ /**
+ * Creates a control loop.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param controlLoops the control loops
+ * @return a response
+ */
+ // @formatter:off
+ @POST
+ @Path("/instantiation")
+ @ApiOperation(
+ value = "Commissions control loop definitions",
+ notes = "Commissions control loop definitions, returning the control loop IDs",
+ response = InstantiationResponse.class,
+ tags = {
+ "Control Loop Instantiation API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME,
+ description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_PATCH_NAME,
+ description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_LATEST_NAME,
+ description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = REQUEST_ID_NAME,
+ description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)
+ },
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response create(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Entity Body of Control Loop", required = true) ControlLoops controlLoops) {
+
+ try {
+ InstantiationResponse response = provider.createControlLoops(controlLoops);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("creation of control loop failed", e);
+ return createInstantiationErrorResponse(e, requestId);
+ }
+ }
+
+ /**
+ * Queries details of all control loops.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop to get, null for all control loops
+ * @param version the version of the control loop to get, null for all control loops
+ * @return the control loops
+ */
+ // @formatter:off
+ @GET
+ @Path("/instantiation")
+ @ApiOperation(value = "Query details of the requested control loops",
+ notes = "Queries details of the requested control loops, returning all control loop details",
+ response = ControlLoops.class,
+ tags = {
+ "Clamp control loop Instantiation API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response query(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop definition name", required = true) @QueryParam("name") String name,
+ @ApiParam(value = "Control Loop definition version",
+ required = true) @QueryParam("version") String version) {
+
+ try {
+ ControlLoops response = provider.getControlLoops(name, version);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("commisssioning of control loop failed", e);
+ return createInstantiationErrorResponse(e, requestId);
+ }
+
+ }
+
+ /**
+ * Updates a control loop.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param controlLoops the control loops
+ * @return a response
+ */
+ // @formatter:off
+ @PUT
+ @Path("/instantiation")
+ @ApiOperation(
+ value = "Updates control loop definitions",
+ notes = "Updates control loop definitions, returning the updated control loop definition IDs",
+ response = InstantiationResponse.class,
+ tags = {
+ "Control Loop Instantiation API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME,
+ description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_PATCH_NAME,
+ description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_LATEST_NAME,
+ description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = REQUEST_ID_NAME,
+ description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)
+ },
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response update(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Entity Body of Control Loop", required = true) ControlLoops controlLoops) {
+
+ try {
+ InstantiationResponse response = provider.updateControlLoops(controlLoops);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("update of control loops failed", e);
+ return createInstantiationErrorResponse(e, requestId);
+ }
+ }
+
+ /**
+ * Deletes a control loop definition.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop to delete
+ * @param version the version of the control loop to delete
+ * @return a response
+ */
+ // @formatter:off
+ @DELETE
+ @Path("/instantiation")
+ @ApiOperation(value = "Delete a control loop",
+ notes = "Deletes a control loop, returning optional error details",
+ response = InstantiationResponse.class,
+ tags = {
+ "Clamp Control Loop Instantiation API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME,
+ description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_PATCH_NAME,
+ description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = VERSION_LATEST_NAME,
+ description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(
+ name = REQUEST_ID_NAME,
+ description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+
+ public Response delete(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop definition name", required = true) @QueryParam("name") String name,
+ @ApiParam(value = "Control Loop definition version", required = true)
+ @QueryParam("version") String version) {
+
+ try {
+ InstantiationResponse response = provider.deleteControlLoop(name, version);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.OK)), requestId).entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("delete of control loop failed", e);
+ return createInstantiationErrorResponse(e, requestId);
+ }
+ }
+
+ /**
+ * Issues control loop commands to control loops.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param command the command to issue to control loops
+ * @return the control loop definitions
+ */
+ // @formatter:off
+ @PUT
+ @Path("/instantiation/command")
+ @ApiOperation(value = "Issue a command to the requested control loops",
+ notes = "Issues a command to a control loop, ordering a state change on the control loop",
+ response = InstantiationResponse.class,
+ tags = {
+ "Clamp Control Loop Instantiation API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response issueControlLoopCommand(
+ @HeaderParam(REQUEST_ID_NAME) @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Entity Body of control loop command", required = true) InstantiationCommand command) {
+
+ try {
+ InstantiationResponse response = provider.issueControlLoopCommand(command);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Status.ACCEPTED)), requestId)
+ .entity(response).build();
+
+ } catch (PfModelRuntimeException | PfModelException | ControlLoopException e) {
+ LOGGER.warn("creation of control loop failed", e);
+ return createInstantiationErrorResponse(e, requestId);
+ }
+ }
+
+ /**
+ * create a Instantiation Response from an exception.
+ * @param e the error
+ * @param requestId request ID used in ONAP logging
+ * @return the Instantiation Response
+ */
+ private Response createInstantiationErrorResponse(ErrorResponseInfo e, UUID requestId) {
+ InstantiationResponse resp = new InstantiationResponse();
+ resp.setErrorDetails(e.getErrorResponse().getErrorMessage());
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())),
+ requestId).entity(resp).build();
+ }
+}
+
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java
new file mode 100644
index 000000000..4c99b8e57
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.parameters;
+
+import java.util.List;
+import javax.validation.constraints.NotBlank;
+import lombok.Getter;
+import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams;
+import org.onap.policy.common.endpoints.parameters.RestServerParameters;
+import org.onap.policy.common.endpoints.parameters.TopicParameterGroup;
+import org.onap.policy.common.parameters.ParameterGroupImpl;
+import org.onap.policy.common.parameters.annotations.NotNull;
+import org.onap.policy.models.provider.PolicyModelsProviderParameters;
+
+/**
+ * Class to hold all parameters needed for the Control Loop runtime component.
+ *
+ */
+@NotNull
+@NotBlank
+@Getter
+public class ClRuntimeParameterGroup extends ParameterGroupImpl {
+ private RestServerParameters restServerParameters;
+ private PolicyModelsProviderParameters databaseProviderParameters;
+ private ParticipantParameters participantParameters;
+ private TopicParameterGroup topicParameterGroup;
+ private List<BusTopicParams> healthCheckRestClientParameters;
+
+ /**
+ * Create the Control Loop parameter group.
+ *
+ * @param name the parameter group name
+ */
+ public ClRuntimeParameterGroup(final String name) {
+ super(name);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java
new file mode 100644
index 000000000..a463ad171
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java
@@ -0,0 +1,77 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.parameters;
+
+import java.io.File;
+import javax.ws.rs.core.Response;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.runtime.main.startstop.ClRuntimeCommandLineArguments;
+import org.onap.policy.common.parameters.ValidationResult;
+import org.onap.policy.common.utils.coder.Coder;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+
+/**
+ * This class handles reading, parsing and validating of control loop runtime parameters from JSON files.
+ */
+public class ClRuntimeParameterHandler {
+
+ private static final Coder CODER = new StandardCoder();
+
+ /**
+ * Read the parameters from the parameter file.
+ *
+ * @param arguments the arguments passed to control loop runtime
+ * @return the parameters read from the configuration file
+ * @throws ControlLoopException on parameter exceptions
+ */
+ public ClRuntimeParameterGroup getParameters(final ClRuntimeCommandLineArguments arguments)
+ throws ControlLoopException {
+ ClRuntimeParameterGroup clRuntimeParameterGroup = null;
+
+ // Read the parameters
+ try {
+ // Read the parameters from JSON
+ File file = new File(arguments.getFullConfigurationFilePath());
+ clRuntimeParameterGroup = CODER.decode(file, ClRuntimeParameterGroup.class);
+ } catch (final CoderException e) {
+ throw new ControlLoopException(
+ Response.Status.NOT_ACCEPTABLE, "error reading parameters from \""
+ + arguments.getConfigurationFilePath() + "\"\n" + "(" + e.getClass().getSimpleName() + ")",
+ e);
+ }
+
+ // The JSON processing returns null if there is an empty file
+ if (clRuntimeParameterGroup == null) {
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE,
+ "no parameters found in \"" + arguments.getConfigurationFilePath() + "\"");
+ }
+
+ // validate the parameters
+ final ValidationResult validationResult = clRuntimeParameterGroup.validate();
+ if (!validationResult.isValid()) {
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, "validation error(s) on parameters from \""
+ + arguments.getConfigurationFilePath() + "\"\n" + validationResult.getResult());
+ }
+
+ return clRuntimeParameterGroup;
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java
new file mode 100644
index 000000000..dfc1b2806
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java
@@ -0,0 +1,59 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.runtime.main.parameters;
+
+import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import org.onap.policy.common.parameters.ParameterGroupImpl;
+import org.onap.policy.common.parameters.annotations.Min;
+import org.onap.policy.common.parameters.annotations.NotBlank;
+import org.onap.policy.common.parameters.annotations.NotNull;
+
+/**
+ * Parameters for communicating with participants.
+ */
+@NotNull
+@NotBlank
+@Getter
+public class ParticipantParameters extends ParameterGroupImpl {
+
+ /**
+ * Default maximum message age, in milliseconds, that should be examined. Any message
+ * older than this is discarded.
+ */
+ public static final long DEFAULT_MAX_AGE_MS = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES);
+
+
+ @Min(1)
+ private long heartBeatMs;
+
+ @Min(1)
+ private long maxMessageAgeMs = DEFAULT_MAX_AGE_MS;
+
+ private ParticipantUpdateParameters updateParameters;
+ private ParticipantStateChangeParameters stateChangeParameters;
+
+
+ /**
+ * Constructs the object.
+ */
+ public ParticipantParameters() {
+ super(ParticipantParameters.class.getSimpleName());
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java
new file mode 100644
index 000000000..2eea4ab51
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java
@@ -0,0 +1,53 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.runtime.main.parameters;
+
+import lombok.Getter;
+import org.onap.policy.common.parameters.ParameterGroupImpl;
+import org.onap.policy.common.parameters.annotations.Min;
+import org.onap.policy.common.parameters.annotations.NotBlank;
+import org.onap.policy.common.parameters.annotations.NotNull;
+
+/**
+ * Parameters for Participant STATE-CHANGE requests.
+ */
+@NotNull
+@NotBlank
+@Getter
+public class ParticipantStateChangeParameters extends ParameterGroupImpl {
+
+ /**
+ * Maximum number of times to re-send a request to a PDP.
+ */
+ @Min(value = 0)
+ private int maxRetryCount;
+
+ /**
+ * Maximum time to wait, in milliseconds, for a PDP response.
+ */
+ @Min(value = 0)
+ private long maxWaitMs;
+
+ /**
+ * Constructs the object.
+ */
+ public ParticipantStateChangeParameters() {
+ super(ParticipantStateChangeParameters.class.getSimpleName());
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java
new file mode 100644
index 000000000..2af5be534
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java
@@ -0,0 +1,54 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.clamp.controlloop.runtime.main.parameters;
+
+import lombok.Getter;
+import org.onap.policy.common.parameters.ParameterGroupImpl;
+import org.onap.policy.common.parameters.annotations.Min;
+import org.onap.policy.common.parameters.annotations.NotBlank;
+import org.onap.policy.common.parameters.annotations.NotNull;
+
+/**
+ * Parameters for Participant UPDATE requests.
+ */
+@NotNull
+@NotBlank
+@Getter
+public class ParticipantUpdateParameters extends ParameterGroupImpl {
+
+ /**
+ * Maximum number of times to re-send a request to a PDP.
+ */
+ @Min(value = 0)
+ private int maxRetryCount;
+
+ /**
+ * Maximum time to wait, in milliseconds, for a PDP response.
+ */
+ @Min(value = 0)
+ private long maxWaitMs;
+
+ /**
+ * Constructs the object.
+ */
+ public ParticipantUpdateParameters() {
+ super(ParticipantUpdateParameters.class.getSimpleName());
+ }
+}
+
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java
new file mode 100644
index 000000000..f166de5d6
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/ControlLoopAafFilter.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.rest;
+
+import org.onap.policy.common.endpoints.http.server.aaf.AafGranularAuthFilter;
+import org.onap.policy.common.utils.resources.MessageConstants;
+
+/**
+ * Class to manage AAF filters for the control loop runtime component.
+ */
+public class ControlLoopAafFilter extends AafGranularAuthFilter {
+
+ public static final String AAF_NODETYPE = MessageConstants.POLICY_CLAMP;
+ public static final String AAF_ROOT_PERMISSION = DEFAULT_NAMESPACE + "." + AAF_NODETYPE;
+
+ @Override
+ public String getPermissionTypeRoot() {
+ return AAF_ROOT_PERMISSION;
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java
new file mode 100644
index 000000000..dd3fa30fc
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/rest/RestController.java
@@ -0,0 +1,115 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.rest;
+
+import io.swagger.annotations.Api;
+import io.swagger.annotations.BasicAuthDefinition;
+import io.swagger.annotations.Info;
+import io.swagger.annotations.SecurityDefinition;
+import io.swagger.annotations.SwaggerDefinition;
+import io.swagger.annotations.Tag;
+import java.net.HttpURLConnection;
+import java.util.UUID;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.ResponseBuilder;
+
+/**
+ * Common superclass to provide REST endpoints for the control loop service.
+ */
+// @formatter:off
+@Path("/onap/controlloop/v2")
+@Api(value = "Control Loop API")
+@Produces({MediaType.APPLICATION_JSON, RestController.APPLICATION_YAML})
+@SwaggerDefinition(
+ info = @Info(description =
+ "Control Loop Service", version = "v1.0",
+ title = "Control Loop"),
+ consumes = {MediaType.APPLICATION_JSON, RestController.APPLICATION_YAML},
+ produces = {MediaType.APPLICATION_JSON, RestController.APPLICATION_YAML},
+ schemes = {SwaggerDefinition.Scheme.HTTP, SwaggerDefinition.Scheme.HTTPS},
+ tags = {@Tag(name = "controlloop", description = "Control Loop Service")},
+ securityDefinition = @SecurityDefinition(basicAuthDefinitions = {@BasicAuthDefinition(key = "basicAuth")}))
+// @formatter:on
+public class RestController {
+ public static final String APPLICATION_YAML = "application/yaml";
+
+ public static final String EXTENSION_NAME = "interface info";
+
+ public static final String API_VERSION_NAME = "api-version";
+ public static final String API_VERSION = "1.0.0";
+
+ public static final String LAST_MOD_NAME = "last-mod-release";
+ public static final String LAST_MOD_RELEASE = "Dublin";
+
+ public static final String VERSION_MINOR_NAME = "X-MinorVersion";
+ public static final String VERSION_MINOR_DESCRIPTION =
+ "Used to request or communicate a MINOR version back from the client"
+ + " to the server, and from the server back to the client";
+
+ public static final String VERSION_PATCH_NAME = "X-PatchVersion";
+ public static final String VERSION_PATCH_DESCRIPTION = "Used only to communicate a PATCH version in a response for"
+ + " troubleshooting purposes only, and will not be provided by" + " the client on request";
+
+ public static final String VERSION_LATEST_NAME = "X-LatestVersion";
+ public static final String VERSION_LATEST_DESCRIPTION = "Used only to communicate an API's latest version";
+
+ public static final String REQUEST_ID_NAME = "X-ONAP-RequestID";
+ public static final String REQUEST_ID_HDR_DESCRIPTION = "Used to track REST transactions for logging purpose";
+ public static final String REQUEST_ID_PARAM_DESCRIPTION = "RequestID for http transaction";
+
+ public static final String AUTHORIZATION_TYPE = "basicAuth";
+
+ public static final int AUTHENTICATION_ERROR_CODE = HttpURLConnection.HTTP_UNAUTHORIZED;
+ public static final int AUTHORIZATION_ERROR_CODE = HttpURLConnection.HTTP_FORBIDDEN;
+ public static final int SERVER_ERROR_CODE = HttpURLConnection.HTTP_INTERNAL_ERROR;
+
+ public static final String AUTHENTICATION_ERROR_MESSAGE = "Authentication Error";
+ public static final String AUTHORIZATION_ERROR_MESSAGE = "Authorization Error";
+ public static final String SERVER_ERROR_MESSAGE = "Internal Server Error";
+
+ /**
+ * Adds version headers to the response.
+ *
+ * @param respBuilder response builder
+ * @return the response builder, with version headers
+ */
+ public ResponseBuilder addVersionControlHeaders(ResponseBuilder respBuilder) {
+ return respBuilder.header(VERSION_MINOR_NAME, "0").header(VERSION_PATCH_NAME, "0").header(VERSION_LATEST_NAME,
+ API_VERSION);
+ }
+
+ /**
+ * Adds logging headers to the response.
+ *
+ * @param respBuilder response builder
+ * @return the response builder, with version logging
+ */
+ public ResponseBuilder addLoggingHeaders(ResponseBuilder respBuilder, UUID requestId) {
+ if (requestId == null) {
+ // Generate a random uuid if client does not embed requestId in rest request
+ return respBuilder.header(REQUEST_ID_NAME, UUID.randomUUID());
+ }
+
+ return respBuilder.header(REQUEST_ID_NAME, requestId);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java
new file mode 100644
index 000000000..a4238a9c4
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java
@@ -0,0 +1,186 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.startstop;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicReference;
+import javax.ws.rs.core.Response.Status;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException;
+import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningHandler;
+import org.onap.policy.clamp.controlloop.runtime.instantiation.InstantiationHandler;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
+import org.onap.policy.clamp.controlloop.runtime.main.rest.ControlLoopAafFilter;
+import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringHandler;
+import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.event.comm.TopicSource;
+import org.onap.policy.common.endpoints.http.server.RestServer;
+import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
+import org.onap.policy.common.parameters.ParameterService;
+import org.onap.policy.common.utils.services.ServiceManagerContainer;
+
+/**
+ * This class activates the control loop runtime component as a complete service together with all its controllers,
+ * listeners & handlers.
+ */
+public class ClRuntimeActivator extends ServiceManagerContainer {
+ // Name of the message type for messages on topics
+ private static final String[] MSG_TYPE_NAMES = {"messageType"};
+
+ private final ClRuntimeParameterGroup clRuntimeParameterGroup;
+
+ // Topics from which the application receives and to which the application sends messages
+ private List<TopicSink> topicSinks;
+ private List<TopicSource> topicSources;
+
+ /**
+ * Listens for messages on the topic, decodes them into a message, and then dispatches them.
+ */
+ private final MessageTypeDispatcher msgDispatcher;
+
+ /**
+ * Instantiate the activator for the control loop runtime as a complete service.
+ *
+ * @param clRuntimeParameterGroup the parameters for the control loop runtime service
+ */
+ public ClRuntimeActivator(final ClRuntimeParameterGroup clRuntimeParameterGroup) {
+
+ if (clRuntimeParameterGroup == null || !clRuntimeParameterGroup.isValid()) {
+ throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, "ParameterGroup not valid");
+ }
+
+ this.clRuntimeParameterGroup = clRuntimeParameterGroup;
+
+ topicSinks = TopicEndpointManager.getManager()
+ .addTopicSinks(clRuntimeParameterGroup.getTopicParameterGroup().getTopicSinks());
+
+ topicSources = TopicEndpointManager.getManager()
+ .addTopicSources(clRuntimeParameterGroup.getTopicParameterGroup().getTopicSources());
+
+ try {
+ msgDispatcher = new MessageTypeDispatcher(MSG_TYPE_NAMES);
+ } catch (final RuntimeException e) {
+ throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR,
+ "topic message dispatcher failed to start", e);
+ }
+
+ final AtomicReference<ControlLoopHandler> commissioningHandler = new AtomicReference<>();
+ final AtomicReference<ControlLoopHandler> instantiationHandler = new AtomicReference<>();
+ final AtomicReference<ControlLoopHandler> supervisionHandler = new AtomicReference<>();
+ final AtomicReference<ControlLoopHandler> monitoringHandler = new AtomicReference<>();
+ final AtomicReference<RestServer> restServer = new AtomicReference<>();
+
+ // @formatter:off
+ addAction("Control loop runtime parameters",
+ () -> ParameterService.register(clRuntimeParameterGroup),
+ () -> ParameterService.deregister(clRuntimeParameterGroup.getName()));
+
+ addAction("Topic endpoint management",
+ () -> TopicEndpointManager.getManager().start(),
+ () -> TopicEndpointManager.getManager().shutdown());
+
+ addAction("Commissioning Handler",
+ () -> commissioningHandler.set(new CommissioningHandler(clRuntimeParameterGroup)),
+ () -> commissioningHandler.get().close());
+
+ addAction("Instantiation Handler",
+ () -> instantiationHandler.set(new InstantiationHandler(clRuntimeParameterGroup)),
+ () -> instantiationHandler.get().close());
+
+ addAction("Supervision Handler",
+ () -> supervisionHandler.set(new SupervisionHandler(clRuntimeParameterGroup)),
+ () -> supervisionHandler.get().close());
+
+ addAction("Monitoring Handler",
+ () -> monitoringHandler.set(new MonitoringHandler(clRuntimeParameterGroup)),
+ () -> monitoringHandler.get().close());
+
+ addHandlerActions("Commissioning", commissioningHandler);
+ addHandlerActions("Instantiation", instantiationHandler);
+ addHandlerActions("Supervision", supervisionHandler);
+ addHandlerActions("Monitoring", monitoringHandler);
+
+ addAction("Topic Message Dispatcher", this::registerMsgDispatcher, this::unregisterMsgDispatcher);
+
+ clRuntimeParameterGroup.getRestServerParameters().setName(clRuntimeParameterGroup.getName());
+
+ addAction("REST server",
+ () -> {
+ Set<Class<?>> providerClasses = new HashSet<>();
+ providerClasses.addAll(commissioningHandler.get().getProviderClasses());
+ providerClasses.addAll(instantiationHandler.get().getProviderClasses());
+ providerClasses.addAll(supervisionHandler.get().getProviderClasses());
+ providerClasses.addAll(monitoringHandler.get().getProviderClasses());
+
+ RestServer server = new RestServer(clRuntimeParameterGroup.getRestServerParameters(),
+ ControlLoopAafFilter.class,
+ providerClasses.toArray(new Class<?>[providerClasses.size()]));
+
+ restServer.set(server);
+ restServer.get().start();
+ },
+ () -> restServer.get().stop());
+ // @formatter:on
+ }
+
+ private void addHandlerActions(final String name, final AtomicReference<ControlLoopHandler> handler) {
+ addAction(name + " Providers",
+ () -> handler.get().startProviders(),
+ () -> handler.get().stopProviders());
+ addAction(name + " Listeners",
+ () -> handler.get().startAndRegisterListeners(msgDispatcher),
+ () -> handler.get().stopAndUnregisterListeners(msgDispatcher));
+ addAction(name + " Publishers",
+ () -> handler.get().startAndRegisterPublishers(topicSinks),
+ () -> handler.get().stopAndUnregisterPublishers());
+ }
+
+ /**
+ * Registers the dispatcher with the topic source(s).
+ */
+ private void registerMsgDispatcher() {
+ for (final TopicSource source : topicSources) {
+ source.register(msgDispatcher);
+ }
+ }
+
+ /**
+ * Unregisters the dispatcher from the topic source(s).
+ */
+ private void unregisterMsgDispatcher() {
+ for (final TopicSource source : topicSources) {
+ source.unregister(msgDispatcher);
+ }
+ }
+
+ /**
+ * Get the parameters used by the activator.
+ *
+ * @return the parameters of the activator
+ */
+ public ClRuntimeParameterGroup getParameterGroup() {
+ return clRuntimeParameterGroup;
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java
new file mode 100644
index 000000000..f36bb858b
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.startstop;
+
+import java.io.File;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URL;
+import java.util.Arrays;
+import javax.ws.rs.core.Response;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException;
+import org.onap.policy.clamp.controlloop.common.startstop.CommonCommandLineArguments;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+
+/**
+ * This class reads and handles command line parameters for the control loop runtime service.
+ */
+public class ClRuntimeCommandLineArguments {
+ private static final String FILE_MESSAGE_PREAMBLE = " file \"";
+ private static final int HELP_LINE_LENGTH = 120;
+
+ private final Options options;
+ private final CommonCommandLineArguments commonCommandLineArguments;
+
+ @Getter()
+ @Setter()
+ private String configurationFilePath = null;
+
+ /**
+ * Construct the options for the control loop runtime component.
+ */
+ public ClRuntimeCommandLineArguments() {
+ options = new Options();
+ commonCommandLineArguments = new CommonCommandLineArguments(options);
+ }
+
+ /**
+ * Construct the options for the CLI editor and parse in the given arguments.
+ *
+ * @param args The command line arguments
+ */
+ public ClRuntimeCommandLineArguments(final String[] args) {
+ // Set up the options with the default constructor
+ this();
+
+ // Parse the arguments
+ try {
+ parse(args);
+ } catch (final ControlLoopException e) {
+ throw new ControlLoopRuntimeException(Response.Status.NOT_ACCEPTABLE,
+ "parse error on control loop runtime parameters", e);
+ }
+ }
+
+ /**
+ * Parse the command line options.
+ *
+ * @param args The command line arguments
+ * @return a string with a message for help and version, or null if there is no message
+ * @throws ControlLoopException on command argument errors
+ */
+ public String parse(final String[] args) throws ControlLoopException {
+ // Clear all our arguments
+ setConfigurationFilePath(null);
+ CommandLine commandLine = null;
+ try {
+ commandLine = new DefaultParser().parse(options, args);
+ } catch (final ParseException e) {
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE,
+ "invalid command line arguments specified : " + e.getMessage());
+ }
+
+ // Arguments left over after Commons CLI does its stuff
+ final String[] remainingArgs = commandLine.getArgs();
+
+ if (remainingArgs.length > 0) {
+ throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE,
+ "too many command line arguments specified : " + Arrays.toString(args));
+ }
+
+ if (commandLine.hasOption('h')) {
+ return commonCommandLineArguments.help(Main.class.getName(), options);
+ }
+
+ if (commandLine.hasOption('v')) {
+ return commonCommandLineArguments.version();
+ }
+
+ if (commandLine.hasOption('c')) {
+ setConfigurationFilePath(commandLine.getOptionValue('c'));
+ }
+
+ return null;
+ }
+
+ /**
+ * Validate the command line options.
+ *
+ * @throws ControlLoopException on command argument validation errors
+ */
+ public void validate() throws ControlLoopException {
+ commonCommandLineArguments.validate(configurationFilePath);
+ }
+
+ /**
+ * Gets the full expanded configuration file path.
+ *
+ * @return the configuration file path
+ */
+ public String getFullConfigurationFilePath() {
+ return ResourceUtils.getFilePath4Resource(getConfigurationFilePath());
+ }
+
+ /**
+ * Sets the configuration file path.
+ *
+ * @param configurationFilePath the configuration file path
+ */
+ public void setConfigurationFilePath(final String configurationFilePath) {
+ this.configurationFilePath = configurationFilePath;
+
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java
new file mode 100644
index 000000000..8e60d68cf
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java
@@ -0,0 +1,156 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.main.startstop;
+
+import java.util.Arrays;
+import javax.ws.rs.core.Response;
+import org.onap.policy.clamp.controlloop.common.ControlLoopConstants;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterHandler;
+import org.onap.policy.common.utils.resources.MessageConstants;
+import org.onap.policy.common.utils.services.Registry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class initiates ONAP Policy Framework Control Loop runtime component.
+ */
+public class Main {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(Main.class);
+
+ private ClRuntimeActivator activator;
+ private ClRuntimeParameterGroup parameterGroup;
+
+ /**
+ * Instantiates the control loop runtime service.
+ *
+ * @param args the command line arguments
+ */
+ public Main(final String[] args) {
+ final String argumentString = Arrays.toString(args);
+ LOGGER.info("Starting the control loop runtime service with arguments - {}", argumentString);
+
+ // Check the arguments
+ final ClRuntimeCommandLineArguments arguments = new ClRuntimeCommandLineArguments();
+ try {
+ // The arguments return a string if there is a message to print and we should exit
+ final String argumentMessage = arguments.parse(args);
+ if (argumentMessage != null) {
+ LOGGER.info(argumentMessage);
+ return;
+ }
+ // Validate that the arguments are sane
+ arguments.validate();
+
+ // Read the parameters
+ parameterGroup = new ClRuntimeParameterHandler().getParameters(arguments);
+
+ // Now, create the activator for the service
+ activator = new ClRuntimeActivator(parameterGroup);
+ Registry.register(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR, activator);
+
+ // Start the activator
+ activator.start();
+ } catch (Exception exp) {
+ if (null != activator) {
+ Registry.unregister(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR);
+ }
+ throw new ControlLoopRuntimeException(Response.Status.BAD_REQUEST,
+ String.format(MessageConstants.START_FAILURE_MSG, MessageConstants.POLICY_CLAMP), exp);
+ }
+
+ // Add a shutdown hook to shut everything down in an orderly manner
+ Runtime.getRuntime().addShutdownHook(new ClRuntimeShutdownHookClass());
+ String successMsg = String.format(MessageConstants.START_SUCCESS_MSG, MessageConstants.POLICY_CLAMP);
+ LOGGER.info(successMsg);
+ }
+
+ /**
+ * Check if main is running.
+ */
+ public boolean isRunning() {
+ return activator != null && activator.isAlive();
+ }
+
+ /**
+ * Get the parameters specified in JSON.
+ *
+ * @return the parameters
+ */
+ public ClRuntimeParameterGroup getParameters() {
+ return parameterGroup;
+ }
+
+ /**
+ * Shut down Execution.
+ *
+ * @throws ControlLoopException on shutdown errors
+ */
+ public void shutdown() throws ControlLoopException {
+ // clear the parameterGroup variable
+ parameterGroup = null;
+
+ // clear the cl runtime activator
+ if (activator != null) {
+ activator.stop();
+ }
+ }
+
+ /**
+ * The Class ClRuntimeShutdownHookClass terminates the control loop runtime service when its run method is called.
+ */
+ private class ClRuntimeShutdownHookClass extends Thread {
+ /*
+ * (non-Javadoc)
+ *
+ * @see java.lang.Runnable#run()
+ */
+ @Override
+ public void run() {
+ if (!activator.isAlive()) {
+ return;
+ }
+
+ try {
+ // Shutdown the control loop runtime service and wait for everything to stop
+ activator.stop();
+ } catch (final RuntimeException e) {
+ LOGGER.warn("error occured during shut down of the control loop runtime service", e);
+ }
+ }
+ }
+
+ /**
+ * The main method.
+ *
+ * @param args the arguments
+ */
+ public static void main(final String[] args) { // NOSONAR
+ /*
+ * NOTE: arguments are validated by the constructor, thus sonar is disabled.
+ */
+
+ new Main(args);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java
new file mode 100644
index 000000000..a7ad9180a
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringHandler.java
@@ -0,0 +1,84 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.monitoring;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Set;
+import javax.ws.rs.core.Response;
+import lombok.Getter;
+import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
+import org.onap.policy.clamp.controlloop.runtime.monitoring.rest.MonitoringQueryController;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
+import org.onap.policy.common.utils.services.Registry;
+import org.onap.policy.models.base.PfModelRuntimeException;
+
+/**
+ * This class handles monitoring of control loop definitions,
+ * so only one object of this type should be built at a time.
+ *
+ * <p/>
+ * It is effectively a singleton that is started at system start.
+ */
+public class MonitoringHandler extends ControlLoopHandler {
+
+ @Getter
+ private MonitoringProvider monitoringProvider;
+
+ /**
+ * Gets the Monitoring Handler.
+ *
+ * @return MonitoringHandler
+ */
+ public static MonitoringHandler getInstance() {
+ return Registry.get(MonitoringHandler.class.getName());
+ }
+
+ /**
+ * Create a handler.
+ *
+ * @param controlLoopParameters the parameters for access to the database
+ */
+ public MonitoringHandler(ClRuntimeParameterGroup controlLoopParameters) {
+ super(controlLoopParameters.getDatabaseProviderParameters());
+ }
+
+ @Override
+ public Set<Class<?>> getProviderClasses() {
+ return Set.of(MonitoringQueryController.class);
+ }
+
+ @Override
+ public void startProviders() {
+ monitoringProvider = new MonitoringProvider(getDatabaseProviderParameters());
+ }
+
+ @Override
+ public void stopProviders() {
+ try {
+ monitoringProvider.close();
+ } catch (IOException e) {
+ throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR, "Cannot stop provider", e);
+ }
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java
new file mode 100644
index 000000000..193f8d557
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/MonitoringProvider.java
@@ -0,0 +1,273 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.monitoring;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.time.Instant;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import lombok.NonNull;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatistics;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatistics;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatisticsList;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ClElementStatisticsProvider;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantStatisticsProvider;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.provider.PolicyModelsProviderParameters;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+
+/**
+ * This class provides information about statistics data of CL elements and CL Participants in database to callers.
+ */
+public class MonitoringProvider implements Closeable {
+
+ private static final String DESC_ORDER = "DESC";
+ private final ParticipantStatisticsProvider participantStatisticsProvider;
+ private final ClElementStatisticsProvider clElementStatisticsProvider;
+ private final ControlLoopProvider controlLoopProvider;
+
+ /**
+ * Create a Monitoring provider.
+ *
+ */
+ public MonitoringProvider(PolicyModelsProviderParameters parameters) {
+
+ try {
+ participantStatisticsProvider = new ParticipantStatisticsProvider(parameters);
+ clElementStatisticsProvider = new ClElementStatisticsProvider(parameters);
+ controlLoopProvider = new ControlLoopProvider(parameters);
+ } catch (PfModelException e) {
+ throw new PfModelRuntimeException(e);
+ }
+ }
+
+ @Override
+ public void close() throws IOException {
+ controlLoopProvider.close();
+ clElementStatisticsProvider.close();
+ participantStatisticsProvider.close();
+ }
+
+ /**
+ * Create participant statistics.
+ *
+ * @param participantStatistics the participant statistics
+ * @return the result of create operation
+ * @throws PfModelException on creation errors
+ */
+ public ParticipantStatisticsList createParticipantStatistics(List<ParticipantStatistics> participantStatistics)
+ throws PfModelException {
+ ParticipantStatisticsList participantStatisticsList = new ParticipantStatisticsList();
+ participantStatisticsList.setStatisticsList(participantStatisticsProvider
+ .createParticipantStatistics(participantStatistics));
+
+ return participantStatisticsList;
+ }
+
+ /**
+ * Create clElement statistics.
+ *
+ * @param clElementStatisticsList the clElement statistics
+ * @return the result of create operation
+ * @throws PfModelException on creation errors
+ */
+ public ClElementStatisticsList createClElementStatistics(List<ClElementStatistics> clElementStatisticsList)
+ throws PfModelException {
+ ClElementStatisticsList elementStatisticsList = new ClElementStatisticsList();
+ elementStatisticsList.setClElementStatistics(clElementStatisticsProvider
+ .createClElementStatistics(clElementStatisticsList));
+
+ return elementStatisticsList;
+ }
+
+ /**
+ * Get participant statistics based on specific filters.
+ *
+ * @param name the name of the participant statistics to get, null to get all statistics
+ * @param version the version of the participant statistics to get, null to get all statistics
+ * @param recordCount number of records to be fetched.
+ * @param startTime start of the timestamp, from statistics to be filtered
+ * @param endTime end of the timestamp up to which statistics to be filtered
+ * @return the participant found
+ */
+ public ParticipantStatisticsList fetchFilteredParticipantStatistics(@NonNull final String name,
+ final String version, int recordCount,
+ Instant startTime, Instant endTime) {
+ ParticipantStatisticsList participantStatisticsList = new ParticipantStatisticsList();
+
+ //Additional parameters can be added in filterMap for filtering data.
+ Map<String, Object> filterMap = null;
+ participantStatisticsList.setStatisticsList(participantStatisticsProvider.getFilteredParticipantStatistics(
+ name, version, startTime, endTime, filterMap, DESC_ORDER, recordCount));
+
+ return participantStatisticsList;
+ }
+
+ /**
+ * Get all participant statistics records found for a specific control loop. *
+ *
+ * @param controlLoopName name of the control loop
+ * @param controlLoopVersion version of the control loop
+ * @return All the participant statistics found
+ * @throws PfModelException on errors getting participant statistics
+ */
+ public ParticipantStatisticsList fetchParticipantStatsPerControlLoop(@NonNull final String controlLoopName,
+ @NonNull final String controlLoopVersion)
+ throws PfModelException {
+ ParticipantStatisticsList statisticsList = new ParticipantStatisticsList();
+ List<ParticipantStatistics> participantStatistics = new ArrayList<>();
+ try {
+ //Fetch all participantIds for a specific control loop
+ List<ToscaConceptIdentifier> participantIds = getAllParticipantIdsPerControlLoop(controlLoopName,
+ controlLoopVersion);
+ for (ToscaConceptIdentifier id: participantIds) {
+ participantStatistics.addAll(participantStatisticsProvider.getFilteredParticipantStatistics(
+ id.getName(), id.getVersion(), null, null, null, DESC_ORDER, 0));
+ }
+ statisticsList.setStatisticsList(participantStatistics);
+ } catch (PfModelException e) {
+ throw new PfModelRuntimeException(e);
+ }
+ return statisticsList;
+ }
+
+
+
+ /**
+ * Get clElement statistics based on specific filters.
+ *
+ * @param name the name of the clElement statistics to get, null to get all statistics
+ * @param version the version of the clElement statistics to get, null to get all statistics
+ * @param id UUID of the control loop element
+ * @param startTime start of the timestamp, from statistics to be filtered
+ * @param endTime end of the timestamp up to which statistics to be filtered
+ * @param recordCount number of records to be fetched.
+ * @return the participant found
+ * @throws PfModelException on errors getting control loop statistics
+ */
+ public ClElementStatisticsList fetchFilteredClElementStatistics(@NonNull final String name, final String version,
+ final String id, Instant startTime, Instant endTime,
+ int recordCount) throws PfModelException {
+ ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList();
+ Map<String, Object> filterMap = new HashMap<>();
+ //Adding UUID in filter if present
+ if (id != null) {
+ filterMap.put("localName", id);
+ }
+ clElementStatisticsList.setClElementStatistics(clElementStatisticsProvider.getFilteredClElementStatistics(
+ name, version, startTime, endTime, filterMap, DESC_ORDER, recordCount));
+
+ return clElementStatisticsList;
+ }
+
+
+ /**
+ * Get clElement statistics per control loop.
+ *
+ * @param name the name of the control loop
+ * @param version the version of the control loop
+ * @return the clElement statistics found
+ * @throws PfModelException on errors getting control loop statistics
+ */
+ public ClElementStatisticsList fetchClElementStatsPerControlLoop(@NonNull final String name,
+ @NonNull final String version)
+ throws PfModelException {
+ ClElementStatisticsList clElementStatisticsList = new ClElementStatisticsList();
+ List<ClElementStatistics> clElementStats = new ArrayList<>();
+ try {
+ List<ControlLoopElement> clElements = new ArrayList<>();
+ //Fetch all control loop elements for the control loop
+ ControlLoop controlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(name,
+ version));
+ if (controlLoop != null) {
+ clElements.addAll(controlLoop.getElements().values());
+ //Collect control loop element statistics for each cl element.
+ for (ControlLoopElement clElement : clElements) {
+ clElementStats.addAll(fetchFilteredClElementStatistics(clElement.getParticipantId().getName(),
+ clElement.getParticipantId().getVersion(), clElement.getId().toString(), null,
+ null, 0).getClElementStatistics());
+ }
+ }
+ clElementStatisticsList.setClElementStatistics(clElementStats);
+ } catch (PfModelException e) {
+ throw new PfModelRuntimeException(e);
+ }
+ return clElementStatisticsList;
+ }
+
+ /**
+ * If required, REST end point can be defined for this method to fetch associated participant Ids
+ * for a control loop.
+ *
+ * @param name the name of the control loop
+ * @param version the version of the control loop
+ * @return List of participant Id
+ * @throws PfModelException on errors
+ */
+ public List<ToscaConceptIdentifier> getAllParticipantIdsPerControlLoop(String name, String version)
+ throws PfModelException {
+ List<ToscaConceptIdentifier> participantIds = new ArrayList<>();
+ ControlLoop controlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(name, version));
+ if (controlLoop != null) {
+ for (ControlLoopElement clElement : controlLoop.getElements().values()) {
+ participantIds.add(clElement.getParticipantId());
+ }
+ }
+ return participantIds;
+ }
+
+ /**
+ * If required, REST end point can be defined for this method to fetch associated control loop element Ids
+ * for a control loop.
+ *
+ * @param name the name of the control loop
+ * @param version the version of the control loop
+ * @return Map of control loop Id and participant details
+ * @throws PfModelException on errors
+ */
+ public Map<String, ToscaConceptIdentifier> getAllClElementsIdPerControlLoop(String name, String version)
+ throws PfModelException {
+ Map<String, ToscaConceptIdentifier> clElementId = new HashMap<>();
+ ControlLoop controlLoop = controlLoopProvider.getControlLoop(new ToscaConceptIdentifier(name, version));
+ if (controlLoop != null) {
+ for (ControlLoopElement clElement : controlLoop.getElements().values()) {
+ clElementId.put(clElement.getId().toString(), clElement.getParticipantId());
+ }
+ }
+ return clElementId;
+ }
+
+ public void updateClElementStatistics(List<ClElementStatistics> clElementStatistics) {
+ // TODO Auto-generated method stub
+ }
+
+ public void updateParticipantStatistics(List<ParticipantStatistics> statisticsList) {
+ // TODO Auto-generated method stub
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java
new file mode 100644
index 000000000..2e19ffe3a
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/monitoring/rest/MonitoringQueryController.java
@@ -0,0 +1,371 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.monitoring.rest;
+
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import io.swagger.annotations.ApiResponse;
+import io.swagger.annotations.ApiResponses;
+import io.swagger.annotations.Authorization;
+import io.swagger.annotations.Extension;
+import io.swagger.annotations.ExtensionProperty;
+import io.swagger.annotations.ResponseHeader;
+import java.time.Instant;
+import java.util.UUID;
+import javax.ws.rs.DefaultValue;
+import javax.ws.rs.GET;
+import javax.ws.rs.HeaderParam;
+import javax.ws.rs.Path;
+import javax.ws.rs.QueryParam;
+import javax.ws.rs.core.Response;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ClElementStatisticsList;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ParticipantStatisticsList;
+import org.onap.policy.clamp.controlloop.runtime.main.rest.RestController;
+import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringHandler;
+import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringProvider;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class handles REST endpoints for CL Statistics monitoring.
+ */
+public class MonitoringQueryController extends RestController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MonitoringQueryController.class);
+ private final MonitoringProvider provider;
+
+ /**
+ * Create Monitoring Controller.
+ */
+ public MonitoringQueryController() {
+ this.provider = MonitoringHandler.getInstance().getMonitoringProvider();
+ }
+
+
+ /**
+ * Queries details of control loop participants statistics.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the participant to get, null for all participants statistics
+ * @param recordCount the record count to be fetched
+ * @return the participant statistics
+ */
+ // @formatter:off
+ @GET
+ @Path("/monitoring/participant")
+ @ApiOperation(value = "Query details of the requested participant stats",
+ notes = "Queries details of the requested participant stats, returning all participant stats",
+ response = ParticipantStatisticsList.class,
+ tags = {
+ "Clamp control loop Monitoring API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ }
+ )
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response queryParticipantStatistics(@HeaderParam(REQUEST_ID_NAME)
+ @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop participant name", required = true)
+ @QueryParam("name") final String name,
+ @ApiParam(value = "Control Loop participant version", required = true)
+ @QueryParam("version") final String version,
+ @ApiParam(value = "Record count", required = false) @DefaultValue("0")
+ @QueryParam("recordCount") final int recordCount,
+ @ApiParam(value = "start time", required = false)
+ @QueryParam("startTime") final String startTime,
+ @ApiParam(value = "end time", required = false)
+ @QueryParam("endTime") final String endTime) {
+
+ try {
+ Instant startTimestamp = null;
+ Instant endTimestamp = null;
+
+ if (startTime != null) {
+ startTimestamp = Instant.parse(startTime);
+ }
+ if (endTime != null) {
+ endTimestamp = Instant.parse(endTime);
+ }
+ ParticipantStatisticsList response = provider.fetchFilteredParticipantStatistics(name, version, recordCount,
+ startTimestamp, endTimestamp);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+ .entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException e) {
+ LOGGER.warn("Monitoring of participants statistics failed", e);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())),
+ requestId).build();
+ }
+
+ }
+
+ /**
+ * Queries details of all participant statistics per control loop.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop
+ * @param version version of the control loop
+ * @return the control loop element statistics
+ */
+ // @formatter:off
+ @GET
+ @Path("/monitoring/participants/controlloop")
+ @ApiOperation(value = "Query details of all the participant stats in a control loop",
+ notes = "Queries details of the participant stats, returning all participant stats",
+ response = ClElementStatisticsList.class,
+ tags = {
+ "Clamp control loop Monitoring API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ })
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response queryParticipantStatisticsPerControlLoop(@HeaderParam(REQUEST_ID_NAME)
+ @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop name", required = true)
+ @QueryParam("name") final String name,
+ @ApiParam(value = "Control Loop version", required = true)
+ @QueryParam("version") final String version) {
+
+ try {
+ ParticipantStatisticsList response = provider.fetchParticipantStatsPerControlLoop(name, version);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+ .entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Monitoring of Cl participant statistics failed", e);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())),
+ requestId).build();
+ }
+
+ }
+
+
+
+ /**
+ * Queries details of all control loop element statistics per control loop.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop
+ * @param version version of the control loop
+ * @return the control loop element statistics
+ */
+ // @formatter:off
+ @GET
+ @Path("/monitoring/clelements/controlloop")
+ @ApiOperation(value = "Query details of the requested cl element stats in a control loop",
+ notes = "Queries details of the requested cl element stats, returning all clElement stats",
+ response = ClElementStatisticsList.class,
+ tags = {
+ "Clamp control loop Monitoring API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ })
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response queryElementStatisticsPerControlLoop(@HeaderParam(REQUEST_ID_NAME)
+ @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Control Loop name", required = true)
+ @QueryParam("name") final String name,
+ @ApiParam(value = "Control Loop version", required = true)
+ @QueryParam("version") final String version) {
+
+ try {
+ ClElementStatisticsList response = provider.fetchClElementStatsPerControlLoop(name, version);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+ .entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Monitoring of Cl Element statistics failed", e);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())),
+ requestId).build();
+ }
+
+ }
+
+
+
+
+ /**
+ * Queries details of all control loop element statistics per control loop.
+ *
+ * @param requestId request ID used in ONAP logging
+ * @param name the name of the control loop
+ * @param version version of the control loop
+ * @param id Id of the control loop element
+ * @param recordCount the record count to be fetched
+ * @return the control loop element statistics
+ */
+ // @formatter:off
+ @GET
+ @Path("/monitoring/clelement")
+ @ApiOperation(value = "Query details of the requested cl element stats",
+ notes = "Queries details of the requested cl element stats, returning all clElement stats",
+ response = ClElementStatisticsList.class,
+ tags = {
+ "Clamp control loop Monitoring API"
+ },
+ authorizations = @Authorization(value = AUTHORIZATION_TYPE),
+ responseHeaders = {
+ @ResponseHeader(
+ name = VERSION_MINOR_NAME, description = VERSION_MINOR_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_PATCH_NAME, description = VERSION_PATCH_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = VERSION_LATEST_NAME, description = VERSION_LATEST_DESCRIPTION,
+ response = String.class),
+ @ResponseHeader(name = REQUEST_ID_NAME, description = REQUEST_ID_HDR_DESCRIPTION,
+ response = UUID.class)},
+ extensions = {
+ @Extension(
+ name = EXTENSION_NAME,
+ properties = {
+ @ExtensionProperty(name = API_VERSION_NAME, value = API_VERSION),
+ @ExtensionProperty(name = LAST_MOD_NAME, value = LAST_MOD_RELEASE)
+ }
+ )
+ })
+ @ApiResponses(
+ value = {
+ @ApiResponse(code = AUTHENTICATION_ERROR_CODE, message = AUTHENTICATION_ERROR_MESSAGE),
+ @ApiResponse(code = AUTHORIZATION_ERROR_CODE, message = AUTHORIZATION_ERROR_MESSAGE),
+ @ApiResponse(code = SERVER_ERROR_CODE, message = SERVER_ERROR_MESSAGE)
+ }
+ )
+ // @formatter:on
+ public Response queryElementStatistics(@HeaderParam(REQUEST_ID_NAME)
+ @ApiParam(REQUEST_ID_PARAM_DESCRIPTION) UUID requestId,
+ @ApiParam(value = "Participant name", required = true)
+ @QueryParam("name") final String name,
+ @ApiParam(value = "Participant version", required = true)
+ @QueryParam("version") final String version,
+ @ApiParam(value = "Record count", required = false)
+ @DefaultValue("0") @QueryParam("recordCount") final int recordCount,
+ @ApiParam(value = "Control Loop element id", required = false)
+ @QueryParam("id") final String id,
+ @ApiParam(value = "start time", required = false)
+ @QueryParam("startTime") final String startTime,
+ @ApiParam(value = "end time", required = false)
+ @QueryParam("endTime") final String endTime) {
+
+ try {
+ Instant startTimestamp = null;
+ Instant endTimestamp = null;
+
+ if (startTime != null) {
+ startTimestamp = Instant.parse(startTime);
+ }
+ if (endTime != null) {
+ endTimestamp = Instant.parse(endTime);
+ }
+ ClElementStatisticsList response = provider.fetchFilteredClElementStatistics(name, version, id,
+ startTimestamp, endTimestamp, recordCount);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(Response.Status.OK)), requestId)
+ .entity(response)
+ .build();
+
+ } catch (PfModelRuntimeException | PfModelException e) {
+ LOGGER.warn("Monitoring of Cl Element statistics failed", e);
+ return addLoggingHeaders(addVersionControlHeaders(Response.status(e.getErrorResponse().getResponseCode())),
+ requestId).build();
+ }
+
+ }
+
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java
new file mode 100644
index 000000000..63bff00fc
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionHandler.java
@@ -0,0 +1,450 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.supervision;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.apache.commons.collections4.CollectionUtils;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException;
+import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException;
+import org.onap.policy.clamp.controlloop.common.handler.ControlLoopHandler;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopState;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.Participant;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ParticipantProvider;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopStateChange;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopUpdate;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantMessageType;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningHandler;
+import org.onap.policy.clamp.controlloop.runtime.commissioning.CommissioningProvider;
+import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup;
+import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringHandler;
+import org.onap.policy.clamp.controlloop.runtime.monitoring.MonitoringProvider;
+import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantControlLoopStateChangePublisher;
+import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantControlLoopUpdatePublisher;
+import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStateChangePublisher;
+import org.onap.policy.clamp.controlloop.runtime.supervision.comm.ParticipantStatusListener;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
+import org.onap.policy.common.utils.services.Registry;
+import org.onap.policy.common.utils.services.ServiceManager;
+import org.onap.policy.common.utils.services.ServiceManagerException;
+import org.onap.policy.models.base.PfModelException;
+import org.onap.policy.models.base.PfModelRuntimeException;
+import org.onap.policy.models.tosca.authorative.concepts.ToscaConceptIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class handles supervision of control loop instances, so only one object of this type should be built at a time.
+ *
+ * <p/> It is effectively a singleton that is started at system start.
+ */
+public class SupervisionHandler extends ControlLoopHandler {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionHandler.class);
+
+ private static final String CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE = "Control loop can't transition from state ";
+ private static final String CONTROL_LOOP_IS_ALREADY_IN_STATE = "Control loop is already in state ";
+ private static final String TO_STATE = " to state ";
+ private static final String AND_TRANSITIONING_TO_STATE = " and transitioning to state ";
+
+ private ControlLoopProvider controlLoopProvider;
+ private ParticipantProvider participantProvider;
+ private CommissioningProvider commissioningProvider;
+ private MonitoringProvider monitoringProvider;
+
+ // Publishers for participant communication
+ private ParticipantStateChangePublisher stateChangePublisher;
+ private ParticipantControlLoopUpdatePublisher controlLoopUpdatePublisher;
+ private ParticipantControlLoopStateChangePublisher controlLoopStateChangePublisher;
+
+ // Database scanner
+ private SupervisionScanner scanner;
+
+ /**
+ * Used to manage the services.
+ */
+ private ServiceManager manager;
+ private ServiceManager publisherManager;
+
+ /**
+ * Gets the SupervisionHandler.
+ *
+ * @return SupervisionHandler
+ */
+ public static SupervisionHandler getInstance() {
+ return Registry.get(SupervisionHandler.class.getName());
+ }
+
+ /**
+ * Create a handler.
+ *
+ * @param clRuntimeParameterGroup the parameters for the control loop runtime
+ */
+ public SupervisionHandler(ClRuntimeParameterGroup clRuntimeParameterGroup) {
+ super(clRuntimeParameterGroup.getDatabaseProviderParameters());
+ // @formatter:off
+ this.manager = new ServiceManager()
+ .addAction("ControlLoop Provider",
+ () -> controlLoopProvider = new ControlLoopProvider(getDatabaseProviderParameters()),
+ () -> controlLoopProvider = null)
+ .addAction("Participant Provider",
+ () -> participantProvider = new ParticipantProvider(getDatabaseProviderParameters()),
+ () -> participantProvider = null);
+ // @formatter:on
+ }
+
+ /**
+ * Supervision trigger called when a command is issued on control loops.
+ *
+ * </p> Causes supervision to start or continue supervision on the control loops in question.
+ *
+ * @param controlLoopIdentifierList the control loops for which the supervision command has been issued
+ * @throws ControlLoopException on supervision triggering exceptions
+ */
+ public void triggerControlLoopSupervision(List<ToscaConceptIdentifier> controlLoopIdentifierList)
+ throws ControlLoopException {
+
+ LOGGER.debug("triggering control loop supervision on control loops {}", controlLoopIdentifierList);
+
+ if (CollectionUtils.isEmpty(controlLoopIdentifierList)) {
+ // This is just to force throwing of the exception in certain circumstances.
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE,
+ "The list of control loops for supervision is empty");
+ }
+
+ for (ToscaConceptIdentifier controlLoopId : controlLoopIdentifierList) {
+ try {
+ ControlLoop controlLoop = controlLoopProvider.getControlLoop(controlLoopId);
+
+ superviseControlLoop(controlLoop);
+
+ controlLoopProvider.updateControlLoop(controlLoop);
+ } catch (PfModelException pfme) {
+ throw new ControlLoopException(pfme.getErrorResponse().getResponseCode(), pfme.getMessage(), pfme);
+ }
+ }
+ }
+
+ @Override
+ public void startAndRegisterListeners(MessageTypeDispatcher msgDispatcher) {
+ msgDispatcher.register(ParticipantMessageType.PARTICIPANT_STATUS.name(), new ParticipantStatusListener());
+ }
+
+ @Override
+ public void startAndRegisterPublishers(List<TopicSink> topicSinks) {
+ // TODO: Use a parameter for the timeout
+ // @formatter:off
+ this.publisherManager = new ServiceManager()
+ .addAction("Supervision scanner",
+ () -> scanner = new SupervisionScanner(controlLoopProvider, 10000),
+ () -> scanner = null)
+ .addAction("ControlLoopUpdate publisher",
+ () -> controlLoopUpdatePublisher = new ParticipantControlLoopUpdatePublisher(topicSinks, -1),
+ () -> controlLoopUpdatePublisher.terminate())
+ .addAction("StateChange Publisher",
+ () -> stateChangePublisher = new ParticipantStateChangePublisher(topicSinks, 10000),
+ () -> stateChangePublisher.terminate())
+ .addAction("ControlLoopStateChange Publisher",
+ () -> controlLoopStateChangePublisher =
+ new ParticipantControlLoopStateChangePublisher(topicSinks, -1),
+ () -> controlLoopStateChangePublisher.terminate());
+ // @formatter:on
+ try {
+ publisherManager.start();
+ } catch (final ServiceManagerException exp) {
+ throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR,
+ "Supervision handler start of publishers or scanner failed", exp);
+ }
+ }
+
+ @Override
+ public void stopAndUnregisterPublishers() {
+ try {
+ publisherManager.stop();
+ } catch (final ServiceManagerException exp) {
+ throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR,
+ "Supervision handler stop of publishers or scanner failed", exp);
+ }
+ }
+
+ @Override
+ public void stopAndUnregisterListeners(MessageTypeDispatcher msgDispatcher) {
+ msgDispatcher.unregister(ParticipantMessageType.PARTICIPANT_STATUS.name());
+ }
+
+ /**
+ * Handle a ParticipantStatus message from a participant.
+ *
+ * @param participantStatusMessage the ParticipantStatus message received from a participant
+ */
+ public void handleParticipantStatusMessage(ParticipantStatus participantStatusMessage) {
+ LOGGER.debug("Participant Status received {}", participantStatusMessage);
+
+ try {
+ superviseParticipant(participantStatusMessage);
+ } catch (PfModelException | ControlLoopException svExc) {
+ LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc);
+ return;
+ }
+
+ try {
+ superviseControlLoops(participantStatusMessage);
+ } catch (PfModelException | ControlLoopException svExc) {
+ LOGGER.warn("error supervising participant {}", participantStatusMessage.getParticipantId(), svExc);
+ }
+ }
+
+ /**
+ * Supervise a control loop, performing whatever actions need to be performed on the control loop.
+ *
+ * @param controlLoop the control loop to supervises
+ * @throws ControlLoopException on supervision errors
+ */
+ private void superviseControlLoop(ControlLoop controlLoop) throws ControlLoopException, PfModelException {
+ switch (controlLoop.getOrderedState()) {
+ case UNINITIALISED:
+ superviseControlLoopUninitialization(controlLoop);
+ break;
+
+ case PASSIVE:
+ superviseControlLoopPassivation(controlLoop);
+ break;
+
+ case RUNNING:
+ superviseControlLoopActivation(controlLoop);
+ break;
+
+ default:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE,
+ "A control loop cannot be commanded to go into state " + controlLoop.getOrderedState().name());
+ }
+ }
+
+ /**
+ * Supervise a control loop uninitialisation, performing whatever actions need to be performed on the control loop,
+ * control loop ordered state is UNINITIALIZED.
+ *
+ * @param controlLoop the control loop to supervises
+ * @throws ControlLoopException on supervision errors
+ */
+ private void superviseControlLoopUninitialization(ControlLoop controlLoop) throws ControlLoopException {
+ switch (controlLoop.getState()) {
+ case UNINITIALISED:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE,
+ CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
+ break;
+
+ case UNINITIALISED2PASSIVE:
+ case PASSIVE:
+ controlLoop.setState(ControlLoopState.PASSIVE2UNINITIALISED);
+ sendControlLoopStateChange(controlLoop);
+ break;
+
+ case PASSIVE2UNINITIALISED:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
+ + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
+ break;
+
+ default:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
+ + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
+ break;
+ }
+ }
+
+ private void superviseControlLoopPassivation(ControlLoop controlLoop)
+ throws ControlLoopException, PfModelException {
+ switch (controlLoop.getState()) {
+ case PASSIVE:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE,
+ CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
+ break;
+ case UNINITIALISED:
+ controlLoop.setState(ControlLoopState.UNINITIALISED2PASSIVE);
+ sendControlLoopUpdate(controlLoop);
+ break;
+
+ case UNINITIALISED2PASSIVE:
+ case RUNNING2PASSIVE:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
+ + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
+ break;
+
+ case RUNNING:
+ controlLoop.setState(ControlLoopState.RUNNING2PASSIVE);
+ sendControlLoopStateChange(controlLoop);
+ break;
+
+ default:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
+ + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
+ break;
+ }
+ }
+
+ private void superviseControlLoopActivation(ControlLoop controlLoop) throws ControlLoopException {
+ switch (controlLoop.getState()) {
+ case RUNNING:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE,
+ CONTROL_LOOP_IS_ALREADY_IN_STATE + controlLoop.getState().name());
+ break;
+
+ case PASSIVE2RUNNING:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_IS_ALREADY_IN_STATE
+ + controlLoop.getState().name() + AND_TRANSITIONING_TO_STATE + controlLoop.getOrderedState());
+ break;
+
+ case PASSIVE:
+ controlLoop.setState(ControlLoopState.PASSIVE2RUNNING);
+ sendControlLoopStateChange(controlLoop);
+ break;
+
+ default:
+ exceptionOccured(Response.Status.NOT_ACCEPTABLE, CONTROL_LOOP_CANNOT_TRANSITION_FROM_STATE
+ + controlLoop.getState().name() + TO_STATE + controlLoop.getOrderedState());
+ break;
+ }
+ }
+
+ private void sendControlLoopUpdate(ControlLoop controlLoop) throws PfModelException {
+ ParticipantControlLoopUpdate pclu = new ParticipantControlLoopUpdate();
+ pclu.setControlLoopId(controlLoop.getKey().asIdentifier());
+ pclu.setControlLoop(controlLoop);
+ // TODO: We should look up the correct TOSCA node template here for the control loop
+ // Tiny hack implemented to return the tosca service template entry from the database and be passed onto dmaap
+ commissioningProvider = CommissioningHandler.getInstance().getProvider();
+ pclu.setControlLoopDefinition(commissioningProvider.getToscaServiceTemplate(null, null));
+ controlLoopUpdatePublisher.send(pclu);
+ }
+
+ private void sendControlLoopStateChange(ControlLoop controlLoop) {
+ ParticipantControlLoopStateChange clsc = new ParticipantControlLoopStateChange();
+ clsc.setControlLoopId(controlLoop.getKey().asIdentifier());
+ clsc.setMessageId(UUID.randomUUID());
+ clsc.setOrderedState(controlLoop.getOrderedState());
+
+ controlLoopStateChangePublisher.send(clsc);
+ }
+
+ private void superviseParticipant(ParticipantStatus participantStatusMessage)
+ throws PfModelException, ControlLoopException {
+ if (participantStatusMessage.getParticipantId() == null) {
+ exceptionOccured(Response.Status.NOT_FOUND,
+ "Participant ID on PARTICIPANT_STATUS message is null");
+ }
+
+ List<Participant> participantList =
+ participantProvider.getParticipants(participantStatusMessage.getParticipantId().getName(),
+ participantStatusMessage.getParticipantId().getVersion());
+
+ if (CollectionUtils.isEmpty(participantList)) {
+ Participant participant = new Participant();
+ participant.setName(participantStatusMessage.getParticipantId().getName());
+ participant.setVersion(participantStatusMessage.getParticipantId().getVersion());
+ participant.setDefinition(new ToscaConceptIdentifier("unknown", "0.0.0"));
+ participant.setParticipantState(participantStatusMessage.getState());
+ participant.setHealthStatus(participantStatusMessage.getHealthStatus());
+
+ participantList.add(participant);
+ participantProvider.createParticipants(participantList);
+ } else {
+ for (Participant participant : participantList) {
+ participant.setParticipantState(participantStatusMessage.getState());
+ participant.setHealthStatus(participantStatusMessage.getHealthStatus());
+ }
+ participantProvider.updateParticipants(participantList);
+ }
+
+ monitoringProvider = MonitoringHandler.getInstance().getMonitoringProvider();
+ monitoringProvider.createParticipantStatistics(
+ List.of(participantStatusMessage.getParticipantStatistics()));
+ }
+
+ private void superviseControlLoops(ParticipantStatus participantStatusMessage)
+ throws PfModelException, ControlLoopException {
+ if (CollectionUtils.isEmpty(participantStatusMessage.getControlLoops().getControlLoopList())) {
+ return;
+ }
+
+ for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) {
+ if (controlLoop == null) {
+ exceptionOccured(Response.Status.NOT_FOUND,
+ "PARTICIPANT_STATUS message references unknown control loop: " + controlLoop);
+ }
+
+ ControlLoop dbControlLoop = controlLoopProvider
+ .getControlLoop(new ToscaConceptIdentifier(controlLoop.getName(), controlLoop.getVersion()));
+ if (dbControlLoop == null) {
+ exceptionOccured(Response.Status.NOT_FOUND,
+ "PARTICIPANT_STATUS control loop not found in database: " + controlLoop);
+ }
+
+ for (ControlLoopElement element : controlLoop.getElements().values()) {
+ ControlLoopElement dbElement = dbControlLoop.getElements().get(element.getId());
+
+ if (dbElement == null) {
+ exceptionOccured(Response.Status.NOT_FOUND,
+ "PARTICIPANT_STATUS message references unknown control loop element: " + element);
+ }
+
+ // Replace element entry in the database
+ dbControlLoop.getElements().put(element.getId(), element);
+ }
+ controlLoopProvider.updateControlLoop(dbControlLoop);
+ }
+
+ monitoringProvider = MonitoringHandler.getInstance().getMonitoringProvider();
+ for (ControlLoop controlLoop : participantStatusMessage.getControlLoops().getControlLoopList()) {
+ monitoringProvider.createClElementStatistics(controlLoop.getControlLoopElementStatisticsList(controlLoop));
+ }
+ }
+
+ @Override
+ public void startProviders() {
+ try {
+ manager.start();
+ } catch (final ServiceManagerException exp) {
+ throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR,
+ "Supervision handler start of providers failed", exp);
+ }
+ }
+
+ @Override
+ public void stopProviders() {
+ try {
+ manager.stop();
+ } catch (final ServiceManagerException exp) {
+ throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR,
+ "Supervision handler stop of providers failed", exp);
+ }
+ }
+
+ private void exceptionOccured(Response.Status status, String reason) throws ControlLoopException {
+ throw new ControlLoopException(status, reason);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java
new file mode 100644
index 000000000..0ccfddff3
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/SupervisionScanner.java
@@ -0,0 +1,116 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.supervision;
+
+import java.io.Closeable;
+import java.util.Collection;
+import java.util.List;
+import java.util.TimerTask;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoop;
+import org.onap.policy.clamp.controlloop.models.controlloop.concepts.ControlLoopElement;
+import org.onap.policy.clamp.controlloop.models.controlloop.persistence.provider.ControlLoopProvider;
+import org.onap.policy.models.base.PfModelException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is used to scan the control loops in the database and check if they are in the correct state.
+ */
+public class SupervisionScanner implements Runnable, Closeable {
+ private static final Logger LOGGER = LoggerFactory.getLogger(SupervisionScanner.class);
+
+ private ControlLoopProvider controlLoopProvider;
+ private ScheduledExecutorService timerPool;
+
+ /**
+ * Constructor for instantiating SupervisionScanner.
+ *
+ * @param controlLoopProvider the provider to use to read control loops from the database
+ * @param interval time interval to perform scans
+ */
+ public SupervisionScanner(final ControlLoopProvider controlLoopProvider, final long interval) {
+ this.controlLoopProvider = controlLoopProvider;
+
+ // Kick off the timer
+ timerPool = makeTimerPool();
+ timerPool.scheduleAtFixedRate(this, 0, interval, TimeUnit.SECONDS);
+ }
+
+ @Override
+ public void run() {
+ LOGGER.debug("Scanning control loops in the database . . .");
+
+ try {
+ for (ControlLoop controlLoop : controlLoopProvider.getControlLoops(null, null)) {
+ scanControlLoop(controlLoop);
+ }
+ } catch (PfModelException pfme) {
+ LOGGER.warn("error reading control loops from database", pfme);
+ }
+
+ LOGGER.debug("Control loop scan complete . . .");
+ }
+
+ @Override
+ public void close() {
+ timerPool.shutdown();
+ }
+
+ private void scanControlLoop(final ControlLoop controlLoop) throws PfModelException {
+ LOGGER.debug("scanning control loop {} . . .", controlLoop.getKey().asIdentifier());
+
+ if (controlLoop.getState().equals(controlLoop.getOrderedState().asState())) {
+ LOGGER.debug("control loop {} scanned, OK", controlLoop.getKey().asIdentifier());
+ return;
+ }
+
+ for (ControlLoopElement element : controlLoop.getElements().values()) {
+ if (!element.getState().equals(element.getOrderedState().asState())) {
+ LOGGER.debug("control loop scan: transitioning from state {} to {}", controlLoop.getState(),
+ controlLoop.getOrderedState());
+ return;
+ }
+ }
+
+ LOGGER.debug("control loop scan: transition from state {} to {} completed", controlLoop.getState(),
+ controlLoop.getOrderedState());
+
+ controlLoop.setState(controlLoop.getOrderedState().asState());
+ controlLoopProvider.updateControlLoop(controlLoop);
+ }
+
+ /**
+ * Makes a new timer pool.
+ *
+ * @return a new timer pool
+ */
+ protected ScheduledExecutorService makeTimerPool() {
+ return Executors.newScheduledThreadPool(1);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java
new file mode 100644
index 000000000..c9c8ab851
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopStateChangePublisher.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.supervision.comm;
+
+import java.util.List;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopStateChange;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is used to send ParticipantControlLoopStateChangePublisher messages to participants on DMaaP.
+ */
+public class ParticipantControlLoopStateChangePublisher {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantControlLoopStateChangePublisher.class);
+
+ private TopicSinkClient topicSinkClient;
+
+ /**
+ * Constructor for instantiating ParticipantControlLoopStateChangePublisherPublisher.
+ *
+ * @param topicSinks the topic sinks
+ * @param interval time interval to send ParticipantControlLoopStateChangePublisher messages
+ */
+ public ParticipantControlLoopStateChangePublisher(final List<TopicSink> topicSinks, final long interval) {
+ // TODO: Should not be dependent on the order of topic sinks in the config
+ this.topicSinkClient = new TopicSinkClient(topicSinks.get(0));
+ }
+
+ /**
+ * Terminates the current timer.
+ */
+ public void terminate() {
+ // This is a user initiated message and doesn't need a timer.
+ }
+
+ /**
+ * Get the current time interval used by the timer task.
+ *
+ * @return interval the current time interval
+ */
+ public long getInterval() {
+ // This is a user initiated message and doesn't need a timer.
+ return -1;
+ }
+
+ /**
+ * Method to send ParticipantControlLoopStateChangePublisher status message to participants on demand.
+ *
+ * @param controlLoopStateChange the ParticipantControlLoopStateChangePublisher message
+ */
+ public void send(final ParticipantControlLoopStateChange controlLoopStateChange) {
+ topicSinkClient.send(controlLoopStateChange);
+ LOGGER.debug("Sent ParticipantControlLoopStateChange to Participants - {}", controlLoopStateChange);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java
new file mode 100644
index 000000000..3c5d230c5
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantControlLoopUpdatePublisher.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.supervision.comm;
+
+import java.util.List;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantControlLoopUpdate;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is used to send ParticipantControlLoopUpdate messages to participants on DMaaP.
+ */
+public class ParticipantControlLoopUpdatePublisher {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantControlLoopUpdatePublisher.class);
+
+ private TopicSinkClient topicSinkClient;
+
+ /**
+ * Constructor for instantiating ParticipantUpdatePublisher.
+ *
+ * @param topicSinks the topic sinks
+ * @param interval time interval to send ParticipantControlLoopUpdate messages
+ */
+ public ParticipantControlLoopUpdatePublisher(final List<TopicSink> topicSinks, final long interval) {
+ // TODO: Should not be dependent on the order of topic sinks in the config
+ this.topicSinkClient = new TopicSinkClient(topicSinks.get(0));
+ }
+
+ /**
+ * Terminates the current timer.
+ */
+ public void terminate() {
+ // This is a user initiated message and doesn't need a timer.
+ }
+
+ /**
+ * Get the current time interval used by the timer task.
+ *
+ * @return interval the current time interval
+ */
+ public long getInterval() {
+ // This is a user initiated message and doesn't need a timer.
+ return -1;
+ }
+
+ /**
+ * Method to send ParticipantControlLoopUpdate status message to participants on demand.
+ *
+ * @param participantControlLoopUpdate the ParticipantControlLoopUpdate message
+ */
+ public void send(final ParticipantControlLoopUpdate participantControlLoopUpdate) {
+ topicSinkClient.send(participantControlLoopUpdate);
+ LOGGER.debug("Sent ParticipantControlLoopUpdate to Participants - {}", participantControlLoopUpdate);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java
new file mode 100644
index 000000000..099039115
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStateChangePublisher.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.supervision.comm;
+
+import java.util.List;
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStateChange;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is used to send ParticipantStateChange messages to participants on DMaaP.
+ */
+public class ParticipantStateChangePublisher {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantStateChangePublisher.class);
+
+ private TopicSinkClient topicSinkClient;
+
+ /**
+ * Constructor for instantiating ParticipantStateChangePublisher.
+ *
+ * @param topicSinks the topic sinks
+ * @param interval time interval to send ParticipantStateChange messages
+ */
+ public ParticipantStateChangePublisher(final List<TopicSink> topicSinks, final long interval) {
+ // TODO: Should not be dependent on the order of topic sinks in the config
+ this.topicSinkClient = new TopicSinkClient(topicSinks.get(0));
+ }
+
+ /**
+ * Terminates the current timer.
+ */
+ public void terminate() {
+ // Nothing to terminate, this publisher does not have a timer
+ }
+
+ /**
+ * Get the current time interval used by the timer task.
+ *
+ * @return interval the current time interval
+ */
+ public long getInterval() {
+ return -1;
+ }
+
+ /**
+ * Method to send ParticipantStateChange status message to participants on demand.
+ *
+ * @param participantStateChange the ParticipantStateChange message
+ */
+ public void send(final ParticipantStateChange participantStateChange) {
+ topicSinkClient.send(participantStateChange);
+ LOGGER.debug("Sent ParticipantStateChange to Participants - {}", participantStateChange);
+ }
+}
diff --git a/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java
new file mode 100644
index 000000000..a05f4aa20
--- /dev/null
+++ b/runtime-controlloop/src/main/java/org/onap/policy/clamp/controlloop/runtime/supervision/comm/ParticipantStatusListener.java
@@ -0,0 +1,53 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 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.controlloop.runtime.supervision.comm;
+
+import org.onap.policy.clamp.controlloop.models.messages.dmaap.participant.ParticipantStatus;
+import org.onap.policy.clamp.controlloop.runtime.supervision.SupervisionHandler;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.listeners.ScoListener;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.common.utils.services.Registry;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Listener for ParticipantStatus messages sent by participants.
+ */
+public class ParticipantStatusListener extends ScoListener<ParticipantStatus> {
+ private static final Logger LOGGER = LoggerFactory.getLogger(ParticipantStatusListener.class);
+
+ private final SupervisionHandler supervisionHandler = Registry.get(SupervisionHandler.class.getName());
+
+ /**
+ * Constructs the object.
+ */
+ public ParticipantStatusListener() {
+ super(ParticipantStatus.class);
+ }
+
+ @Override
+ public void onTopicEvent(final CommInfrastructure infra, final String topic, final StandardCoderObject sco,
+ final ParticipantStatus participantStatusMessage) {
+ LOGGER.debug("ParticipantStatus message received from participant - {}", participantStatusMessage);
+ supervisionHandler.handleParticipantStatusMessage(participantStatusMessage);
+ }
+}