4b8357a236cb1dfd29231a3d68aa79f7f5f8c0fa
[policy/drools-pdp.git] / policy-management / src / test / java / org / onap / policy / drools / system / PolicyEngineManagerTest.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018-2019 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.system;
22
23 import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
24 import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
25 import static org.assertj.core.api.Assertions.assertThatThrownBy;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertFalse;
28 import static org.junit.Assert.assertNotNull;
29 import static org.junit.Assert.assertNull;
30 import static org.junit.Assert.assertTrue;
31 import static org.mockito.Matchers.any;
32 import static org.mockito.Matchers.anyLong;
33 import static org.mockito.Mockito.doThrow;
34 import static org.mockito.Mockito.mock;
35 import static org.mockito.Mockito.never;
36 import static org.mockito.Mockito.times;
37 import static org.mockito.Mockito.verify;
38 import static org.mockito.Mockito.when;
39
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Properties;
44 import java.util.function.BiConsumer;
45 import java.util.function.Consumer;
46 import org.junit.Before;
47 import org.junit.Test;
48 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
49 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
50 import org.onap.policy.common.endpoints.event.comm.TopicSink;
51 import org.onap.policy.common.endpoints.event.comm.TopicSource;
52 import org.onap.policy.common.endpoints.http.server.HttpServletServer;
53 import org.onap.policy.common.endpoints.http.server.HttpServletServerFactory;
54 import org.onap.policy.common.utils.gson.GsonTestUtils;
55 import org.onap.policy.drools.controller.DroolsController;
56 import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
57 import org.onap.policy.drools.features.PolicyEngineFeatureAPI;
58 import org.onap.policy.drools.persistence.SystemPersistence;
59 import org.onap.policy.drools.properties.DroolsProperties;
60 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
61 import org.onap.policy.drools.protocol.configuration.ControllerConfiguration;
62 import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
63 import org.onap.policy.drools.protocol.configuration.PdpdConfiguration;
64
65 public class PolicyEngineManagerTest {
66
67     private static final String EXPECTED = "expected exception";
68
69     private static final String NOOP_STR = CommInfrastructure.NOOP.name();
70     private static final String MY_NAME = "my-name";
71     private static final String CONTROLLER1 = "controller-a";
72     private static final String CONTROLLER2 = "controller-b";
73     private static final String CONTROLLER3 = "controller-c";
74     private static final String CONTROLLER4 = "controller-d";
75     private static final String FEATURE1 = "feature-a";
76     private static final String FEATURE2 = "feature-b";
77     private static final String MY_TOPIC = "my-topic";
78     private static final String MESSAGE = "my-message";
79
80     private static final Object MY_EVENT = new Object();
81
82     private static final GsonTestUtils gson = new GsonMgmtTestBuilder().addTopicSourceMock().addTopicSinkMock()
83                     .addHttpServletServerMock().build();
84
85     private Properties properties;
86     private PolicyEngineFeatureAPI prov1;
87     private PolicyEngineFeatureAPI prov2;
88     private List<PolicyEngineFeatureAPI> providers;
89     private PolicyControllerFeatureAPI contProv1;
90     private PolicyControllerFeatureAPI contProv2;
91     private List<PolicyControllerFeatureAPI> contProviders;
92     private String[] globalInitArgs;
93     private TopicSource source1;
94     private TopicSource source2;
95     private List<TopicSource> sources;
96     private TopicSink sink1;
97     private TopicSink sink2;
98     private List<TopicSink> sinks;
99     private HttpServletServer server1;
100     private HttpServletServer server2;
101     private List<HttpServletServer> servers;
102     private HttpServletServerFactory serverFactory;
103     private TopicEndpoint endpoint;
104     private PolicyController controller;
105     private PolicyController controller2;
106     private PolicyController controller3;
107     private PolicyController controller4;
108     private List<PolicyController> controllers;
109     private PolicyControllerFactory controllerFactory;
110     private boolean jmxStarted;
111     private boolean jmxStopped;
112     private long threadSleepMs;
113     private int threadExitCode;
114     private boolean threadStarted;
115     private boolean threadInterrupted;
116     private Thread shutdownThread;
117     private boolean shouldInterrupt;
118     private EventProtocolCoder coder;
119     private SystemPersistence persist;
120     private PolicyEngine engine;
121     private DroolsConfiguration drools3;
122     private DroolsConfiguration drools4;
123     private ControllerConfiguration config3;
124     private ControllerConfiguration config4;
125     private PdpdConfiguration pdpConfig;
126     private String pdpConfigJson;
127     private PolicyEngineManager mgr;
128
129     /**
130      * Initializes the object to be tested.
131      *
132      * @throws Exception if an error occurs
133      */
134     @Before
135     public void setUp() throws Exception {
136
137         properties = new Properties();
138         prov1 = mock(PolicyEngineFeatureAPI.class);
139         prov2 = mock(PolicyEngineFeatureAPI.class);
140         providers = Arrays.asList(prov1, prov2);
141         contProv1 = mock(PolicyControllerFeatureAPI.class);
142         contProv2 = mock(PolicyControllerFeatureAPI.class);
143         contProviders = Arrays.asList(contProv1, contProv2);
144         globalInitArgs = null;
145         source1 = mock(TopicSource.class);
146         source2 = mock(TopicSource.class);
147         sources = Arrays.asList(source1, source2);
148         sink1 = mock(TopicSink.class);
149         sink2 = mock(TopicSink.class);
150         sinks = Arrays.asList(sink1, sink2);
151         server1 = mock(HttpServletServer.class);
152         server2 = mock(HttpServletServer.class);
153         servers = Arrays.asList(server1, server2);
154         serverFactory = mock(HttpServletServerFactory.class);
155         endpoint = mock(TopicEndpoint.class);
156         controller = mock(PolicyController.class);
157         controller2 = mock(PolicyController.class);
158         controller3 = mock(PolicyController.class);
159         controller4 = mock(PolicyController.class);
160         // do NOT include controller3 or controller4 in the list
161         controllers = Arrays.asList(controller, controller2);
162         controllerFactory = mock(PolicyControllerFactory.class);
163         jmxStarted = false;
164         jmxStopped = false;
165         threadSleepMs = -1;
166         threadExitCode = -1;
167         threadStarted = false;
168         threadInterrupted = false;
169         shutdownThread = null;
170         shouldInterrupt = false;
171         coder = mock(EventProtocolCoder.class);
172         persist = mock(SystemPersistence.class);
173         engine = mock(PolicyEngine.class);
174         drools3 = new DroolsConfiguration();
175         drools4 = new DroolsConfiguration();
176         config3 = new ControllerConfiguration();
177         config4 = new ControllerConfiguration();
178         pdpConfig = new PdpdConfiguration();
179
180         when(prov1.getName()).thenReturn(FEATURE1);
181         when(prov2.getName()).thenReturn(FEATURE2);
182
183         when(controllerFactory.build(any(), any())).thenReturn(controller);
184         when(controllerFactory.inventory()).thenReturn(controllers);
185         when(controllerFactory.get(CONTROLLER1)).thenReturn(controller);
186         when(controllerFactory.get(CONTROLLER2)).thenReturn(controller2);
187         // do NOT return controller3 or controller4
188
189         when(server1.getPort()).thenReturn(1001);
190         when(server1.waitedStart(anyLong())).thenReturn(true);
191         when(server1.stop()).thenReturn(true);
192
193         when(server2.getPort()).thenReturn(1002);
194         when(server2.waitedStart(anyLong())).thenReturn(true);
195         when(server2.stop()).thenReturn(true);
196
197         when(serverFactory.build(any())).thenReturn(servers);
198
199         when(source1.getTopic()).thenReturn("source1-topic");
200         when(source1.start()).thenReturn(true);
201         when(source1.stop()).thenReturn(true);
202
203         when(source2.getTopic()).thenReturn("source2-topic");
204         when(source2.start()).thenReturn(true);
205         when(source2.stop()).thenReturn(true);
206
207         when(sink1.getTopic()).thenReturn("sink1-topic");
208         when(sink1.start()).thenReturn(true);
209         when(sink1.stop()).thenReturn(true);
210         when(sink1.send(any())).thenReturn(true);
211         when(sink1.getTopicCommInfrastructure()).thenReturn(CommInfrastructure.NOOP);
212
213         when(sink2.getTopic()).thenReturn("sink2-topic");
214         when(sink2.start()).thenReturn(true);
215         when(sink2.stop()).thenReturn(true);
216
217         when(controller.getName()).thenReturn(CONTROLLER1);
218         when(controller.start()).thenReturn(true);
219         when(controller.stop()).thenReturn(true);
220         when(controller.lock()).thenReturn(true);
221         when(controller.unlock()).thenReturn(true);
222
223         when(controller2.getName()).thenReturn(CONTROLLER2);
224         when(controller2.start()).thenReturn(true);
225         when(controller2.stop()).thenReturn(true);
226         when(controller2.lock()).thenReturn(true);
227         when(controller2.unlock()).thenReturn(true);
228
229         when(controller3.getName()).thenReturn(CONTROLLER3);
230         when(controller3.start()).thenReturn(true);
231         when(controller3.stop()).thenReturn(true);
232         when(controller3.lock()).thenReturn(true);
233         when(controller3.unlock()).thenReturn(true);
234
235         when(controller4.getName()).thenReturn(CONTROLLER4);
236         when(controller4.start()).thenReturn(true);
237         when(controller4.stop()).thenReturn(true);
238         when(controller4.lock()).thenReturn(true);
239         when(controller4.unlock()).thenReturn(true);
240
241         when(endpoint.addTopicSources(any())).thenReturn(sources);
242         when(endpoint.addTopicSinks(any())).thenReturn(sinks);
243         when(endpoint.start()).thenReturn(true);
244         when(endpoint.stop()).thenReturn(true);
245         when(endpoint.lock()).thenReturn(true);
246         when(endpoint.unlock()).thenReturn(true);
247         when(endpoint.getTopicSink(CommInfrastructure.NOOP, MY_TOPIC)).thenReturn(sink1);
248         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(Arrays.asList(sink1));
249
250         when(coder.encode(any(), any())).thenReturn(MESSAGE);
251
252         when(persist.getControllerProperties(CONTROLLER3)).thenReturn(properties);
253         when(persist.getControllerProperties(CONTROLLER4)).thenReturn(properties);
254
255         when(engine.createPolicyController(CONTROLLER3, properties)).thenReturn(controller3);
256         when(engine.createPolicyController(CONTROLLER4, properties)).thenReturn(controller4);
257
258         config3.setName(CONTROLLER3);
259         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_CREATE);
260         config3.setDrools(drools3);
261
262         config4.setName(CONTROLLER4);
263         config4.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE);
264         config4.setDrools(drools4);
265
266         pdpConfig.getControllers().add(config3);
267         pdpConfig.getControllers().add(config4);
268         pdpConfig.setEntity(PdpdConfiguration.CONFIG_ENTITY_CONTROLLER);
269
270         pdpConfigJson = gson.gsonEncode(pdpConfig);
271
272         mgr = new PolicyEngineManagerImpl();
273     }
274
275     @Test
276     public void testSerialize() {
277         mgr.configure(properties);
278         gson.compareGson(mgr, PolicyEngineManagerTest.class);
279     }
280
281     @Test
282     public void testFactory() {
283         mgr = new PolicyEngineManager();
284
285         assertNotNull(mgr.getEngineProviders());
286         assertNotNull(mgr.getControllerProviders());
287         assertNotNull(mgr.getTopicEndpointManager());
288         assertNotNull(mgr.getServletFactory());
289         assertNotNull(mgr.getControllerFactory());
290         assertNotNull(mgr.makeShutdownThread());
291         assertNotNull(mgr.getProtocolCoder());
292         assertNotNull(mgr.getPersistenceManager());
293         assertNotNull(mgr.getPolicyEngine());
294     }
295
296     @Test
297     public void testBoot() throws Exception {
298         String[] args = {"boot-a", "boot-b"};
299
300         // arrange for first provider to throw exceptions
301         when(prov1.beforeBoot(mgr, args)).thenThrow(new RuntimeException(EXPECTED));
302         when(prov1.afterBoot(mgr)).thenThrow(new RuntimeException(EXPECTED));
303
304         mgr.boot(args);
305
306         verify(prov1).beforeBoot(mgr, args);
307         verify(prov2).beforeBoot(mgr, args);
308
309         assertTrue(globalInitArgs == args);
310
311         verify(prov1).afterBoot(mgr);
312         verify(prov2).afterBoot(mgr);
313
314         // global init throws exception - still calls afterBoot
315         setUp();
316         mgr = new PolicyEngineManagerImpl() {
317             @Override
318             protected void globalInitContainer(String[] cliArgs) {
319                 throw new RuntimeException(EXPECTED);
320             }
321         };
322         mgr.boot(args);
323         verify(prov2).afterBoot(mgr);
324
325         // other tests
326         checkBeforeAfter(
327             (prov, flag) -> when(prov.beforeBoot(mgr, args)).thenReturn(flag),
328             (prov, flag) -> when(prov.afterBoot(mgr)).thenReturn(flag),
329             () -> mgr.boot(args),
330             prov -> verify(prov).beforeBoot(mgr, args),
331             () -> assertTrue(globalInitArgs == args),
332             prov -> verify(prov).afterBoot(mgr));
333     }
334
335     @Test
336     public void testSetEnvironment_testGetEnvironment_testGetEnvironmentProperty_setEnvironmentProperty() {
337         Properties props1 = new Properties();
338         props1.put("prop1-a", "value1-a");
339         props1.put("prop1-b", "value1-b");
340
341         mgr.setEnvironment(props1);
342
343         Properties env = mgr.getEnvironment();
344         assertEquals(props1, env);
345
346         // add more properties
347         Properties props2 = new Properties();
348         String propKey = "prop2-a";
349         props2.put(propKey, "value2-a");
350         props2.put("prop2-b", "value2-b");
351
352         mgr.setEnvironment(props2);
353
354         assertTrue(mgr.getEnvironment() == env);
355
356         // new env should have a union of the properties
357         props1.putAll(props2);
358         assertEquals(props1, env);
359
360         assertEquals("value2-a", mgr.getEnvironmentProperty(propKey));
361
362         String newValue = "new-value";
363         mgr.setEnvironmentProperty(propKey, newValue);
364         assertEquals(newValue, mgr.getEnvironmentProperty(propKey));
365
366         props1.setProperty(propKey, newValue);
367         assertEquals(props1, env);
368
369         assertNotNull(mgr.getEnvironmentProperty("PATH"));
370         assertNull(mgr.getEnvironmentProperty("unknown-env-property"));
371     }
372
373     @Test
374     public void testDefaultTelemetryConfig() {
375         Properties config = mgr.defaultTelemetryConfig();
376         assertNotNull(config);
377         assertFalse(config.isEmpty());
378     }
379
380     @Test
381     public void testConfigureProperties() throws Exception {
382         // arrange for first provider to throw exceptions
383         when(prov1.beforeConfigure(mgr, properties)).thenThrow(new RuntimeException(EXPECTED));
384         when(prov1.afterConfigure(mgr)).thenThrow(new RuntimeException(EXPECTED));
385
386         mgr.configure(properties);
387
388         verify(prov1).beforeConfigure(mgr, properties);
389         verify(prov2).beforeConfigure(mgr, properties);
390
391         assertTrue(mgr.getProperties() == properties);
392
393         assertEquals(sources, mgr.getSources());
394         assertEquals(sinks, mgr.getSinks());
395         assertEquals(servers, mgr.getHttpServers());
396
397         verify(source1).register(mgr);
398         verify(source2).register(mgr);
399
400         verify(prov1).afterConfigure(mgr);
401         verify(prov2).afterConfigure(mgr);
402
403         // middle stuff throws exception - still calls afterXxx
404         setUp();
405         when(endpoint.addTopicSources(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
406         when(endpoint.addTopicSinks(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
407         when(serverFactory.build(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
408         mgr.configure(properties);
409         verify(prov2).afterConfigure(mgr);
410
411         // null properties - nothing should be invoked
412         setUp();
413         Properties nullProps = null;
414         assertThatIllegalArgumentException().isThrownBy(() -> mgr.configure(nullProps));
415         verify(prov1, never()).beforeConfigure(mgr, properties);
416         verify(prov1, never()).afterConfigure(mgr);
417
418         // other tests
419         checkBeforeAfter(
420             (prov, flag) -> when(prov.beforeConfigure(mgr, properties)).thenReturn(flag),
421             (prov, flag) -> when(prov.afterConfigure(mgr)).thenReturn(flag),
422             () -> mgr.configure(properties),
423             prov -> verify(prov).beforeConfigure(mgr, properties),
424             () -> assertTrue(mgr.getProperties() == properties),
425             prov -> verify(prov).afterConfigure(mgr));
426     }
427
428     @Test
429     public void testConfigurePdpdConfiguration() throws Exception {
430         mgr.configure(properties);
431         assertTrue(mgr.configure(pdpConfig));
432
433         verify(controllerFactory).patch(controller3, drools3);
434         verify(controllerFactory).patch(controller4, drools4);
435
436         // invalid params
437         PdpdConfiguration nullConfig = null;
438         assertThatIllegalArgumentException().isThrownBy(() -> mgr.configure(nullConfig));
439
440         pdpConfig.setEntity("unknown-entity");
441         assertThatIllegalArgumentException().isThrownBy(() -> mgr.configure(pdpConfig));
442
443         // source list of size 1
444         setUp();
445         when(endpoint.addTopicSources(any())).thenReturn(Arrays.asList(source1));
446         mgr.configure(properties);
447         assertTrue(mgr.configure(pdpConfig));
448
449         verify(controllerFactory).patch(controller3, drools3);
450         verify(controllerFactory).patch(controller4, drools4);
451     }
452
453     @Test
454     public void testCreatePolicyController() throws Exception {
455         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
456
457         verify(contProv1).beforeCreate(MY_NAME, properties);
458         verify(contProv2).beforeCreate(MY_NAME, properties);
459         verify(controller, never()).lock();
460         verify(contProv1).afterCreate(controller);
461         verify(contProv2).afterCreate(controller);
462
463         // first provider throws exceptions - same result
464         setUp();
465         when(contProv1.beforeCreate(MY_NAME, properties)).thenThrow(new RuntimeException(EXPECTED));
466         when(contProv1.afterCreate(controller)).thenThrow(new RuntimeException(EXPECTED));
467         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
468
469         verify(contProv1).beforeCreate(MY_NAME, properties);
470         verify(contProv2).beforeCreate(MY_NAME, properties);
471         verify(controller, never()).lock();
472         verify(contProv1).afterCreate(controller);
473         verify(contProv2).afterCreate(controller);
474
475         // locked - same result, but engine locked
476         setUp();
477         mgr.lock();
478         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
479         verify(contProv1).beforeCreate(MY_NAME, properties);
480         verify(controller, times(2)).lock();
481         verify(contProv2).afterCreate(controller);
482
483         // empty name in properties - same result
484         setUp();
485         properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, "");
486         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
487         verify(contProv1).beforeCreate(MY_NAME, properties);
488
489         // matching name in properties - same result
490         setUp();
491         properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, MY_NAME);
492         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
493         verify(contProv1).beforeCreate(MY_NAME, properties);
494
495         // mismatching name in properties - nothing should happen besides exception
496         setUp();
497         properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, "mistmatched-name");
498         assertThatIllegalStateException().isThrownBy(() -> mgr.createPolicyController(MY_NAME, properties));
499         verify(contProv1, never()).beforeCreate(MY_NAME, properties);
500
501         // first provider generates controller - stops after first provider
502         setUp();
503         when(contProv1.beforeCreate(MY_NAME, properties)).thenReturn(controller2);
504         assertEquals(controller2, mgr.createPolicyController(MY_NAME, properties));
505         verify(contProv1).beforeCreate(MY_NAME, properties);
506         verify(contProv2, never()).beforeCreate(MY_NAME, properties);
507         verify(controller, never()).lock();
508         verify(contProv1, never()).afterCreate(controller);
509         verify(contProv2, never()).afterCreate(controller);
510
511         // first provider returns true - stops after first provider afterXxx
512         setUp();
513         when(contProv1.afterCreate(controller)).thenReturn(true);
514         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
515         verify(contProv1).beforeCreate(MY_NAME, properties);
516         verify(contProv2).beforeCreate(MY_NAME, properties);
517         verify(contProv1).afterCreate(controller);
518         verify(contProv2, never()).afterCreate(controller);
519     }
520
521     @Test
522     public void testUpdatePolicyControllers() throws Exception {
523         assertEquals(Arrays.asList(controller3, controller4), mgr.updatePolicyControllers(pdpConfig.getControllers()));
524
525         // controller3 was CREATE
526         verify(controllerFactory).patch(controller3, drools3);
527         verify(controller3, never()).lock();
528         verify(controller3, never()).unlock();
529
530         // controller4 was UPDATE
531         verify(controllerFactory).patch(controller4, drools4);
532         verify(controller4, never()).lock();
533         verify(controller4).unlock();
534
535         // invalid args
536         assertTrue(mgr.updatePolicyControllers(null).isEmpty());
537         assertTrue(mgr.updatePolicyControllers(Collections.emptyList()).isEmpty());
538
539         // force exception in the first controller with invalid operation
540         setUp();
541         config3.setOperation("unknown-operation");
542         assertEquals(Arrays.asList(controller4), mgr.updatePolicyControllers(pdpConfig.getControllers()));
543
544         // controller3 should NOT have been done
545         verify(controllerFactory, never()).patch(controller3, drools3);
546
547         // controller4 should still be done
548         verify(controllerFactory).patch(controller4, drools4);
549         verify(controller4, never()).lock();
550         verify(controller4).unlock();
551     }
552
553     @Test
554     public void testUpdatePolicyController() throws Exception {
555         assertEquals(controller3, mgr.updatePolicyController(config3));
556         verify(engine).createPolicyController(CONTROLLER3, properties);
557
558         // invalid parameters
559         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(null));
560
561         // invalid name
562         setUp();
563         config3.setName(null);
564         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
565
566         config3.setName("");
567         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
568
569         // invalid operation
570         setUp();
571         config3.setOperation(null);
572         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
573
574         config3.setOperation("");
575         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
576
577         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK);
578         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
579
580         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK);
581         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
582
583         // exception from get() - should create controller
584         setUp();
585         when(controllerFactory.get(CONTROLLER3)).thenThrow(new IllegalArgumentException(EXPECTED));
586         assertEquals(controller3, mgr.updatePolicyController(config3));
587         verify(engine).createPolicyController(CONTROLLER3, properties);
588
589         // null properties
590         setUp();
591         when(persist.getControllerProperties(CONTROLLER3)).thenReturn(null);
592         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
593
594         // throw linkage error
595         setUp();
596         when(persist.getControllerProperties(CONTROLLER3)).thenThrow(new LinkageError(EXPECTED));
597         assertThatIllegalStateException().isThrownBy(() -> mgr.updatePolicyController(config3));
598
599         /*
600          * For remaining tests, the factory will return the controller instead of creating
601          * one.
602          */
603         setUp();
604         when(controllerFactory.get(CONTROLLER3)).thenReturn(controller3);
605
606         assertEquals(controller3, mgr.updatePolicyController(config3));
607
608         // should NOT have created a new controller
609         verify(engine, never()).createPolicyController(any(), any());
610
611         int countPatch = 0;
612         int countLock = 0;
613         int countUnlock = 0;
614
615         // check different operations
616
617         // CREATE only invokes patch() (note: mgr.update() has already been called)
618         verify(controllerFactory, times(++countPatch)).patch(controller3, drools3);
619         verify(controller3, times(countLock)).lock();
620         verify(controller3, times(countUnlock)).unlock();
621
622         // UPDATE invokes unlock() and patch()
623         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE);
624         assertEquals(controller3, mgr.updatePolicyController(config3));
625         verify(controllerFactory, times(++countPatch)).patch(controller3, drools3);
626         verify(controller3, times(countLock)).lock();
627         verify(controller3, times(++countUnlock)).unlock();
628
629         // LOCK invokes lock()
630         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK);
631         assertEquals(controller3, mgr.updatePolicyController(config3));
632         verify(controllerFactory, times(countPatch)).patch(controller3, drools3);
633         verify(controller3, times(++countLock)).lock();
634         verify(controller3, times(countUnlock)).unlock();
635
636         // UNLOCK invokes unlock()
637         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK);
638         assertEquals(controller3, mgr.updatePolicyController(config3));
639         verify(controllerFactory, times(countPatch)).patch(controller3, drools3);
640         verify(controller3, times(countLock)).lock();
641         verify(controller3, times(++countUnlock)).unlock();
642
643         // invalid operation
644         config3.setOperation("invalid-operation");
645         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
646     }
647
648     @Test
649     public void testStart() throws Throwable {
650         // normal success case
651         testStart(true, () -> {
652             // arrange for first provider, server, source, and sink to throw exceptions
653             when(prov1.beforeStart(mgr)).thenThrow(new RuntimeException(EXPECTED));
654             when(prov1.afterStart(mgr)).thenThrow(new RuntimeException(EXPECTED));
655             when(server1.waitedStart(anyLong())).thenThrow(new RuntimeException(EXPECTED));
656             when(source1.start()).thenThrow(new RuntimeException(EXPECTED));
657             when(sink1.start()).thenThrow(new RuntimeException(EXPECTED));
658         });
659
660         // servlet wait fails - still does everything
661         testStart(false, () -> when(server1.waitedStart(anyLong())).thenReturn(false));
662
663         // topic source fails to start - still does everything
664         testStart(false, () -> when(source1.start()).thenReturn(false));
665
666         // topic sink fails to start - still does everything
667         testStart(false, () -> when(sink1.start()).thenReturn(false));
668
669         // controller fails to start - still does everything
670         testStart(false, () -> when(controller.start()).thenReturn(false));
671
672         // controller throws an exception - still does everything
673         testStart(false, () -> when(controller.start()).thenThrow(new RuntimeException(EXPECTED)));
674
675         // endpoint manager fails to start - still does everything
676         testStart(false, () -> when(endpoint.start()).thenReturn(false));
677
678         // endpoint manager throws an exception - still does everything AND succeeds
679         testStart(true, () -> when(endpoint.start()).thenThrow(new IllegalStateException(EXPECTED)));
680
681         // locked - nothing other than beforeXxx methods should be invoked
682         setUp();
683         mgr.configure(properties);
684         mgr.lock();
685         assertThatIllegalStateException().isThrownBy(() -> mgr.start());
686         verify(prov2).beforeStart(mgr);
687         verify(server2, never()).waitedStart(anyLong());
688         verify(source2, never()).start();
689         verify(sink1, never()).start();
690         verify(controller, never()).start();
691         verify(endpoint, never()).start();
692         assertFalse(jmxStarted);
693         verify(prov1, never()).afterStart(mgr);
694
695         // other tests
696         checkBeforeAfter(
697             (prov, flag) -> when(prov.beforeStart(mgr)).thenReturn(flag),
698             (prov, flag) -> when(prov.afterStart(mgr)).thenReturn(flag),
699             () -> {
700                 mgr.configure(properties);
701                 assertTrue(mgr.start());
702             },
703             prov -> verify(prov).beforeStart(mgr),
704             () -> assertTrue(jmxStarted),
705             prov -> verify(prov).afterStart(mgr));
706     }
707
708     /**
709      * Tests the start() method, after setting some option.
710      *
711      * @param expectedResult what start() is expected to return
712      * @param setOption function that sets an option
713      * @throws Throwable if an error occurs during setup
714      */
715     private void testStart(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
716         setUp();
717         setOption.run();
718
719         mgr.configure(properties);
720         assertEquals(expectedResult, mgr.start());
721
722         verify(prov1).beforeStart(mgr);
723         verify(prov2).beforeStart(mgr);
724
725         verify(server1).waitedStart(anyLong());
726         verify(server2).waitedStart(anyLong());
727
728         verify(source1).start();
729         verify(source2).start();
730
731         verify(sink1).start();
732         verify(sink2).start();
733
734         verify(controller).start();
735         verify(controller2).start();
736
737         verify(endpoint).start();
738
739         assertTrue(jmxStarted);
740
741         verify(prov1).afterStart(mgr);
742         verify(prov2).afterStart(mgr);
743     }
744
745     @Test
746     public void testStop() throws Throwable {
747         // normal success case
748         testStop(true, () -> {
749             // arrange for first provider, server, source, and sink to throw exceptions
750             when(prov1.beforeStop(mgr)).thenThrow(new RuntimeException(EXPECTED));
751             when(prov1.afterStop(mgr)).thenThrow(new RuntimeException(EXPECTED));
752             when(server1.stop()).thenThrow(new RuntimeException(EXPECTED));
753             when(source1.stop()).thenThrow(new RuntimeException(EXPECTED));
754             when(sink1.stop()).thenThrow(new RuntimeException(EXPECTED));
755         });
756
757         // not alive - shouldn't run anything besides beforeStop()
758         setUp();
759         mgr.configure(properties);
760         assertTrue(mgr.stop());
761         verify(prov1).beforeStop(mgr);
762         verify(prov2).beforeStop(mgr);
763         verify(controller, never()).stop();
764         verify(source1, never()).stop();
765         verify(sink1, never()).stop();
766         verify(endpoint, never()).stop();
767         verify(server1, never()).stop();
768         verify(prov1, never()).afterStop(mgr);
769         verify(prov2, never()).afterStop(mgr);
770
771         // controller fails to stop - still does everything
772         testStop(false, () -> when(controller.stop()).thenReturn(false));
773
774         // controller throws an exception - still does everything
775         testStop(false, () -> when(controller.stop()).thenThrow(new RuntimeException(EXPECTED)));
776
777         // topic source fails to stop - still does everything
778         testStop(false, () -> when(source1.stop()).thenReturn(false));
779
780         // topic sink fails to stop - still does everything
781         testStop(false, () -> when(sink1.stop()).thenReturn(false));
782
783         // endpoint manager fails to stop - still does everything
784         testStop(false, () -> when(endpoint.stop()).thenReturn(false));
785
786         // servlet fails to stop - still does everything
787         testStop(false, () -> when(server1.stop()).thenReturn(false));
788
789         // other tests
790         checkBeforeAfter(
791             (prov, flag) -> when(prov.beforeStop(mgr)).thenReturn(flag),
792             (prov, flag) -> when(prov.afterStop(mgr)).thenReturn(flag),
793             () -> {
794                 mgr.configure(properties);
795                 mgr.start();
796                 assertTrue(mgr.stop());
797             },
798             prov -> verify(prov).beforeStop(mgr),
799             () -> verify(endpoint).stop(),
800             prov -> verify(prov).afterStop(mgr));
801     }
802
803     /**
804      * Tests the stop() method, after setting some option.
805      *
806      * @param expectedResult what stop() is expected to return
807      * @param setOption function that sets an option
808      * @throws Throwable if an error occurs during setup
809      */
810     private void testStop(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
811         setUp();
812         setOption.run();
813
814         mgr.configure(properties);
815         mgr.start();
816         assertEquals(expectedResult, mgr.stop());
817
818         verify(prov1).beforeStop(mgr);
819         verify(prov2).beforeStop(mgr);
820
821         verify(controller).stop();
822         verify(controller2).stop();
823
824         verify(source1).stop();
825         verify(source2).stop();
826
827         verify(sink1).stop();
828         verify(sink2).stop();
829
830         verify(endpoint).stop();
831
832         verify(server1).stop();
833         verify(server2).stop();
834
835         verify(prov1).afterStop(mgr);
836         verify(prov2).afterStop(mgr);
837     }
838
839     @Test
840     public void testShutdown() throws Throwable {
841         // normal success case
842         testShutdown(() -> {
843             // arrange for first provider, source, and sink to throw exceptions
844             when(prov1.beforeShutdown(mgr)).thenThrow(new RuntimeException(EXPECTED));
845             when(prov1.afterShutdown(mgr)).thenThrow(new RuntimeException(EXPECTED));
846             doThrow(new RuntimeException(EXPECTED)).when(source1).shutdown();
847             doThrow(new RuntimeException(EXPECTED)).when(sink1).shutdown();
848         });
849
850         assertNotNull(shutdownThread);
851         assertTrue(threadStarted);
852         assertTrue(threadInterrupted);
853
854         // other tests
855         checkBeforeAfter(
856             (prov, flag) -> when(prov.beforeShutdown(mgr)).thenReturn(flag),
857             (prov, flag) -> when(prov.afterShutdown(mgr)).thenReturn(flag),
858             () -> {
859                 mgr.configure(properties);
860                 mgr.start();
861                 mgr.shutdown();
862             },
863             prov -> verify(prov).beforeShutdown(mgr),
864             () -> assertTrue(jmxStopped),
865             prov -> verify(prov).afterShutdown(mgr));
866     }
867
868     /**
869      * Tests the shutdown() method, after setting some option.
870      *
871      * @param setOption function that sets an option
872      * @throws Throwable if an error occurs during setup
873      */
874     private void testShutdown(RunnableWithEx setOption) throws Throwable {
875         setUp();
876         setOption.run();
877
878         mgr.configure(properties);
879         mgr.start();
880         mgr.shutdown();
881
882         verify(prov1).beforeShutdown(mgr);
883         verify(prov2).beforeShutdown(mgr);
884
885         verify(source1).shutdown();
886         verify(source2).shutdown();
887
888         verify(sink1).shutdown();
889         verify(sink2).shutdown();
890
891         verify(controllerFactory).shutdown();
892         verify(endpoint).shutdown();
893         verify(serverFactory).destroy();
894
895         assertTrue(jmxStopped);
896
897         verify(prov1).afterShutdown(mgr);
898         verify(prov2).afterShutdown(mgr);
899     }
900
901     @Test
902     public void testShutdownThreadRun() throws Throwable {
903         // arrange for first server to throw exceptions
904         testShutdownThreadRun(() -> doThrow(new RuntimeException(EXPECTED)).when(server1).shutdown());
905
906         // sleep throws an exception
907         testShutdownThreadRun(() -> shouldInterrupt = true);
908     }
909
910     /**
911      * Tests the ShutdownThread.run() method, after setting some option.
912      *
913      * @param setOption function that sets an option
914      * @throws Throwable if an error occurs during setup
915      */
916     private void testShutdownThreadRun(RunnableWithEx setOption) throws Throwable {
917         setUp();
918         setOption.run();
919
920         mgr.configure(properties);
921         mgr.start();
922         mgr.shutdown();
923
924         assertNotNull(shutdownThread);
925
926         shutdownThread.run();
927
928         assertTrue(threadSleepMs >= 0);
929         assertEquals(0, threadExitCode);
930
931         verify(server1).shutdown();
932         verify(server2).shutdown();
933     }
934
935     @Test
936     public void testIsAlive() {
937         mgr.configure(properties);
938         assertFalse(mgr.isAlive());
939
940         mgr.start();
941         assertTrue(mgr.isAlive());
942
943         mgr.stop();
944         assertFalse(mgr.isAlive());
945     }
946
947     @Test
948     public void testLock() throws Throwable {
949         // normal success case
950         testLock(true, () -> {
951             // arrange for first provider to throw exceptions
952             when(prov1.beforeLock(mgr)).thenThrow(new RuntimeException(EXPECTED));
953             when(prov1.afterLock(mgr)).thenThrow(new RuntimeException(EXPECTED));
954         });
955
956         // already locked - shouldn't run anything besides beforeLock()
957         setUp();
958         mgr.configure(properties);
959         mgr.lock();
960         assertTrue(mgr.lock());
961         verify(prov1, times(2)).beforeLock(mgr);
962         verify(prov2, times(2)).beforeLock(mgr);
963         verify(controller).lock();
964         verify(controller2).lock();
965         verify(endpoint).lock();
966         verify(prov1).afterLock(mgr);
967         verify(prov2).afterLock(mgr);
968
969         // controller fails to lock - still does everything
970         testLock(false, () -> when(controller.lock()).thenReturn(false));
971
972         // controller throws an exception - still does everything
973         testLock(false, () -> when(controller.lock()).thenThrow(new RuntimeException(EXPECTED)));
974
975         // endpoint manager fails to lock - still does everything
976         testLock(false, () -> when(endpoint.lock()).thenReturn(false));
977
978         // other tests
979         checkBeforeAfter(
980             (prov, flag) -> when(prov.beforeLock(mgr)).thenReturn(flag),
981             (prov, flag) -> when(prov.afterLock(mgr)).thenReturn(flag),
982             () -> {
983                 mgr.configure(properties);
984                 mgr.start();
985                 assertTrue(mgr.lock());
986             },
987             prov -> verify(prov).beforeLock(mgr),
988             () -> verify(endpoint).lock(),
989             prov -> verify(prov).afterLock(mgr));
990     }
991
992     /**
993      * Tests the lock() method, after setting some option.
994      *
995      * @param expectedResult what lock() is expected to return
996      * @param setOption function that sets an option
997      * @throws Throwable if an error occurs during setup
998      */
999     private void testLock(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
1000         setUp();
1001         setOption.run();
1002
1003         mgr.configure(properties);
1004         assertEquals(expectedResult, mgr.lock());
1005
1006         verify(prov1).beforeLock(mgr);
1007         verify(prov2).beforeLock(mgr);
1008
1009         verify(controller).lock();
1010         verify(controller2).lock();
1011
1012         verify(endpoint).lock();
1013
1014         verify(prov1).afterLock(mgr);
1015         verify(prov2).afterLock(mgr);
1016     }
1017
1018     @Test
1019     public void testUnlock() throws Throwable {
1020         // normal success case
1021         testUnlock(true, () -> {
1022             // arrange for first provider to throw exceptions
1023             when(prov1.beforeUnlock(mgr)).thenThrow(new RuntimeException(EXPECTED));
1024             when(prov1.afterUnlock(mgr)).thenThrow(new RuntimeException(EXPECTED));
1025         });
1026
1027         // not locked - shouldn't run anything besides beforeUnlock()
1028         setUp();
1029         mgr.configure(properties);
1030         assertTrue(mgr.unlock());
1031         verify(prov1).beforeUnlock(mgr);
1032         verify(prov2).beforeUnlock(mgr);
1033         verify(controller, never()).unlock();
1034         verify(controller2, never()).unlock();
1035         verify(endpoint, never()).unlock();
1036         verify(prov1, never()).afterUnlock(mgr);
1037         verify(prov2, never()).afterUnlock(mgr);
1038
1039         // controller fails to unlock - still does everything
1040         testUnlock(false, () -> when(controller.unlock()).thenReturn(false));
1041
1042         // controller throws an exception - still does everything
1043         testUnlock(false, () -> when(controller.unlock()).thenThrow(new RuntimeException(EXPECTED)));
1044
1045         // endpoint manager fails to unlock - still does everything
1046         testUnlock(false, () -> when(endpoint.unlock()).thenReturn(false));
1047
1048         // other tests
1049         checkBeforeAfter(
1050             (prov, flag) -> when(prov.beforeUnlock(mgr)).thenReturn(flag),
1051             (prov, flag) -> when(prov.afterUnlock(mgr)).thenReturn(flag),
1052             () -> {
1053                 mgr.configure(properties);
1054                 mgr.lock();
1055                 assertTrue(mgr.unlock());
1056             },
1057             prov -> verify(prov).beforeUnlock(mgr),
1058             () -> verify(endpoint).unlock(),
1059             prov -> verify(prov).afterUnlock(mgr));
1060     }
1061
1062     /**
1063      * Tests the unlock() method, after setting some option.
1064      *
1065      * @param expectedResult what unlock() is expected to return
1066      * @param setOption function that sets an option
1067      * @throws Throwable if an error occurs during setup
1068      */
1069     private void testUnlock(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
1070         setUp();
1071         setOption.run();
1072
1073         mgr.configure(properties);
1074         mgr.lock();
1075         assertEquals(expectedResult, mgr.unlock());
1076
1077         verify(prov1).beforeUnlock(mgr);
1078         verify(prov2).beforeUnlock(mgr);
1079
1080         verify(controller).unlock();
1081         verify(controller2).unlock();
1082
1083         verify(endpoint).unlock();
1084
1085         verify(prov1).afterUnlock(mgr);
1086         verify(prov2).afterUnlock(mgr);
1087     }
1088
1089     @Test
1090     public void testIsLocked() {
1091         mgr.configure(properties);
1092         assertFalse(mgr.isLocked());
1093
1094         mgr.lock();
1095         assertTrue(mgr.isLocked());
1096
1097         mgr.unlock();
1098         assertFalse(mgr.isLocked());
1099     }
1100
1101     @Test
1102     public void testRemovePolicyControllerString() {
1103         mgr.removePolicyController(MY_NAME);
1104
1105         verify(controllerFactory).destroy(MY_NAME);
1106     }
1107
1108     @Test
1109     public void testRemovePolicyControllerPolicyController() {
1110         mgr.removePolicyController(controller);
1111
1112         verify(controllerFactory).destroy(controller);
1113     }
1114
1115     @Test
1116     public void testGetPolicyControllers() {
1117         assertEquals(controllers, mgr.getPolicyControllers());
1118     }
1119
1120     @Test
1121     public void testGetPolicyControllerIds() {
1122         assertEquals(Arrays.asList(CONTROLLER1, CONTROLLER2), mgr.getPolicyControllerIds());
1123     }
1124
1125     @Test
1126     public void testGetProperties() {
1127         properties.setProperty("prop-x", "value-x");
1128         properties.setProperty("prop-y", "value-y");
1129
1130         mgr.configure(properties);
1131         assertEquals(properties, mgr.getProperties());
1132     }
1133
1134     @Test
1135     public void testGetSources() {
1136         mgr.configure(properties);
1137         assertEquals(sources, mgr.getSources());
1138     }
1139
1140     @Test
1141     public void testGetSinks() {
1142         mgr.configure(properties);
1143         assertEquals(sinks, mgr.getSinks());
1144     }
1145
1146     @Test
1147     public void testGetHttpServers() {
1148         mgr.configure(properties);
1149         assertEquals(servers, mgr.getHttpServers());
1150     }
1151
1152     @Test
1153     public void testGetFeatures() {
1154         assertEquals(Arrays.asList(FEATURE1, FEATURE2), mgr.getFeatures());
1155     }
1156
1157     @Test
1158     public void testGetFeatureProviders() {
1159         assertEquals(providers, mgr.getFeatureProviders());
1160     }
1161
1162     @Test
1163     public void testGetFeatureProvider() {
1164         assertEquals(prov1, mgr.getFeatureProvider(FEATURE1));
1165         assertEquals(prov2, mgr.getFeatureProvider(FEATURE2));
1166
1167         // null feature
1168         assertThatIllegalArgumentException().isThrownBy(() -> mgr.getFeatureProvider(null));
1169
1170         // empty feature
1171         assertThatIllegalArgumentException().isThrownBy(() -> mgr.getFeatureProvider(""));
1172
1173         // unknown feature
1174         assertThatIllegalArgumentException().isThrownBy(() -> mgr.getFeatureProvider("unknown-feature"));
1175     }
1176
1177     @Test
1178     public void testOnTopicEvent() {
1179         mgr.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, pdpConfigJson);
1180
1181         verify(controllerFactory).patch(controller3, drools3);
1182         verify(controllerFactory).patch(controller4, drools4);
1183
1184         // null json - no additional patches
1185         mgr.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, null);
1186
1187         verify(controllerFactory).patch(controller3, drools3);
1188         verify(controllerFactory).patch(controller4, drools4);
1189     }
1190
1191     @Test
1192     public void testDeliverStringObject() throws Exception {
1193         mgr.configure(properties);
1194         mgr.start();
1195
1196         assertTrue(mgr.deliver(MY_TOPIC, MY_EVENT));
1197
1198         verify(sink1).send(MESSAGE);
1199
1200         // invalid parameters
1201         String nullStr = null;
1202         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(nullStr, MY_EVENT));
1203         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver("", MY_EVENT));
1204
1205         Object nullObj = null;
1206         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(MY_TOPIC, nullObj));
1207
1208         // locked
1209         mgr.lock();
1210         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1211         mgr.unlock();
1212
1213         // not running
1214         mgr.stop();
1215         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1216
1217         // issues with topic
1218         setUp();
1219         mgr.configure(properties);
1220         mgr.start();
1221
1222         // null sinks
1223         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(null);
1224         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1225
1226         // empty sinks
1227         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(Collections.emptyList());
1228         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1229
1230         // too many sinks
1231         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(sinks);
1232         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1233     }
1234
1235     @Test
1236     public void testDeliverStringStringObject() {
1237         mgr.configure(properties);
1238         mgr.start();
1239
1240         assertTrue(mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
1241
1242         verify(sink1).send(MESSAGE);
1243
1244         // invalid parameters
1245         String nullStr = null;
1246         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(nullStr, MY_TOPIC, MY_EVENT));
1247         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver("", MY_TOPIC, MY_EVENT));
1248         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver("unknown-bus-type", MY_TOPIC, MY_EVENT));
1249
1250         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(NOOP_STR, nullStr, MY_EVENT));
1251         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(NOOP_STR, "", MY_EVENT));
1252
1253         Object nullObj = null;
1254         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(NOOP_STR, MY_TOPIC, nullObj));
1255
1256         // locked
1257         mgr.lock();
1258         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
1259         mgr.unlock();
1260
1261         // not running
1262         mgr.stop();
1263         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
1264     }
1265
1266     @Test
1267     public void testDeliverCommInfrastructureStringObject() throws Exception {
1268         mgr.configure(properties);
1269         mgr.start();
1270
1271         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1272
1273         verify(controller, never()).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
1274
1275         verify(coder).encode(MY_TOPIC, MY_EVENT);
1276         verify(sink1).send(MESSAGE);
1277
1278         // invalid parameters
1279         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, null, MY_EVENT));
1280         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, "", MY_EVENT));
1281
1282         Object nullObj = null;
1283         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, nullObj));
1284
1285         // locked
1286         mgr.lock();
1287         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1288         mgr.unlock();
1289
1290         // not started
1291         mgr.stop();
1292         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1293
1294         // send() throws an exception
1295         setUp();
1296         mgr.configure(properties);
1297         mgr.start();
1298         when(sink1.send(any())).thenThrow(new ArithmeticException(EXPECTED));
1299         assertThatThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT))
1300                         .isInstanceOf(ArithmeticException.class);
1301
1302         /*
1303          * For remaining tests, have the controller handle delivery.
1304          */
1305         setUp();
1306         mgr.configure(properties);
1307         mgr.start();
1308         DroolsController drools = mock(DroolsController.class);
1309         when(coder.getDroolsController(MY_TOPIC, MY_EVENT)).thenReturn(drools);
1310         when(controllerFactory.get(drools)).thenReturn(controller);
1311         when(controller.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT)).thenReturn(true);
1312
1313         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1314
1315         verify(controller).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
1316
1317         verify(coder, never()).encode(MY_TOPIC, MY_EVENT);
1318         verify(sink1, never()).send(MESSAGE);
1319
1320         // controller throws exception, so should drop into regular handling
1321         when(controller.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT)).thenThrow(new RuntimeException(EXPECTED));
1322
1323         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1324
1325         // should have attempted this again
1326         verify(controller, times(2)).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
1327
1328         // now these should have been called
1329         verify(coder).encode(MY_TOPIC, MY_EVENT);
1330         verify(sink1).send(MESSAGE);
1331     }
1332
1333     @Test
1334     public void testDeliverCommInfrastructureStringString() {
1335         mgr.configure(properties);
1336
1337         // not started yet
1338         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
1339
1340         // start it
1341         mgr.start();
1342
1343         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
1344         verify(sink1).send(MESSAGE);
1345         verify(sink2, never()).send(any());
1346
1347         // invalid parameters
1348         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, null, MESSAGE));
1349         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, "", MESSAGE));
1350
1351         String nullStr = null;
1352         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, nullStr));
1353         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, ""));
1354
1355         // unknown topic
1356         assertThatIllegalStateException()
1357                         .isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, "unknown-topic", MESSAGE));
1358
1359         // locked
1360         mgr.lock();
1361         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
1362         mgr.unlock();
1363     }
1364
1365     @Test
1366     public void testActivate() throws Throwable {
1367         // normal success case
1368         testActivate(() -> {
1369             // arrange for first provider and controller to throw exceptions
1370             when(prov1.beforeActivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1371             when(prov1.afterActivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1372             when(controller.start()).thenThrow(new RuntimeException(EXPECTED));
1373         });
1374
1375         // controller generates linkage error
1376         testActivate(() -> when(controller.start()).thenThrow(new LinkageError(EXPECTED)));
1377
1378         // other tests
1379         checkBeforeAfter(
1380             (prov, flag) -> when(prov.beforeActivate(mgr)).thenReturn(flag),
1381             (prov, flag) -> when(prov.afterActivate(mgr)).thenReturn(flag),
1382             () -> {
1383                 mgr.configure(properties);
1384                 mgr.lock();
1385                 mgr.activate();
1386             },
1387             prov -> verify(prov).beforeActivate(mgr),
1388             () -> assertFalse(mgr.isLocked()),
1389             prov -> verify(prov).afterActivate(mgr));
1390     }
1391
1392     /**
1393      * Tests the activate() method, after setting some option.
1394      *
1395      * @param setOption function that sets an option
1396      * @throws Throwable if an error occurs during setup
1397      */
1398     private void testActivate(RunnableWithEx setOption) throws Throwable {
1399         setUp();
1400         setOption.run();
1401
1402         mgr.configure(properties);
1403         mgr.lock();
1404         mgr.activate();
1405
1406         verify(prov1).beforeActivate(mgr);
1407         verify(prov2).beforeActivate(mgr);
1408
1409         // unlocked by activate() AND by unlock() (which is invoked by activate())
1410         verify(controller, times(2)).unlock();
1411         verify(controller2, times(2)).unlock();
1412
1413         verify(controller).start();
1414         verify(controller2).start();
1415
1416         assertFalse(mgr.isLocked());
1417
1418         verify(prov1).afterActivate(mgr);
1419         verify(prov2).afterActivate(mgr);
1420     }
1421
1422     @Test
1423     public void testDeactivate() throws Throwable {
1424         // normal success case
1425         testDeactivate(() -> {
1426             // arrange for first provider and controller to throw exceptions
1427             when(prov1.beforeDeactivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1428             when(prov1.afterDeactivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1429             when(controller.stop()).thenThrow(new RuntimeException(EXPECTED));
1430         });
1431
1432         // controller generates linkage error
1433         testDeactivate(() -> when(controller.stop()).thenThrow(new LinkageError(EXPECTED)));
1434
1435         // other tests
1436         checkBeforeAfter(
1437             (prov, flag) -> when(prov.beforeDeactivate(mgr)).thenReturn(flag),
1438             (prov, flag) -> when(prov.afterDeactivate(mgr)).thenReturn(flag),
1439             () -> {
1440                 mgr.configure(properties);
1441                 mgr.deactivate();
1442             },
1443             prov -> verify(prov).beforeDeactivate(mgr),
1444             () -> assertTrue(mgr.isLocked()),
1445             prov -> verify(prov).afterDeactivate(mgr));
1446     }
1447
1448     /**
1449      * Tests the deactivate() method, after setting some option.
1450      *
1451      * @param setOption function that sets an option
1452      * @throws Throwable if an error occurs during setup
1453      */
1454     private void testDeactivate(RunnableWithEx setOption) throws Throwable {
1455         setUp();
1456         setOption.run();
1457
1458         mgr.configure(properties);
1459         mgr.deactivate();
1460
1461         verify(prov1).beforeDeactivate(mgr);
1462         verify(prov2).beforeDeactivate(mgr);
1463
1464         verify(controller).lock();
1465         verify(controller2).lock();
1466
1467         verify(controller).stop();
1468         verify(controller2).stop();
1469
1470         assertTrue(mgr.isLocked());
1471
1472         verify(prov1).afterDeactivate(mgr);
1473         verify(prov2).afterDeactivate(mgr);
1474     }
1475
1476     @Test
1477     public void testControllerConfig() throws Exception {
1478         mgr.configure(properties);
1479         assertTrue(mgr.configure(pdpConfig));
1480
1481         verify(controllerFactory).patch(controller3, drools3);
1482         verify(controllerFactory).patch(controller4, drools4);
1483
1484         // empty controllers
1485         pdpConfig.getControllers().clear();
1486         assertFalse(mgr.configure(pdpConfig));
1487
1488         // null controllers
1489         pdpConfig.setControllers(null);
1490         assertFalse(mgr.configure(pdpConfig));
1491
1492         // arrange for controller3 to fail
1493         setUp();
1494         config3.setOperation("fail-3");
1495         assertFalse(mgr.configure(pdpConfig));
1496
1497         verify(controllerFactory, never()).patch(controller3, drools3);
1498         verify(controllerFactory).patch(controller4, drools4);
1499
1500         // arrange for both controllers to fail
1501         setUp();
1502         config3.setOperation("fail-3");
1503         config4.setOperation("fail-4");
1504         assertFalse(mgr.configure(pdpConfig));
1505     }
1506
1507     @Test
1508     public void testToString() {
1509         assertTrue(mgr.toString().startsWith("PolicyEngineManager ["));
1510     }
1511
1512     /**
1513      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries
1514      * combinations where beforeXxx and afterXxx return {@code true} and {@code false}.
1515      *
1516      * @param setBefore function to set the return value of a provider's beforeXxx method
1517      * @param setAfter function to set the return value of a provider's afterXxx method
1518      * @param action invokes the operation
1519      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1520      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1521      *        and the afterXxx loop was invoked
1522      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1523      * @throws Exception if an error occurs while calling {@link #setUp()}
1524      */
1525     private void checkBeforeAfter(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1526                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1527                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1528                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1529
1530         checkBeforeAfter_FalseFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
1531         checkBeforeAfter_FalseTrue(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
1532         checkBeforeAfter_TrueFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
1533
1534         // don't need to test true-true, as it's behavior is a subset of true-false
1535     }
1536
1537     /**
1538      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
1539      * case where both the beforeXxx and afterXxx methods return {@code false}.
1540      *
1541      * @param setBefore function to set the return value of a provider's beforeXxx method
1542      * @param setAfter function to set the return value of a provider's afterXxx method
1543      * @param action invokes the operation
1544      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1545      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1546      *        and the afterXxx loop was invoked
1547      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1548      * @throws Exception if an error occurs while calling {@link #setUp()}
1549      */
1550     private void checkBeforeAfter_FalseFalse(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1551                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1552                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1553                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1554
1555         setUp();
1556
1557         // configure for the test
1558         setBefore.accept(prov1, false);
1559         setBefore.accept(prov2, false);
1560
1561         setAfter.accept(prov1, false);
1562         setAfter.accept(prov2, false);
1563
1564         // run the action
1565         action.run();
1566
1567         // verify that various methods were invoked
1568         verifyBefore.accept(prov1);
1569         verifyBefore.accept(prov2);
1570
1571         verifyMiddle.run();
1572
1573         verifyAfter.accept(prov1);
1574         verifyAfter.accept(prov2);
1575     }
1576
1577     /**
1578      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
1579      * case where the first provider's afterXxx returns {@code true}, while the others
1580      * return {@code false}.
1581      *
1582      * @param setBefore function to set the return value of a provider's beforeXxx method
1583      * @param setAfter function to set the return value of a provider's afterXxx method
1584      * @param action invokes the operation
1585      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1586      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1587      *        and the afterXxx loop was invoked
1588      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1589      * @throws Exception if an error occurs while calling {@link #setUp()}
1590      */
1591     private void checkBeforeAfter_FalseTrue(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1592                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1593                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1594                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1595
1596         setUp();
1597
1598         // configure for the test
1599         setBefore.accept(prov1, false);
1600         setBefore.accept(prov2, false);
1601
1602         setAfter.accept(prov1, true);
1603         setAfter.accept(prov2, false);
1604
1605         // run the action
1606         action.run();
1607
1608         // verify that various methods were invoked
1609         verifyBefore.accept(prov1);
1610         verifyBefore.accept(prov2);
1611
1612         verifyMiddle.run();
1613
1614         verifyAfter.accept(prov1);
1615         assertThatThrownBy(() -> verifyAfter.accept(prov2)).isInstanceOf(AssertionError.class);
1616     }
1617
1618     /**
1619      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
1620      * case where the first provider's beforeXxx returns {@code true}, while the others
1621      * return {@code false}.
1622      *
1623      * @param setBefore function to set the return value of a provider's beforeXxx method
1624      * @param setAfter function to set the return value of a provider's afterXxx method
1625      * @param action invokes the operation
1626      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1627      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1628      *        and the afterXxx loop was invoked
1629      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1630      * @throws Exception if an error occurs while calling {@link #setUp()}
1631      */
1632     private void checkBeforeAfter_TrueFalse(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1633                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1634                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1635                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1636
1637         setUp();
1638
1639         // configure for the test
1640         setBefore.accept(prov1, true);
1641         setBefore.accept(prov2, false);
1642
1643         setAfter.accept(prov1, false);
1644         setAfter.accept(prov2, false);
1645
1646         // run the action
1647         action.run();
1648
1649         // verify that various methods were invoked
1650         verifyBefore.accept(prov1);
1651
1652         // remaining methods should not have been invoked
1653         assertThatThrownBy(() -> verifyBefore.accept(prov2)).isInstanceOf(AssertionError.class);
1654
1655         assertThatThrownBy(() -> verifyMiddle.run()).isInstanceOf(AssertionError.class);
1656
1657         assertThatThrownBy(() -> verifyAfter.accept(prov1)).isInstanceOf(AssertionError.class);
1658         assertThatThrownBy(() -> verifyAfter.accept(prov2)).isInstanceOf(AssertionError.class);
1659     }
1660
1661     /**
1662      * Manager with overrides.
1663      */
1664     private class PolicyEngineManagerImpl extends PolicyEngineManager {
1665
1666         @Override
1667         protected List<PolicyEngineFeatureAPI> getEngineProviders() {
1668             return providers;
1669         }
1670
1671         @Override
1672         protected List<PolicyControllerFeatureAPI> getControllerProviders() {
1673             return contProviders;
1674         }
1675
1676         @Override
1677         protected void globalInitContainer(String[] cliArgs) {
1678             globalInitArgs = cliArgs;
1679         }
1680
1681         @Override
1682         protected TopicEndpoint getTopicEndpointManager() {
1683             return endpoint;
1684         }
1685
1686         @Override
1687         protected HttpServletServerFactory getServletFactory() {
1688             return serverFactory;
1689         }
1690
1691         @Override
1692         protected PolicyControllerFactory getControllerFactory() {
1693             return controllerFactory;
1694         }
1695
1696         @Override
1697         protected void startPdpJmxListener() {
1698             jmxStarted = true;
1699         }
1700
1701         @Override
1702         protected void stopPdpJmxListener() {
1703             jmxStopped = true;
1704         }
1705
1706         @Override
1707         protected Thread makeShutdownThread() {
1708             shutdownThread = new MyShutdown();
1709             return shutdownThread;
1710         }
1711
1712         @Override
1713         protected EventProtocolCoder getProtocolCoder() {
1714             return coder;
1715         }
1716
1717         @Override
1718         protected SystemPersistence getPersistenceManager() {
1719             return persist;
1720         }
1721
1722         @Override
1723         protected PolicyEngine getPolicyEngine() {
1724             return engine;
1725         }
1726
1727         /**
1728          * Shutdown thread with overrides.
1729          */
1730         private class MyShutdown extends ShutdownThread {
1731
1732             @Override
1733             protected void doSleep(long sleepMs) throws InterruptedException {
1734                 threadSleepMs = sleepMs;
1735
1736                 if (shouldInterrupt) {
1737                     throw new InterruptedException(EXPECTED);
1738                 }
1739             }
1740
1741             @Override
1742             protected void doExit(int code) {
1743                 threadExitCode = code;
1744             }
1745
1746             @Override
1747             public synchronized void start() {
1748                 threadStarted = true;
1749             }
1750
1751             @Override
1752             public void interrupt() {
1753                 threadInterrupted = true;
1754             }
1755         }
1756     }
1757
1758     @FunctionalInterface
1759     private static interface RunnableWithEx {
1760         void run() throws Exception;
1761     }
1762 }