summaryrefslogtreecommitdiffstats
path: root/feature-lifecycle/src
diff options
context:
space:
mode:
authorJorge Hernandez <jorge.hernandez-herrero@att.com>2019-03-28 01:12:10 -0500
committerjhh <jorge.hernandez-herrero@att.com>2019-04-02 20:48:41 -0500
commit964b127fc75e202d3c661454dbea58acf2b234c6 (patch)
treec2991183110e1ba8ec0905350eb2256d5f3c1cec /feature-lifecycle/src
parent92cb38f9137d80328445d7b22cb51716783e3aea (diff)
Initial lifecycle state machine.
Basic state machine and initial functionality to transition to the Active state via Engine commands and StateUpdate messages. Change-Id: I8212bd264dbbea446d4e682918f9360e3984887f Issue-ID: POLICY-1608 Signed-off-by: Jorge Hernandez <jorge.hernandez-herrero@att.com> Signed-off-by: jhh <jorge.hernandez-herrero@att.com>
Diffstat (limited to 'feature-lifecycle/src')
-rw-r--r--feature-lifecycle/src/assembly/zip.xml70
-rw-r--r--feature-lifecycle/src/main/feature/config/feature-lifecycle.properties34
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java69
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java325
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleState.java78
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java67
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateDefault.java88
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStatePassive.java63
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateSafe.java41
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminated.java86
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTest.java44
-rw-r--r--feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupported.java78
-rw-r--r--feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureAPI1
-rw-r--r--feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI1
-rw-r--r--feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyEngineFeatureAPI1
-rw-r--r--feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java191
-rw-r--r--feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java221
-rw-r--r--feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateSafeTest.java52
-rw-r--r--feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminatedTest.java178
-rw-r--r--feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTestTest.java52
-rw-r--r--feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupportedTest.java108
-rw-r--r--feature-lifecycle/src/test/resources/echo.drl36
-rw-r--r--feature-lifecycle/src/test/resources/echo.kmodule26
-rw-r--r--feature-lifecycle/src/test/resources/echo.pom29
-rw-r--r--feature-lifecycle/src/test/resources/feature-lifecycle.properties26
25 files changed, 1965 insertions, 0 deletions
diff --git a/feature-lifecycle/src/assembly/zip.xml b/feature-lifecycle/src/assembly/zip.xml
new file mode 100644
index 00000000..a59418b2
--- /dev/null
+++ b/feature-lifecycle/src/assembly/zip.xml
@@ -0,0 +1,70 @@
+<!--
+ ============LICENSE_START=======================================================
+ ONAP
+ ================================================================================
+ Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+ -->
+
+<!-- Defines how we build the .zip file which is our distribution. -->
+
+<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd">
+
+ <id>feature-lifecycle</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>target</directory>
+ <outputDirectory>lib/feature</outputDirectory>
+ <includes>
+ <include>feature-lifecycle-${project.version}.jar</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>target/assembly/lib</directory>
+ <outputDirectory>lib/dependencies</outputDirectory>
+ <includes>
+ <include>*.jar</include>
+ </includes>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/feature/config</directory>
+ <outputDirectory>config</outputDirectory>
+ <fileMode>0644</fileMode>
+ <excludes/>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/feature/bin</directory>
+ <outputDirectory>bin</outputDirectory>
+ <fileMode>0755</fileMode>
+ <excludes/>
+ </fileSet>
+ <fileSet>
+ <directory>src/main/feature/install</directory>
+ <outputDirectory>install</outputDirectory>
+ <fileMode>0755</fileMode>
+ <excludes/>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/feature-lifecycle/src/main/feature/config/feature-lifecycle.properties b/feature-lifecycle/src/main/feature/config/feature-lifecycle.properties
new file mode 100644
index 00000000..2cdc2abf
--- /dev/null
+++ b/feature-lifecycle/src/main/feature/config/feature-lifecycle.properties
@@ -0,0 +1,34 @@
+# ============LICENSE_START=======================================================
+# ONAP
+# ================================================================================
+# Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+
+dmaap.source.topics=POLICY-PDP-PAP
+dmaap.sink.topics=POLICY-PDP-PAP
+
+dmaap.source.topics.POLICY-PDP-PAP.servers=${env:DMAAP_SERVERS}
+dmaap.source.topics.POLICY-PDP-PAP.effectiveTopic=${env:POLICY_PDP_PAP_TOPIC}
+dmaap.source.topics.POLICY-PDP-PAP.apiKey=${env:POLICY_PDP_PAP_API_KEY}
+dmaap.source.topics.POLICY-PDP-PAP.apiSecret=${env:POLICY_PDP_PAP_API_SECRET}
+dmaap.source.topics.POLICY-PDP-PAP.managed=false
+dmaap.source.topics.POLICY-PDP-PAP.https=true
+
+dmaap.sink.topics.POLICY-PDP-PAP.servers=${env:DMAAP_SERVERS}
+dmaap.sink.topics.POLICY-PDP-PAP.effectiveTopic=${env:POLICY_PDP_PAP_TOPIC}
+dmaap.sink.topics.POLICY-PDP-PAP.apiKey=${env:POLICY_PDP_PAP_API_KEY}
+dmaap.sink.topics.POLICY-PDP-PAP.apiSecret=${env:POLICY_PDP_PAP_API_SECRET}
+dmaap.sink.topics.POLICY-PDP-PAP.managed=false
+dmaap.sink.topics.POLICY-PDP-PAP.https=true
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java
new file mode 100644
index 00000000..5e1ddaea
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFeature.java
@@ -0,0 +1,69 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import org.onap.policy.drools.features.DroolsControllerFeatureAPI;
+import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
+import org.onap.policy.drools.features.PolicyEngineFeatureAPI;
+import org.onap.policy.drools.system.PolicyEngine;
+
+/**
+ * This class hooks the Lifecycle State Machine to the PDP-D.
+ */
+public class LifecycleFeature
+ implements PolicyEngineFeatureAPI, DroolsControllerFeatureAPI, PolicyControllerFeatureAPI {
+ /**
+ * Lifecycle FSM.
+ */
+ protected static final LifecycleFsm fsm = new LifecycleFsm();
+
+ @Override
+ public int getSequenceNumber() {
+ return 10;
+ }
+
+ /**
+ * The 'afterStart' hook on the Policy Engine tell us when the engine is functional.
+ */
+ @Override
+ public boolean afterStart(PolicyEngine engine) {
+ fsm.start();
+ return false;
+ }
+
+ /**
+ * The 'afterStop' hook on the Policy Engine tell us when the engine is stopping.
+ */
+ @Override
+ public boolean afterStop(PolicyEngine engine) {
+ fsm.stop();
+ return false;
+ }
+
+ /**
+ * The 'beforeShutdown' hook on the Policy Engine tell us when the engine is going away.
+ */
+ @Override
+ public boolean beforeShutdown(PolicyEngine engine) {
+ fsm.shutdown();
+ return false;
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java
new file mode 100644
index 00000000..61f6de99
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleFsm.java
@@ -0,0 +1,325 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Properties;
+import java.util.UUID;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.Setter;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.common.capabilities.Startable;
+import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
+import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
+import org.onap.policy.common.endpoints.event.comm.TopicSink;
+import org.onap.policy.common.endpoints.event.comm.TopicSource;
+import org.onap.policy.common.endpoints.event.comm.client.TopicSinkClient;
+import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher;
+import org.onap.policy.common.endpoints.listeners.ScoListener;
+import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
+import org.onap.policy.common.utils.coder.StandardCoderObject;
+import org.onap.policy.common.utils.network.NetworkUtil;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.concepts.PdpStatus;
+import org.onap.policy.models.pdp.concepts.PolicyTypeIdent;
+import org.onap.policy.models.pdp.enums.PdpHealthStatus;
+import org.onap.policy.models.pdp.enums.PdpMessageType;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lifecycle FSM.
+ */
+public class LifecycleFsm implements Startable {
+
+ protected static final String CONFIGURATION_PROPERTIES_NAME = "feature-lifecycle";
+ protected static final String POLICY_TYPE_VERSION = "1.0.0";
+ protected static final long DEFAULT_STATUS_TIMER_SECONDS = 60L;
+ protected static final String PDP_MESSAGE_NAME = "messageName";
+
+ private static final Logger logger = LoggerFactory.getLogger(LifecycleFsm.class);
+
+ protected final Properties properties;
+
+ protected TopicSource source;
+ protected TopicSinkClient client;
+
+ protected volatile LifecycleState state = new LifecycleStateTerminated(this);
+
+ @GsonJsonIgnore
+ protected ScheduledThreadPoolExecutor scheduler = new ScheduledThreadPoolExecutor(1);
+
+ @GsonJsonIgnore
+ protected ScheduledFuture<?> statusTask;
+
+ @GsonJsonIgnore
+ protected MessageTypeDispatcher sourceDispatcher = new MessageTypeDispatcher(new String[]{PDP_MESSAGE_NAME});
+
+ @GsonJsonIgnore
+ protected MessageNameDispatcher nameDispatcher = new MessageNameDispatcher(PdpStateChange.class, this);
+
+ @Getter
+ @Setter
+ protected long statusTimerSeconds = DEFAULT_STATUS_TIMER_SECONDS;
+
+ @Getter
+ protected String pdpGroup;
+
+ @Getter
+ protected String pdpSubgroup;
+
+ /**
+ * Constructor.
+ */
+ public LifecycleFsm() {
+ this.properties = SystemPersistence.manager.getProperties(CONFIGURATION_PROPERTIES_NAME);
+
+ scheduler.setContinueExistingPeriodicTasksAfterShutdownPolicy(false);
+ scheduler.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
+ scheduler.setRemoveOnCancelPolicy(true);
+ }
+
+ @Override
+ public boolean isAlive() {
+ return client != null && client.getSink().isAlive();
+ }
+
+ /**
+ * Current state.
+ */
+ public PdpState state() {
+ return state.state();
+ }
+
+ /* ** FSM events - entry points of events into the FSM ** */
+
+ @Override
+ public synchronized boolean start() {
+ logger.info("lifecycle event: start");
+ return state.start();
+ }
+
+ @Override
+ public synchronized boolean stop() {
+ logger.info("lifecycle event: stop");
+ return state.stop();
+ }
+
+ @Override
+ public synchronized void shutdown() {
+ logger.info("lifecycle event: shutdown");
+ state.shutdown();
+ }
+
+ /**
+ * Status reporting event.
+ * @return true if successful
+ */
+ public synchronized boolean status() {
+ logger.info("lifecycle event: status");
+ return state.status();
+ }
+
+ public synchronized void stateChange(PdpStateChange stateChange) {
+ logger.info("lifecycle event: state-change");
+ state.stateChange(stateChange);
+ }
+
+ /* ** FSM State Actions ** */
+
+ protected boolean startAction() {
+ if (isAlive()) {
+ return true;
+ }
+
+ return startIo() && startTimers();
+ }
+
+ protected boolean stopAction() {
+ if (!isAlive()) {
+ return true;
+ }
+
+ boolean successTimers = stopTimers();
+ boolean successIo = stopIo();
+ return successTimers && successIo;
+ }
+
+ protected void shutdownAction() {
+ shutdownIo();
+ shutdownTimers();
+ }
+
+ protected boolean statusAction(PdpState state) {
+ if (!isAlive()) {
+ return false;
+ }
+
+ return client.send(statusPayload(state));
+ }
+
+ protected void setGroupAction(String group, String subgroup) {
+ this.pdpGroup = group;
+ this.pdpSubgroup = subgroup;
+ }
+
+ protected void transitionToAction(@NonNull LifecycleState newState) {
+ state = newState;
+ }
+
+ /* ** Action Helpers ** */
+
+ private boolean startIo() {
+ return source() && sink();
+ }
+
+ private boolean startTimers() {
+ statusTask =
+ this.scheduler.scheduleAtFixedRate(() -> status(), 0, statusTimerSeconds, TimeUnit.SECONDS);
+ return !statusTask.isCancelled() && !statusTask.isDone();
+ }
+
+ private boolean stopIo() {
+ source.unregister(sourceDispatcher);
+ boolean successSource = source.stop();
+ boolean successSink = client.getSink().stop();
+ return successSource && successSink;
+ }
+
+ private boolean stopTimers() {
+ boolean success = true;
+ if (statusTask != null) {
+ success = statusTask.cancel(false);
+ }
+
+ return success;
+ }
+
+ private void shutdownIo() {
+ client.getSink().shutdown();
+ source.shutdown();
+ }
+
+ private void shutdownTimers() {
+ scheduler.shutdownNow();
+ }
+
+ private PdpStatus statusPayload(PdpState state) {
+ PdpStatus status = new PdpStatus();
+ status.setRequestId(UUID.randomUUID().toString());
+ status.setTimestampMs(System.currentTimeMillis());
+ status.setInstance(NetworkUtil.getHostname());
+ status.setState(state);
+ status.setHealthy(isAlive() ? PdpHealthStatus.HEALTHY : PdpHealthStatus.NOT_HEALTHY);
+ status.setPdpType("drools"); // TODO: enum ?
+ return status;
+ }
+
+ private boolean source() {
+ List<TopicSource> sources = TopicEndpoint.manager.addTopicSources(properties);
+ if (sources.isEmpty()) {
+ return false;
+ }
+
+ if (sources.size() != 1) {
+ logger.warn("Lifecycle Manager: unexpected: more than one source configured ({})", sources.size());
+ }
+
+ this.source = sources.get(0);
+ this.source.register(this.sourceDispatcher);
+ this.sourceDispatcher.register(PdpMessageType.PDP_STATE_CHANGE.name(), nameDispatcher);
+ return source.start();
+ }
+
+ private boolean sink() {
+ List<TopicSink> sinks = TopicEndpoint.manager.addTopicSinks(properties);
+ if (sinks.isEmpty()) {
+ logger.error("Lifecycle Manager sinks have not been configured");
+ return false;
+ }
+
+ if (sinks.size() != 1) {
+ logger.warn("Lifecycle Manager: unexpected: more than one sink configured ({})", sinks.size());
+ }
+
+ this.client = new TopicSinkClient(sinks.get(0));
+ return this.client.getSink().start();
+ }
+
+ private List<PolicyTypeIdent> getCapabilities() {
+ List<PolicyTypeIdent> capabilities = new ArrayList<>();
+ for (DroolsController dc : DroolsController.factory.inventory()) {
+ if (!dc.isBrained()) {
+ continue;
+ }
+
+ for (String domain : dc.getBaseDomainNames()) {
+ // HACK: until legacy controllers are removed
+ if (StringUtils.countMatches(domain, ".") > 1) {
+ capabilities.add(new PolicyTypeIdent(domain, POLICY_TYPE_VERSION));
+ } else {
+ logger.info("legacy controller {} with domain {}", dc.getCanonicalSessionNames(), domain);
+ }
+ }
+ }
+ return capabilities;
+ }
+
+
+ /* **** IO listeners ***** */
+
+ /**
+ * PDP State Change Message Listener.
+ */
+ public static class MessageNameDispatcher extends ScoListener<PdpStateChange> {
+
+ protected final LifecycleFsm fsm;
+
+ /**
+ * Constructor.
+ */
+ public MessageNameDispatcher(Class<PdpStateChange> clazz, LifecycleFsm fsm) {
+ super(clazz);
+ this.fsm = fsm;
+ }
+
+ @Override
+ public void onTopicEvent(CommInfrastructure commInfrastructure, String topic,
+ StandardCoderObject standardCoderObject, PdpStateChange pdpStateChange) {
+
+ if (pdpStateChange == null) {
+ logger.warn("pdp-state-chage null from {}:{}", commInfrastructure, topic);
+ return;
+ }
+
+ fsm.stateChange(pdpStateChange);
+ }
+ }
+
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleState.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleState.java
new file mode 100644
index 00000000..e07df151
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleState.java
@@ -0,0 +1,78 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.NonNull;
+import org.onap.policy.common.capabilities.Startable;
+import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.concepts.PdpUpdate;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lifecycle State.
+ */
+public abstract class LifecycleState implements Startable {
+
+ private static final Logger logger = LoggerFactory.getLogger(LifecycleState.class);
+
+ @GsonJsonIgnore
+ protected LifecycleFsm fsm;
+
+ /**
+ * Constructor.
+ * @param manager Lifecycle Manager.
+ */
+ public LifecycleState(@NonNull LifecycleFsm manager) {
+ this.fsm = manager;
+ }
+
+ /**
+ * change state.
+ * @param newState new state
+ */
+ public abstract boolean transitionToState(@NonNull LifecycleState newState);
+
+ /**
+ * current state.
+ * @return state
+ */
+ public abstract PdpState state();
+
+ /**
+ * status event.
+ */
+ public abstract boolean status();
+
+ /**
+ * update event.
+ * @param update message
+ */
+ public abstract void update(@NonNull PdpUpdate update);
+
+ /**
+ * state change event .
+ * @param change message
+ */
+ public abstract void stateChange(@NonNull PdpStateChange change);
+} \ No newline at end of file
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java
new file mode 100644
index 00000000..d5be822b
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateActive.java
@@ -0,0 +1,67 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.ToString;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lifecycle Active State.
+ */
+@ToString
+public class LifecycleStateActive extends LifecycleStateDefault {
+
+ private static final Logger logger = LoggerFactory.getLogger(LifecycleStateActive.class);
+
+ /**
+ * Constructor.
+ * @param manager fsm
+ */
+ public LifecycleStateActive(LifecycleFsm manager) {
+ super(manager);
+ }
+
+ @Override
+ public PdpState state() {
+ return PdpState.ACTIVE;
+ }
+
+ @Override
+ public void stateChange(PdpStateChange change) {
+ synchronized (fsm) {
+ if (change.getState() == PdpState.ACTIVE) {
+ fsm.setGroupAction(change.getPdpGroup(), change.getPdpSubgroup());
+ return;
+ }
+
+ if (change.getState() != PdpState.PASSIVE) {
+ logger.warn("{}: state-change: {}", this, change);
+ return;
+ }
+
+ fsm.setGroupAction(change.getPdpGroup(), change.getPdpSubgroup());
+ fsm.transitionToAction(new LifecycleStatePassive(fsm));
+ }
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateDefault.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateDefault.java
new file mode 100644
index 00000000..18e40012
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateDefault.java
@@ -0,0 +1,88 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.NonNull;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Support class for default functionality for operational states.
+ */
+public abstract class LifecycleStateDefault extends LifecycleStateUnsupported {
+
+ private static final Logger logger = LoggerFactory.getLogger(LifecycleState.class);
+
+ public LifecycleStateDefault(LifecycleFsm manager) {
+ super(manager);
+ }
+
+
+ @Override
+ public boolean transitionToState(@NonNull LifecycleState newState) {
+ logger.info("{}: state-change from {} to {}", this, state(), newState.state());
+
+ synchronized (fsm) {
+ if (state() == newState.state()) {
+ return false;
+ }
+
+ fsm.transitionToAction(newState);
+ return true;
+ }
+ }
+
+ @Override
+ public boolean start() {
+ logger.warn("{}: start", this);
+ return false;
+ }
+
+ @Override
+ public boolean stop() {
+ synchronized (fsm) {
+ boolean success = fsm.statusAction(PdpState.TERMINATED);
+ success = fsm.stopAction() && success;
+ return transitionToState(new LifecycleStateTerminated(fsm)) && success;
+ }
+ }
+
+ @Override
+ public void shutdown() {
+ synchronized (fsm) {
+ stop();
+ fsm.shutdownAction();
+ }
+ }
+
+ @Override
+ public boolean isAlive() {
+ return true;
+ }
+
+ @Override
+ public boolean status() {
+ synchronized (fsm) {
+ return fsm.statusAction(state());
+ }
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStatePassive.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStatePassive.java
new file mode 100644
index 00000000..dc184e87
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStatePassive.java
@@ -0,0 +1,63 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.ToString;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lifecycle Passive State.
+ */
+
+@ToString
+public class LifecycleStatePassive extends LifecycleStateDefault {
+
+ private static final Logger logger = LoggerFactory.getLogger(LifecycleStatePassive.class);
+
+ /**
+ * Constructor.
+ * @param manager fsm
+ */
+ public LifecycleStatePassive(LifecycleFsm manager) {
+ super(manager);
+ }
+
+ @Override
+ public PdpState state() {
+ return PdpState.PASSIVE;
+ }
+
+ @Override
+ public void stateChange(PdpStateChange change) {
+ synchronized (fsm) {
+ if (change.getState() != PdpState.ACTIVE) {
+ logger.warn("{}: state-change: {}", this, change);
+ return;
+ }
+
+ fsm.setGroupAction(change.getPdpGroup(), change.getPdpSubgroup());
+ fsm.transitionToAction(new LifecycleStateActive(fsm));
+ }
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateSafe.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateSafe.java
new file mode 100644
index 00000000..e0ea2b5e
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateSafe.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.ToString;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Lifecycle Safe State.
+ */
+
+@ToString
+public class LifecycleStateSafe extends LifecycleStateUnsupported {
+
+ public LifecycleStateSafe(LifecycleFsm manager) {
+ super(manager);
+ }
+
+ @Override
+ public PdpState state() {
+ return PdpState.SAFE;
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminated.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminated.java
new file mode 100644
index 00000000..7455349c
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminated.java
@@ -0,0 +1,86 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.ToString;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.enums.PdpState;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Lifecycle Terminated State.
+ */
+
+@ToString
+public class LifecycleStateTerminated extends LifecycleStateDefault {
+
+ private static final Logger logger = LoggerFactory.getLogger(LifecycleStateTerminated.class);
+
+ protected LifecycleStateTerminated(LifecycleFsm manager) {
+ super(manager);
+ }
+
+ @Override
+ public PdpState state() {
+ return PdpState.TERMINATED;
+ }
+
+ @Override
+ public boolean start() {
+ synchronized (fsm) {
+ boolean success = fsm.startAction();
+ if (success) {
+ return transitionToState(new LifecycleStatePassive(fsm));
+ }
+
+ return false;
+ }
+ }
+
+ @Override
+ public boolean stop() {
+ logger.warn("{}: stop", this);
+ return true;
+ }
+
+ @Override
+ public void shutdown() {
+ logger.warn("{}: shutdown", this);
+ }
+
+ @Override
+ public boolean isAlive() {
+ return false;
+ }
+
+ @Override
+ public boolean status() {
+ logger.warn("{}: status", this);
+ return false;
+ }
+
+ @Override
+ public void stateChange(PdpStateChange change) {
+ logger.warn("{}: state-change: {}", this, change);
+ return;
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTest.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTest.java
new file mode 100644
index 00000000..03ca3778
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateTest.java
@@ -0,0 +1,44 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import lombok.ToString;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Lifecycle Test State.
+ */
+@ToString
+public class LifecycleStateTest extends LifecycleStateUnsupported {
+
+ /**
+ * Constructor.
+ * @param manager fsm
+ */
+ public LifecycleStateTest(LifecycleFsm manager) {
+ super(manager);
+ }
+
+ @Override
+ public PdpState state() {
+ return PdpState.TEST;
+ }
+}
diff --git a/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupported.java b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupported.java
new file mode 100644
index 00000000..ed6fa1a5
--- /dev/null
+++ b/feature-lifecycle/src/main/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupported.java
@@ -0,0 +1,78 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.concepts.PdpUpdate;
+
+/**
+ * Support class for those unsupported states not yet implemented.
+ */
+public abstract class LifecycleStateUnsupported extends LifecycleState {
+
+ /**
+ * Constructor.
+ * @param manager Lifecycle Manager.
+ */
+ public LifecycleStateUnsupported(LifecycleFsm manager) {
+ super(manager);
+ }
+
+ @Override
+ public boolean transitionToState(LifecycleState newState) {
+ throw new UnsupportedOperationException("transitionToAction: " + this);
+ }
+
+ @Override
+ public boolean start() {
+ throw new UnsupportedOperationException("start: " + this);
+ }
+
+ @Override
+ public boolean stop() {
+ throw new UnsupportedOperationException("stop: " + this);
+ }
+
+ @Override
+ public void shutdown() {
+ throw new UnsupportedOperationException("shutdown: " + this);
+ }
+
+ @Override
+ public boolean isAlive() {
+ throw new UnsupportedOperationException("isAlive: " + this);
+ }
+
+ @Override
+ public boolean status() {
+ throw new UnsupportedOperationException("status: " + this);
+ }
+
+ @Override
+ public void update(PdpUpdate update) {
+ throw new UnsupportedOperationException("start: " + this);
+ }
+
+ @Override
+ public void stateChange(PdpStateChange change) {
+ throw new UnsupportedOperationException("start: " + this);
+ }
+}
diff --git a/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureAPI b/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureAPI
new file mode 100644
index 00000000..78cf97eb
--- /dev/null
+++ b/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.DroolsControllerFeatureAPI
@@ -0,0 +1 @@
+org.onap.policy.drools.lifecycle.LifecycleFeature
diff --git a/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI b/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI
new file mode 100644
index 00000000..78cf97eb
--- /dev/null
+++ b/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyControllerFeatureAPI
@@ -0,0 +1 @@
+org.onap.policy.drools.lifecycle.LifecycleFeature
diff --git a/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyEngineFeatureAPI b/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyEngineFeatureAPI
new file mode 100644
index 00000000..78cf97eb
--- /dev/null
+++ b/feature-lifecycle/src/main/resources/META-INF/services/org.onap.policy.drools.features.PolicyEngineFeatureAPI
@@ -0,0 +1 @@
+org.onap.policy.drools.lifecycle.LifecycleFeature
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java
new file mode 100644
index 00000000..eb83bc61
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateActiveTest.java
@@ -0,0 +1,191 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * =============LICENSE_END========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.concepts.PdpStatus;
+import org.onap.policy.models.pdp.enums.PdpMessageType;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Lifecycle State Active Test.
+ */
+public class LifecycleStateActiveTest {
+
+ private LifecycleFsm fsm;
+
+ @BeforeClass
+ public static void setUp() {
+ SystemPersistence.manager.setConfigurationDir("src/test/resources");
+ LoggerUtil.setLevel("org.onap.policy.common.endpoints", "WARN");
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ SystemPersistence.manager.setConfigurationDir(null);
+ }
+
+ /**
+ * Start tests in the Active state.
+ */
+ @Before
+ public void startActive() throws CoderException {
+ fsm = new LifecycleFsm();
+ fsm.setStatusTimerSeconds(15);
+ assertTrue(fsm.start());
+
+ goActive();
+ assertActive();
+ }
+
+ private void goActive() throws CoderException {
+ PdpStateChange change = new PdpStateChange();
+ change.setPdpGroup("A");
+ change.setPdpSubgroup("a");
+ change.setState(PdpState.ACTIVE);
+ change.setName("test");
+
+ fsm.source.offer(new StandardCoder().encode(change));
+ }
+
+ @Test
+ public void constructor() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new LifecycleStateActive(null));
+ fsm.shutdown();
+ }
+
+ @Test
+ public void start() {
+ assertActive();
+ assertFalse(fsm.start());
+ assertActive();
+
+ fsm.shutdown();
+ }
+
+ private void assertActive() {
+ assertEquals(PdpState.ACTIVE, fsm.state());
+ assertEquals("A", fsm.getPdpGroup());
+ assertEquals("a", fsm.getPdpSubgroup());
+ assertTrue(fsm.isAlive());
+ await().atMost(fsm.getStatusTimerSeconds() + 1, TimeUnit.SECONDS).until(isStatus(PdpState.ACTIVE));
+ }
+
+ @Test
+ public void stop() {
+ assertTrue(fsm.stop());
+ assertBasicTerminated();
+
+ fsm.shutdown();
+ }
+
+ private void assertBasicTerminated() {
+ assertEquals(PdpState.TERMINATED, fsm.state());
+ assertFalse(fsm.isAlive());
+ assertFalse(fsm.state.isAlive());
+ await().atMost(1, TimeUnit.SECONDS).until(isStatus(PdpState.TERMINATED));
+ }
+
+ @Test
+ public void shutdown() {
+ fsm.shutdown();
+
+ assertBasicTerminated();
+
+ assertTrue(fsm.statusTask.isCancelled());
+ assertTrue(fsm.statusTask.isDone());
+ }
+
+ private Callable<Boolean> isStatus(PdpState state) {
+ return () -> {
+ if (fsm.client.getSink().getRecentEvents().length == 0) {
+ return false;
+ }
+
+ List<String> events = Arrays.asList(fsm.client.getSink().getRecentEvents());
+ PdpStatus status =
+ new StandardCoder().decode(events.get(events.size() - 1), PdpStatus.class);
+
+ return status.getMessageName() == PdpMessageType.PDP_STATUS && state == status.getState();
+ };
+ }
+
+ @Test
+ public void status() {
+ await().atMost(fsm.getStatusTimerSeconds() + 1, TimeUnit.SECONDS).until(isStatus(PdpState.ACTIVE));
+ int preCount = fsm.client.getSink().getRecentEvents().length;
+
+ assertTrue(fsm.status());
+ assertEquals(preCount + 1, fsm.client.getSink().getRecentEvents().length);
+
+ fsm.shutdown();
+ }
+
+ @Test
+ public void stateChange() throws CoderException {
+ assertActive();
+
+ /* dup */
+ PdpStateChange change = new PdpStateChange();
+ change.setPdpGroup("B");
+ change.setPdpSubgroup("b");
+ change.setState(PdpState.ACTIVE);
+ change.setName("test");
+
+ fsm.source.offer(new StandardCoder().encode(change));
+ assertEquals(PdpState.ACTIVE, fsm.state());
+ assertEquals("B", fsm.getPdpGroup());
+ assertEquals("b", fsm.getPdpSubgroup());
+
+ change.setState(PdpState.SAFE);
+ fsm.source.offer(new StandardCoder().encode(change));
+ assertEquals(PdpState.ACTIVE, fsm.state());
+
+ change.setState(PdpState.TERMINATED);
+ fsm.source.offer(new StandardCoder().encode(change));
+ assertEquals(PdpState.ACTIVE, fsm.state());
+
+ change.setState(PdpState.PASSIVE);
+ fsm.source.offer(new StandardCoder().encode(change));
+ assertEquals(PdpState.PASSIVE, fsm.state());
+ await().atMost(fsm.getStatusTimerSeconds() + 1, TimeUnit.SECONDS).until(isStatus(PdpState.PASSIVE));
+
+ fsm.shutdown();
+ }
+}
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java
new file mode 100644
index 00000000..fbc2eeba
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStatePassiveTest.java
@@ -0,0 +1,221 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+import org.awaitility.core.ConditionTimeoutException;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.common.utils.coder.CoderException;
+import org.onap.policy.common.utils.coder.StandardCoder;
+import org.onap.policy.common.utils.network.NetworkUtil;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.concepts.PdpStatus;
+import org.onap.policy.models.pdp.enums.PdpHealthStatus;
+import org.onap.policy.models.pdp.enums.PdpMessageType;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Lifecycle State Passive Tests.
+ */
+public class LifecycleStatePassiveTest {
+
+ private LifecycleFsm fsm;
+
+ @BeforeClass
+ public static void setUp() {
+ SystemPersistence.manager.setConfigurationDir("src/test/resources");
+ LoggerUtil.setLevel("org.onap.policy.common.endpoints", "WARN");
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ SystemPersistence.manager.setConfigurationDir(null);
+ }
+
+ /**
+ * Start tests in the Passive state.
+ */
+ @Before
+ public void startPassive() {
+ /* start every test in passive mode */
+ fsm = new LifecycleFsm();
+ fsm.setStatusTimerSeconds(15L);
+ simpleStart();
+
+ assertEquals(0, fsm.client.getSink().getRecentEvents().length);
+ }
+
+ @Test
+ public void constructor() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new LifecycleStatePassive(null));
+ fsm.shutdown();
+ }
+
+ @Test
+ public void start() {
+ assertEquals(0, fsm.client.getSink().getRecentEvents().length);
+ assertFalse(fsm.start());
+ assertBasicPassive();
+
+ fsm.shutdown();
+ }
+
+ private Callable<Boolean> isStatus(PdpState state, int count) {
+ return () -> {
+ if (!fsm.client.getSink().isAlive()) {
+ return false;
+ }
+
+ if (fsm.client.getSink().getRecentEvents().length != count) {
+ return false;
+ }
+
+ String[] events = fsm.client.getSink().getRecentEvents();
+ PdpStatus status =
+ new StandardCoder().decode(events[events.length - 1], PdpStatus.class);
+
+ return status.getMessageName() == PdpMessageType.PDP_STATUS && state == status.getState();
+ };
+ }
+
+ @Test
+ public void stop() {
+ simpleStop();
+ assertBasicTerminated();
+ }
+
+ private void simpleStart() {
+ assertTrue(fsm.start());
+ assertBasicPassive();
+ }
+
+ private void simpleStop() {
+ assertTrue(fsm.stop());
+ assertBasicTerminated();
+ }
+
+ @Test
+ public void shutdown() throws CoderException {
+ simpleStop();
+
+ fsm.shutdown();
+ assertExtendedTerminated();
+ }
+
+ @Test
+ public void status() {
+ status(PdpState.PASSIVE);
+ fsm.shutdown();
+ }
+
+ private void status(PdpState state) {
+ await()
+ .atMost(5, TimeUnit.SECONDS)
+ .until(isStatus(state, 1));
+
+ await()
+ .atMost(fsm.statusTimerSeconds + 2, TimeUnit.SECONDS)
+ .until(isStatus(state, 2));
+
+ await()
+ .atMost(fsm.statusTimerSeconds + 2, TimeUnit.SECONDS)
+ .until(isStatus(state, 3));
+
+ assertTrue(fsm.status());
+ await()
+ .atMost(200, TimeUnit.MILLISECONDS)
+ .until(isStatus(state, 4));
+ }
+
+ @Test
+ public void update() {
+ // TODO
+ fsm.shutdown();
+ }
+
+ @Test
+ public void stateChange() throws CoderException {
+ PdpStateChange change = new PdpStateChange();
+ change.setPdpGroup("A");
+ change.setPdpSubgroup("a");
+ change.setState(PdpState.ACTIVE);
+ change.setName("test");
+
+ fsm.source.offer(new StandardCoder().encode(change));
+ assertEquals(PdpState.ACTIVE, fsm.state.state());
+ assertEquals("A", fsm.pdpGroup);
+ assertEquals("a", fsm.pdpSubgroup);
+
+ fsm.shutdown();
+ }
+
+ private void assertBasicTerminated() {
+ assertEquals(PdpState.TERMINATED, fsm.state.state());
+ assertFalse(fsm.isAlive());
+ assertFalse(fsm.state.isAlive());
+ }
+
+ private void assertExtendedTerminated() throws CoderException {
+ assertBasicTerminated();
+ assertTrue(fsm.statusTask.isCancelled());
+ assertTrue(fsm.statusTask.isDone());
+
+ assertEquals(1, fsm.client.getSink().getRecentEvents().length);
+ PdpStatus status = new StandardCoder().decode(fsm.client.getSink().getRecentEvents()[0], PdpStatus.class);
+ assertEquals("drools", status.getPdpType());
+ assertEquals(PdpState.TERMINATED, status.getState());
+ assertEquals(PdpHealthStatus.HEALTHY, status.getHealthy());
+ assertEquals(NetworkUtil.getHostname(), status.getInstance());
+ assertEquals(PdpMessageType.PDP_STATUS, status.getMessageName());
+
+ assertThatThrownBy( () -> await()
+ .atMost(fsm.statusTimerSeconds + 5, TimeUnit.SECONDS)
+ .until(isStatus(PdpState.TERMINATED, 2))).isInstanceOf(ConditionTimeoutException.class);
+ }
+
+ private void assertBasicPassive() {
+ assertEquals(PdpState.PASSIVE, fsm.state.state());
+ assertNotNull(fsm.source);
+ assertNotNull(fsm.client);
+ assertNotNull(fsm.statusTask);
+
+ assertTrue(fsm.isAlive());
+ assertTrue(fsm.source.isAlive());
+ assertTrue(fsm.client.getSink().isAlive());
+
+ assertFalse(fsm.statusTask.isCancelled());
+ assertFalse(fsm.statusTask.isDone());
+ }
+}
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateSafeTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateSafeTest.java
new file mode 100644
index 00000000..81ce85f1
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateSafeTest.java
@@ -0,0 +1,52 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * TEST State Junits.
+ */
+public class LifecycleStateSafeTest extends LifecycleStateUnsupportedTest {
+
+ public LifecycleStateSafeTest() {
+ super(new LifecycleStateSafe(new LifecycleFsm()));
+ }
+
+ @Override
+ public LifecycleState create(LifecycleFsm fsm) {
+ return new LifecycleStateSafe(fsm);
+ }
+
+ @Test
+ public void constructor() {
+ super.constructor();
+ assertEquals(PdpState.SAFE, new LifecycleStateSafe(new LifecycleFsm()).state());
+ }
+
+ @Test
+ public void state() {
+ assertEquals(PdpState.SAFE, state.state());
+ }
+} \ No newline at end of file
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminatedTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminatedTest.java
new file mode 100644
index 00000000..b77fdcd9
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTerminatedTest.java
@@ -0,0 +1,178 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.utils.logging.LoggerUtil;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * Lifecycle State Terminated Tests.
+ */
+public class LifecycleStateTerminatedTest {
+ private LifecycleFsm fsm = new LifecycleFsm();
+
+ @BeforeClass
+ public static void setUp() {
+ SystemPersistence.manager.setConfigurationDir("src/test/resources");
+ LoggerUtil.setLevel("org.onap.policy.common.endpoints", "WARN");
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ SystemPersistence.manager.setConfigurationDir(null);
+ }
+
+ @Test
+ public void constructor() {
+ assertThatIllegalArgumentException().isThrownBy(() -> new LifecycleStateTerminated(null));
+
+ LifecycleState state = new LifecycleStateTerminated(new LifecycleFsm());
+ assertNull(state.fsm.source);
+ assertNull(state.fsm.client);
+ assertNull(state.fsm.statusTask);
+
+ assertEquals(PdpState.TERMINATED, state.state());
+ assertEquals(PdpState.TERMINATED, state.fsm.state.state());
+ assertFalse(state.isAlive());
+ }
+
+ @Test
+ public void stop() {
+ assertEquals(PdpState.TERMINATED, fsm.state.state());
+ assertFalse(fsm.isAlive());
+
+ simpleStop();
+ }
+
+ private void simpleStart() {
+ assertTrue(fsm.start());
+ assertBasicPassive();
+ }
+
+ private void simpleStop() {
+ assertTrue(fsm.stop());
+ assertBasicTerminated();
+ }
+
+ @Test
+ public void bounce() {
+ assertBasicTerminated();
+ simpleStart();
+ simpleStop();
+
+ assertFalse(fsm.source.isAlive());
+ assertFalse(fsm.client.getSink().isAlive());
+ assertExtendedTerminated();
+ }
+
+ @Test
+ public void doubleBounce() {
+ bounce();
+ bounce();
+ }
+
+ @Test
+ public void doubleStartBounce() {
+ simpleStart();
+ assertFalse(fsm.start());
+ assertBasicPassive();
+ simpleStop();
+ }
+
+ @Test
+ public void shutdown() {
+ assertBasicTerminated();
+ fsm.shutdown();
+ assertBasicTerminated();
+
+ fsm = new LifecycleFsm();
+ }
+
+ @Test
+ public void status() {
+ assertBasicTerminated();
+ assertFalse(fsm.status());
+ assertBasicTerminated();
+ }
+
+ @Test
+ public void changeState() {
+ assertFalse(fsm.state.transitionToState(new LifecycleStateTerminated(fsm)));
+ assertEquals(PdpState.TERMINATED, fsm.state.state());
+ }
+
+ @Test
+ public void update() {
+ // TODO
+ }
+
+ @Test
+ public void stateChange() {
+ PdpStateChange change = new PdpStateChange();
+ change.setPdpGroup("A");
+ change.setPdpSubgroup("a");
+ change.setState(PdpState.ACTIVE);
+ change.setName("test");
+
+ fsm.stateChange(change);
+
+ assertEquals(PdpState.TERMINATED, fsm.state.state());
+ }
+
+ private void assertBasicTerminated() {
+ assertEquals(PdpState.TERMINATED, fsm.state.state());
+ assertFalse(fsm.isAlive());
+ assertFalse(fsm.state.isAlive());
+ }
+
+ private void assertExtendedTerminated() {
+ assertBasicTerminated();
+ assertTrue(fsm.statusTask.isCancelled());
+ assertTrue(fsm.statusTask.isDone());
+ assertFalse(fsm.scheduler.isShutdown());
+ }
+
+ private void assertBasicPassive() {
+ assertEquals(PdpState.PASSIVE, fsm.state.state());
+ assertNotNull(fsm.source);
+ assertNotNull(fsm.client);
+ assertNotNull(fsm.statusTask);
+
+ assertTrue(fsm.isAlive());
+ assertTrue(fsm.source.isAlive());
+ assertTrue(fsm.client.getSink().isAlive());
+
+ assertFalse(fsm.statusTask.isCancelled());
+ assertFalse(fsm.statusTask.isDone());
+ }
+} \ No newline at end of file
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTestTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTestTest.java
new file mode 100644
index 00000000..c086dfb4
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateTestTest.java
@@ -0,0 +1,52 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.junit.Assert.assertEquals;
+
+import org.junit.Test;
+import org.onap.policy.models.pdp.enums.PdpState;
+
+/**
+ * TEST State Junits.
+ */
+public class LifecycleStateTestTest extends LifecycleStateUnsupportedTest {
+
+ public LifecycleStateTestTest() {
+ super(new LifecycleStateTest(new LifecycleFsm()));
+ }
+
+ @Override
+ public LifecycleState create(LifecycleFsm fsm) {
+ return new LifecycleStateTest(fsm);
+ }
+
+ @Test
+ public void constructor() {
+ super.constructor();
+ assertEquals(PdpState.TEST, new LifecycleStateTest(new LifecycleFsm()).state());
+ }
+
+ @Test
+ public void state() {
+ assertEquals(PdpState.TEST, state.state());
+ }
+} \ No newline at end of file
diff --git a/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupportedTest.java b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupportedTest.java
new file mode 100644
index 00000000..01efca32
--- /dev/null
+++ b/feature-lifecycle/src/test/java/org/onap/policy/drools/lifecycle/LifecycleStateUnsupportedTest.java
@@ -0,0 +1,108 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.lifecycle;
+
+import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
+import static org.assertj.core.api.Java6Assertions.assertThatThrownBy;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.models.pdp.concepts.PdpStateChange;
+import org.onap.policy.models.pdp.concepts.PdpUpdate;
+
+/**
+ * Lifecycle State Unsupported Test.
+ */
+public abstract class LifecycleStateUnsupportedTest {
+
+ protected final LifecycleState state;
+
+ @BeforeClass
+ public static void setUp() {
+ SystemPersistence.manager.setConfigurationDir("src/test/resources");
+ }
+
+ @AfterClass
+ public static void tearDown() {
+ SystemPersistence.manager.setConfigurationDir(null);
+ }
+
+ public LifecycleStateUnsupportedTest(LifecycleState state) {
+ this.state = state;
+ }
+
+ public abstract LifecycleState create(LifecycleFsm fsm);
+
+ @Test
+ public void constructor() {
+ assertThatIllegalArgumentException().isThrownBy(() -> create(null));
+ }
+
+ @Test
+ public void start() {
+ assertThatThrownBy(() -> state.start())
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void stop() {
+ assertThatThrownBy(() -> state.stop())
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void shutdown() {
+ assertThatThrownBy(() -> state.shutdown())
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void isAlive() {
+ assertThatThrownBy(() -> state.isAlive())
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void status() {
+ assertThatThrownBy(() -> state.status())
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void update() {
+ assertThatThrownBy(() -> state.update(new PdpUpdate()))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void stateChange() {
+ assertThatThrownBy(() -> state.stateChange(new PdpStateChange()))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+
+ @Test
+ public void changeState() {
+ assertThatThrownBy(() -> state.transitionToState(new LifecycleStateActive(new LifecycleFsm())))
+ .isInstanceOf(UnsupportedOperationException.class);
+ }
+}
diff --git a/feature-lifecycle/src/test/resources/echo.drl b/feature-lifecycle/src/test/resources/echo.drl
new file mode 100644
index 00000000..c044f2cb
--- /dev/null
+++ b/feature-lifecycle/src/test/resources/echo.drl
@@ -0,0 +1,36 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.drools.test;
+
+rule "INIT"
+lock-on-active
+when
+then
+ insert("hello, I am up!");
+end
+
+rule "ECHO"
+when
+ $o : Object();
+then
+ System.out.println("ECHO: " + $o);
+ retract($o);
+end
diff --git a/feature-lifecycle/src/test/resources/echo.kmodule b/feature-lifecycle/src/test/resources/echo.kmodule
new file mode 100644
index 00000000..1019bd3d
--- /dev/null
+++ b/feature-lifecycle/src/test/resources/echo.kmodule
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ feature-controller-logging
+ ================================================================================
+ Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+ -->
+
+<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
+ <kbase name="controller-logs">
+ <ksession name="test" />
+ </kbase>
+</kmodule>
diff --git a/feature-lifecycle/src/test/resources/echo.pom b/feature-lifecycle/src/test/resources/echo.pom
new file mode 100644
index 00000000..7e654793
--- /dev/null
+++ b/feature-lifecycle/src/test/resources/echo.pom
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ============LICENSE_START=======================================================
+ Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ ================================================================================
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ ============LICENSE_END=========================================================
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>org.onap.policy.drools.test</groupId>
+ <artifactId>echo</artifactId>
+ <version>1.4.0-SNAPSHOT</version>
+
+</project>
diff --git a/feature-lifecycle/src/test/resources/feature-lifecycle.properties b/feature-lifecycle/src/test/resources/feature-lifecycle.properties
new file mode 100644
index 00000000..a972016f
--- /dev/null
+++ b/feature-lifecycle/src/test/resources/feature-lifecycle.properties
@@ -0,0 +1,26 @@
+# ============LICENSE_START=======================================================
+# ONAP
+# ================================================================================
+# Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+# ================================================================================
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ============LICENSE_END=========================================================
+
+noop.source.topics=POLICY-PDP-PAP
+noop.sink.topics=POLICY-PDP-PAP
+
+noop.source.topics.POLICY-PDP-PAP.servers=noop
+noop.source.topics.POLICY-PDP-PAP.managed=false
+
+noop.sink.topics.POLICY-PDP-PAP.servers=noop
+noop.sink.topics.POLICY-PDP-PAP.managed=false