diff options
29 files changed, 2102 insertions, 2 deletions
diff --git a/feature-lifecycle/lombok.config b/feature-lifecycle/lombok.config new file mode 100644 index 00000000..2384843f --- /dev/null +++ b/feature-lifecycle/lombok.config @@ -0,0 +1,3 @@ +config.stopBubbling = true +lombok.addLombokGeneratedAnnotation = true +lombok.nonNull.exceptionType = IllegalArgumentException diff --git a/feature-lifecycle/pom.xml b/feature-lifecycle/pom.xml new file mode 100644 index 00000000..fa3aff69 --- /dev/null +++ b/feature-lifecycle/pom.xml @@ -0,0 +1,126 @@ +<!-- + ============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========================================================= + --> + +<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> + + <parent> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>drools-pdp</artifactId> + <version>1.4.0-SNAPSHOT</version> + </parent> + + <artifactId>feature-lifecycle</artifactId> + + <name>feature-lifecycle</name> + <description>Loadable module to support the lifecycle modes</description> + + <properties> + <maven.compiler.source>1.8</maven.compiler.source> + <maven.compiler.target>1.8</maven.compiler.target> + </properties> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>zipfile</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>${project.artifactId}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/zip.xml</descriptor> + </descriptors> + <appendAssemblyId>false</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>false</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <includeScope>runtime</includeScope> + <excludeTransitive>true</excludeTransitive> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <dependencies> + + <dependency> + <groupId>org.onap.policy.drools-pdp</groupId> + <artifactId>policy-management</artifactId> + <version>${project.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.projectlombok</groupId> + <artifactId>lombok</artifactId> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.assertj</groupId> + <artifactId>assertj-core</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.awaitility</groupId> + <artifactId>awaitility</artifactId> + <version>3.0.0</version> + <scope>test</scope> + </dependency> + + </dependencies> + +</project> 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 diff --git a/policy-management/pom.xml b/policy-management/pom.xml index d48d3ffb..fedfe7a3 100644 --- a/policy-management/pom.xml +++ b/policy-management/pom.xml @@ -70,7 +70,6 @@ </goals> <phase>prepare-package</phase> <configuration> - <transitive>false</transitive> <outputDirectory>${project.build.directory}/assembly/lib</outputDirectory> <overWriteReleases>false</overWriteReleases> <overWriteSnapshots>true</overWriteSnapshots> @@ -150,7 +149,6 @@ </excludes> <suppressionsLocation>${project.baseUri}checkstyle-suppressions.xml</suppressionsLocation> <consoleOutput>true</consoleOutput> - <failsOnViolation>true</failsOnViolation> <violationSeverity>warning</violationSeverity> </configuration> </execution> @@ -182,6 +180,12 @@ </dependency> <dependency> + <groupId>org.onap.policy.models</groupId> + <artifactId>policy-models-pdp</artifactId> + <version>${policy.models.version}</version> + </dependency> + + <dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-server</artifactId> </dependency> @@ -63,6 +63,7 @@ <commons.io.version>2.5</commons.io.version> <xml.apis.version>1.4.01</xml.apis.version> <policy.common.version>1.4.0-SNAPSHOT</policy.common.version> + <policy.models.version>2.0.0-SNAPSHOT</policy.models.version> </properties> <modules> @@ -82,6 +83,7 @@ <module>feature-distributed-locking</module> <module>feature-controller-logging</module> <module>feature-mdc-filters</module> + <module>feature-lifecycle</module> <module>packages</module> </modules> |