summaryrefslogtreecommitdiffstats
path: root/policy-management
diff options
context:
space:
mode:
Diffstat (limited to 'policy-management')
-rw-r--r--policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java247
-rw-r--r--policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineManagerTest.java1742
2 files changed, 1898 insertions, 91 deletions
diff --git a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java
index 60e5a1b8..b181ff59 100644
--- a/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java
+++ b/policy-management/src/main/java/org/onap/policy/drools/system/PolicyEngine.java
@@ -38,6 +38,7 @@ import org.onap.policy.common.endpoints.event.comm.TopicListener;
import org.onap.policy.common.endpoints.event.comm.TopicSink;
import org.onap.policy.common.endpoints.event.comm.TopicSource;
import org.onap.policy.common.endpoints.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
import org.onap.policy.common.endpoints.properties.PolicyEndPointProperties;
import org.onap.policy.drools.controller.DroolsController;
import org.onap.policy.drools.core.PolicyContainer;
@@ -392,7 +393,7 @@ class PolicyEngineManager implements PolicyEngine {
@Override
public synchronized void boot(String[] cliArgs) {
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeBoot(this, cliArgs)) {
return;
@@ -404,12 +405,12 @@ class PolicyEngineManager implements PolicyEngine {
}
try {
- PolicyContainer.globalInit(cliArgs);
+ globalInitContainer(cliArgs);
} catch (final Exception e) {
logger.error("{}: cannot init policy-container because of {}", this, e.getMessage(), e);
}
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterBoot(this)) {
return;
@@ -478,7 +479,7 @@ class PolicyEngineManager implements PolicyEngine {
}
/* policy-engine dispatch pre configure hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeConfigure(this, properties)) {
return;
@@ -492,7 +493,7 @@ class PolicyEngineManager implements PolicyEngine {
this.properties = properties;
try {
- this.sources = TopicEndpoint.manager.addTopicSources(properties);
+ this.sources = getTopicEndpointManager().addTopicSources(properties);
for (final TopicSource source : this.sources) {
source.register(this);
}
@@ -501,19 +502,19 @@ class PolicyEngineManager implements PolicyEngine {
}
try {
- this.sinks = TopicEndpoint.manager.addTopicSinks(properties);
+ this.sinks = getTopicEndpointManager().addTopicSinks(properties);
} catch (final IllegalArgumentException e) {
logger.error("{}: add-sinks failed", this, e);
}
try {
- this.httpServers = HttpServletServer.factory.build(properties);
+ this.httpServers = getServletFactory().build(properties);
} catch (final IllegalArgumentException e) {
logger.error("{}: add-http-servers failed", this, e);
}
/* policy-engine dispatch post configure hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterConfigure(this)) {
return;
@@ -571,7 +572,7 @@ class PolicyEngineManager implements PolicyEngine {
}
PolicyController controller;
- for (final PolicyControllerFeatureAPI controllerFeature : PolicyControllerFeatureAPI.providers.getList()) {
+ for (final PolicyControllerFeatureAPI controllerFeature : getControllerProviders()) {
try {
controller = controllerFeature.beforeCreate(tempName, properties);
if (controller != null) {
@@ -583,13 +584,13 @@ class PolicyEngineManager implements PolicyEngine {
}
}
- controller = PolicyController.factory.build(tempName, properties);
+ controller = getControllerFactory().build(tempName, properties);
if (this.isLocked()) {
controller.lock();
}
// feature hook
- for (final PolicyControllerFeatureAPI controllerFeature : PolicyControllerFeatureAPI.providers.getList()) {
+ for (final PolicyControllerFeatureAPI controllerFeature : getControllerProviders()) {
try {
if (controllerFeature.afterCreate(controller)) {
return controller;
@@ -609,9 +610,7 @@ class PolicyEngineManager implements PolicyEngine {
final List<PolicyController> policyControllers = new ArrayList<>();
if (configControllers == null || configControllers.isEmpty()) {
- if (logger.isInfoEnabled()) {
- logger.info("No controller configuration provided: " + configControllers);
- }
+ logger.info("No controller configuration provided: {}", configControllers);
return policyControllers;
}
@@ -656,7 +655,7 @@ class PolicyEngineManager implements PolicyEngine {
}
try {
- policyController = PolicyController.factory.get(controllerName);
+ policyController = getControllerFactory().get(controllerName);
} catch (final IllegalArgumentException e) {
// not found
logger.warn("Policy Controller " + controllerName + " not found", e);
@@ -674,7 +673,7 @@ class PolicyEngineManager implements PolicyEngine {
logger.warn("controller {} does not exist. Attempting recovery from disk", controllerName);
final Properties controllerProperties =
- SystemPersistence.manager.getControllerProperties(controllerName);
+ getPersistenceManager().getControllerProperties(controllerName);
/*
* returned properties cannot be null (per implementation) assert (properties !=
@@ -696,18 +695,18 @@ class PolicyEngineManager implements PolicyEngine {
controllerProperties.setProperty(DroolsProperties.RULES_ARTIFACTID, DroolsController.NO_ARTIFACT_ID);
controllerProperties.setProperty(DroolsProperties.RULES_VERSION, DroolsController.NO_VERSION);
- policyController = PolicyEngine.manager.createPolicyController(controllerName, controllerProperties);
+ policyController = getPolicyEngine().createPolicyController(controllerName, controllerProperties);
/* fall through to do brain update operation */
}
switch (operation) {
case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_CREATE:
- PolicyController.factory.patch(policyController, configController.getDrools());
+ getControllerFactory().patch(policyController, configController.getDrools());
break;
case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE:
policyController.unlock();
- PolicyController.factory.patch(policyController, configController.getDrools());
+ getControllerFactory().patch(policyController, configController.getDrools());
break;
case ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK:
policyController.lock();
@@ -736,7 +735,7 @@ class PolicyEngineManager implements PolicyEngine {
public synchronized boolean start() {
/* policy-engine dispatch pre start hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeStart(this)) {
return true;
@@ -792,7 +791,7 @@ class PolicyEngineManager implements PolicyEngine {
/* Start Policy Controllers */
- final List<PolicyController> controllers = PolicyController.factory.inventory();
+ final List<PolicyController> controllers = getControllerFactory().inventory();
for (final PolicyController controller : controllers) {
try {
if (!controller.start()) {
@@ -808,7 +807,7 @@ class PolicyEngineManager implements PolicyEngine {
/* Start managed Topic Endpoints */
try {
- if (!TopicEndpoint.manager.start()) {
+ if (!getTopicEndpointManager().start()) {
success = false;
}
} catch (final IllegalStateException e) {
@@ -818,10 +817,10 @@ class PolicyEngineManager implements PolicyEngine {
// Start the JMX listener
- PdpJmxListener.start();
+ startPdpJmxListener();
/* policy-engine dispatch after start hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterStart(this)) {
return success;
@@ -839,7 +838,7 @@ class PolicyEngineManager implements PolicyEngine {
public synchronized boolean stop() {
/* policy-engine dispatch pre stop hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeStop(this)) {
return true;
@@ -859,7 +858,7 @@ class PolicyEngineManager implements PolicyEngine {
this.alive = false;
- final List<PolicyController> controllers = PolicyController.factory.inventory();
+ final List<PolicyController> controllers = getControllerFactory().inventory();
for (final PolicyController controller : controllers) {
try {
if (!controller.stop()) {
@@ -894,7 +893,7 @@ class PolicyEngineManager implements PolicyEngine {
}
/* stop all managed topics sources and sinks */
- if (!TopicEndpoint.manager.stop()) {
+ if (!getTopicEndpointManager().stop()) {
success = false;
}
@@ -908,9 +907,11 @@ class PolicyEngineManager implements PolicyEngine {
logger.error("{}: cannot start http-server {} because of {}", this, httpServer, e.getMessage(), e);
}
}
+
+ // stop JMX?
/* policy-engine dispatch pre stop hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterStop(this)) {
return success;
@@ -932,42 +933,11 @@ class PolicyEngineManager implements PolicyEngine {
* ..) are stuck
*/
- Thread exitThread = new Thread(new Runnable() {
- private static final long SHUTDOWN_MAX_GRACE_TIME = 30000L;
-
- @Override
- public void run() {
- try {
- Thread.sleep(SHUTDOWN_MAX_GRACE_TIME);
- logger.warn("{}: abnormal termination - shutdown graceful time period expiration",
- PolicyEngineManager.this);
- } catch (final InterruptedException e) {
- /* courtesy to shutdown() to allow it to return */
- synchronized (PolicyEngineManager.this) {
- }
- logger.info("{}: finishing a graceful shutdown ", PolicyEngineManager.this, e);
- } finally {
- /*
- * shut down the Policy Engine owned http servers as the very last thing
- */
- for (final HttpServletServer httpServer : PolicyEngineManager.this.getHttpServers()) {
- try {
- httpServer.shutdown();
- } catch (final Exception e) {
- logger.error("{}: cannot shutdown http-server {} because of {}", PolicyEngineManager.this,
- httpServer, e.getMessage(), e);
- }
- }
-
- logger.info("{}: exit", PolicyEngineManager.this);
- System.exit(0);
- }
- }
- });
+ Thread exitThread = makeShutdownThread();
exitThread.start();
/* policy-engine dispatch pre shutdown hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeShutdown(this)) {
return;
@@ -999,16 +969,16 @@ class PolicyEngineManager implements PolicyEngine {
}
/* Shutdown managed resources */
- PolicyController.factory.shutdown();
- TopicEndpoint.manager.shutdown();
- HttpServletServer.factory.destroy();
+ getControllerFactory().shutdown();
+ getTopicEndpointManager().shutdown();
+ getServletFactory().destroy();
// Stop the JMX listener
- PdpJmxListener.stop();
+ stopPdpJmxListener();
/* policy-engine dispatch post shutdown hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterShutdown(this)) {
return;
@@ -1022,6 +992,52 @@ class PolicyEngineManager implements PolicyEngine {
exitThread.interrupt();
logger.info("{}: normal termination", this);
}
+
+ /**
+ * Thread that shuts down http servers.
+ */
+ protected class ShutdownThread extends Thread {
+ private static final long SHUTDOWN_MAX_GRACE_TIME = 30000L;
+
+ @Override
+ public void run() {
+ try {
+ doSleep(SHUTDOWN_MAX_GRACE_TIME);
+ logger.warn("{}: abnormal termination - shutdown graceful time period expiration",
+ PolicyEngineManager.this);
+ } catch (final InterruptedException e) {
+ /* courtesy to shutdown() to allow it to return */
+ synchronized (PolicyEngineManager.this) {
+ }
+ logger.info("{}: finishing a graceful shutdown ", PolicyEngineManager.this, e);
+ } finally {
+ /*
+ * shut down the Policy Engine owned http servers as the very last thing
+ */
+ for (final HttpServletServer httpServer : PolicyEngineManager.this.getHttpServers()) {
+ try {
+ httpServer.shutdown();
+ } catch (final Exception e) {
+ logger.error("{}: cannot shutdown http-server {} because of {}", PolicyEngineManager.this,
+ httpServer, e.getMessage(), e);
+ }
+ }
+
+ logger.info("{}: exit", PolicyEngineManager.this);
+ doExit(0);
+ }
+ }
+
+ // these may be overridden by junit tests
+
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ Thread.sleep(sleepMs);
+ }
+
+ protected void doExit(int code) {
+ System.exit(code);
+ }
+ }
@Override
public boolean isAlive() {
@@ -1032,7 +1048,7 @@ class PolicyEngineManager implements PolicyEngine {
public synchronized boolean lock() {
/* policy-engine dispatch pre lock hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeLock(this)) {
return true;
@@ -1050,7 +1066,7 @@ class PolicyEngineManager implements PolicyEngine {
this.locked = true;
boolean success = true;
- final List<PolicyController> controllers = PolicyController.factory.inventory();
+ final List<PolicyController> controllers = getControllerFactory().inventory();
for (final PolicyController controller : controllers) {
try {
success = controller.lock() && success;
@@ -1060,10 +1076,10 @@ class PolicyEngineManager implements PolicyEngine {
}
}
- success = TopicEndpoint.manager.lock() && success;
+ success = getTopicEndpointManager().lock() && success;
/* policy-engine dispatch post lock hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterLock(this)) {
return success;
@@ -1081,7 +1097,7 @@ class PolicyEngineManager implements PolicyEngine {
public synchronized boolean unlock() {
/* policy-engine dispatch pre unlock hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeUnlock(this)) {
return true;
@@ -1099,7 +1115,7 @@ class PolicyEngineManager implements PolicyEngine {
this.locked = false;
boolean success = true;
- final List<PolicyController> controllers = PolicyController.factory.inventory();
+ final List<PolicyController> controllers = getControllerFactory().inventory();
for (final PolicyController controller : controllers) {
try {
success = controller.unlock() && success;
@@ -1110,10 +1126,10 @@ class PolicyEngineManager implements PolicyEngine {
}
}
- success = TopicEndpoint.manager.unlock() && success;
+ success = getTopicEndpointManager().unlock() && success;
/* policy-engine dispatch after unlock hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterUnlock(this)) {
return success;
@@ -1134,25 +1150,25 @@ class PolicyEngineManager implements PolicyEngine {
@Override
public void removePolicyController(String name) {
- PolicyController.factory.destroy(name);
+ getControllerFactory().destroy(name);
}
@Override
public void removePolicyController(PolicyController controller) {
- PolicyController.factory.destroy(controller);
+ getControllerFactory().destroy(controller);
}
@JsonIgnore
@Override
public List<PolicyController> getPolicyControllers() {
- return PolicyController.factory.inventory();
+ return getControllerFactory().inventory();
}
@JsonProperty("controllers")
@Override
public List<String> getPolicyControllerIds() {
final List<String> controllerNames = new ArrayList<>();
- for (final PolicyController controller : PolicyController.factory.inventory()) {
+ for (final PolicyController controller : getControllerFactory().inventory()) {
controllerNames.add(controller.getName());
}
return controllerNames;
@@ -1185,7 +1201,7 @@ class PolicyEngineManager implements PolicyEngine {
@Override
public List<String> getFeatures() {
final List<String> features = new ArrayList<>();
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
features.add(feature.getName());
}
return features;
@@ -1194,7 +1210,7 @@ class PolicyEngineManager implements PolicyEngine {
@JsonIgnore
@Override
public List<PolicyEngineFeatureAPI> getFeatureProviders() {
- return PolicyEngineFeatureAPI.providers.getList();
+ return getEngineProviders();
}
@Override
@@ -1203,7 +1219,7 @@ class PolicyEngineManager implements PolicyEngine {
throw new IllegalArgumentException("A feature name must be provided");
}
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
if (feature.getName().equals(featureName)) {
return feature;
}
@@ -1246,8 +1262,8 @@ class PolicyEngineManager implements PolicyEngine {
throw new IllegalStateException(ENGINE_LOCKED_MSG);
}
- final List<? extends TopicSink> topicSinks = TopicEndpoint.manager.getTopicSinks(topic);
- if (topicSinks == null || topicSinks.isEmpty() || topicSinks.size() > 1) {
+ final List<? extends TopicSink> topicSinks = getTopicEndpointManager().getTopicSinks(topic);
+ if (topicSinks == null || topicSinks.size() != 1) {
throw new IllegalStateException("Cannot ensure correct delivery on topic " + topic + ": " + topicSinks);
}
@@ -1321,8 +1337,8 @@ class PolicyEngineManager implements PolicyEngine {
* additional processing
*/
try {
- final DroolsController droolsController = EventProtocolCoder.manager.getDroolsController(topic, event);
- final PolicyController controller = PolicyController.factory.get(droolsController);
+ final DroolsController droolsController = getProtocolCoder().getDroolsController(topic, event);
+ final PolicyController controller = getControllerFactory().get(droolsController);
if (controller != null) {
return controller.deliver(busType, topic, event);
}
@@ -1337,7 +1353,7 @@ class PolicyEngineManager implements PolicyEngine {
* cannot route through the controller, send directly through the topic sink
*/
try {
- final String json = EventProtocolCoder.manager.encode(topic, event);
+ final String json = getProtocolCoder().encode(topic, event);
return this.deliver(busType, topic, json);
} catch (final Exception e) {
@@ -1367,7 +1383,7 @@ class PolicyEngineManager implements PolicyEngine {
}
try {
- final TopicSink sink = TopicEndpoint.manager.getTopicSink(busType, topic);
+ final TopicSink sink = getTopicEndpointManager().getTopicSink(busType, topic);
if (sink == null) {
throw new IllegalStateException("Inconsistent State: " + this);
@@ -1386,7 +1402,7 @@ class PolicyEngineManager implements PolicyEngine {
public synchronized void activate() {
/* policy-engine dispatch pre activate hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeActivate(this)) {
return;
@@ -1414,7 +1430,7 @@ class PolicyEngineManager implements PolicyEngine {
this.unlock();
/* policy-engine dispatch post activate hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterActivate(this)) {
return;
@@ -1430,7 +1446,7 @@ class PolicyEngineManager implements PolicyEngine {
public synchronized void deactivate() {
/* policy-engine dispatch pre deactivate hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.beforeDeactivate(this)) {
return;
@@ -1453,7 +1469,7 @@ class PolicyEngineManager implements PolicyEngine {
}
/* policy-engine dispatch post deactivate hook */
- for (final PolicyEngineFeatureAPI feature : PolicyEngineFeatureAPI.providers.getList()) {
+ for (final PolicyEngineFeatureAPI feature : getEngineProviders()) {
try {
if (feature.afterDeactivate(this)) {
return;
@@ -1487,6 +1503,55 @@ class PolicyEngineManager implements PolicyEngine {
return "PolicyEngineManager [alive=" + this.alive + ", locked=" + this.locked + "]";
}
+ // these methods may be overridden by junit tests
+
+ protected List<PolicyEngineFeatureAPI> getEngineProviders() {
+ return PolicyEngineFeatureAPI.providers.getList();
+ }
+
+ protected List<PolicyControllerFeatureAPI> getControllerProviders() {
+ return PolicyControllerFeatureAPI.providers.getList();
+ }
+
+ protected void globalInitContainer(String[] cliArgs) {
+ PolicyContainer.globalInit(cliArgs);
+ }
+
+ protected TopicEndpoint getTopicEndpointManager() {
+ return TopicEndpoint.manager;
+ }
+
+ protected HttpServletServerFactory getServletFactory() {
+ return HttpServletServer.factory;
+ }
+
+ protected PolicyControllerFactory getControllerFactory() {
+ return PolicyController.factory;
+ }
+
+ protected void startPdpJmxListener() {
+ PdpJmxListener.start();
+ }
+
+ protected void stopPdpJmxListener() {
+ PdpJmxListener.stop();
+ }
+
+ protected Thread makeShutdownThread() {
+ return new ShutdownThread();
+ }
+
+ protected EventProtocolCoder getProtocolCoder() {
+ return EventProtocolCoder.manager;
+ }
+
+ protected SystemPersistence getPersistenceManager() {
+ return SystemPersistence.manager;
+ }
+
+ protected PolicyEngine getPolicyEngine() {
+ return PolicyEngine.manager;
+ }
}
diff --git a/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineManagerTest.java b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineManagerTest.java
new file mode 100644
index 00000000..0cc880d1
--- /dev/null
+++ b/policy-management/src/test/java/org/onap/policy/drools/system/PolicyEngineManagerTest.java
@@ -0,0 +1,1742 @@
+/*
+ * ============LICENSE_START=======================================================
+ * ONAP
+ * ================================================================================
+ * Copyright (C) 2018 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.system;
+
+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 static org.mockito.Matchers.any;
+import static org.mockito.Matchers.anyLong;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static org.onap.policy.common.utils.test.PolicyAssert.assertThrows;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Properties;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+import org.junit.Before;
+import org.junit.Test;
+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.http.server.HttpServletServer;
+import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
+import org.onap.policy.common.utils.test.PolicyAssert.RunnableWithEx;
+import org.onap.policy.drools.controller.DroolsController;
+import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
+import org.onap.policy.drools.features.PolicyEngineFeatureAPI;
+import org.onap.policy.drools.persistence.SystemPersistence;
+import org.onap.policy.drools.properties.DroolsProperties;
+import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
+import org.onap.policy.drools.protocol.configuration.ControllerConfiguration;
+import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
+import org.onap.policy.drools.protocol.configuration.PdpdConfiguration;
+
+public class PolicyEngineManagerTest {
+
+ private static final String EXPECTED = "expected exception";
+
+ private static final String NOOP_STR = CommInfrastructure.NOOP.name();
+ private static final String MY_NAME = "my-name";
+ private static final String CONTROLLER1 = "controller-a";
+ private static final String CONTROLLER2 = "controller-b";
+ private static final String CONTROLLER3 = "controller-c";
+ private static final String CONTROLLER4 = "controller-d";
+ private static final String FEATURE1 = "feature-a";
+ private static final String FEATURE2 = "feature-b";
+ private static final String MY_TOPIC = "my-topic";
+ private static final String MESSAGE = "my-message";
+
+ private static final Object MY_EVENT = new Object();
+
+ private static final Gson encoder = new GsonBuilder().disableHtmlEscaping().create();
+
+ private Properties properties;
+ private PolicyEngineFeatureAPI prov1;
+ private PolicyEngineFeatureAPI prov2;
+ private List<PolicyEngineFeatureAPI> providers;
+ private PolicyControllerFeatureAPI contProv1;
+ private PolicyControllerFeatureAPI contProv2;
+ private List<PolicyControllerFeatureAPI> contProviders;
+ private String[] globalInitArgs;
+ private TopicSource source1;
+ private TopicSource source2;
+ private List<TopicSource> sources;
+ private TopicSink sink1;
+ private TopicSink sink2;
+ private List<TopicSink> sinks;
+ private HttpServletServer server1;
+ private HttpServletServer server2;
+ private List<HttpServletServer> servers;
+ private HttpServletServerFactory serverFactory;
+ private TopicEndpoint endpoint;
+ private PolicyController controller;
+ private PolicyController controller2;
+ private PolicyController controller3;
+ private PolicyController controller4;
+ private List<PolicyController> controllers;
+ private PolicyControllerFactory controllerFactory;
+ private boolean jmxStarted;
+ private boolean jmxStopped;
+ private long threadSleepMs;
+ private int threadExitCode;
+ private boolean threadStarted;
+ private boolean threadInterrupted;
+ private Thread shutdownThread;
+ private boolean shouldInterrupt;
+ private EventProtocolCoder coder;
+ private SystemPersistence persist;
+ private PolicyEngine engine;
+ private DroolsConfiguration drools3;
+ private DroolsConfiguration drools4;
+ private ControllerConfiguration config3;
+ private ControllerConfiguration config4;
+ private PdpdConfiguration pdpConfig;
+ private String pdpConfigJson;
+ private PolicyEngineManager mgr;
+
+ /**
+ * Initializes the object to be tested.
+ *
+ * @throws Exception if an error occurs
+ */
+ @Before
+ public void setUp() throws Exception {
+
+ properties = new Properties();
+ prov1 = mock(PolicyEngineFeatureAPI.class);
+ prov2 = mock(PolicyEngineFeatureAPI.class);
+ providers = Arrays.asList(prov1, prov2);
+ contProv1 = mock(PolicyControllerFeatureAPI.class);
+ contProv2 = mock(PolicyControllerFeatureAPI.class);
+ contProviders = Arrays.asList(contProv1, contProv2);
+ globalInitArgs = null;
+ source1 = mock(TopicSource.class);
+ source2 = mock(TopicSource.class);
+ sources = Arrays.asList(source1, source2);
+ sink1 = mock(TopicSink.class);
+ sink2 = mock(TopicSink.class);
+ sinks = Arrays.asList(sink1, sink2);
+ server1 = mock(HttpServletServer.class);
+ server2 = mock(HttpServletServer.class);
+ servers = Arrays.asList(server1, server2);
+ serverFactory = mock(HttpServletServerFactory.class);
+ endpoint = mock(TopicEndpoint.class);
+ controller = mock(PolicyController.class);
+ controller2 = mock(PolicyController.class);
+ controller3 = mock(PolicyController.class);
+ controller4 = mock(PolicyController.class);
+ // do NOT include controller3 or controller4 in the list
+ controllers = Arrays.asList(controller, controller2);
+ controllerFactory = mock(PolicyControllerFactory.class);
+ jmxStarted = false;
+ jmxStopped = false;
+ threadSleepMs = -1;
+ threadExitCode = -1;
+ threadStarted = false;
+ threadInterrupted = false;
+ shutdownThread = null;
+ shouldInterrupt = false;
+ coder = mock(EventProtocolCoder.class);
+ persist = mock(SystemPersistence.class);
+ engine = mock(PolicyEngine.class);
+ drools3 = new DroolsConfiguration();
+ drools4 = new DroolsConfiguration();
+ config3 = new ControllerConfiguration();
+ config4 = new ControllerConfiguration();
+ pdpConfig = new PdpdConfiguration();
+
+ when(prov1.getName()).thenReturn(FEATURE1);
+ when(prov2.getName()).thenReturn(FEATURE2);
+
+ when(controllerFactory.build(any(), any())).thenReturn(controller);
+ when(controllerFactory.inventory()).thenReturn(controllers);
+ when(controllerFactory.get(CONTROLLER1)).thenReturn(controller);
+ when(controllerFactory.get(CONTROLLER2)).thenReturn(controller2);
+ // do NOT return controller3 or controller4
+
+ when(server1.waitedStart(anyLong())).thenReturn(true);
+ when(server1.stop()).thenReturn(true);
+
+ when(server2.waitedStart(anyLong())).thenReturn(true);
+ when(server2.stop()).thenReturn(true);
+
+ when(serverFactory.build(any())).thenReturn(servers);
+
+ when(source1.start()).thenReturn(true);
+ when(source1.stop()).thenReturn(true);
+
+ when(source2.start()).thenReturn(true);
+ when(source2.stop()).thenReturn(true);
+
+ when(sink1.start()).thenReturn(true);
+ when(sink1.stop()).thenReturn(true);
+ when(sink1.send(any())).thenReturn(true);
+ when(sink1.getTopicCommInfrastructure()).thenReturn(CommInfrastructure.NOOP);
+
+ when(sink2.start()).thenReturn(true);
+ when(sink2.stop()).thenReturn(true);
+
+ when(controller.getName()).thenReturn(CONTROLLER1);
+ when(controller.start()).thenReturn(true);
+ when(controller.stop()).thenReturn(true);
+ when(controller.lock()).thenReturn(true);
+ when(controller.unlock()).thenReturn(true);
+
+ when(controller2.getName()).thenReturn(CONTROLLER2);
+ when(controller2.start()).thenReturn(true);
+ when(controller2.stop()).thenReturn(true);
+ when(controller2.lock()).thenReturn(true);
+ when(controller2.unlock()).thenReturn(true);
+
+ when(controller3.getName()).thenReturn(CONTROLLER3);
+ when(controller3.start()).thenReturn(true);
+ when(controller3.stop()).thenReturn(true);
+ when(controller3.lock()).thenReturn(true);
+ when(controller3.unlock()).thenReturn(true);
+
+ when(controller4.getName()).thenReturn(CONTROLLER4);
+ when(controller4.start()).thenReturn(true);
+ when(controller4.stop()).thenReturn(true);
+ when(controller4.lock()).thenReturn(true);
+ when(controller4.unlock()).thenReturn(true);
+
+ when(endpoint.addTopicSources(any())).thenReturn(sources);
+ when(endpoint.addTopicSinks(any())).thenReturn(sinks);
+ when(endpoint.start()).thenReturn(true);
+ when(endpoint.stop()).thenReturn(true);
+ when(endpoint.lock()).thenReturn(true);
+ when(endpoint.unlock()).thenReturn(true);
+ when(endpoint.getTopicSink(CommInfrastructure.NOOP, MY_TOPIC)).thenReturn(sink1);
+ when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(Arrays.asList(sink1));
+
+ when(coder.encode(any(), any())).thenReturn(MESSAGE);
+
+ when(persist.getControllerProperties(CONTROLLER3)).thenReturn(properties);
+ when(persist.getControllerProperties(CONTROLLER4)).thenReturn(properties);
+
+ when(engine.createPolicyController(CONTROLLER3, properties)).thenReturn(controller3);
+ when(engine.createPolicyController(CONTROLLER4, properties)).thenReturn(controller4);
+
+ config3.setName(CONTROLLER3);
+ config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_CREATE);
+ config3.setDrools(drools3);
+
+ config4.setName(CONTROLLER4);
+ config4.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE);
+ config4.setDrools(drools4);
+
+ pdpConfig.getControllers().add(config3);
+ pdpConfig.getControllers().add(config4);
+ pdpConfig.setEntity(PdpdConfiguration.CONFIG_ENTITY_CONTROLLER);
+
+ pdpConfigJson = encoder.toJson(pdpConfig);
+
+ mgr = new PolicyEngineManagerImpl();
+ }
+
+ @Test
+ public void testFactory() {
+ mgr = new PolicyEngineManager();
+
+ assertNotNull(mgr.getEngineProviders());
+ assertNotNull(mgr.getControllerProviders());
+ assertNotNull(mgr.getTopicEndpointManager());
+ assertNotNull(mgr.getServletFactory());
+ assertNotNull(mgr.getControllerFactory());
+ assertNotNull(mgr.makeShutdownThread());
+ assertNotNull(mgr.getProtocolCoder());
+ assertNotNull(mgr.getPersistenceManager());
+ assertNotNull(mgr.getPolicyEngine());
+ }
+
+ @Test
+ public void testBoot() throws Exception {
+ String[] args = {"boot-a", "boot-b"};
+
+ // arrange for first provider to throw exceptions
+ when(prov1.beforeBoot(mgr, args)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterBoot(mgr)).thenThrow(new RuntimeException(EXPECTED));
+
+ mgr.boot(args);
+
+ verify(prov1).beforeBoot(mgr, args);
+ verify(prov2).beforeBoot(mgr, args);
+
+ assertTrue(globalInitArgs == args);
+
+ verify(prov1).afterBoot(mgr);
+ verify(prov2).afterBoot(mgr);
+
+ // global init throws exception - still calls afterBoot
+ setUp();
+ mgr = new PolicyEngineManagerImpl() {
+ @Override
+ protected void globalInitContainer(String[] cliArgs) {
+ throw new RuntimeException(EXPECTED);
+ }
+ };
+ mgr.boot(args);
+ verify(prov2).afterBoot(mgr);
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeBoot(mgr, args)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterBoot(mgr)).thenReturn(flag),
+ () -> mgr.boot(args),
+ prov -> verify(prov).beforeBoot(mgr, args),
+ () -> assertTrue(globalInitArgs == args),
+ prov -> verify(prov).afterBoot(mgr));
+ }
+
+ @Test
+ public void testSetEnvironment_testGetEnvironment_testGetEnvironmentProperty_setEnvironmentProperty() {
+ Properties props1 = new Properties();
+ props1.put("prop1-a", "value1-a");
+ props1.put("prop1-b", "value1-b");
+
+ mgr.setEnvironment(props1);
+
+ Properties env = mgr.getEnvironment();
+ assertEquals(props1, env);
+
+ // add more properties
+ Properties props2 = new Properties();
+ String propKey = "prop2-a";
+ props2.put(propKey, "value2-a");
+ props2.put("prop2-b", "value2-b");
+
+ mgr.setEnvironment(props2);
+
+ assertTrue(mgr.getEnvironment() == env);
+
+ // new env should have a union of the properties
+ props1.putAll(props2);
+ assertEquals(props1, env);
+
+ assertEquals("value2-a", mgr.getEnvironmentProperty(propKey));
+
+ String newValue = "new-value";
+ mgr.setEnvironmentProperty(propKey, newValue);
+ assertEquals(newValue, mgr.getEnvironmentProperty(propKey));
+
+ props1.setProperty(propKey, newValue);
+ assertEquals(props1, env);
+
+ assertNotNull(mgr.getEnvironmentProperty("JAVA_HOME"));
+ assertNull(mgr.getEnvironmentProperty("unknown-env-property"));
+ }
+
+ @Test
+ public void testDefaultTelemetryConfig() {
+ Properties config = mgr.defaultTelemetryConfig();
+ assertNotNull(config);
+ assertFalse(config.isEmpty());
+ }
+
+ @Test
+ public void testConfigureProperties() throws Exception {
+ // arrange for first provider to throw exceptions
+ when(prov1.beforeConfigure(mgr, properties)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterConfigure(mgr)).thenThrow(new RuntimeException(EXPECTED));
+
+ mgr.configure(properties);
+
+ verify(prov1).beforeConfigure(mgr, properties);
+ verify(prov2).beforeConfigure(mgr, properties);
+
+ assertTrue(mgr.getProperties() == properties);
+
+ assertEquals(sources, mgr.getSources());
+ assertEquals(sinks, mgr.getSinks());
+ assertEquals(servers, mgr.getHttpServers());
+
+ verify(source1).register(mgr);
+ verify(source2).register(mgr);
+
+ verify(prov1).afterConfigure(mgr);
+ verify(prov2).afterConfigure(mgr);
+
+ // middle stuff throws exception - still calls afterXxx
+ setUp();
+ when(endpoint.addTopicSources(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
+ when(endpoint.addTopicSinks(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
+ when(serverFactory.build(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
+ mgr.configure(properties);
+ verify(prov2).afterConfigure(mgr);
+
+ // null properties - nothing should be invoked
+ setUp();
+ Properties nullProps = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.configure(nullProps));
+ verify(prov1, never()).beforeConfigure(mgr, properties);
+ verify(prov1, never()).afterConfigure(mgr);
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeConfigure(mgr, properties)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterConfigure(mgr)).thenReturn(flag),
+ () -> mgr.configure(properties),
+ prov -> verify(prov).beforeConfigure(mgr, properties),
+ () -> assertTrue(mgr.getProperties() == properties),
+ prov -> verify(prov).afterConfigure(mgr));
+ }
+
+ @Test
+ public void testConfigurePdpdConfiguration() throws Exception {
+ mgr.configure(properties);
+ assertTrue(mgr.configure(pdpConfig));
+
+ verify(controllerFactory).patch(controller3, drools3);
+ verify(controllerFactory).patch(controller4, drools4);
+
+ // invalid params
+ PdpdConfiguration nullConfig = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.configure(nullConfig));
+
+ pdpConfig.setEntity("unknown-entity");
+ assertThrows(IllegalArgumentException.class, () -> mgr.configure(pdpConfig));
+
+ // source list of size 1
+ setUp();
+ when(endpoint.addTopicSources(any())).thenReturn(Arrays.asList(source1));
+ mgr.configure(properties);
+ assertTrue(mgr.configure(pdpConfig));
+
+ verify(controllerFactory).patch(controller3, drools3);
+ verify(controllerFactory).patch(controller4, drools4);
+ }
+
+ @Test
+ public void testCreatePolicyController() throws Exception {
+ assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
+
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+ verify(contProv2).beforeCreate(MY_NAME, properties);
+ verify(controller, never()).lock();
+ verify(contProv1).afterCreate(controller);
+ verify(contProv2).afterCreate(controller);
+
+ // first provider throws exceptions - same result
+ setUp();
+ when(contProv1.beforeCreate(MY_NAME, properties)).thenThrow(new RuntimeException(EXPECTED));
+ when(contProv1.afterCreate(controller)).thenThrow(new RuntimeException(EXPECTED));
+ assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
+
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+ verify(contProv2).beforeCreate(MY_NAME, properties);
+ verify(controller, never()).lock();
+ verify(contProv1).afterCreate(controller);
+ verify(contProv2).afterCreate(controller);
+
+ // locked - same result, but engine locked
+ setUp();
+ mgr.lock();
+ assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+ verify(controller, times(2)).lock();
+ verify(contProv2).afterCreate(controller);
+
+ // empty name in properties - same result
+ setUp();
+ properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, "");
+ assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+
+ // matching name in properties - same result
+ setUp();
+ properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, MY_NAME);
+ assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+
+ // mismatching name in properties - nothing should happen besides exception
+ setUp();
+ properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, "mistmatched-name");
+ assertThrows(IllegalStateException.class, () -> mgr.createPolicyController(MY_NAME, properties));
+ verify(contProv1, never()).beforeCreate(MY_NAME, properties);
+
+ // first provider generates controller - stops after first provider
+ setUp();
+ when(contProv1.beforeCreate(MY_NAME, properties)).thenReturn(controller2);
+ assertEquals(controller2, mgr.createPolicyController(MY_NAME, properties));
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+ verify(contProv2, never()).beforeCreate(MY_NAME, properties);
+ verify(controller, never()).lock();
+ verify(contProv1, never()).afterCreate(controller);
+ verify(contProv2, never()).afterCreate(controller);
+
+ // first provider returns true - stops after first provider afterXxx
+ setUp();
+ when(contProv1.afterCreate(controller)).thenReturn(true);
+ assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
+ verify(contProv1).beforeCreate(MY_NAME, properties);
+ verify(contProv2).beforeCreate(MY_NAME, properties);
+ verify(contProv1).afterCreate(controller);
+ verify(contProv2, never()).afterCreate(controller);
+ }
+
+ @Test
+ public void testUpdatePolicyControllers() throws Exception {
+ assertEquals(Arrays.asList(controller3, controller4), mgr.updatePolicyControllers(pdpConfig.getControllers()));
+
+ // controller3 was CREATE
+ verify(controllerFactory).patch(controller3, drools3);
+ verify(controller3, never()).lock();
+ verify(controller3, never()).unlock();
+
+ // controller4 was UPDATE
+ verify(controllerFactory).patch(controller4, drools4);
+ verify(controller4, never()).lock();
+ verify(controller4).unlock();
+
+ // invalid args
+ assertTrue(mgr.updatePolicyControllers(null).isEmpty());
+ assertTrue(mgr.updatePolicyControllers(Collections.emptyList()).isEmpty());
+
+ // force exception in the first controller with invalid operation
+ setUp();
+ config3.setOperation("unknown-operation");
+ assertEquals(Arrays.asList(controller4), mgr.updatePolicyControllers(pdpConfig.getControllers()));
+
+ // controller3 should NOT have been done
+ verify(controllerFactory, never()).patch(controller3, drools3);
+
+ // controller4 should still be done
+ verify(controllerFactory).patch(controller4, drools4);
+ verify(controller4, never()).lock();
+ verify(controller4).unlock();
+ }
+
+ @Test
+ public void testUpdatePolicyController() throws Exception {
+ assertEquals(controller3, mgr.updatePolicyController(config3));
+ verify(engine).createPolicyController(CONTROLLER3, properties);
+
+ // invalid parameters
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(null));
+
+ // invalid name
+ setUp();
+ config3.setName(null);
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ config3.setName("");
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ // invalid operation
+ setUp();
+ config3.setOperation(null);
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ config3.setOperation("");
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK);
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK);
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ // exception from get() - should create controller
+ setUp();
+ when(controllerFactory.get(CONTROLLER3)).thenThrow(new IllegalArgumentException(EXPECTED));
+ assertEquals(controller3, mgr.updatePolicyController(config3));
+ verify(engine).createPolicyController(CONTROLLER3, properties);
+
+ // null properties
+ setUp();
+ when(persist.getControllerProperties(CONTROLLER3)).thenReturn(null);
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+
+ // throw linkage error
+ setUp();
+ when(persist.getControllerProperties(CONTROLLER3)).thenThrow(new LinkageError(EXPECTED));
+ assertThrows(IllegalStateException.class, () -> mgr.updatePolicyController(config3));
+
+ /*
+ * For remaining tests, the factory will return the controller instead of creating
+ * one.
+ */
+ setUp();
+ when(controllerFactory.get(CONTROLLER3)).thenReturn(controller3);
+
+ assertEquals(controller3, mgr.updatePolicyController(config3));
+
+ // should NOT have created a new controller
+ verify(engine, never()).createPolicyController(any(), any());
+
+ int countPatch = 0;
+ int countLock = 0;
+ int countUnlock = 0;
+
+ // check different operations
+
+ // CREATE only invokes patch() (note: mgr.update() has already been called)
+ verify(controllerFactory, times(++countPatch)).patch(controller3, drools3);
+ verify(controller3, times(countLock)).lock();
+ verify(controller3, times(countUnlock)).unlock();
+
+ // UPDATE invokes unlock() and patch()
+ config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE);
+ assertEquals(controller3, mgr.updatePolicyController(config3));
+ verify(controllerFactory, times(++countPatch)).patch(controller3, drools3);
+ verify(controller3, times(countLock)).lock();
+ verify(controller3, times(++countUnlock)).unlock();
+
+ // LOCK invokes lock()
+ config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK);
+ assertEquals(controller3, mgr.updatePolicyController(config3));
+ verify(controllerFactory, times(countPatch)).patch(controller3, drools3);
+ verify(controller3, times(++countLock)).lock();
+ verify(controller3, times(countUnlock)).unlock();
+
+ // UNLOCK invokes unlock()
+ config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK);
+ assertEquals(controller3, mgr.updatePolicyController(config3));
+ verify(controllerFactory, times(countPatch)).patch(controller3, drools3);
+ verify(controller3, times(countLock)).lock();
+ verify(controller3, times(++countUnlock)).unlock();
+
+ // invalid operation
+ config3.setOperation("invalid-operation");
+ assertThrows(IllegalArgumentException.class, () -> mgr.updatePolicyController(config3));
+ }
+
+ @Test
+ public void testStart() throws Throwable {
+ // normal success case
+ testStart(true, () -> {
+ // arrange for first provider, server, source, and sink to throw exceptions
+ when(prov1.beforeStart(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterStart(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(server1.waitedStart(anyLong())).thenThrow(new RuntimeException(EXPECTED));
+ when(source1.start()).thenThrow(new RuntimeException(EXPECTED));
+ when(sink1.start()).thenThrow(new RuntimeException(EXPECTED));
+ });
+
+ // servlet wait fails - still does everything
+ testStart(false, () -> when(server1.waitedStart(anyLong())).thenReturn(false));
+
+ // topic source fails to start - still does everything
+ testStart(false, () -> when(source1.start()).thenReturn(false));
+
+ // topic sink fails to start - still does everything
+ testStart(false, () -> when(sink1.start()).thenReturn(false));
+
+ // controller fails to start - still does everything
+ testStart(false, () -> when(controller.start()).thenReturn(false));
+
+ // controller throws an exception - still does everything
+ testStart(false, () -> when(controller.start()).thenThrow(new RuntimeException(EXPECTED)));
+
+ // endpoint manager fails to start - still does everything
+ testStart(false, () -> when(endpoint.start()).thenReturn(false));
+
+ // endpoint manager throws an exception - still does everything AND succeeds
+ testStart(true, () -> when(endpoint.start()).thenThrow(new IllegalStateException(EXPECTED)));
+
+ // locked - nothing other than beforeXxx methods should be invoked
+ setUp();
+ mgr.configure(properties);
+ mgr.lock();
+ assertThrows(IllegalStateException.class, () -> mgr.start());
+ verify(prov2).beforeStart(mgr);
+ verify(server2, never()).waitedStart(anyLong());
+ verify(source2, never()).start();
+ verify(sink1, never()).start();
+ verify(controller, never()).start();
+ verify(endpoint, never()).start();
+ assertFalse(jmxStarted);
+ verify(prov1, never()).afterStart(mgr);
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeStart(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterStart(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ assertTrue(mgr.start());
+ },
+ prov -> verify(prov).beforeStart(mgr),
+ () -> assertTrue(jmxStarted),
+ prov -> verify(prov).afterStart(mgr));
+ }
+
+ /**
+ * Tests the start() method, after setting some option.
+ *
+ * @param expectedResult what start() is expected to return
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testStart(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ assertEquals(expectedResult, mgr.start());
+
+ verify(prov1).beforeStart(mgr);
+ verify(prov2).beforeStart(mgr);
+
+ verify(server1).waitedStart(anyLong());
+ verify(server2).waitedStart(anyLong());
+
+ verify(source1).start();
+ verify(source2).start();
+
+ verify(sink1).start();
+ verify(sink2).start();
+
+ verify(controller).start();
+ verify(controller2).start();
+
+ verify(endpoint).start();
+
+ assertTrue(jmxStarted);
+
+ verify(prov1).afterStart(mgr);
+ verify(prov2).afterStart(mgr);
+ }
+
+ @Test
+ public void testStop() throws Throwable {
+ // normal success case
+ testStop(true, () -> {
+ // arrange for first provider, server, source, and sink to throw exceptions
+ when(prov1.beforeStop(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterStop(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(server1.stop()).thenThrow(new RuntimeException(EXPECTED));
+ when(source1.stop()).thenThrow(new RuntimeException(EXPECTED));
+ when(sink1.stop()).thenThrow(new RuntimeException(EXPECTED));
+ });
+
+ // not alive - shouldn't run anything besides beforeStop()
+ setUp();
+ mgr.configure(properties);
+ assertTrue(mgr.stop());
+ verify(prov1).beforeStop(mgr);
+ verify(prov2).beforeStop(mgr);
+ verify(controller, never()).stop();
+ verify(source1, never()).stop();
+ verify(sink1, never()).stop();
+ verify(endpoint, never()).stop();
+ verify(server1, never()).stop();
+ verify(prov1, never()).afterStop(mgr);
+ verify(prov2, never()).afterStop(mgr);
+
+ // controller fails to stop - still does everything
+ testStop(false, () -> when(controller.stop()).thenReturn(false));
+
+ // controller throws an exception - still does everything
+ testStop(false, () -> when(controller.stop()).thenThrow(new RuntimeException(EXPECTED)));
+
+ // topic source fails to stop - still does everything
+ testStop(false, () -> when(source1.stop()).thenReturn(false));
+
+ // topic sink fails to stop - still does everything
+ testStop(false, () -> when(sink1.stop()).thenReturn(false));
+
+ // endpoint manager fails to stop - still does everything
+ testStop(false, () -> when(endpoint.stop()).thenReturn(false));
+
+ // servlet fails to stop - still does everything
+ testStop(false, () -> when(server1.stop()).thenReturn(false));
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeStop(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterStop(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ mgr.start();
+ assertTrue(mgr.stop());
+ },
+ prov -> verify(prov).beforeStop(mgr),
+ () -> verify(endpoint).stop(),
+ prov -> verify(prov).afterStop(mgr));
+ }
+
+ /**
+ * Tests the stop() method, after setting some option.
+ *
+ * @param expectedResult what stop() is expected to return
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testStop(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ mgr.start();
+ assertEquals(expectedResult, mgr.stop());
+
+ verify(prov1).beforeStop(mgr);
+ verify(prov2).beforeStop(mgr);
+
+ verify(controller).stop();
+ verify(controller2).stop();
+
+ verify(source1).stop();
+ verify(source2).stop();
+
+ verify(sink1).stop();
+ verify(sink2).stop();
+
+ verify(endpoint).stop();
+
+ verify(server1).stop();
+ verify(server2).stop();
+
+ verify(prov1).afterStop(mgr);
+ verify(prov2).afterStop(mgr);
+ }
+
+ @Test
+ public void testShutdown() throws Throwable {
+ // normal success case
+ testShutdown(() -> {
+ // arrange for first provider, source, and sink to throw exceptions
+ when(prov1.beforeShutdown(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterShutdown(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ doThrow(new RuntimeException(EXPECTED)).when(source1).shutdown();
+ doThrow(new RuntimeException(EXPECTED)).when(sink1).shutdown();
+ });
+
+ assertNotNull(shutdownThread);
+ assertTrue(threadStarted);
+ assertTrue(threadInterrupted);
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeShutdown(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterShutdown(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ mgr.start();
+ mgr.shutdown();
+ },
+ prov -> verify(prov).beforeShutdown(mgr),
+ () -> assertTrue(jmxStopped),
+ prov -> verify(prov).afterShutdown(mgr));
+ }
+
+ /**
+ * Tests the shutdown() method, after setting some option.
+ *
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testShutdown(RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ mgr.start();
+ mgr.shutdown();
+
+ verify(prov1).beforeShutdown(mgr);
+ verify(prov2).beforeShutdown(mgr);
+
+ verify(source1).shutdown();
+ verify(source2).shutdown();
+
+ verify(sink1).shutdown();
+ verify(sink2).shutdown();
+
+ verify(controllerFactory).shutdown();
+ verify(endpoint).shutdown();
+ verify(serverFactory).destroy();
+
+ assertTrue(jmxStopped);
+
+ verify(prov1).afterShutdown(mgr);
+ verify(prov2).afterShutdown(mgr);
+ }
+
+ @Test
+ public void testShutdownThreadRun() throws Throwable {
+ // arrange for first server to throw exceptions
+ testShutdownThreadRun(() -> doThrow(new RuntimeException(EXPECTED)).when(server1).shutdown());
+
+ // sleep throws an exception
+ testShutdownThreadRun(() -> shouldInterrupt = true);
+ }
+
+ /**
+ * Tests the ShutdownThread.run() method, after setting some option.
+ *
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testShutdownThreadRun(RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ mgr.start();
+ mgr.shutdown();
+
+ assertNotNull(shutdownThread);
+
+ shutdownThread.run();
+
+ assertTrue(threadSleepMs >= 0);
+ assertEquals(0, threadExitCode);
+
+ verify(server1).shutdown();
+ verify(server2).shutdown();
+ }
+
+ @Test
+ public void testIsAlive() {
+ mgr.configure(properties);
+ assertFalse(mgr.isAlive());
+
+ mgr.start();
+ assertTrue(mgr.isAlive());
+
+ mgr.stop();
+ assertFalse(mgr.isAlive());
+ }
+
+ @Test
+ public void testLock() throws Throwable {
+ // normal success case
+ testLock(true, () -> {
+ // arrange for first provider to throw exceptions
+ when(prov1.beforeLock(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterLock(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ });
+
+ // already locked - shouldn't run anything besides beforeLock()
+ setUp();
+ mgr.configure(properties);
+ mgr.lock();
+ assertTrue(mgr.lock());
+ verify(prov1, times(2)).beforeLock(mgr);
+ verify(prov2, times(2)).beforeLock(mgr);
+ verify(controller).lock();
+ verify(controller2).lock();
+ verify(endpoint).lock();
+ verify(prov1).afterLock(mgr);
+ verify(prov2).afterLock(mgr);
+
+ // controller fails to lock - still does everything
+ testLock(false, () -> when(controller.lock()).thenReturn(false));
+
+ // controller throws an exception - still does everything
+ testLock(false, () -> when(controller.lock()).thenThrow(new RuntimeException(EXPECTED)));
+
+ // endpoint manager fails to lock - still does everything
+ testLock(false, () -> when(endpoint.lock()).thenReturn(false));
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeLock(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterLock(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ mgr.start();
+ assertTrue(mgr.lock());
+ },
+ prov -> verify(prov).beforeLock(mgr),
+ () -> verify(endpoint).lock(),
+ prov -> verify(prov).afterLock(mgr));
+ }
+
+ /**
+ * Tests the lock() method, after setting some option.
+ *
+ * @param expectedResult what lock() is expected to return
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testLock(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ assertEquals(expectedResult, mgr.lock());
+
+ verify(prov1).beforeLock(mgr);
+ verify(prov2).beforeLock(mgr);
+
+ verify(controller).lock();
+ verify(controller2).lock();
+
+ verify(endpoint).lock();
+
+ verify(prov1).afterLock(mgr);
+ verify(prov2).afterLock(mgr);
+ }
+
+ @Test
+ public void testUnlock() throws Throwable {
+ // normal success case
+ testUnlock(true, () -> {
+ // arrange for first provider to throw exceptions
+ when(prov1.beforeUnlock(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterUnlock(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ });
+
+ // not locked - shouldn't run anything besides beforeUnlock()
+ setUp();
+ mgr.configure(properties);
+ assertTrue(mgr.unlock());
+ verify(prov1).beforeUnlock(mgr);
+ verify(prov2).beforeUnlock(mgr);
+ verify(controller, never()).unlock();
+ verify(controller2, never()).unlock();
+ verify(endpoint, never()).unlock();
+ verify(prov1, never()).afterUnlock(mgr);
+ verify(prov2, never()).afterUnlock(mgr);
+
+ // controller fails to unlock - still does everything
+ testUnlock(false, () -> when(controller.unlock()).thenReturn(false));
+
+ // controller throws an exception - still does everything
+ testUnlock(false, () -> when(controller.unlock()).thenThrow(new RuntimeException(EXPECTED)));
+
+ // endpoint manager fails to unlock - still does everything
+ testUnlock(false, () -> when(endpoint.unlock()).thenReturn(false));
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeUnlock(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterUnlock(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ mgr.lock();
+ assertTrue(mgr.unlock());
+ },
+ prov -> verify(prov).beforeUnlock(mgr),
+ () -> verify(endpoint).unlock(),
+ prov -> verify(prov).afterUnlock(mgr));
+ }
+
+ /**
+ * Tests the unlock() method, after setting some option.
+ *
+ * @param expectedResult what unlock() is expected to return
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testUnlock(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ mgr.lock();
+ assertEquals(expectedResult, mgr.unlock());
+
+ verify(prov1).beforeUnlock(mgr);
+ verify(prov2).beforeUnlock(mgr);
+
+ verify(controller).unlock();
+ verify(controller2).unlock();
+
+ verify(endpoint).unlock();
+
+ verify(prov1).afterUnlock(mgr);
+ verify(prov2).afterUnlock(mgr);
+ }
+
+ @Test
+ public void testIsLocked() {
+ mgr.configure(properties);
+ assertFalse(mgr.isLocked());
+
+ mgr.lock();
+ assertTrue(mgr.isLocked());
+
+ mgr.unlock();
+ assertFalse(mgr.isLocked());
+ }
+
+ @Test
+ public void testRemovePolicyControllerString() {
+ mgr.removePolicyController(MY_NAME);
+
+ verify(controllerFactory).destroy(MY_NAME);
+ }
+
+ @Test
+ public void testRemovePolicyControllerPolicyController() {
+ mgr.removePolicyController(controller);
+
+ verify(controllerFactory).destroy(controller);
+ }
+
+ @Test
+ public void testGetPolicyControllers() {
+ assertEquals(controllers, mgr.getPolicyControllers());
+ }
+
+ @Test
+ public void testGetPolicyControllerIds() {
+ assertEquals(Arrays.asList(CONTROLLER1, CONTROLLER2), mgr.getPolicyControllerIds());
+ }
+
+ @Test
+ public void testGetProperties() {
+ properties.setProperty("prop-x", "value-x");
+ properties.setProperty("prop-y", "value-y");
+
+ mgr.configure(properties);
+ assertEquals(properties, mgr.getProperties());
+ }
+
+ @Test
+ public void testGetSources() {
+ mgr.configure(properties);
+ assertEquals(sources, mgr.getSources());
+ }
+
+ @Test
+ public void testGetSinks() {
+ mgr.configure(properties);
+ assertEquals(sinks, mgr.getSinks());
+ }
+
+ @Test
+ public void testGetHttpServers() {
+ mgr.configure(properties);
+ assertEquals(servers, mgr.getHttpServers());
+ }
+
+ @Test
+ public void testGetFeatures() {
+ assertEquals(Arrays.asList(FEATURE1, FEATURE2), mgr.getFeatures());
+ }
+
+ @Test
+ public void testGetFeatureProviders() {
+ assertEquals(providers, mgr.getFeatureProviders());
+ }
+
+ @Test
+ public void testGetFeatureProvider() {
+ assertEquals(prov1, mgr.getFeatureProvider(FEATURE1));
+ assertEquals(prov2, mgr.getFeatureProvider(FEATURE2));
+
+ // null feature
+ assertThrows(IllegalArgumentException.class, () -> mgr.getFeatureProvider(null));
+
+ // empty feature
+ assertThrows(IllegalArgumentException.class, () -> mgr.getFeatureProvider(""));
+
+ // unknown feature
+ assertThrows(IllegalArgumentException.class, () -> mgr.getFeatureProvider("unknown-feature"));
+ }
+
+ @Test
+ public void testOnTopicEvent() {
+ mgr.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, pdpConfigJson);
+
+ verify(controllerFactory).patch(controller3, drools3);
+ verify(controllerFactory).patch(controller4, drools4);
+
+ // null json - no additional patches
+ mgr.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, null);
+
+ verify(controllerFactory).patch(controller3, drools3);
+ verify(controllerFactory).patch(controller4, drools4);
+ }
+
+ @Test
+ public void testDeliverStringObject() throws Exception {
+ mgr.configure(properties);
+ mgr.start();
+
+ assertTrue(mgr.deliver(MY_TOPIC, MY_EVENT));
+
+ verify(sink1).send(MESSAGE);
+
+ // invalid parameters
+ String nullStr = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(nullStr, MY_EVENT));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver("", MY_EVENT));
+
+ Object nullObj = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(MY_TOPIC, nullObj));
+
+ // locked
+ mgr.lock();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(MY_TOPIC, MY_EVENT));
+ mgr.unlock();
+
+ // not running
+ mgr.stop();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(MY_TOPIC, MY_EVENT));
+
+ // issues with topic
+ setUp();
+ mgr.configure(properties);
+ mgr.start();
+
+ // null sinks
+ when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(null);
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(MY_TOPIC, MY_EVENT));
+
+ // empty sinks
+ when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(Collections.emptyList());
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(MY_TOPIC, MY_EVENT));
+
+ // too many sinks
+ when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(sinks);
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(MY_TOPIC, MY_EVENT));
+ }
+
+ @Test
+ public void testDeliverStringStringObject() {
+ mgr.configure(properties);
+ mgr.start();
+
+ assertTrue(mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
+
+ verify(sink1).send(MESSAGE);
+
+ // invalid parameters
+ String nullStr = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(nullStr, MY_TOPIC, MY_EVENT));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver("", MY_TOPIC, MY_EVENT));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver("unknown-bus-type", MY_TOPIC, MY_EVENT));
+
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(NOOP_STR, nullStr, MY_EVENT));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(NOOP_STR, "", MY_EVENT));
+
+ Object nullObj = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(NOOP_STR, MY_TOPIC, nullObj));
+
+ // locked
+ mgr.lock();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
+ mgr.unlock();
+
+ // not running
+ mgr.stop();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
+ }
+
+ @Test
+ public void testDeliverCommInfrastructureStringObject() throws Exception {
+ mgr.configure(properties);
+ mgr.start();
+
+ assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
+
+ verify(controller, never()).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
+
+ verify(coder).encode(MY_TOPIC, MY_EVENT);
+ verify(sink1).send(MESSAGE);
+
+ // invalid parameters
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, null, MY_EVENT));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, "", MY_EVENT));
+
+ Object nullObj = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, nullObj));
+
+ // locked
+ mgr.lock();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
+ mgr.unlock();
+
+ // not started
+ mgr.stop();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
+
+ // send() throws an exception
+ setUp();
+ mgr.configure(properties);
+ mgr.start();
+ when(sink1.send(any())).thenThrow(new ArithmeticException(EXPECTED));
+ assertThrows(ArithmeticException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
+
+ /*
+ * For remaining tests, have the controller handle delivery.
+ */
+ setUp();
+ mgr.configure(properties);
+ mgr.start();
+ DroolsController drools = mock(DroolsController.class);
+ when(coder.getDroolsController(MY_TOPIC, MY_EVENT)).thenReturn(drools);
+ when(controllerFactory.get(drools)).thenReturn(controller);
+ when(controller.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT)).thenReturn(true);
+
+ assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
+
+ verify(controller).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
+
+ verify(coder, never()).encode(MY_TOPIC, MY_EVENT);
+ verify(sink1, never()).send(MESSAGE);
+
+ // controller throws exception, so should drop into regular handling
+ when(controller.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT)).thenThrow(new RuntimeException(EXPECTED));
+
+ assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
+
+ // should have attempted this again
+ verify(controller, times(2)).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
+
+ // now these should have been called
+ verify(coder).encode(MY_TOPIC, MY_EVENT);
+ verify(sink1).send(MESSAGE);
+ }
+
+ @Test
+ public void testDeliverCommInfrastructureStringString() {
+ mgr.configure(properties);
+
+ // not started yet
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
+
+ // start it
+ mgr.start();
+
+ assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
+ verify(sink1).send(MESSAGE);
+ verify(sink2, never()).send(any());
+
+ // invalid parameters
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, null, MESSAGE));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, "", MESSAGE));
+
+ String nullStr = null;
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, nullStr));
+ assertThrows(IllegalArgumentException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, ""));
+
+ // unknown topic
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(CommInfrastructure.NOOP, "unknown-topic", MESSAGE));
+
+ // locked
+ mgr.lock();
+ assertThrows(IllegalStateException.class, () -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
+ mgr.unlock();
+ }
+
+ @Test
+ public void testActivate() throws Throwable {
+ // normal success case
+ testActivate(() -> {
+ // arrange for first provider and controller to throw exceptions
+ when(prov1.beforeActivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterActivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(controller.start()).thenThrow(new RuntimeException(EXPECTED));
+ });
+
+ // controller generates linkage error
+ testActivate(() -> when(controller.start()).thenThrow(new LinkageError(EXPECTED)));
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeActivate(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterActivate(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ mgr.lock();
+ mgr.activate();
+ },
+ prov -> verify(prov).beforeActivate(mgr),
+ () -> assertFalse(mgr.isLocked()),
+ prov -> verify(prov).afterActivate(mgr));
+ }
+
+ /**
+ * Tests the activate() method, after setting some option.
+ *
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testActivate(RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ mgr.lock();
+ mgr.activate();
+
+ verify(prov1).beforeActivate(mgr);
+ verify(prov2).beforeActivate(mgr);
+
+ // unlocked by activate() AND by unlock() (which is invoked by activate())
+ verify(controller, times(2)).unlock();
+ verify(controller2, times(2)).unlock();
+
+ verify(controller).start();
+ verify(controller2).start();
+
+ assertFalse(mgr.isLocked());
+
+ verify(prov1).afterActivate(mgr);
+ verify(prov2).afterActivate(mgr);
+ }
+
+ @Test
+ public void testDeactivate() throws Throwable {
+ // normal success case
+ testDeactivate(() -> {
+ // arrange for first provider and controller to throw exceptions
+ when(prov1.beforeDeactivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(prov1.afterDeactivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
+ when(controller.stop()).thenThrow(new RuntimeException(EXPECTED));
+ });
+
+ // controller generates linkage error
+ testDeactivate(() -> when(controller.stop()).thenThrow(new LinkageError(EXPECTED)));
+
+ // other tests
+ checkBeforeAfter(
+ (prov, flag) -> when(prov.beforeDeactivate(mgr)).thenReturn(flag),
+ (prov, flag) -> when(prov.afterDeactivate(mgr)).thenReturn(flag),
+ () -> {
+ mgr.configure(properties);
+ mgr.deactivate();
+ },
+ prov -> verify(prov).beforeDeactivate(mgr),
+ () -> assertTrue(mgr.isLocked()),
+ prov -> verify(prov).afterDeactivate(mgr));
+ }
+
+ /**
+ * Tests the deactivate() method, after setting some option.
+ *
+ * @param setOption function that sets an option
+ * @throws Throwable if an error occurs during setup
+ */
+ private void testDeactivate(RunnableWithEx setOption) throws Throwable {
+ setUp();
+ setOption.run();
+
+ mgr.configure(properties);
+ mgr.deactivate();
+
+ verify(prov1).beforeDeactivate(mgr);
+ verify(prov2).beforeDeactivate(mgr);
+
+ verify(controller).lock();
+ verify(controller2).lock();
+
+ verify(controller).stop();
+ verify(controller2).stop();
+
+ assertTrue(mgr.isLocked());
+
+ verify(prov1).afterDeactivate(mgr);
+ verify(prov2).afterDeactivate(mgr);
+ }
+
+ @Test
+ public void testControllerConfig() throws Exception {
+ mgr.configure(properties);
+ assertTrue(mgr.configure(pdpConfig));
+
+ verify(controllerFactory).patch(controller3, drools3);
+ verify(controllerFactory).patch(controller4, drools4);
+
+ // empty controllers
+ pdpConfig.getControllers().clear();
+ assertFalse(mgr.configure(pdpConfig));
+
+ // null controllers
+ pdpConfig.setControllers(null);
+ assertFalse(mgr.configure(pdpConfig));
+
+ // arrange for controller3 to fail
+ setUp();
+ config3.setOperation("fail-3");
+ assertFalse(mgr.configure(pdpConfig));
+
+ verify(controllerFactory, never()).patch(controller3, drools3);
+ verify(controllerFactory).patch(controller4, drools4);
+
+ // arrange for both controllers to fail
+ setUp();
+ config3.setOperation("fail-3");
+ config4.setOperation("fail-4");
+ assertFalse(mgr.configure(pdpConfig));
+ }
+
+ @Test
+ public void testToString() {
+ assertTrue(mgr.toString().startsWith("PolicyEngineManager ["));
+ }
+
+ /**
+ * Performs an operation that has a beforeXxx method and an afterXxx method. Tries
+ * combinations where beforeXxx and afterXxx return {@code true} and {@code false}.
+ *
+ * @param setBefore function to set the return value of a provider's beforeXxx method
+ * @param setAfter function to set the return value of a provider's afterXxx method
+ * @param action invokes the operation
+ * @param verifyBefore verifies that a provider's beforeXxx method was invoked
+ * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
+ * and the afterXxx loop was invoked
+ * @param verifyAfter verifies that a provider's afterXxx method was invoked
+ * @throws Exception if an error occurs while calling {@link #setUp()}
+ */
+ private void checkBeforeAfter(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
+ BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
+ Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
+ Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
+
+ checkBeforeAfter_FalseFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
+ checkBeforeAfter_FalseTrue(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
+ checkBeforeAfter_TrueFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
+
+ // don't need to test true-true, as it's behavior is a subset of true-false
+ }
+
+ /**
+ * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
+ * case where both the beforeXxx and afterXxx methods return {@code false}.
+ *
+ * @param setBefore function to set the return value of a provider's beforeXxx method
+ * @param setAfter function to set the return value of a provider's afterXxx method
+ * @param action invokes the operation
+ * @param verifyBefore verifies that a provider's beforeXxx method was invoked
+ * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
+ * and the afterXxx loop was invoked
+ * @param verifyAfter verifies that a provider's afterXxx method was invoked
+ * @throws Exception if an error occurs while calling {@link #setUp()}
+ */
+ private void checkBeforeAfter_FalseFalse(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
+ BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
+ Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
+ Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
+
+ setUp();
+
+ // configure for the test
+ setBefore.accept(prov1, false);
+ setBefore.accept(prov2, false);
+
+ setAfter.accept(prov1, false);
+ setAfter.accept(prov2, false);
+
+ // run the action
+ action.run();
+
+ // verify that various methods were invoked
+ verifyBefore.accept(prov1);
+ verifyBefore.accept(prov2);
+
+ verifyMiddle.run();
+
+ verifyAfter.accept(prov1);
+ verifyAfter.accept(prov2);
+ }
+
+ /**
+ * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
+ * case where the first provider's afterXxx returns {@code true}, while the others
+ * return {@code false}.
+ *
+ * @param setBefore function to set the return value of a provider's beforeXxx method
+ * @param setAfter function to set the return value of a provider's afterXxx method
+ * @param action invokes the operation
+ * @param verifyBefore verifies that a provider's beforeXxx method was invoked
+ * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
+ * and the afterXxx loop was invoked
+ * @param verifyAfter verifies that a provider's afterXxx method was invoked
+ * @throws Exception if an error occurs while calling {@link #setUp()}
+ */
+ private void checkBeforeAfter_FalseTrue(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
+ BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
+ Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
+ Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
+
+ setUp();
+
+ // configure for the test
+ setBefore.accept(prov1, false);
+ setBefore.accept(prov2, false);
+
+ setAfter.accept(prov1, true);
+ setAfter.accept(prov2, false);
+
+ // run the action
+ action.run();
+
+ // verify that various methods were invoked
+ verifyBefore.accept(prov1);
+ verifyBefore.accept(prov2);
+
+ verifyMiddle.run();
+
+ verifyAfter.accept(prov1);
+ assertThrows(AssertionError.class, () -> verifyAfter.accept(prov2));
+ }
+
+ /**
+ * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
+ * case where the first provider's beforeXxx returns {@code true}, while the others
+ * return {@code false}.
+ *
+ * @param setBefore function to set the return value of a provider's beforeXxx method
+ * @param setAfter function to set the return value of a provider's afterXxx method
+ * @param action invokes the operation
+ * @param verifyBefore verifies that a provider's beforeXxx method was invoked
+ * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
+ * and the afterXxx loop was invoked
+ * @param verifyAfter verifies that a provider's afterXxx method was invoked
+ * @throws Exception if an error occurs while calling {@link #setUp()}
+ */
+ private void checkBeforeAfter_TrueFalse(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
+ BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
+ Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
+ Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
+
+ setUp();
+
+ // configure for the test
+ setBefore.accept(prov1, true);
+ setBefore.accept(prov2, false);
+
+ setAfter.accept(prov1, false);
+ setAfter.accept(prov2, false);
+
+ // run the action
+ action.run();
+
+ // verify that various methods were invoked
+ verifyBefore.accept(prov1);
+
+ // remaining methods should not have been invoked
+ assertThrows(AssertionError.class, () -> verifyBefore.accept(prov2));
+
+ assertThrows(AssertionError.class, () -> verifyMiddle.run());
+
+ assertThrows(AssertionError.class, () -> verifyAfter.accept(prov1));
+ assertThrows(AssertionError.class, () -> verifyAfter.accept(prov2));
+ }
+
+ /**
+ * Manager with overrides.
+ */
+ private class PolicyEngineManagerImpl extends PolicyEngineManager {
+
+ @Override
+ protected List<PolicyEngineFeatureAPI> getEngineProviders() {
+ return providers;
+ }
+
+ @Override
+ protected List<PolicyControllerFeatureAPI> getControllerProviders() {
+ return contProviders;
+ }
+
+ @Override
+ protected void globalInitContainer(String[] cliArgs) {
+ globalInitArgs = cliArgs;
+ }
+
+ @Override
+ protected TopicEndpoint getTopicEndpointManager() {
+ return endpoint;
+ }
+
+ @Override
+ protected HttpServletServerFactory getServletFactory() {
+ return serverFactory;
+ }
+
+ @Override
+ protected PolicyControllerFactory getControllerFactory() {
+ return controllerFactory;
+ }
+
+ @Override
+ protected void startPdpJmxListener() {
+ jmxStarted = true;
+ }
+
+ @Override
+ protected void stopPdpJmxListener() {
+ jmxStopped = true;
+ }
+
+ @Override
+ protected Thread makeShutdownThread() {
+ shutdownThread = new MyShutdown();
+ return shutdownThread;
+ }
+
+ @Override
+ protected EventProtocolCoder getProtocolCoder() {
+ return coder;
+ }
+
+ @Override
+ protected SystemPersistence getPersistenceManager() {
+ return persist;
+ }
+
+ @Override
+ protected PolicyEngine getPolicyEngine() {
+ return engine;
+ }
+
+ /**
+ * Shutdown thread with overrides.
+ */
+ private class MyShutdown extends ShutdownThread {
+
+ @Override
+ protected void doSleep(long sleepMs) throws InterruptedException {
+ threadSleepMs = sleepMs;
+
+ if (shouldInterrupt) {
+ throw new InterruptedException(EXPECTED);
+ }
+ }
+
+ @Override
+ protected void doExit(int code) {
+ threadExitCode = code;
+ }
+
+ @Override
+ public synchronized void start() {
+ threadStarted = true;
+ }
+
+ @Override
+ public void interrupt() {
+ threadInterrupted = true;
+ }
+ }
+ }
+}