aboutsummaryrefslogtreecommitdiffstats
path: root/appc-oam/appc-oam-bundle/src/main/java/org/onap
diff options
context:
space:
mode:
Diffstat (limited to 'appc-oam/appc-oam-bundle/src/main/java/org/onap')
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/AppcOam.java339
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/OAMCommandStatus.java74
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/Converter.java156
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/DmaapOutgoingMessage.java137
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/MessageAdapter.java183
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/OAMContext.java75
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseActionRunnable.java254
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseCommon.java265
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseProcessor.java216
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamMmodeProcessor.java170
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamRestartProcessor.java208
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStartProcessor.java151
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStopProcessor.java120
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/AsyncTaskHelper.java394
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleFilter.java128
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleHelper.java274
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/ConfigurationHelper.java106
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/OperationHelper.java205
-rw-r--r--appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/StateHelper.java144
19 files changed, 3599 insertions, 0 deletions
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/AppcOam.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/AppcOam.java
new file mode 100644
index 000000000..97938339c
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/AppcOam.java
@@ -0,0 +1,339 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.google.common.util.concurrent.Futures;
+import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.sal.binding.api.BindingAwareBroker;
+import org.opendaylight.controller.sal.binding.api.NotificationProviderService;
+import org.opendaylight.controller.sal.binding.api.RpcProviderRegistry;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.AppcOamService;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.AppcState;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.GetAppcStateOutput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.GetAppcStateOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.GetMetricsOutput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.GetMetricsOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.MaintenanceModeInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.MaintenanceModeOutput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.MaintenanceModeOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.RestartInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.RestartOutput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.RestartOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StartInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StartOutput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StartOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StopInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StopOutput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StopOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.CommonHeader;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.get.metrics.output.Metrics;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.get.metrics.output.MetricsBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.get.metrics.output.metrics.KpiValues;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.get.metrics.output.metrics.KpiValuesBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.Status;
+import org.opendaylight.yangtools.yang.common.RpcError;
+import org.opendaylight.yangtools.yang.common.RpcResult;
+import org.opendaylight.yangtools.yang.common.RpcResultBuilder;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.metricservice.MetricRegistry;
+import org.onap.appc.metricservice.MetricService;
+import org.onap.appc.metricservice.metric.Metric;
+import org.onap.appc.oam.processor.OamMmodeProcessor;
+import org.onap.appc.oam.processor.OamRestartProcessor;
+import org.onap.appc.oam.processor.OamStartProcessor;
+import org.onap.appc.oam.processor.OamStopProcessor;
+import org.onap.appc.oam.util.AsyncTaskHelper;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.onap.appc.statemachine.impl.readers.AppcOamMetaDataReader.AppcOperation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+/**
+ * RPC class of APP-C OAM API.
+ * <p>Implement all the RPCs defined in AppcOamService through yang model definition.
+ * <p>All RPC methods' JAVADOC are using "inheritDoc" to use the description from the yang model file.
+ */
+public class AppcOam implements AutoCloseable, AppcOamService {
+ /**
+ * Invalid state message format with fliexible operation, appc name and state values
+ */
+ public final static String INVALID_STATE_MESSAGE_FORMAT = "%s API is not allowed when %s is in the %s state.";
+
+ private final EELFLogger logger = EELFManager.getInstance().getLogger(AppcOam.class);
+
+ private boolean isMetricEnabled = false;
+
+ /**
+ * Represents our RPC implementation registration
+ */
+ private BindingAwareBroker.RpcRegistration<AppcOamService> rpcRegistration;
+
+
+ /**
+ * The yang rpc names with value mapping to AppcOperation
+ */
+ public enum RPC {
+ maintenance_mode(AppcOperation.MaintenanceMode),
+ start(AppcOperation.Start),
+ stop(AppcOperation.Stop),
+ restart(AppcOperation.Restart);
+
+ AppcOperation appcOperation;
+
+ RPC(AppcOperation appcOperation) {
+ this.appcOperation = appcOperation;
+ }
+
+ public AppcOperation getAppcOperation() {
+ return appcOperation;
+ }
+ }
+
+ private AsyncTaskHelper asyncTaskHelper;
+ private ConfigurationHelper configurationHelper;
+ private OperationHelper operationHelper;
+ private StateHelper stateHelper;
+
+ /**
+ * APP-C OAM contructor
+ *
+ * @param dataBroker object of The ODL data store broker. Provides access to a conceptual data
+ * tree store
+ * and also provides the ability to subscribe for changes to data under a
+ * given branch
+ * of the tree. Not used in this class.
+ * @param notificationProviderService object of ODL Notification Service that provides publish/subscribe
+ * capabilities for YANG modeled notifications. Not used in this class.
+ * @param rpcProviderRegistry object of RpcProviderResigstry. Used to register our RPCs.
+ */
+ @SuppressWarnings({"unused", "nls"})
+ public AppcOam(DataBroker dataBroker,
+ NotificationProviderService notificationProviderService,
+ RpcProviderRegistry rpcProviderRegistry) {
+
+ configurationHelper = new ConfigurationHelper(logger);
+ String appName = configurationHelper.getAppcName();
+ logger.info(Msg.COMPONENT_INITIALIZING, appName, "oam");
+
+ if (rpcProviderRegistry != null) {
+ rpcRegistration = rpcProviderRegistry.addRpcImplementation(AppcOamService.class, this);
+ }
+
+ isMetricEnabled = configurationHelper.isMetricEnabled();
+
+ initHelpers();
+
+ logger.info(Msg.COMPONENT_INITIALIZED, appName, "oam");
+ }
+
+ /**
+ * Initialize helper classes.
+ * <p>Note: ConfigurationHelper initializetion is in included here
+ * because it is needed for extracting the AppName used in the debug logs within the constructor.
+ */
+ private void initHelpers() {
+ operationHelper = new OperationHelper();
+ asyncTaskHelper = new AsyncTaskHelper(logger);
+ stateHelper = new StateHelper(logger, configurationHelper);
+ }
+
+ /**
+ * Implements the close of the service
+ *
+ * @see AutoCloseable#close()
+ */
+ @SuppressWarnings("nls")
+ @Override
+ public void close() throws Exception {
+ String appName = configurationHelper.getAppcName();
+ logger.info(Msg.COMPONENT_TERMINATING, appName, "oam");
+
+ asyncTaskHelper.close();
+
+ if (rpcRegistration != null) {
+ rpcRegistration.close();
+ }
+ logger.info(Msg.COMPONENT_TERMINATED, appName, "oam");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Future<RpcResult<GetMetricsOutput>> getMetrics() {
+
+ if (!isMetricEnabled) {
+ logger.error("Metric Service not enabled returning failure");
+ RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput>
+ status(false).withError(RpcError.ErrorType.APPLICATION, "Metric Service not enabled").build();
+ return Futures.immediateFuture(result);
+ }
+
+ MetricService metricService;
+ try {
+ metricService = operationHelper.getService(MetricService.class);
+ } catch (APPCException e) {
+ logger.error("MetricService not found", e);
+ RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput>
+ status(false).withError(RpcError.ErrorType.APPLICATION, "Metric Service not found").build();
+ return Futures.immediateFuture(result);
+ }
+
+ Map<String, MetricRegistry> allMetricRegitry = metricService.getAllRegistry();
+ if (allMetricRegitry == null || allMetricRegitry.isEmpty()) {
+ logger.error("No metrics registered returning failure");
+ RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput>
+ status(false).withError(RpcError.ErrorType.APPLICATION, "No metrics Registered").build();
+ return Futures.immediateFuture(result);
+ }
+
+ List<Metrics> metricsList = new ArrayList<>();
+
+ logger.debug("Iterating metric registry list");
+ for (MetricRegistry metricRegistry : allMetricRegitry.values()) {
+ logger.debug("Iterating metric registry :" + metricRegistry.toString());
+ Metric[] metrics = metricRegistry.metrics();
+ if (metrics != null && metrics.length > 0) {
+ logger.debug("Iterating though metrics in registry");
+ for (Metric metric : metrics) {
+ logger.debug("Iterating though metrics: " + metric.name());
+ MetricsBuilder metricsBuilder = new MetricsBuilder();
+ metricsBuilder.setKpiName(metric.name());
+ metricsBuilder.setLastResetTime(metric.getLastModified());
+ List<KpiValues> kpiList = new ArrayList<>();
+ Map<String, String> metricsOutput = metric.getMetricsOutput();
+ for (Map.Entry<String, String> kpi : metricsOutput.entrySet()) {
+ KpiValuesBuilder kpiValuesBuilder = new KpiValuesBuilder();
+ kpiValuesBuilder.setName(kpi.getKey());
+ kpiValuesBuilder.setValue(kpi.getValue());
+ kpiList.add(kpiValuesBuilder.build());
+ }
+ metricsBuilder.setKpiValues(kpiList);
+ metricsList.add(metricsBuilder.build());
+ }
+ }
+ }
+
+ GetMetricsOutputBuilder outputBuilder = new GetMetricsOutputBuilder();
+ outputBuilder.setMetrics(metricsList);
+ RpcResult<GetMetricsOutput> result = RpcResultBuilder.<GetMetricsOutput>
+ status(true).withResult(outputBuilder.build()).build();
+ return Futures.immediateFuture(result);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Future<RpcResult<StopOutput>> stop(StopInput stopInput) {
+ logger.debug("Entering Stop with Input : " + stopInput);
+ final CommonHeader commonHeader = stopInput.getCommonHeader();
+
+ OamStopProcessor oamStopProcessor =
+ new OamStopProcessor(logger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+ Status status = oamStopProcessor.processRequest(stopInput);
+
+ StopOutputBuilder stopOutputBuilder = new StopOutputBuilder();
+ stopOutputBuilder.setStatus(status);
+ stopOutputBuilder.setCommonHeader(commonHeader);
+ return RpcResultBuilder.success(stopOutputBuilder.build()).buildFuture();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Future<RpcResult<RestartOutput>> restart(RestartInput input) {
+ logger.debug("Entering restart with Input : " + input);
+ final CommonHeader commonHeader = input.getCommonHeader();
+
+ OamRestartProcessor oamRestartProcessor =
+ new OamRestartProcessor(logger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+ Status status = oamRestartProcessor.processRequest(input);
+
+ RestartOutputBuilder restartOutputBuilder = new RestartOutputBuilder();
+ restartOutputBuilder.setStatus(status);
+ restartOutputBuilder.setCommonHeader(commonHeader);
+
+ return RpcResultBuilder.success(restartOutputBuilder.build()).buildFuture();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Future<RpcResult<MaintenanceModeOutput>> maintenanceMode(MaintenanceModeInput maintenanceModeInput) {
+ logger.debug("Entering MaintenanceMode with Input : " + maintenanceModeInput);
+ final CommonHeader commonHeader = maintenanceModeInput.getCommonHeader();
+
+ OamMmodeProcessor oamMmodeProcessor =
+ new OamMmodeProcessor(logger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+ Status status = oamMmodeProcessor.processRequest(maintenanceModeInput);
+
+ MaintenanceModeOutputBuilder maintenanceModeOutputBuilder = new MaintenanceModeOutputBuilder();
+ maintenanceModeOutputBuilder.setStatus(status);
+ maintenanceModeOutputBuilder.setCommonHeader(commonHeader);
+ return RpcResultBuilder.success(maintenanceModeOutputBuilder.build()).buildFuture();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Future<RpcResult<GetAppcStateOutput>> getAppcState() {
+ AppcState appcState = stateHelper.getCurrentOamYangState();
+
+ GetAppcStateOutputBuilder builder = new GetAppcStateOutputBuilder();
+ builder.setState(appcState);
+ return RpcResultBuilder.success(builder.build()).buildFuture();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Future<RpcResult<StartOutput>> start(StartInput startInput) {
+ logger.debug("Input received : " + startInput);
+ final CommonHeader commonHeader = startInput.getCommonHeader();
+
+ OamStartProcessor oamStartProcessor =
+ new OamStartProcessor(logger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+ Status status = oamStartProcessor.processRequest(startInput);
+
+ StartOutputBuilder startOutputBuilder = new StartOutputBuilder();
+ startOutputBuilder.setStatus(status);
+ startOutputBuilder.setCommonHeader(commonHeader);
+ StartOutput startOutput = startOutputBuilder.build();
+ return RpcResultBuilder.success(startOutput).buildFuture();
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/OAMCommandStatus.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/OAMCommandStatus.java
new file mode 100644
index 000000000..294b9d2cf
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/OAMCommandStatus.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam;
+
+import org.onap.appc.executor.objects.Params;
+import org.onap.appc.util.MessageFormatter;
+
+import java.util.Map;
+
+public enum OAMCommandStatus {
+
+ ACCEPTED(100, "ACCEPTED - request accepted"),
+ UNEXPECTED_ERROR(200, "UNEXPECTED ERROR - ${errorMsg}"),
+ REJECTED(300, "REJECTED - ${errorMsg}"),
+ INVALID_PARAMETER(302, "INVALID PARAMETER - ${errorMsg}" ),
+ TIMEOUT(303, "OPERATION TIMEOUT REACHED - ${errorMsg}"),
+ ABORT(304, "OPERATION ABORT - ${errorMsg}"),
+ SUCCESS(400, "SUCCESS - request has been processed successfully");
+
+ final String TO_STRING_FORMAT = "OAMCommandStatus{responseCode=%d, responseMessage='%s'}";
+
+ private int responseCode;
+ private String responseMessage;
+
+ OAMCommandStatus(int responseCode, String responseMessage) {
+ this.responseCode = responseCode;
+ this.responseMessage = responseMessage;
+ }
+
+ public String getResponseMessage() {
+ return responseMessage;
+ }
+
+ public int getResponseCode() {
+ return responseCode;
+ }
+
+ /**
+ * Get formated message of passed in params
+ *
+ * @param params of Params object with name value pairs for message
+ * @return message string
+ */
+ public String getFormattedMessage(Params params) {
+ Map<String,Object> paramsMap = params != null ? params.getParams() : null;
+ return MessageFormatter.format(getResponseMessage(), paramsMap);
+ }
+
+ @Override
+ public String toString() {
+ return String.format(TO_STRING_FORMAT, responseCode, responseMessage);
+ }}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/Converter.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/Converter.java
new file mode 100644
index 000000000..152ffc9cc
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/Converter.java
@@ -0,0 +1,156 @@
+/*-/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.messageadapter;
+
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.MapperFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ObjectWriter;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.MaintenanceModeOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.RestartOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StartOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StopOutputBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.CommonHeader;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.Status;
+import org.opendaylight.yangtools.concepts.Builder;
+import org.opendaylight.yangtools.yang.binding.DataContainer;
+import org.onap.appc.oam.AppcOam;
+
+import java.text.SimpleDateFormat;
+import java.util.TimeZone;
+
+public class Converter {
+ private static final String ISO_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
+ private static final SimpleDateFormat isoFormatter = new SimpleDateFormat(ISO_FORMAT);
+
+ static {
+ isoFormatter.setTimeZone(TimeZone.getTimeZone("UTC"));
+ }
+
+
+ private static Builder<?> convAsyncResponseToBuilder1(AppcOam.RPC rpcName,
+ CommonHeader commonHeader,
+ Status status) {
+ Builder<?> outObj;
+ if (rpcName == null) {
+ throw new IllegalArgumentException("empty asyncResponse.rpcName");
+ }
+ if (commonHeader == null) {
+ throw new IllegalArgumentException("empty asyncResponse.commonHeader");
+ }
+ if (status == null) {
+ throw new IllegalArgumentException("empty asyncResponse.status");
+ }
+ switch (rpcName) {
+ case maintenance_mode:
+ outObj = new MaintenanceModeOutputBuilder();
+ ((MaintenanceModeOutputBuilder) outObj).setCommonHeader(commonHeader);
+ ((MaintenanceModeOutputBuilder) outObj).setStatus(status);
+ return outObj;
+
+ case start:
+ outObj = new StartOutputBuilder();
+ ((StartOutputBuilder) outObj).setCommonHeader(commonHeader);
+ ((StartOutputBuilder) outObj).setStatus(status);
+ return outObj;
+
+ case stop:
+ outObj = new StopOutputBuilder();
+ ((StopOutputBuilder) outObj).setCommonHeader(commonHeader);
+ ((StopOutputBuilder) outObj).setStatus(status);
+ return outObj;
+
+ case restart:
+ outObj = new RestartOutputBuilder();
+ ((RestartOutputBuilder) outObj).setCommonHeader(commonHeader);
+ ((RestartOutputBuilder) outObj).setStatus(status);
+ return outObj;
+
+ default:
+ throw new IllegalArgumentException(rpcName + " action is not supported");
+ }
+ }
+
+ static String convAsyncResponseToUebOutgoingMessageJsonString(OAMContext oamContext) throws
+ JsonProcessingException {
+ AppcOam.RPC rpcName = oamContext.getRpcName();
+ CommonHeader commonHeader = oamContext.getCommonHeader();
+ Status status = oamContext.getStatus();
+
+ DmaapOutgoingMessage dmaapOutgoingMessage = convAsyncResponseToUebOutgoingMessage(rpcName, commonHeader,
+ status);
+ ObjectMapper objectMapper = new ObjectMapper();
+ objectMapper.addMixInAnnotations(dmaapOutgoingMessage.getBody().getOutput().getClass(),
+ MixInFlagsMessage.class);
+ objectMapper.addMixInAnnotations(Status.class, MixIn.class);
+ objectMapper.addMixInAnnotations(CommonHeader.class, MixInCommonHeader.class);
+ ObjectWriter writer = objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL).configure
+ (MapperFeature.SORT_PROPERTIES_ALPHABETICALLY, true).writer();
+ return writer.writeValueAsString(dmaapOutgoingMessage);
+ }
+
+ private static DmaapOutgoingMessage convAsyncResponseToUebOutgoingMessage(AppcOam.RPC rpcName, CommonHeader
+ commonHeader, Status status) throws JsonProcessingException {
+ DmaapOutgoingMessage outObj = new DmaapOutgoingMessage();
+ String correlationID = commonHeader.getRequestId();
+ outObj.setCorrelationID(correlationID);
+ outObj.setType("response");
+ outObj.setRpcName(rpcName.name());
+ Builder<?> builder = Converter.convAsyncResponseToBuilder1(rpcName, commonHeader, status);
+ Object messageBody = builder.build();
+
+ DmaapOutgoingMessage.Body body = new DmaapOutgoingMessage.Body(messageBody);
+ outObj.setBody(body);
+ return outObj;
+ }
+
+
+ abstract class MixIn {
+ // to be removed during serialization
+ @JsonIgnore
+ abstract Class<? extends DataContainer> getImplementedInterface();
+
+ @JsonValue
+ abstract java.lang.String getValue();
+ }
+
+ abstract class MixInCommonHeader extends MixIn {
+ @JsonProperty("request-id")
+ abstract java.lang.String getRequestId();
+
+ @JsonProperty("originator-id")
+ abstract java.lang.String getOriginatorId();
+
+ }
+
+ abstract class MixInFlagsMessage extends MixIn {
+ @JsonProperty("common-header")
+ abstract CommonHeader getCommonHeader();
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/DmaapOutgoingMessage.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/DmaapOutgoingMessage.java
new file mode 100644
index 000000000..25bf0cf8b
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/DmaapOutgoingMessage.java
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.messageadapter;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.databind.annotation.JsonSerialize;
+
+/**
+ * This class represents a message being sent out to DMaaP by APPC as async response.
+ * note the structure of this class must be adapted to the sync message sent to DMaaP represented in org.onap.appc.listener.LCM.domainmodel.DmaapOutgoingMessage
+ *
+ */
+@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class DmaapOutgoingMessage {
+
+ @JsonProperty("type")
+ private String type;
+
+ @JsonProperty("correlation-id")
+ private String correlationID;
+
+ private final static String defaultCambriaPartition = "MSO";
+ @JsonProperty("cambria.partition")
+ private String cambriaPartition = defaultCambriaPartition;
+
+ @JsonProperty("rpc-name")
+ private String rpcName;
+
+ @JsonProperty("body")
+ private Body body;
+
+ public DmaapOutgoingMessage() {
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public void setType(String type) {
+ this.type = type;
+ }
+
+ public String getCorrelationID() {
+ return correlationID;
+ }
+
+ public void setCorrelationID(String correlationID) {
+ this.correlationID = correlationID;
+ }
+
+ public String getCambriaPartition() {
+ return cambriaPartition;
+ }
+
+ public void setCambriaPartition(String cambriaPartition) {
+ this.cambriaPartition = cambriaPartition;
+ }
+
+ public String getRpcName() {
+ return rpcName;
+ }
+
+ public void setRpcName(String rpcName) {
+ this.rpcName = rpcName;
+ }
+
+ public Body getBody() {
+ return body;
+ }
+
+ public void setBody(Body body) {
+ this.body = body;
+ }
+
+ @Override
+ public String toString() {
+ return "DmaapOutgoingMessage{" +
+ "cambriaPartition='" + cambriaPartition + '\'' +
+ ", rpcName='" + rpcName + '\'' +
+ ", body=" + body +
+ '}';
+ }
+
+ @JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
+ @JsonIgnoreProperties(ignoreUnknown = true)
+ public static class Body {
+ public Body() {
+ }
+
+ public Body(Object output) {
+ this.output = output;
+ }
+
+ @JsonProperty("output")
+ private Object output;
+
+ public Object getOutput() {
+ return output;
+ }
+
+ public void setOutput(Object body) {
+ this.output = body;
+ }
+
+ @Override
+ public String toString() {
+ return "Body{" +
+ "output=" + output +
+ '}';
+ }
+ }
+}
+
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/MessageAdapter.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/MessageAdapter.java
new file mode 100644
index 000000000..91836cb40
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/MessageAdapter.java
@@ -0,0 +1,183 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.messageadapter;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.commons.lang.ObjectUtils;
+import org.onap.appc.adapter.message.MessageAdapterFactory;
+import org.onap.appc.adapter.message.Producer;
+import org.onap.appc.configuration.Configuration;
+import org.onap.appc.configuration.ConfigurationFactory;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+
+import java.util.HashSet;
+import java.util.Properties;
+
+public class MessageAdapter {
+
+ private final EELFLogger logger = EELFManager.getInstance().getLogger(MessageAdapter.class);
+
+ private final String PROP_APPC_OAM_DISABLED = "appc.OAM.disabled";
+ private final String PROP_APPC_OAM_TOPIC_WRITE = "appc.OAM.topic.write";
+ private String PROP_APPC_OAM_CLIENT_KEY = "appc.OAM.client.key";
+ private String PROP_APPC_OAM_CLIENT_SECRET = "appc.OAM.client.secret";
+ private String PROP_APPC_OAM_POOLMEMBERS = "appc.OAM.poolMembers";
+
+ private Producer producer;
+ private String partition;
+ private Configuration configuration;
+ private HashSet<String> pool;
+ private String writeTopic;
+ private String apiKey;
+ private String apiSecret;
+ private boolean isDisabled;
+
+ /**
+ * Initialize producer client to post messages using configuration properties.
+ */
+ public void init() {
+ configuration = ConfigurationFactory.getConfiguration();
+ Properties properties = configuration.getProperties();
+ updateProperties(properties);
+
+ if (isAppcOamPropsListenerEnabled()) {
+ createProducer();
+ } else {
+ logger.warn(String.format("The listener %s is disabled and will not be run", "appc.OAM"));
+ }
+ }
+
+ /**
+ * Create producer using MessageAdapterFactory which is found through bundle context.
+ */
+ void createProducer() {
+ BundleContext ctx = FrameworkUtil.getBundle(MessageAdapter.class).getBundleContext();
+ if (ctx == null) {
+ logger.warn("MessageAdapter cannot create producer due to no bundle context.");
+ return;
+ }
+
+ ServiceReference svcRef = ctx.getServiceReference(MessageAdapterFactory.class.getName());
+ if (svcRef == null) {
+ logger.warn("MessageAdapter cannot create producer due to no MessageAdapterFactory service reference.");
+ return;
+ }
+
+ Producer localProducer = ((MessageAdapterFactory) ctx.getService(svcRef)).createProducer(pool, writeTopic,
+ apiKey, apiSecret);
+
+ for (String url : pool) {
+ if (url.contains("3905") || url.contains("https")) {
+ localProducer.useHttps(true);
+ break;
+ }
+ }
+
+ producer = localProducer;
+
+ logger.debug("MessageAdapter created producer.");
+ }
+
+ /**
+ * Read property value to set writeTopic, apiKey, apiSecret and pool.
+ *
+ * @param props of configuration
+ */
+ private void updateProperties(Properties props) {
+ logger.trace("Entering to updateProperties with Properties = " + ObjectUtils.toString(props));
+
+ pool = new HashSet<>();
+ if (props != null) {
+ isDisabled = Boolean.parseBoolean(props.getProperty(PROP_APPC_OAM_DISABLED));
+ writeTopic = props.getProperty(PROP_APPC_OAM_TOPIC_WRITE);
+ apiKey = props.getProperty(PROP_APPC_OAM_CLIENT_KEY);
+ apiSecret = props.getProperty(PROP_APPC_OAM_CLIENT_SECRET);
+ String hostnames = props.getProperty(PROP_APPC_OAM_POOLMEMBERS);
+ if (hostnames != null && !hostnames.isEmpty()) {
+ for (String name : hostnames.split(",")) {
+ pool.add(name);
+ }
+ }
+ }
+ }
+
+ /**
+ * Get producer. If it is null, call createProducer to create it again.
+ *
+ * @return Producer
+ */
+ Producer getProducer() {
+ if (producer == null) {
+ // In case, producer was not properly set yet, set it again.
+ logger.info("Calling createProducer as producer is null.");
+ createProducer();
+ }
+
+ return producer;
+ }
+
+ /**
+ * Posts message to UEB. As UEB accepts only json messages this method first convert uebMessage to json format
+ * and post it to UEB.
+ *
+ * @param oamContext response data that based on it a message will be send to UEB (the format of the message that
+ * will be sent to UEB based on the action and its YANG domainmodel).
+ */
+ public void post(OAMContext oamContext) {
+ if (logger.isTraceEnabled()) {
+ logger.trace("Entering to post with AsyncResponse = " + ObjectUtils.toString(oamContext));
+ }
+
+ boolean success;
+ String jsonMessage;
+ try {
+ jsonMessage = Converter.convAsyncResponseToUebOutgoingMessageJsonString(oamContext);
+ if (logger.isDebugEnabled()) {
+ logger.debug("UEB Response = " + jsonMessage);
+ }
+
+ Producer myProducer = getProducer();
+ success = myProducer != null && myProducer.post(this.partition, jsonMessage);
+ } catch (JsonProcessingException e1) {
+ logger.error("Error generating Json from UEB message " + e1.getMessage());
+ success = false;
+ } catch (Exception e) {
+ logger.error("Error sending message to UEB " + e.getMessage(), e);
+ success = false;
+ }
+
+ if (logger.isTraceEnabled()) {
+ logger.trace("Exiting from post with (success = " + ObjectUtils.toString(success) + ")");
+ }
+ }
+
+ private boolean isAppcOamPropsListenerEnabled() {
+ return !isDisabled;
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/OAMContext.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/OAMContext.java
new file mode 100644
index 000000000..3af395923
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/messageadapter/OAMContext.java
@@ -0,0 +1,75 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.messageadapter;
+
+
+
+
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.*;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.CommonHeader;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.CommonHeaderBuilder;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.Status;import org.onap.appc.oam.AppcOam;
+
+public class OAMContext {
+
+ private AppcOam.RPC rpcName;
+ private CommonHeader commonHeader;
+ private Status status;
+
+
+ public AppcOam.RPC getRpcName() {
+ return rpcName;
+ }
+
+ public void setRpcName(AppcOam.RPC rpcName) {
+ this.rpcName = rpcName;
+ }
+
+ public CommonHeader getCommonHeader() {
+ return commonHeader;
+ }
+
+ public void setCommonHeader(CommonHeader commonHeader) {
+ this.commonHeader = commonHeader;
+ }
+
+ public Status getStatus() {
+ return status;
+ }
+
+ public void setStatus(Status status) {
+ this.status = status;
+ }
+
+
+ @Override
+ public String toString() {
+ return "OAMContext {" +
+ "rpcName=" + rpcName +
+ ", commonHeader=" + commonHeader +
+ ", status=" + status +
+ '}';
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseActionRunnable.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseActionRunnable.java
new file mode 100644
index 000000000..927334670
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseActionRunnable.java
@@ -0,0 +1,254 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.OAMCommandStatus;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Future;
+
+/**
+ * Base runnable actions for OAM APIs, such as maintenance mode, restart, start and stop API.
+ *
+ * <p>This class holds the general action async handling methods for all OAM APIs.
+ * <p>Specific API action runnable will overwrite the general methods to add specific behaviors.
+ *
+ * <p>Subclass constructor must set the following class variables:
+ * <br> - actionName
+ * <br> - auditMsg
+ * <br> - finalState
+ */
+public abstract class BaseActionRunnable extends BaseCommon implements Runnable {
+ /** Abort due to rejection message format with flexible operation name */
+ final String ABORT_MESSAGE_FORMAT = "Aborting %s operation due to %s.";
+ /** Timeout message format with flexible operation name */
+ final String TIMEOUT_MESSAGE_FORMAT = "%s operation has reached timeout %d milliseconds.";
+ /** Failure message format with flexible number of bundles */
+ final String BUNDLE_OPERATION_FAILED_FORMAT = "%d bundle(s) failed, see logs for details.";
+ final String NEW_RPC_OPERATION_REQUEST = "new %s operation request";
+ final String DUE_TO_EXECUTION_ERROR = "due to execution error.";
+
+ private boolean isWaiting = false;
+ long startTimeMs = 0;
+ long timeoutMs = 0;
+ boolean doTimeoutChecking = false;
+
+ String actionName = "Need to be reset";
+ Msg auditMsg;
+ AppcOamStates finalState;
+
+ BaseProcessor myParent;
+ Map<String, Future<?>> bundleNameToFuture = new HashMap<>();
+
+ /**
+ * Constructor
+ *
+ * @param parent BaseProcessor who has called this constructor.
+ */
+ BaseActionRunnable(BaseProcessor parent) {
+ super(parent.logger, parent.configurationHelper, parent.stateHelper, parent.operationHelper);
+
+ rpc = parent.rpc;
+ commonHeader = parent.commonHeader;
+ myParent = parent;
+ setTimeoutValues();
+ }
+
+ /**
+ * Collect the timeout value for this {@link BaseActionRunnable}
+ */
+ void setTimeoutValues() {
+ startTime = myParent.startTime;
+ timeoutMs = myParent.getTimeoutMilliseconds();
+ doTimeoutChecking = timeoutMs != 0;
+ if (doTimeoutChecking) {
+ startTimeMs = startTime.getTime();
+ }
+ logDebug("%s action runnable check timeout (%s) with timeout (%d)ms, and startMs (%d)",
+ rpc.name(), Boolean.toString(doTimeoutChecking), timeoutMs, startTimeMs);
+ }
+
+
+ /**
+ * Abort operation handling due to outside interruption, does<br>
+ * - set ABORT status<br>
+ * - send notification message<br>
+ * - add audit log
+ *
+ * @param newRpc of the new AppcOam.RPC operation.
+ */
+ void abortRunnable(final AppcOam.RPC newRpc) {
+ resetLogProperties(false);
+
+ String additionalMsg = String.format(NEW_RPC_OPERATION_REQUEST, newRpc);
+ logDebug("%s action aborted due to %s", rpc.name(), additionalMsg);
+ setStatus(OAMCommandStatus.ABORT, String.format(ABORT_MESSAGE_FORMAT, rpc.name(), additionalMsg));
+ operationHelper.sendNotificationMessage(rpc, commonHeader, status);
+ auditInfoLog(auditMsg);
+
+ resetLogProperties(true);
+ }
+
+ @Override
+ public void run() {
+ try {
+ setInitialLogProperties();
+ logDebug(String.format("===========in %s run (waiting: %s)=======",
+ actionName, Boolean.toString(isWaiting)));
+
+ if (isWaiting) {
+ if (!checkState()) {
+ keepWaiting();
+ }
+ } else {
+ if (doAction()) {
+ isWaiting = !checkState();
+ } else {
+ postDoAction(false);
+ }
+ }
+ } catch (Exception e) {
+ logDebug(String.format("%s got exception %s", actionName, e.getMessage()));
+ logger.error(actionName + " exception", e);
+
+ } finally {
+ clearRequestLogProperties();
+ }
+ }
+
+ /**
+ * Keep waiting to be override by children classes for different behaviors.
+ * Timeout is validated here.
+ */
+ void keepWaiting() {
+ logDebug(String.format("%s runnable waiting, current state is %s.",
+ actionName, stateHelper.getCurrentOamState()));
+
+ isTimeout("keepWaiting");
+ }
+
+ /**
+ * Check if the timeout milliseconds has reached.
+ *
+ * @param parentName String of the caller, for logging purpose.
+ * @return true if the timeout has reached, otherwise false.
+ */
+ boolean isTimeout(String parentName) {
+ logDebug(String.format("%s task isTimeout called from %s", actionName, parentName));
+ if (doTimeoutChecking
+ && System.currentTimeMillis() - startTimeMs > timeoutMs) {
+ logger.error(String.format("%s operation timeout (%d) ms has reached, abort with error state.",
+ actionName, timeoutMs));
+
+ setStatus(OAMCommandStatus.TIMEOUT, String.format(TIMEOUT_MESSAGE_FORMAT, rpc.name(), timeoutMs));
+ postAction(AppcOamStates.Error);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Check if all bundle operations are successful through BundleHelper.
+ * If there's failed bundler operation, set error status and trigger postAction with Error state.
+ *
+ * @return true if bundler operations have failure, otherwise false.
+ */
+ boolean hasBundleOperationFailure() {
+ long failedTask = myParent.bundleHelper.getFailedMetrics(bundleNameToFuture);
+ if (failedTask == 0) {
+ return false;
+ }
+
+ setStatus(OAMCommandStatus.UNEXPECTED_ERROR, String.format(BUNDLE_OPERATION_FAILED_FORMAT, failedTask));
+ postAction(AppcOamStates.Error);
+ return true;
+ }
+
+ /**
+ * Set class <b>status</b> to ABORT with abort message.
+ */
+ void setAbortStatus() {
+ setStatus(OAMCommandStatus.ABORT, String.format(ABORT_MESSAGE_FORMAT, rpc.name(), DUE_TO_EXECUTION_ERROR));
+ }
+
+ /**
+ * Final handling. The thread is cancelled.
+ *
+ * @param setState boolean to indicate if set OAM state or not
+ */
+ void postDoAction(boolean setState) {
+ logDebug(String.format("Finished %s task", actionName));
+ }
+
+ /**
+ * Handling for after doAction. does post notification, issue audit log and set OAM state based on input.
+ *
+ * @param state of AppcOamState to be set as OAM state when it is not null.
+ */
+ void postAction(AppcOamStates state) {
+ operationHelper.sendNotificationMessage(rpc, commonHeader, status);
+
+ if (state != null) {
+ stateHelper.setState(state);
+ }
+
+ auditInfoLog(auditMsg);
+
+ myParent.cancelAsyncTask();
+ }
+
+ /**
+ * Check state
+ *
+ * @return true if final state reached, otherwise return false
+ */
+ boolean checkState() {
+ if (isTimeout("checkState")) {
+ myParent.bundleHelper.cancelUnfinished(bundleNameToFuture);
+ return true;
+ }
+
+ if (!myParent.bundleHelper.isAllTaskDone(bundleNameToFuture)) {
+ return false;
+ }
+
+ if (hasBundleOperationFailure()) {
+ return true;
+ }
+
+ if (stateHelper.getBundlesState() == finalState) {
+ setStatus(OAMCommandStatus.SUCCESS);
+ postDoAction(true);
+ return true;
+ }
+ return false;
+ }
+
+ abstract boolean doAction();
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseCommon.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseCommon.java
new file mode 100644
index 000000000..93e79dd2f
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseCommon.java
@@ -0,0 +1,265 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.i18n.EELFResourceManager;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.CommonHeader;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.Status;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.StatusBuilder;
+import org.onap.appc.exceptions.InvalidInputException;
+import org.onap.appc.exceptions.InvalidStateException;
+import org.onap.appc.executor.objects.Params;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.logging.LoggingConstants;
+import org.onap.appc.logging.LoggingUtils;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.OAMCommandStatus;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.slf4j.MDC;
+
+import java.net.InetAddress;
+import java.time.Instant;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeoutException;
+
+import static com.att.eelf.configuration.Configuration.MDC_INSTANCE_UUID;
+import static com.att.eelf.configuration.Configuration.MDC_KEY_REQUEST_ID;
+import static com.att.eelf.configuration.Configuration.MDC_SERVER_FQDN;
+import static com.att.eelf.configuration.Configuration.MDC_SERVER_IP_ADDRESS;
+import static com.att.eelf.configuration.Configuration.MDC_SERVICE_NAME;
+
+/**
+ * Common handling methods of <br>
+ * - BaseProcessor (for REST sync handling) <br>
+ * - BaseActionRunnable (for REST async handling)
+ */
+public abstract class BaseCommon {
+ final EELFLogger logger;
+ final ConfigurationHelper configurationHelper;
+ final StateHelper stateHelper;
+ final OperationHelper operationHelper;
+
+ Status status;
+ Date startTime;
+
+ AppcOam.RPC rpc;
+ CommonHeader commonHeader;
+
+ private final List<String> MDC_KEYS = Arrays.asList(
+ LoggingConstants.MDCKeys.PARTNER_NAME,
+ LoggingConstants.MDCKeys.SERVER_NAME,
+ MDC_INSTANCE_UUID,
+ MDC_KEY_REQUEST_ID,
+ MDC_SERVER_FQDN,
+ MDC_SERVER_IP_ADDRESS,
+ MDC_SERVICE_NAME
+ );
+ private Map<String, String> oldMdcContent = new HashMap<>();
+
+ /**
+ * Constructor
+ *
+ * @param eelfLogger for logging
+ * @param configurationHelperIn for property reading
+ * @param stateHelperIn for APP-C OAM state checking
+ * @param operationHelperIn for operational helper
+ */
+ BaseCommon(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelperIn,
+ StateHelper stateHelperIn,
+ OperationHelper operationHelperIn) {
+ logger = eelfLogger;
+ configurationHelper = configurationHelperIn;
+ stateHelper = stateHelperIn;
+ operationHelper = operationHelperIn;
+ }
+
+ /**
+ * Audit log the passed in message at INFO level.
+ * @param msg the Msg to be audit logged.
+ */
+ void auditInfoLog(Msg msg) {
+ LoggingUtils.auditInfo(startTime.toInstant(),
+ Instant.now(),
+ String.valueOf(status.getCode()),
+ status.getMessage(),
+ getClass().getCanonicalName(),
+ msg,
+ configurationHelper.getAppcName(),
+ stateHelper.getCurrentOamState().toString()
+ );
+ }
+
+ /**
+ * Set MDC properties.
+ */
+ public final void setInitialLogProperties() {
+ MDC.put(MDC_KEY_REQUEST_ID, commonHeader.getRequestId());
+ MDC.put(LoggingConstants.MDCKeys.PARTNER_NAME, commonHeader.getOriginatorId());
+ MDC.put(MDC_INSTANCE_UUID, ""); // value should be created in the future
+ MDC.put(MDC_SERVICE_NAME, rpc.name());
+ try {
+ //!!!Don't change the following to a .getHostName() again please. It's wrong!MDC.put(MDC_SERVER_FQDN,
+ // InetAddress.getLocalHost().getCanonicalHostName());
+ MDC.put(MDC_SERVER_FQDN, InetAddress.getLocalHost().getCanonicalHostName());
+ MDC.put(MDC_SERVER_IP_ADDRESS, InetAddress.getLocalHost().getHostAddress());
+ MDC.put(LoggingConstants.MDCKeys.SERVER_NAME, InetAddress.getLocalHost().getHostName());
+ } catch (Exception e) {
+ logger.error("MDC constant error", e);
+ }
+ }
+
+ /**
+ * Clear MDC properties.
+ */
+ public final void clearRequestLogProperties() {
+ for (String key : MDC_KEYS) {
+ try {
+ MDC.remove(key);
+ } catch (Exception e) {
+ logger.error(
+ String.format("Unable to clear the Log properties (%s) due to exception: %s", key, e.getMessage()));
+ }
+ }
+ }
+
+ /**
+ * Reset MDC log properties based on passed in condition. does:<br>
+ * - persist existing MDC setting and set my MDC log properties <br>
+ * - or re-apply persisted MDC log properties
+ * @param useMdcMap boolean to indicate whether to persist the existing MDC setting and set my MDC log properties,
+ * or to re-apply the persisted MDC log properties.
+ */
+ void resetLogProperties(boolean useMdcMap) {
+ if (useMdcMap) {
+ for (Map.Entry<String, String> aEntry : oldMdcContent.entrySet()) {
+ MDC.put(aEntry.getKey(), aEntry.getValue());
+ }
+ return;
+ }
+
+ // persist existing log properties and set my log properties
+ oldMdcContent.clear();
+ for (String key : MDC_KEYS) {
+ String value = MDC.get(key);
+ if (value != null) {
+ oldMdcContent.put(key, value);
+ }
+ }
+ setInitialLogProperties();
+ }
+
+ /**
+ * Set class <b>status</b> by calling setStatus(OAMCommandStatus, Params) with null paramter.
+ * @see #setStatus(OAMCommandStatus, String)
+ *
+ * @param oamCommandStatus of the to be set new state
+ */
+ void setStatus(OAMCommandStatus oamCommandStatus) {
+ setStatus(oamCommandStatus, null);
+ }
+
+ /**
+ * Create Status based on the passed in parameter, then set the class <b>status</b> with it.
+ *
+ * @param oamCommandStatus of the current OAM command status
+ * @param message to be set in the new status
+ */
+ void setStatus(OAMCommandStatus oamCommandStatus, String message) {
+ Params params = new Params().addParam("errorMsg", message);
+
+ StatusBuilder statusBuilder = new StatusBuilder();
+ statusBuilder.setCode(oamCommandStatus.getResponseCode());
+ if (params != null) {
+ statusBuilder.setMessage(oamCommandStatus.getFormattedMessage(params));
+ } else {
+ statusBuilder.setMessage(oamCommandStatus.getResponseMessage());
+ }
+
+ status = statusBuilder.build();
+ }
+
+ /**
+ * Set class <b>status</b> with error status calculated from the passed in paremeter
+ * and audit log the error message.
+ * @param t of the error Throwable.
+ */
+ void setErrorStatus(Throwable t) {
+ resetLogProperties(false);
+
+ final String appName = configurationHelper.getAppcName();
+ String exceptionMessage = t.getMessage() != null ? t.getMessage() : t.toString();
+
+ OAMCommandStatus oamCommandStatus;
+ String errorMessage;
+ if (t instanceof InvalidInputException) {
+ oamCommandStatus = OAMCommandStatus.INVALID_PARAMETER;
+ errorMessage = EELFResourceManager.format(Msg.OAM_OPERATION_INVALID_INPUT, t.getMessage());
+ } else if (t instanceof InvalidStateException) {
+ exceptionMessage = String.format(AppcOam.INVALID_STATE_MESSAGE_FORMAT,
+ rpc.getAppcOperation(), appName, stateHelper.getCurrentOamState());
+ oamCommandStatus = OAMCommandStatus.REJECTED;
+ errorMessage = EELFResourceManager.format(Msg.INVALID_STATE_TRANSITION, exceptionMessage);
+ } else if (t instanceof TimeoutException) {
+ oamCommandStatus = OAMCommandStatus.TIMEOUT;
+ errorMessage = EELFResourceManager.format(Msg.OAM_OPERATION_EXCEPTION, t,
+ appName, t.getClass().getSimpleName(), rpc.name(), exceptionMessage);
+ } else {
+ oamCommandStatus = OAMCommandStatus.UNEXPECTED_ERROR;
+ errorMessage = EELFResourceManager.format(Msg.OAM_OPERATION_EXCEPTION, t,
+ appName, t.getClass().getSimpleName(), rpc.name(), exceptionMessage);
+ }
+
+ setStatus(oamCommandStatus, exceptionMessage);
+
+ LoggingUtils.logErrorMessage(
+ String.valueOf(status.getCode()),
+ status.getMessage(),
+ LoggingConstants.TargetNames.APPC,
+ LoggingConstants.TargetNames.APPC_OAM_PROVIDER,
+ errorMessage,
+ AppcOam.class.getCanonicalName());
+
+ resetLogProperties(true);
+ }
+
+ /**
+ * Genral debug log when debug logging level is enabled.
+ * @param message of the log message format
+ * @param args of the objects listed in the message format
+ */
+ void logDebug(String message, Object... args) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format(message, args));
+ }
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseProcessor.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseProcessor.java
new file mode 100644
index 000000000..414b94605
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/BaseProcessor.java
@@ -0,0 +1,216 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.Status;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.exceptions.InvalidInputException;
+import org.onap.appc.exceptions.InvalidStateException;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.oam.OAMCommandStatus;
+import org.onap.appc.oam.util.AsyncTaskHelper;
+import org.onap.appc.oam.util.BundleHelper;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+
+import java.util.Date;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Base processor for OAM APIs, such as maintenance mode, restart, start and stop API.
+ *
+ * <p>This class holds the general API request sync handling methods for all OAM APIs.
+ * <p>Specific API processor will overwrite the general methods to add specific behaviors.
+ */
+public abstract class BaseProcessor extends BaseCommon {
+ /** lock to serialize incoming OAM operations. */
+ private static final Object LOCK = new Object();
+
+ final AsyncTaskHelper asyncTaskHelper;
+ final BundleHelper bundleHelper;
+
+ /** the requestTimeoutSeconds to use for this OAM operation */
+ private Integer requestTimeoutSeconds;
+ Msg auditMsg;
+ BaseActionRunnable runnable;
+ private Future<?> scheduledRunnable = null;
+
+ /**
+ * Constructor
+ *
+ * @param eelfLogger for logging
+ * @param configurationHelperIn for property reading
+ * @param stateHelperIn for APP-C OAM state checking
+ * @param asyncTaskHelperIn for scheduling async task
+ * @param operationHelperIn for operational helper
+ */
+ BaseProcessor(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelperIn,
+ StateHelper stateHelperIn,
+ AsyncTaskHelper asyncTaskHelperIn,
+ OperationHelper operationHelperIn) {
+ super(eelfLogger, configurationHelperIn, stateHelperIn, operationHelperIn);
+
+ asyncTaskHelper = asyncTaskHelperIn;
+ bundleHelper = new BundleHelper(eelfLogger, configurationHelper, stateHelper);
+ }
+
+ /**
+ * Process synch handling and schedule asynch task
+ *
+ * @param requestInput of REST API request
+ * @return Status of new APP-C OAM state
+ */
+ public Status processRequest(final Object requestInput) {
+ startTime = new Date();
+ commonHeader = operationHelper.getCommonHeader(requestInput);
+ setStatus(OAMCommandStatus.ACCEPTED);
+
+ try {
+ preProcess(requestInput);
+ scheduleAsyncTask();
+ } catch (Exception e) {
+ setErrorStatus(e);
+ } finally {
+ postProcess();
+ }
+
+ return status;
+ }
+
+ /**
+ * Preprocess before actual handling of the REST API call. Does:
+ * <p> - commonHeader validation
+ * <p> - get NextState as well as validate if next state is valid
+ * <p> - set logging properties
+ * <p> - set appcCurrentState to next state
+ *
+ * @throws InvalidInputException when commonHeader validation failed
+ * @throws APPCException when state validation failed
+ */
+ protected void preProcess(final Object requestInput)
+ throws InvalidInputException, APPCException, InvalidStateException,InterruptedException,TimeoutException {
+ setInitialLogProperties();
+ operationHelper.isInputValid(requestInput);
+
+ //The OAM request may specify timeout value
+ requestTimeoutSeconds = operationHelper.getParamRequestTimeout(requestInput);
+
+ //All OAM operation pass through here first to validate if an OAM state change is allowed.
+ //If a state change is allowed cancel the occurring OAM (if any) before starting this one.
+ //we will synchronized so that only one can do this at any given time.
+ synchronized(LOCK) {
+ AppcOamStates currentOamState = stateHelper.getCurrentOamState();
+
+ //make sure this OAM operation can transition to the desired OAM operation
+ AppcOamStates nextState = operationHelper.getNextState(
+ rpc.getAppcOperation(), currentOamState);
+
+ stateHelper.setState(nextState);
+
+
+ try {
+ //cancel the BaseActionRunnable currently executing
+ //it got to be completely terminated before proceeding
+ asyncTaskHelper.cancelBaseActionRunnable(
+ rpc,
+ currentOamState,
+ getTimeoutMilliseconds(),
+ TimeUnit.MILLISECONDS
+ );
+ } catch (TimeoutException e) {
+ stateHelper.setState(AppcOamStates.Error);
+ throw e;
+ }
+
+
+ }
+ }
+
+ /**
+ * Post process includes audit logging as well as clear MDC properties.
+ */
+ private void postProcess() {
+ auditInfoLog(auditMsg);
+ clearRequestLogProperties();
+ }
+
+ /**
+ * Schedule async task through AsyncTaskHelper.
+ */
+ protected void scheduleAsyncTask() {
+ if (runnable == null) {
+ logger.error(String.format(
+ "Skipped schedule async task for rpc(%s) due to runnable is null", rpc.name()));
+ return;
+ }
+
+ scheduledRunnable = asyncTaskHelper.scheduleBaseRunnable(
+ runnable, runnable::abortRunnable, getInitialDelayMillis(), getDelayMillis());
+ }
+
+
+ /**
+ * The timeout for this OAM operation. The timeout source is chosen in the following order:
+ * request, config file, default value
+ * @return - the timeout for this OAM operation.
+ */
+ long getTimeoutMilliseconds() {
+ return configurationHelper.getOAMOperationTimeoutValue(this.requestTimeoutSeconds);
+ }
+
+
+ /**
+ * @return initialDelayMillis - the time to delay first execution of {@link BaseActionRunnable}
+ */
+ protected long getInitialDelayMillis(){
+ return 0L;
+ }
+
+ /**
+ * @return delayMillis the delay between the consecutive executions of {@link BaseActionRunnable}
+ */
+ private long getDelayMillis(){
+ return 1000L;
+ }
+
+ /**
+ * Cancel the scheduled {@link BaseActionRunnable} through AsyncTaskHelper
+ */
+ void cancelAsyncTask() {
+ if (scheduledRunnable == null) {
+ logger.error(String.format(
+ "Skipped cancel schedule async task for rpc(%s) due to scheduledRunnable is null", rpc.name()));
+ return;
+ }
+ scheduledRunnable.cancel(true);
+ }
+
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamMmodeProcessor.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamMmodeProcessor.java
new file mode 100644
index 000000000..23f9fd130
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamMmodeProcessor.java
@@ -0,0 +1,170 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.exceptions.InvalidInputException;
+import org.onap.appc.exceptions.InvalidStateException;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.OAMCommandStatus;
+import org.onap.appc.oam.util.AsyncTaskHelper;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.onap.appc.requesthandler.LCMStateManager;
+import org.onap.appc.requesthandler.RequestHandler;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+
+import java.util.concurrent.TimeoutException;
+
+/**
+ * Processor to handle maintenance mode OAM API.
+ */
+public class OamMmodeProcessor extends BaseProcessor {
+ /**
+ * Constructor
+ *
+ * @param eelfLogger for logging
+ * @param configurationHelper for property reading
+ * @param stateHelper for APP-C OAM state checking
+ * @param asyncTaskHelper for scheduling async task
+ * @param operationHelper for operational helper
+ */
+ public OamMmodeProcessor(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelper,
+ StateHelper stateHelper,
+ AsyncTaskHelper asyncTaskHelper,
+ OperationHelper operationHelper) {
+ super(eelfLogger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+
+ rpc = AppcOam.RPC.maintenance_mode;
+ auditMsg = Msg.OAM_OPERATION_ENTERING_MAINTENANCE_MODE;
+ }
+
+ @Override
+ protected void preProcess(final Object requestInput)
+ throws InvalidInputException, InvalidStateException, APPCException, InterruptedException, TimeoutException {
+ super.preProcess(requestInput);
+
+ //Close the gate so that no more new LCM request will be excepted.
+ LCMStateManager lcmStateManager = operationHelper.getService(LCMStateManager.class);
+ lcmStateManager.disableLCMOperations();
+ }
+
+ @Override
+ protected void scheduleAsyncTask() {
+ runnable = new MyRunnable(this);
+ super.scheduleAsyncTask();
+ }
+
+ /**
+ * {@inheritDoc}
+ * For maintenance mode we want a longer delay before initial execution of {@link BaseActionRunnable}
+ * so that any accepted LCM actions have time to git scheduled in the Dispatcher.
+ */
+ @Override
+ protected long getInitialDelayMillis(){
+ //wait ten seconds before
+ return 10000L;
+ }
+
+ /**
+ * This runnable does the async handling for the maintenance mode REST API, and will be scheduled to run
+ * until terminating condition reaches.
+ *
+ * <p>The runnable will continue run if: <br>
+ * - the runnable is not canceled outside <br>
+ * - the in progress LCM request count is not zero<br>
+ * <p> When LCM request count reaches to zero, this runnable will: <br>
+ * - post message through operationHelper <br>
+ * - set APP-C OAM state to maintenance mode <br>
+ * - audit log the state <br>
+ * - terminate this runnable itself <br>
+ */
+ class MyRunnable extends BaseActionRunnable {
+ private int inprogressRequestCount;
+
+ MyRunnable(BaseProcessor parent) {
+ super(parent);
+
+ actionName = "OAM Maintenance mode";
+ auditMsg = Msg.OAM_OPERATION_MAINTENANCE_MODE;
+ finalState = AppcOamStates.MaintenanceMode;
+ }
+
+ @Override
+ boolean doAction() {
+ // always return true, so that we can check the LCM request count
+ return true;
+ }
+
+ @Override
+ boolean checkState() {
+ logDebug(String.format("Executing %s task", actionName));
+
+
+ boolean hasError = false;
+ try {
+ inprogressRequestCount = getInprogressLCMRequestCount();
+ if (inprogressRequestCount > 0) {
+ // if there are still LCM request in progress, keep waiting
+ return false;
+ }
+
+ setStatus(OAMCommandStatus.SUCCESS);
+ } catch (Exception e) {
+ setErrorStatus(e);
+ hasError = true;
+ }
+
+ postAction(hasError ? AppcOamStates.Error : finalState);
+ return true;
+ }
+
+ /**
+ * Get in progress LCM request count through RequestHandler.
+ * @return thecount of in progress LCM request
+ * @throws APPCException if RequestHandler throws it.
+ */
+ private int getInprogressLCMRequestCount() throws APPCException {
+ RequestHandler requestHandler = operationHelper.getService(RequestHandler.class);
+
+ if (requestHandler == null) {
+ return 0;
+ }
+
+ return requestHandler.getInprogressRequestCount();
+ }
+
+ @Override
+ void keepWaiting() {
+ logDebug("The application '%s' has '%s' outstanding LCM request to complete" +
+ " before coming to a complete maintenance_mode.",
+ configurationHelper.getAppcName(), inprogressRequestCount);
+ }
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamRestartProcessor.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamRestartProcessor.java
new file mode 100644
index 000000000..b23b4d2aa
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamRestartProcessor.java
@@ -0,0 +1,208 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.util.AsyncTaskHelper;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.onap.appc.requesthandler.LCMStateManager;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+
+/**
+ * Processor to handle restart OAM API.
+ */
+public class OamRestartProcessor extends BaseProcessor {
+ /**
+ * Action phases:
+ * <br> -ToStop: call bundles stop
+ * <br> -Stopped: check if all bundle state reached stopped
+ * <br> -ToStart: call bundles start
+ * <br> -Started: action is full completed
+ * <br> -Error: indication of error, such as timeout reached, bundler operation failure and etc.
+ */
+ private enum ActionPhases {
+ ToStop,
+ Stopped,
+ ToStart,
+ Started,
+ Error
+ }
+
+ /**
+ * Constructor
+ *
+ * @param eelfLogger for logging
+ * @param configurationHelper for property reading
+ * @param stateHelper for APP-C OAM state checking
+ * @param asyncTaskHelper for scheduling async task
+ * @param operationHelper for operational helper
+ */
+ public OamRestartProcessor(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelper,
+ StateHelper stateHelper,
+ AsyncTaskHelper asyncTaskHelper,
+ OperationHelper operationHelper) {
+ super(eelfLogger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+
+ rpc = AppcOam.RPC.restart;
+ auditMsg = Msg.OAM_OPERATION_RESTARTING;
+ }
+
+ @Override
+ protected void scheduleAsyncTask() {
+ runnable = new MyRunnable(this);
+ super.scheduleAsyncTask();
+ }
+
+ /**
+ * This runnable does the async handling for the restart REST API. And it will be scheduled to run one time.
+ *
+ * <p>This runnable will the following operations: <br>
+ * - do APP-C OAM bundle stop and then start through BundlerHelper<br>
+ * - and always enable LCM operation handling (which can be disabled through maintenance mode API).<br>
+ * <p>Once above operations are done, the runnale will <br>
+ * - post message through operatonHelper <br>
+ * - set APP-C OAM state to started <br>
+ * - audit log the state <br>
+ */
+ class MyRunnable extends BaseActionRunnable {
+
+ ActionPhases currentPhase = ActionPhases.ToStop;
+ private LCMStateManager lcmStateManager;
+
+ MyRunnable(BaseProcessor parent) {
+ super(parent);
+
+ actionName = "OAM Restart";
+ auditMsg = Msg.OAM_OPERATION_RESTARTED;
+ finalState = AppcOamStates.Started;
+ }
+
+ /**
+ * Do restart action, include stop then start and always enable LCM operation.
+ * @return true if action is successful, false when aciton is failed or aborted
+ */
+ @Override
+ boolean doAction() {
+ logDebug(String.format("Executing %s task at phase (%s)",
+ actionName, currentPhase == null ? "null" : currentPhase.name()));
+
+ boolean isBundleOperationCompleted = true;
+ try {
+ switch (currentPhase) {
+ case ToStop:
+ isBundleOperationCompleted = bundleHelper.bundleOperations(
+ AppcOam.RPC.stop, bundleNameToFuture, myParent.asyncTaskHelper, this);
+ currentPhase = ActionPhases.Stopped;
+ break;
+ case Stopped:
+ // check state
+ AppcOamStates currentState = stateHelper.getBundlesState();
+ if (currentState == AppcOamStates.Stopped) {
+ currentPhase = ActionPhases.ToStart;
+ } else {
+ logDebug(String.format("%s task is waiting in stopped phase, current state is %s",
+ actionName, currentState));
+ }
+ break;
+ case ToStart:
+ isBundleOperationCompleted = bundleHelper.bundleOperations(
+ AppcOam.RPC.start, bundleNameToFuture, myParent.asyncTaskHelper, this);
+ currentPhase = ActionPhases.Started;
+ break;
+ case Error:
+ // do nothing
+ break;
+ default:
+ // Should not reach log it and return false;
+ logger.error("%s task doAction reached %s phase. not supported. return false.",
+ actionName, currentPhase.name());
+ stateHelper.setState(AppcOamStates.Error);
+ return false;
+ }
+
+ if (isTimeout("restart doAction")
+ || hasBundleOperationFailure()) {
+ currentPhase = ActionPhases.Error;
+ return true;
+ }
+ if (isBundleOperationCompleted) {
+ return true;
+ }
+
+ setAbortStatus();
+ } catch (APPCException e) {
+ setErrorStatus(e);
+ stateHelper.setState(AppcOamStates.Error);
+ }
+
+ return false;
+ }
+
+ /**
+ * With additional to get the LCMStateManager service
+ * @see BaseActionRunnable#checkState()
+ */
+ @Override
+ boolean checkState() {
+ switch (currentPhase) {
+ case Started:
+ try {
+ lcmStateManager = operationHelper.getService(LCMStateManager.class);
+ return super.checkState();
+ } catch (APPCException e) {
+ logDebug("LCMStateManager is not available.");
+ }
+ break;
+ default:
+ // in all the other ActionPhase, we want the run go back to doAction
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Final handling. The thread is cancelled.
+ * @param setState boolean to indicate if set OAM state or not
+ */
+ @Override
+ void postDoAction(boolean setState) {
+ AppcOamStates newState = null;
+ if (setState) {
+ logDebug("Always enable LCM operation");
+ lcmStateManager.enableLCMOperations();
+ newState = finalState;
+ }
+ postAction(newState);
+ super.postDoAction(setState);
+ }
+
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStartProcessor.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStartProcessor.java
new file mode 100644
index 000000000..2ca353244
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStartProcessor.java
@@ -0,0 +1,151 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.util.AsyncTaskHelper;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.onap.appc.requesthandler.LCMStateManager;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+
+/**
+ * Processor to handle start OAM API.
+ */
+public class OamStartProcessor extends BaseProcessor {
+
+ /**
+ * Constructor
+ *
+ * @param eelfLogger for logging
+ * @param configurationHelper for property reading
+ * @param stateHelper for APP-C OAM state checking
+ * @param asyncTaskHelper for scheduling async task
+ * @param operationHelper for operational helper
+ */
+ public OamStartProcessor(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelper,
+ StateHelper stateHelper,
+ AsyncTaskHelper asyncTaskHelper,
+ OperationHelper operationHelper) {
+ super(eelfLogger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+
+ rpc = AppcOam.RPC.start;
+ auditMsg = Msg.OAM_OPERATION_STARTING;
+ }
+
+ @Override
+ protected void scheduleAsyncTask() {
+ runnable = new MyRunnable(this);
+ super.scheduleAsyncTask();
+ }
+
+ /**
+ * This runnable does the async handling for the start REST API. And it will be scheduled to run one time.
+ *
+ * <p>This runnable will the following operations: <br>
+ * - do APP-C OAM bundle start through BundlerHelper<br>
+ * - and always enable LCM operation handling (which can be disabled through maintenance mode API).<br>
+ * <p>Once above operations are done, the runnale will <br>
+ * - post message through operatonHelper <br>
+ * - set APP-C OAM state to started <br>
+ * - audit log the state <br>
+ */
+ class MyRunnable extends BaseActionRunnable {
+
+ private LCMStateManager lcmStateManager;
+
+ MyRunnable(BaseProcessor parent) {
+ super(parent);
+ actionName = "OAM Start";
+ auditMsg = Msg.OAM_OPERATION_STARTED;
+ finalState = AppcOamStates.Started;
+ }
+
+ /**
+ * Do start action, include start bundle if needed and always enable LCM operation.
+ * @return true if action is successful, false when aciton is failed or aborted
+ */
+ @Override
+ boolean doAction() {
+ logDebug(String.format("Executing %s task", actionName));
+
+ boolean isBundleOperationCompleted = true;
+ try {
+ if (stateHelper.getState() != AppcOamStates.Started) {
+ logDebug("Start - APPC OAM state is not started, start the bundles");
+ isBundleOperationCompleted = bundleHelper.bundleOperations(
+ rpc, bundleNameToFuture, myParent.asyncTaskHelper, this);
+ }
+
+ if (isBundleOperationCompleted) {
+ return true;
+ }
+
+ setAbortStatus();
+ } catch (APPCException e) {
+ setErrorStatus(e);
+ stateHelper.setState(AppcOamStates.Error);
+ }
+
+ return false;
+ }
+
+ /**
+ * With additional to get the LCMStateManager service
+ * @see BaseActionRunnable#checkState()
+ */
+ @Override
+ boolean checkState() {
+ try {
+ lcmStateManager = operationHelper.getService(LCMStateManager.class);
+ return super.checkState();
+ } catch (APPCException e) {
+ logDebug("LCMStateManager is not available.");
+ return false;
+ }
+ }
+
+ /**
+ * Final handling
+ * @param setState boolean to indicate if set OAM state or not
+ */
+ @Override
+ void postDoAction(boolean setState) {
+ AppcOamStates newState = null;
+ if (setState) {
+ logDebug("Always enable LCM operation");
+ lcmStateManager.enableLCMOperations();
+ newState = finalState;
+ }
+ postAction(newState);
+ super.postDoAction(setState);
+ }
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStopProcessor.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStopProcessor.java
new file mode 100644
index 000000000..8710e0f08
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/processor/OamStopProcessor.java
@@ -0,0 +1,120 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.processor;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.i18n.Msg;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.util.AsyncTaskHelper;
+import org.onap.appc.oam.util.ConfigurationHelper;
+import org.onap.appc.oam.util.OperationHelper;
+import org.onap.appc.oam.util.StateHelper;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+
+/**
+ * Processor to handle stop OAM API.
+ */
+public class OamStopProcessor extends BaseProcessor {
+ /**
+ * Constructor
+ *
+ * @param eelfLogger for logging
+ * @param configurationHelper for property reading
+ * @param stateHelper for APP-C OAM state checking
+ * @param asyncTaskHelper for scheduling async task
+ * @param operationHelper for operational helper
+ */
+ public OamStopProcessor(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelper,
+ StateHelper stateHelper,
+ AsyncTaskHelper asyncTaskHelper,
+ OperationHelper operationHelper) {
+ super(eelfLogger, configurationHelper, stateHelper, asyncTaskHelper, operationHelper);
+
+ rpc = AppcOam.RPC.stop;
+ auditMsg = Msg.OAM_OPERATION_STOPPING;
+ }
+
+
+ @Override
+ protected void scheduleAsyncTask() {
+ runnable = new MyRunnable(this);
+ super.scheduleAsyncTask();
+ }
+
+ /**
+ * This runnable does the async handling for the stop REST API. And it will be scheduled to run one time.
+ *
+ * <p>This runnable will the following operations: <br>
+ * - do APP-C OAM bundle stop and then refresh through BundlerHelper<br>
+ * <p>Once above operations are done, the runnale will <br>
+ * - post message through operatonHelper <br>
+ * - set APP-C OAM state to started <br>
+ * - audit log the state <br>
+ */
+ class MyRunnable extends BaseActionRunnable {
+
+ MyRunnable(BaseProcessor parent) {
+ super(parent);
+ actionName = "OAM Stop";
+ auditMsg = Msg.OAM_OPERATION_STOPPED;
+ finalState = AppcOamStates.Stopped;
+ }
+
+ /**
+ * Do stop action, include stop bundle .
+ * @return true if action is successful, false when aciton is failed.
+ */
+ @Override
+ boolean doAction() {
+ logDebug(String.format("Executing %s task", actionName));
+
+ try {
+ boolean isBundleOperationCompleted = bundleHelper.bundleOperations(
+ rpc, bundleNameToFuture, myParent.asyncTaskHelper, this);
+ if (isBundleOperationCompleted) {
+ return true;
+ }
+
+ setAbortStatus();
+ } catch (APPCException e) {
+ setErrorStatus(e);
+ stateHelper.setState(AppcOamStates.Error);
+ }
+ return false;
+ }
+
+ /**
+ * Final handling
+ * @param setState boolean to indicate if set OAM state or not
+ */
+ @Override
+ void postDoAction(boolean setState) {
+ postAction(setState ? finalState : null);
+ super.postDoAction(setState);
+ }
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/AsyncTaskHelper.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/AsyncTaskHelper.java
new file mode 100644
index 000000000..2f74bf5a9
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/AsyncTaskHelper.java
@@ -0,0 +1,394 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.processor.BaseActionRunnable;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.FrameworkUtil;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import java.util.function.Consumer;
+
+/**
+ * The AsyncTaskHelper class manages an internal parent child data structure. The parent is a transient singleton,
+ * meaning only one can exist at any given time. The parent is scheduled with the
+ * {@link #scheduleBaseRunnable(Runnable, Consumer, long, long)} and is executed at configured interval. It can be
+ * terminated by using the {@link Future#cancel(boolean)} or the {@link Future#cancel(boolean)} returned from \
+ * {@link #scheduleBaseRunnable(Runnable, Consumer, long, long)}.
+ * <p>
+ * The children are scheduled using {@link #submitBaseSubCallable(Callable)}} and can only be scheduled if a parent
+ * is scheduled. Children only execute once, but can be terminated preemptively by the {@link Future#cancel(boolean)}
+ * returned from {@link #submitBaseSubCallable(Callable)} or indirectly by terminating the parent via the method
+ * described above.
+ * <p>
+ * This class augments the meaning of {@link Future#isDone()} in that it guarantees that this method only returns true
+ * if the scheduled {@link Runnable} or {@link Callable} is not currently executing and is not going to execute in the
+ * future. This is different than the Java core implementation of {@link Future#isDone()} in which it will return
+ * true immediately after the {@link Future#cancel(boolean)} is called. Even if a Thread is actively executing the
+ * {@link Runnable} or {@link Callable} and has not return yet. See Java BUG JDK-8073704
+ * <p>
+ * The parent {@link Future#isDone()} has an additional augmentation in that it will not return true until all of its
+ * children's {@link Future#isDone()} also return true.
+ *
+ */
+@SuppressWarnings("unchecked")
+public class AsyncTaskHelper {
+
+ private final EELFLogger logger;
+ private final ScheduledExecutorService scheduledExecutorService;
+ private final ThreadPoolExecutor bundleOperationService;
+
+ /** Reference to {@link MyFuture} return from {@link #scheduleBaseRunnable(Runnable, Consumer, long, long)} */
+ private MyFuture backgroundBaseRunnableFuture;
+
+ /** The cancel Callback from {@link #scheduleBaseRunnable(Runnable, Consumer, long, long)} */
+ private Consumer<AppcOam.RPC> cancelCallBackForBaseRunnable;
+
+ /** All Futures created by thus calls which have not completed -- {@link Future#isDone()} equals false */
+ private Set<MyFuture> myFutureSet = new HashSet<>();
+
+ /**
+ * Constructor
+ * @param eelfLogger of the logger
+ */
+ public AsyncTaskHelper(EELFLogger eelfLogger) {
+ logger = eelfLogger;
+
+ scheduledExecutorService = Executors.newSingleThreadScheduledExecutor(
+ (runnable) -> {
+ Bundle bundle = FrameworkUtil.getBundle(AppcOam.class);
+ return new Thread(runnable, bundle.getSymbolicName() + " scheduledExecutor");
+ }
+ );
+
+ bundleOperationService = new ThreadPoolExecutor(
+ 0,
+ 10,
+ 10,
+ TimeUnit.SECONDS,
+ new LinkedBlockingQueue(), //BlockingQueue<Runnable> workQueue
+ (runnable) -> {
+ Bundle bundle = FrameworkUtil.getBundle(AppcOam.class);
+ return new Thread(runnable, bundle.getSymbolicName() + " bundle operation executor");
+ }
+ );
+ }
+
+ /**
+ * Terminate the class <bS>ScheduledExecutorService</b>
+ */
+ public void close() {
+ logDebug("Start shutdown scheduleExcutorService.");
+ bundleOperationService.shutdownNow();
+ scheduledExecutorService.shutdownNow();
+ logDebug("Completed shutdown scheduleExcutorService.");
+ }
+
+
+ /**
+ * Cancel currently executing {@link BaseActionRunnable} if any.
+ * This method returns immediately if there is currently no {@link BaseActionRunnable} actively executing.
+ * @param rpcCausingAbort - The RPC causing the abort
+ * @param stateBeingAbborted - The current state being canceled
+ * @param timeout - The amount of time to wait for a cancel to complete
+ * @param timeUnit - The unit of time of timeout
+ * @throws TimeoutException - If {@link BaseActionRunnable} has not completely cancelled within the timeout period
+ * @throws InterruptedException - If the Thread waiting for the abort
+ */
+ public synchronized void cancelBaseActionRunnable(final AppcOam.RPC rpcCausingAbort,
+ AppcOamStates stateBeingAbborted,
+ long timeout, TimeUnit timeUnit)
+ throws TimeoutException,InterruptedException {
+
+ final MyFuture localBackgroundBaseRunnableFuture = backgroundBaseRunnableFuture;
+ final Consumer<AppcOam.RPC> localCancelCallBackForBaseRunnable = cancelCallBackForBaseRunnable;
+
+ if (localBackgroundBaseRunnableFuture == null || localBackgroundBaseRunnableFuture.isDone()) {
+ return;
+ }
+
+ if (localCancelCallBackForBaseRunnable != null) {
+ localCancelCallBackForBaseRunnable.accept(rpcCausingAbort);
+ }
+ localBackgroundBaseRunnableFuture.cancel(true);
+
+ long timeoutMillis = timeUnit.toMillis(timeout);
+ long expiryTime = System.currentTimeMillis() + timeoutMillis;
+ while (!(localBackgroundBaseRunnableFuture.isDone())) {
+ long sleepTime = expiryTime - System.currentTimeMillis();
+ if (sleepTime < 1) {
+ break;
+ }
+ this.wait(sleepTime);
+ }
+
+ if (!localBackgroundBaseRunnableFuture.isDone()) {
+ throw new TimeoutException(String.format("Unable to abort %s in timely manner.",stateBeingAbborted));
+ }
+ }
+
+ /**
+ * Schedule a {@link BaseActionRunnable} to begin async execution. This is the Parent {@link Runnable} for the
+ * children that are submitted by {@link #submitBaseSubCallable(Callable)}
+ *
+ * The currently executing {@link BaseActionRunnable} must fully be terminated before the next can be scheduled.
+ * This means all Tasks' {@link MyFuture#isDone()} must equal true and all threads must return to their respective
+ * thread pools.
+ *
+ * @param runnable of the to be scheduled service.
+ * @param cancelCallBack to be invoked when
+ * {@link #cancelBaseActionRunnable(AppcOam.RPC, AppcOamStates, long, TimeUnit)} is invoked.
+ * @param initialDelayMillis the time to delay first execution
+ * @param delayMillis the delay between the termination of one
+ * execution and the commencement of the next
+ * @return The {@link BaseActionRunnable}'s {@link Future}
+ * @throws IllegalStateException if there is currently executing Task
+ */
+ public synchronized Future<?> scheduleBaseRunnable(final Runnable runnable,
+ final Consumer<AppcOam.RPC> cancelCallBack,
+ long initialDelayMillis,
+ long delayMillis)
+ throws IllegalStateException {
+
+ if (backgroundBaseRunnableFuture != null && !backgroundBaseRunnableFuture.isDone()) {
+ throw new IllegalStateException("Unable to schedule background task when one is already running. All task must fully terminated before another can be scheduled. ");
+ }
+
+ this.cancelCallBackForBaseRunnable = cancelCallBack;
+
+ backgroundBaseRunnableFuture = new MyFuture(runnable) {
+ /**
+ * augments the cancel operation to cancel all subTack too,
+ */
+ @Override
+ public boolean cancel(final boolean mayInterruptIfRunning) {
+ boolean cancel;
+ synchronized (AsyncTaskHelper.this) {
+ cancel = super.cancel(mayInterruptIfRunning);
+ //clone the set to prevent java.util.ConcurrentModificationException. The synchronized prevents
+ //other threads from modifying this set, but not itself. The f->f.cancel may modify myFutureSet by
+ //removing an entry which breaks the iteration in the forEach.
+ (new HashSet<MyFuture>(myFutureSet))
+ .stream().filter(f->!this.equals(f)).forEach(f->f.cancel(mayInterruptIfRunning));
+ }
+ return cancel;
+ }
+
+ /**
+ * augments the isDone operation to return false until all subTask have completed too.
+ */
+ @Override
+ public boolean isDone() {
+ synchronized (AsyncTaskHelper.this) {
+ return myFutureSet.isEmpty();
+ }
+ }
+ };
+ backgroundBaseRunnableFuture.setFuture(
+ scheduledExecutorService.scheduleWithFixedDelay(
+ backgroundBaseRunnableFuture, initialDelayMillis, delayMillis, TimeUnit.MILLISECONDS)
+ );
+ return backgroundBaseRunnableFuture;
+ }
+
+ /**
+ * Submits children {@link Callable} to be executed as soon as possible, A parent must have been scheduled
+ * previously via {@link #scheduleBaseRunnable(Runnable, Consumer, long, long)}
+ * @param callable the Callable to be submitted
+ * @return The {@link Callable}'s {@link Future}
+ */
+ synchronized Future<?> submitBaseSubCallable(final Callable callable) {
+
+ if (backgroundBaseRunnableFuture == null
+ || backgroundBaseRunnableFuture.isCancelled()
+ || backgroundBaseRunnableFuture.isDone()){
+ throw new IllegalStateException("Unable to schedule subCallable when a base Runnable is not running.");
+ }
+
+ //Make sure the pool is ready to go
+ if(bundleOperationService.getPoolSize() != bundleOperationService.getMaximumPoolSize()){
+ bundleOperationService.setCorePoolSize(bundleOperationService.getMaximumPoolSize());
+ bundleOperationService.prestartAllCoreThreads();
+ bundleOperationService.setCorePoolSize(0);
+ }
+
+ MyFuture<?> myFuture = new MyFuture(callable);
+ myFuture.setFuture(bundleOperationService.submit((Callable)myFuture));
+ return myFuture;
+ }
+
+ /**
+ * Genral debug log when debug logging level is enabled.
+ * @param message of the log message format
+ * @param args of the objects listed in the message format
+ */
+ private void logDebug(String message, Object... args) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format(message, args));
+ }
+ }
+
+ /**
+ * This class has two purposes. First it insures {@link #isDone()} only returns true if the deligate is not
+ * currently running and will not be running in the future: See Java BUG JDK-8073704 Second this class maintains
+ * the {@link #myFutureSet } by insurring that itself is removed when {@link #isDone()} returns true.
+ *
+ * See {@link #scheduleBaseRunnable(Runnable, Consumer, long, long)} and {@link #submitBaseSubCallable(Callable)}
+ * for usage of this class
+ */
+ private class MyFuture<T> implements Future<T>, Runnable, Callable<T> {
+
+ private Future<T> future;
+ private final Runnable runnable;
+ private final Callable<T> callable;
+ private boolean isRunning;
+
+ MyFuture(Runnable runnable) {
+ this.runnable = runnable;
+ this.callable = null;
+ myFutureSet.add(this);
+ }
+
+ MyFuture(Callable<T> callable) {
+ this.runnable = null;
+ this.callable = callable;
+ myFutureSet.add(this);
+ }
+
+ void setFuture(Future<T> future) {
+ this.future = future;
+ }
+
+ @Override
+ public boolean cancel(boolean mayInterruptIfRunning) {
+ synchronized (AsyncTaskHelper.this) {
+ if (!isRunning) {
+ myFutureSetRemove();
+ }
+
+ return future.cancel(mayInterruptIfRunning);
+ }
+ }
+
+ @Override
+ public boolean isCancelled() {
+ synchronized (AsyncTaskHelper.this) {
+ return future.isCancelled();
+ }
+ }
+
+ @Override
+ public boolean isDone() {
+ synchronized (AsyncTaskHelper.this) {
+ return future.isDone() && !isRunning;
+ }
+ }
+
+ @Override
+ public T get() throws InterruptedException, ExecutionException {
+ return future.get();
+ }
+
+ @Override
+ public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
+ return future.get(timeout, unit);
+ }
+
+ @Override
+ public void run() {
+ synchronized (AsyncTaskHelper.this) {
+ if(future.isCancelled()){
+ return;
+ }
+ isRunning = true;
+ }
+ try {
+ runnable.run();
+ } finally {
+ synchronized (AsyncTaskHelper.this) {
+ isRunning = false;
+
+ //The Base Runnable is expected to run again.
+ //unless it has been canceled.
+ //so only removed if it is canceled.
+ if (future.isCancelled()) {
+ myFutureSetRemove();
+ }
+ }
+ }
+ }
+
+ @Override
+ public T call() throws Exception {
+ synchronized (AsyncTaskHelper.this) {
+ if(future.isCancelled()){
+ throw new CancellationException();
+ }
+ isRunning = true;
+ }
+ try {
+ return callable.call();
+ } finally {
+ synchronized (AsyncTaskHelper.this){
+ isRunning = false;
+ myFutureSetRemove();
+ }
+ }
+ }
+
+
+ /**
+ * Removes this from the the myFutureSet.
+ * When all the BaseActionRunnable is Done notify any thread waiting in
+ * {@link AsyncTaskHelper#cancelBaseActionRunnable(AppcOam.RPC, AppcOamStates, long, TimeUnit)}
+ */
+ void myFutureSetRemove(){
+ synchronized (AsyncTaskHelper.this) {
+ myFutureSet.remove(this);
+ if(myFutureSet.isEmpty()){
+ backgroundBaseRunnableFuture = null;
+ cancelCallBackForBaseRunnable = null;
+ AsyncTaskHelper.this.notifyAll();
+
+ }
+ }
+ }
+
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleFilter.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleFilter.java
new file mode 100644
index 000000000..6dd98f165
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleFilter.java
@@ -0,0 +1,128 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.util;
+
+import org.osgi.framework.Bundle;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+
+
+/**
+ *
+ * Utility Class that splits a given bundleSet into two sets: bundleToStopSet and
+ * bundleToNotStopSet
+ *
+ * The bundleToStopSet is defined as: all bundles which match at least one of
+ * the stopRegexes but exceptRegexes none of the
+ *
+ * The bundleToNotStopSet is defined as all bundles which are not a member of
+ * the bundleToStopSet
+ *
+ */
+class BundleFilter {
+
+ private final Map<String, Bundle> bundleToStopSet;
+ private final Map<String, Bundle> bundleToNotStopSet;
+
+
+ /**
+ * BundleFilter a bundle filter
+ * @param stopRegexes - An array of regular expression used to pick out which bundles are candidates for stopping
+ * @param exceptRegexes - An array of regular expression used to override which bundles are candidates for stopping
+ * @param bundles - An array of the bundle to be split into {@link #getBundlesToStop()} {@link #getBundlesToNotStop()}
+ */
+ BundleFilter(String[] stopRegexes, String[] exceptRegexes, Bundle[] bundles) {
+
+ Pattern[] stopPatterns = toPattern(stopRegexes);
+ Pattern[] exceptPatterns = toPattern(exceptRegexes);
+
+ Map<String, Bundle> bundleToStop = new HashMap<>();
+ Map<String, Bundle> bundleToNotStop = new HashMap<>();
+
+ for (Bundle bundle : bundles) {
+ String symbolicName = bundle.getSymbolicName();
+ if (isMatch(symbolicName,stopPatterns) && !isMatch(symbolicName,exceptPatterns)) {
+ bundleToStop.put(symbolicName, bundle);
+ } else {
+ bundleToNotStop.put(symbolicName, bundle);
+ }
+ }
+
+ this.bundleToStopSet = Collections.unmodifiableMap(bundleToStop);
+ this.bundleToNotStopSet = Collections.unmodifiableMap(bundleToNotStop);
+ }
+
+ /**
+ * Determines if the value matches any of the regular expressions.
+ *
+ * @param value
+ * - the value that is to be matched
+ * @param patterns
+ * - the array of {@link Pattern} to match the value against
+ * @return boolean true if there is a match
+ */
+ private boolean isMatch(String value,Pattern[] patterns) {
+ for (Pattern pattern : patterns) {
+ if (pattern.matcher(value).matches()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * This method converts an Array of regular expression in String form into a
+ * Array of {@link Pattern}
+ *
+ * @param regex
+ * - A string array of regular expressions
+ * @return Pattern Array of compiled regular expressions
+ */
+ private Pattern[] toPattern(String[] regex) {
+ Pattern[] pattern = new Pattern[regex.length];
+ for (int i = 0; i < regex.length; i++ ) {
+ pattern[i] = Pattern.compile(regex[i]);
+ }
+ return pattern;
+ }
+
+
+ /**@return Map of bundles that are to be stopped */
+ Map<String, Bundle> getBundlesToStop(){
+ return bundleToStopSet;
+ }
+
+ /**
+ *
+ * @return Map of bundles that are not to be stopped
+ */
+ Map<String, Bundle> getBundlesToNotStop() {
+ return bundleToNotStopSet;
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleHelper.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleHelper.java
new file mode 100644
index 000000000..3a4602542
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/BundleHelper.java
@@ -0,0 +1,274 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.apache.commons.lang3.ArrayUtils;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.processor.BaseCommon;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.FrameworkUtil;
+
+import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Future;
+
+/**
+ * Utility class provides general bundle operational helps.
+ */
+public class BundleHelper {
+ private final static String PROP_BUNDLE_TO_STOP = "appc.OAM.ToStop.properties";
+ private final static String PROP_BUNDLES_TO_NOT_STOP = "appc.OAM.ToNotStop.properties";
+
+ private final EELFLogger logger;
+ private final StateHelper stateHelper;
+ private final ConfigurationHelper configurationHelper;
+
+ /**
+ * Constructor
+ *
+ * @param eelfLogger of the logger
+ * @param configurationHelperIn of ConfigurationHelper instance
+ * @param stateHelperIn of StateHelper instance
+ */
+ public BundleHelper(EELFLogger eelfLogger,
+ ConfigurationHelper configurationHelperIn,
+ StateHelper stateHelperIn) {
+ logger = eelfLogger;
+ configurationHelper = configurationHelperIn;
+ stateHelper = stateHelperIn;
+ }
+
+ /**
+ * Handle bundle operations, such as stop or start bundle.
+ *
+ * @param rpc enum indicate if the operation is to stop, start or restart
+ * @return boolean to indicate if the operation is successful (true) or failed (false)
+ * @throws APPCException when error occurs
+ */
+ public boolean bundleOperations(AppcOam.RPC rpc,
+ Map<String, Future<?>> threads,
+ AsyncTaskHelper taskHelper,
+ BaseCommon baseCommon)
+ throws APPCException {
+ long mStartTime = System.currentTimeMillis();
+ logDebug(String.format("Entering OAM bundleOperations with rpc (%s).", rpc.name()));
+
+ String action = rpc.getAppcOperation().toString();
+ if (rpc != AppcOam.RPC.stop && rpc != AppcOam.RPC.start) {
+ throw new APPCException("rpc(" + rpc + ") is not supported by bundleOperation.");
+ }
+
+ AppcOamStates originalState = stateHelper.getState();
+
+ boolean isBundleOperationComplete = true;
+
+ Map<String, Bundle> appcLcmBundles = getAppcLcmBundles();
+ for (Map.Entry<String, Bundle> bundleEntry : appcLcmBundles.entrySet()) {
+ String bundleName = bundleEntry.getKey();
+ Bundle bundle = bundleEntry.getValue();
+
+ logDebug("OAM launch thread for %s bundle %s", action, bundleName);
+ if (rpc == AppcOam.RPC.start) {
+ // Abort in the interruption case.
+ // such as when a Stop request is receive while APPC is still trying to Start Up.
+ if (!stateHelper.isSameState(originalState)) {
+ logger.warn("OAM %s bundle operation aborted since OAM state is no longer %s!",
+ originalState.name());
+ isBundleOperationComplete = false;
+ break;
+ }
+ }
+
+ threads.put(bundleName,
+ taskHelper.submitBaseSubCallable(new BundleTask(rpc, bundle,baseCommon)));
+ }
+
+ logDebug(String.format("Leaving OAM bundleOperations with rpc (%s) with complete(%s), elasped (%d) ms.",
+ rpc.name(), Boolean.toString(isBundleOperationComplete), getElapseTimeMs(mStartTime)));
+
+ return isBundleOperationComplete;
+ }
+
+ private long getElapseTimeMs(long mStartTime) {
+ return System.currentTimeMillis() - mStartTime;
+ }
+
+ /**
+ * Check if all BundleTasks are completed
+ * @param bundleNameFutureMap with bundle name and BundleTask Future object
+ * @return true if all are done, otherwise, false
+ */
+ public boolean isAllTaskDone(Map<String, Future<?>> bundleNameFutureMap) {
+ boolean anyNotDone = bundleNameFutureMap.values().stream().anyMatch((f) -> !f.isDone());
+ return !anyNotDone;
+ }
+
+ /**
+ * Cancel BundleTasks which are not finished
+ * @param bundleNameFutureMap with bundle name and BundleTask Future object
+ */
+ public void cancelUnfinished(Map<String, Future<?>> bundleNameFutureMap) {
+ bundleNameFutureMap.values().stream().filter((f)
+ -> !f.isDone()).forEach((f)
+ -> f.cancel(true));
+ }
+
+ /**
+ * Get number of failed BundleTasks
+ * @param bundleNameFutureMap with bundle name and BundleTask Future object
+ * @return number(long) of the failed BundleTasks
+ */
+ public long getFailedMetrics(Map<String, Future<?>> bundleNameFutureMap) {
+ return bundleNameFutureMap.values().stream().map((f) -> {
+ try {
+ return f.get();
+ } catch (Exception e) {
+ // should not get here
+ throw new RuntimeException(e);
+ }
+ }).filter((b) -> ((BundleTask)b).failException != null).count();
+ }
+
+ /**
+ * Gets the list of Appc-bundles to be stopped/started
+ *
+ * @return Map of bundle symbolic name and bundle instance
+ */
+ Map<String, Bundle> getAppcLcmBundles() {
+ logDebug("In getAppcLcmBundles");
+
+ String[] bundlesToStop = readPropsFromPropListName(PROP_BUNDLE_TO_STOP);
+ String[] regExBundleNotStop = readPropsFromPropListName(PROP_BUNDLES_TO_NOT_STOP);
+
+ BundleFilter bundleList = new BundleFilter(bundlesToStop, regExBundleNotStop, getBundleList());
+
+ logger.info(String.format("(%d) APPC bundles to Stop/Start: %s.", bundleList.getBundlesToStop().size(),
+ bundleList.getBundlesToStop().toString()));
+
+ logger.debug(String.format("(%d) APPC bundles that won't be Stopped/Started: %s.",
+ bundleList.getBundlesToNotStop().size(), bundleList.getBundlesToNotStop().toString()));
+
+ return bundleList.getBundlesToStop();
+ }
+
+ /**
+ * Gets a list of all user desired bundles that should be stopped/Started as part of
+ * OAM Stop and Start API
+ *
+ * @param propListKey String of the properties list property name
+ * @return properties values of the related
+ */
+ String[] readPropsFromPropListName(String propListKey) {
+ // get properties list by properties list name
+ String[] propNames = configurationHelper.readProperty(propListKey);
+ // go through each property to get the property values
+ String[] propValue = ArrayUtils.EMPTY_STRING_ARRAY;
+ if (propNames != null) {
+ for (String aPropName : propNames) {
+ propValue = ArrayUtils.addAll(propValue, configurationHelper.readProperty(aPropName));
+ }
+ }
+ return propValue;
+ }
+
+ /**
+ * Get all bundle list of APP-C
+ * @return Array of Bundle
+ */
+ Bundle[] getBundleList() {
+ BundleContext myBundleContext = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
+ if (myBundleContext != null) {
+ return myBundleContext.getBundles();
+ }
+ return null;
+ }
+
+ /**
+ * Genral debug log when debug logging level is enabled.
+ * @param message of the log message format
+ * @param args of the objects listed in the message format
+ */
+ private void logDebug(String message, Object... args) {
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format(message, args));
+ }
+ }
+
+ /**
+ * Runnable to execute bundle operations: start or stop
+ */
+ class BundleTask implements Callable<BundleTask> {
+ Exception failException;
+
+ private AppcOam.RPC rpc;
+ private Bundle bundle;
+ private String bundleName;
+ private String actionName;
+ private final BaseCommon baseCommon;
+
+ BundleTask(AppcOam.RPC rpcIn, Bundle bundleIn, BaseCommon baseCommon) {
+ rpc = rpcIn;
+ actionName = rpc.getAppcOperation().toString();
+ bundle = bundleIn;
+ bundleName = bundle.getSymbolicName();
+ this.baseCommon = baseCommon;
+ }
+
+ @Override
+ public BundleTask call() throws Exception {
+ try {
+ baseCommon.setInitialLogProperties();
+
+ long bundleOperStartTime = System.currentTimeMillis();
+ logDebug(String.format("OAM %s bundle %s ===>", actionName, bundleName));
+ switch (rpc) {
+ case start:
+ bundle.start();
+ break;
+ case stop:
+ bundle.stop();
+ break;
+ default:
+ // should do nothing
+ }
+ logDebug(String.format("OAM %s bundle %s completed <=== elasped %d",
+ actionName, bundleName, getElapseTimeMs(bundleOperStartTime)));
+ } catch (BundleException e) {
+ logger.error(String.format("Exception encountered when OAM %s bundle %s ",
+ actionName, bundleName), e);
+ failException = e;
+ }
+ finally {
+ baseCommon.clearRequestLogProperties();
+ }
+ return this;
+ }
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/ConfigurationHelper.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/ConfigurationHelper.java
new file mode 100644
index 000000000..3c5629f11
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/ConfigurationHelper.java
@@ -0,0 +1,106 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.apache.commons.lang3.ArrayUtils;
+import org.onap.appc.Constants;
+import org.onap.appc.configuration.Configuration;
+import org.onap.appc.configuration.ConfigurationFactory;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Utility class provides general configuration helps
+ */
+public class ConfigurationHelper {
+ final static String PROP_KEY_APPC_NAME = Constants.PROPERTY_APPLICATION_NAME;
+ final static String PROP_KEY_METRIC_STATE = "metric.enabled";
+ private final String OAM_OPERATION_TIMEOUT_SECOND = "appc.OAM.api.timeout";
+ /** Default operation timeout set to 1 minute */
+ private final int DEFAULT_OAM_OPERATION_TIMEOUT = 60;
+
+ private final EELFLogger logger;
+ private Configuration configuration = ConfigurationFactory.getConfiguration();
+
+ public ConfigurationHelper(EELFLogger eelfLogger) {
+ logger = eelfLogger;
+ }
+
+ public String getAppcName() {
+ return configuration.getProperty(PROP_KEY_APPC_NAME);
+ }
+
+ public boolean isMetricEnabled() {
+ return configuration.getBooleanProperty(PROP_KEY_METRIC_STATE, false);
+ }
+
+ public Configuration getConfig() {
+ return configuration;
+ }
+
+ /**
+ * Read property value of a specified property key
+ *
+ * @param propertyKey string of the property key
+ * @return String[] of the property values associated with the propertyKey
+ */
+ String[] readProperty(String propertyKey) {
+ String propertyValue = configuration.getProperty(propertyKey);
+ if (propertyValue == null) {
+ return ArrayUtils.EMPTY_STRING_ARRAY;
+ }
+
+ if (logger.isDebugEnabled()) {
+ logger.debug(String.format("Property[%s] has value (%s).", propertyKey, propertyValue));
+ }
+
+ if (propertyValue.contains(",")) {
+ return propertyValue.split("\\s*,\\s*");
+ }
+ return new String[]{propertyValue};
+ }
+
+
+
+
+
+ /**
+ * This method returns timeout in milliseconds. The source is chosen in the following order:
+ * The overrideTimeoutSeconds argument
+ * or {@link #OAM_OPERATION_TIMEOUT_SECOND} found in the configuration file
+ * or the {@link #DEFAULT_OAM_OPERATION_TIMEOUT}
+ * @param overrideTimeoutSeconds or null to us the other sources
+ * @return timeout in milliseconds
+ */
+ public long getOAMOperationTimeoutValue(Integer overrideTimeoutSeconds) {
+ return TimeUnit.SECONDS.toMillis(
+ overrideTimeoutSeconds == null ?
+ getConfig().getIntegerProperty(OAM_OPERATION_TIMEOUT_SECOND, DEFAULT_OAM_OPERATION_TIMEOUT)
+ :
+ overrideTimeoutSeconds
+ );
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/OperationHelper.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/OperationHelper.java
new file mode 100644
index 000000000..9eec0b67b
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/OperationHelper.java
@@ -0,0 +1,205 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import com.att.eelf.configuration.EELFManager;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.MaintenanceModeInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.RestartInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StartInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.StopInput;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.CommonHeader;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.common.header.common.header.Flags;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.status.Status;
+import org.onap.appc.exceptions.APPCException;
+import org.onap.appc.exceptions.InvalidInputException;
+import org.onap.appc.exceptions.InvalidStateException;
+import org.onap.appc.lifecyclemanager.LifecycleManager;
+import org.onap.appc.lifecyclemanager.objects.LifecycleException;
+import org.onap.appc.lifecyclemanager.objects.NoTransitionDefinedException;
+import org.onap.appc.oam.AppcOam;
+import org.onap.appc.oam.messageadapter.MessageAdapter;
+import org.onap.appc.oam.messageadapter.OAMContext;
+import org.onap.appc.statemachine.impl.readers.AppcOamMetaDataReader;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+
+/**
+ * Utility class provides general operational helps.
+ */
+@SuppressWarnings("unchecked")
+public class OperationHelper {
+ final String MISSING_COMMON_HEADER_MESSAGE = "Missing common header";
+ final String MISSING_FIELD_MESSAGE = "Common header must have both originatorId and requestId";
+ final String NOT_SUPPORT_FLAG = "Flags is not supported by this operation";
+ final String NO_SERVICE_REF_FORMAT = "Using the BundleContext failed to get service reference for %s";
+
+ private final EELFLogger logger = EELFManager.getInstance().getLogger(OperationHelper.class);
+ private LifecycleManager lifecycleMgr;
+ private MessageAdapter messageAdapter;
+
+ public OperationHelper() {
+ // do nothing
+ }
+
+ /**
+ * This method is used to validate OAM REST API input due to the following ODL bugs results no validation : </tt>
+ * <p> - <a href="https://bugs.opendaylight.org/show_bug.cgi?id=8088">
+ * Bug 8088 - Mandatory attributes in RPC input are not honoured</a>
+ * <p> - <a href="https://bugs.opendaylight.org/show_bug.cgi?id=5830">
+ * Bug 5830 - Mandatory leaf enforcement is not correct with presence container</a>
+ *
+ * @param inputObject object from the OAM REST API input object
+ * @throws InvalidInputException is thrown when the commonHeader is invalid
+ */
+ public void isInputValid(final Object inputObject) throws InvalidInputException {
+ CommonHeader commonHeader = getCommonHeader(inputObject);
+ if (commonHeader == null) {
+ throw new InvalidInputException(MISSING_COMMON_HEADER_MESSAGE);
+ }
+
+ if (commonHeader.getOriginatorId() == null
+ || commonHeader.getRequestId() == null) {
+ throw new InvalidInputException(MISSING_FIELD_MESSAGE);
+ }
+
+ // check Flags
+ if (inputObject instanceof MaintenanceModeInput
+ && commonHeader.getFlags() != null) {
+ throw new InvalidInputException(NOT_SUPPORT_FLAG);
+ }
+ }
+
+ /**
+ * Get commonHead of the inputObject (expecting the inputObject of OAM REST API)
+ * @param inputObject the OAM REST API input object
+ * @return CommonHeader of the inputObject. If the inputObject is not a OAM REST API input, null is returned.
+ */
+ public CommonHeader getCommonHeader(final Object inputObject) {
+ if (inputObject instanceof StartInput) {
+ return ((StartInput)inputObject).getCommonHeader();
+ }
+ if (inputObject instanceof StopInput) {
+ return ((StopInput)inputObject).getCommonHeader();
+ }
+ if (inputObject instanceof MaintenanceModeInput) {
+ return ((MaintenanceModeInput)inputObject).getCommonHeader();
+ }
+ if (inputObject instanceof RestartInput) {
+ return ((RestartInput)inputObject).getCommonHeader();
+ }
+ return null;
+ }
+
+ public Integer getParamRequestTimeout(final Object inputObject) {
+ if (inputObject instanceof MaintenanceModeInput) {
+ // maintanence mode, we do not support request timeout
+ return 0;
+ }
+
+ CommonHeader commonHeader = getCommonHeader(inputObject);
+ if (commonHeader == null) {
+ return 0;
+ }
+
+ Flags inputFlags = commonHeader.getFlags();
+ if (inputFlags == null) {
+ return null;
+ }
+ return inputFlags.getRequestTimeout();
+ }
+ /**
+ * Get service instance using bundle context.
+ *
+ * @param _class of the expected service instance
+ * @param <T> of the expected service instance
+ * @return service instance of the expected
+ * @throws APPCException when cannot find service reference or service isntance
+ */
+ public <T> T getService(Class<T> _class) throws APPCException {
+ BundleContext bctx = FrameworkUtil.getBundle(_class).getBundleContext();
+ if (bctx != null) {
+ ServiceReference sref = bctx.getServiceReference(_class.getName());
+ if (sref != null) {
+ if (logger.isTraceEnabled()) {
+ logger.debug("Using the BundleContext got the service reference for " + _class.getName());
+ }
+ return (T) bctx.getService(sref);
+ }
+ }
+
+ throw new APPCException(String.format(NO_SERVICE_REF_FORMAT, _class.getName()));
+ }
+
+ /**
+ * Get next valid state from life cycle manager.
+ *
+ * @param operation of the AppcOperation for the state changes
+ * @param currentState of AppcOamStates
+ * @return next AppcOamStates based on the currentState and operation
+ * @throws APPCException If life cycle manager instance cannot be retrieved
+ * @throws InvalidStateException when the operation is not supported on the currentState
+ */
+ public AppcOamStates getNextState(AppcOamMetaDataReader.AppcOperation operation, AppcOamStates currentState)
+ throws APPCException, InvalidStateException {
+ if (lifecycleMgr == null) {
+ lifecycleMgr = getService(LifecycleManager.class);
+ }
+
+ try {
+ String nextState = lifecycleMgr.getNextState("APPC", currentState.name(), operation.toString());
+ if (nextState != null) {
+ return AppcOamStates.valueOf(nextState);
+ }
+ } catch (LifecycleException |NoTransitionDefinedException ex) {
+ logger.error("Invalid next state based on the current state and attempted Operation " + ex.getMessage());
+ }
+
+ throw new InvalidStateException(String.format(AppcOam.INVALID_STATE_MESSAGE_FORMAT, operation, "APPC", currentState));
+ }
+
+ /**
+ * Post notification through MessageAdapter.
+ *
+ * @param rpc of REST API RPC
+ * @param commonHeader of REST API request common header
+ * @param status of the to be post message
+ */
+ public void sendNotificationMessage(AppcOam.RPC rpc, CommonHeader commonHeader, Status status) {
+ if (messageAdapter == null) {
+ messageAdapter = new MessageAdapter();
+ messageAdapter.init();
+
+ }
+
+ OAMContext oamContext = new OAMContext();
+ oamContext.setRpcName(rpc);
+ oamContext.setCommonHeader(commonHeader);
+ oamContext.setStatus(status);
+ messageAdapter.post(oamContext);
+ }
+}
diff --git a/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/StateHelper.java b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/StateHelper.java
new file mode 100644
index 000000000..871a07676
--- /dev/null
+++ b/appc-oam/appc-oam-bundle/src/main/java/org/onap/appc/oam/util/StateHelper.java
@@ -0,0 +1,144 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP : APPC
+ * ================================================================================
+ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Copyright (C) 2017 Amdocs
+ * =============================================================================
+ * 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.
+ *
+ * ECOMP is a trademark and service mark of AT&T Intellectual Property.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.appc.oam.util;
+
+import com.att.eelf.configuration.EELFLogger;
+import org.opendaylight.yang.gen.v1.org.onap.appc.oam.rev170303.AppcState;
+import org.onap.appc.statemachine.impl.readers.AppcOamStates;
+import org.osgi.framework.Bundle;
+
+import java.util.Map;
+
+/*
+ * Utility class provides general state helps
+ */
+public class StateHelper {
+ /** logger inherited from AppcOam */
+ private final EELFLogger logger;
+ private ConfigurationHelper configurationHelper;
+ /** APP-C OAM current state in AppcOamStates value */
+ private volatile AppcOamStates appcOamCurrentState;
+
+ /**
+ * Constructor
+ *
+ * @param eelfLogger of the logger
+ */
+ public StateHelper(EELFLogger eelfLogger, ConfigurationHelper cHelper) {
+ logger = eelfLogger;
+ configurationHelper = cHelper;
+ appcOamCurrentState = AppcOamStates.Unknown;
+ }
+
+ /**
+ * Set the passed in state to the class <b>appOamCurrentState</b>.
+ *
+ * @param appcOamStates of the new state
+ */
+ public void setState(AppcOamStates appcOamStates) {
+ appcOamCurrentState = appcOamStates;
+ }
+
+ /**
+ * Get the state
+ * @return the class <b>appOamCurrentState</b>
+ */
+ public AppcOamStates getState() {
+ return appcOamCurrentState;
+ }
+
+ /**
+ * Validate if the passed in state is the same as the class <b>appOamCurrentState</b>.
+ *
+ * @param appcOamStates of the to be compared state
+ * @return true if they are the same, otherwise false
+ */
+ boolean isSameState(AppcOamStates appcOamStates) {
+ return appcOamCurrentState == appcOamStates;
+ }
+
+ /**
+ * Get APP-C OAM current state
+ *
+ * <p>When appcOamCurrentState is null or unknown, reset it with APPC LCM bundle state.
+ *
+ * @return AppcOamStates of the current APP-C OAM state
+ */
+ public AppcOamStates getCurrentOamState() {
+ if (appcOamCurrentState == null || appcOamCurrentState.equals(AppcOamStates.Unknown)) {
+ appcOamCurrentState = getBundlesState();
+ }
+ return appcOamCurrentState;
+ }
+
+ /**
+ * Use getCurrentOamState to get current OAM AppcOamStates and then convert to AppcState of Yang.
+ *
+ * @return AppcState of current OAM state
+ */
+ public AppcState getCurrentOamYangState() {
+ try {
+ AppcOamStates appcOamStates = getCurrentOamState();
+ return AppcState.valueOf(appcOamStates.name());
+ } catch (Exception ex) {
+ logger.error(String.format("Unable to determine the current APP-C OAM state due to %s.", ex.getMessage()));
+ }
+ return AppcState.Unknown;
+ }
+
+ /**
+ * Get APPC state from the state of the set of APPC LCM bundles.
+ * <p>The state of each bundle will be checked and the lowest state will be uses as the returning AppcOamStates.
+ * <p>The bundle state order are defined in OSGI bundle (@see org.osgi.framework.Bundle) class
+ * as the int value assigned to each state as the following: <br>
+ * - UNINSTALLED (1) <br>
+ * - INSTALLED (2) <br>
+ * - RESOLVED (4) <br>
+ * - STARTING (8) <br>
+ * - STOPPING (16) <br>
+ * - ACTIVE (32) <br>
+ *
+ * @return AppcOamStates
+ */
+ public AppcOamStates getBundlesState() {
+ BundleHelper bundleHelper = new BundleHelper(logger, configurationHelper, this);
+ Map<String, Bundle> lcmBundleMap = bundleHelper.getAppcLcmBundles();
+ if (lcmBundleMap == null || lcmBundleMap.isEmpty()) {
+ return AppcOamStates.Unknown;
+ }
+
+ // As we are picking up the lowest bundle state as general APP-C state, we will start with ACTIVE
+ int currentState = Bundle.ACTIVE;
+ for (Bundle bundle : lcmBundleMap.values()) {
+ int bundleState = bundle.getState();
+ logger.trace(String.format("getBundlesState: [%s] has state (%d)", bundle.getSymbolicName(), bundleState));
+ if (bundleState < currentState) {
+ currentState = bundleState;
+ }
+ }
+ return AppcOamStates.getOamStateFromBundleState(currentState);
+ }
+
+}