move all hard install config to environment vars
[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         System.setProperty("propS-a", "valueS-a");
373         assertEquals("valueS-a", mgr.getEnvironmentProperty("propS-a"));
374
375         Properties props3 = new Properties();
376         props3.put("prop3-a", "${env:HOME}");
377         mgr.setEnvironment(props3);
378         assertEquals(System.getenv("HOME"), mgr.getEnvironmentProperty("prop3-a"));
379         assertEquals("valueS-a", mgr.getEnvironmentProperty("propS-a"));
380         assertEquals(newValue, mgr.getEnvironmentProperty(propKey));
381     }
382
383     @Test
384     public void testDefaultTelemetryConfig() {
385         Properties config = mgr.defaultTelemetryConfig();
386         assertNotNull(config);
387         assertFalse(config.isEmpty());
388     }
389
390     @Test
391     public void testConfigureProperties() throws Exception {
392         // arrange for first provider to throw exceptions
393         when(prov1.beforeConfigure(mgr, properties)).thenThrow(new RuntimeException(EXPECTED));
394         when(prov1.afterConfigure(mgr)).thenThrow(new RuntimeException(EXPECTED));
395
396         mgr.configure(properties);
397
398         verify(prov1).beforeConfigure(mgr, properties);
399         verify(prov2).beforeConfigure(mgr, properties);
400
401         assertTrue(mgr.getProperties() == properties);
402
403         assertEquals(sources, mgr.getSources());
404         assertEquals(sinks, mgr.getSinks());
405         assertEquals(servers, mgr.getHttpServers());
406
407         verify(source1).register(mgr);
408         verify(source2).register(mgr);
409
410         verify(prov1).afterConfigure(mgr);
411         verify(prov2).afterConfigure(mgr);
412
413         // middle stuff throws exception - still calls afterXxx
414         setUp();
415         when(endpoint.addTopicSources(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
416         when(endpoint.addTopicSinks(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
417         when(serverFactory.build(properties)).thenThrow(new IllegalArgumentException(EXPECTED));
418         mgr.configure(properties);
419         verify(prov2).afterConfigure(mgr);
420
421         // null properties - nothing should be invoked
422         setUp();
423         Properties nullProps = null;
424         assertThatIllegalArgumentException().isThrownBy(() -> mgr.configure(nullProps));
425         verify(prov1, never()).beforeConfigure(mgr, properties);
426         verify(prov1, never()).afterConfigure(mgr);
427
428         // other tests
429         checkBeforeAfter(
430             (prov, flag) -> when(prov.beforeConfigure(mgr, properties)).thenReturn(flag),
431             (prov, flag) -> when(prov.afterConfigure(mgr)).thenReturn(flag),
432             () -> mgr.configure(properties),
433             prov -> verify(prov).beforeConfigure(mgr, properties),
434             () -> assertTrue(mgr.getProperties() == properties),
435             prov -> verify(prov).afterConfigure(mgr));
436     }
437
438     @Test
439     public void testConfigurePdpdConfiguration() throws Exception {
440         mgr.configure(properties);
441         assertTrue(mgr.configure(pdpConfig));
442
443         verify(controllerFactory).patch(controller3, drools3);
444         verify(controllerFactory).patch(controller4, drools4);
445
446         // invalid params
447         PdpdConfiguration nullConfig = null;
448         assertThatIllegalArgumentException().isThrownBy(() -> mgr.configure(nullConfig));
449
450         pdpConfig.setEntity("unknown-entity");
451         assertThatIllegalArgumentException().isThrownBy(() -> mgr.configure(pdpConfig));
452
453         // source list of size 1
454         setUp();
455         when(endpoint.addTopicSources(any())).thenReturn(Arrays.asList(source1));
456         mgr.configure(properties);
457         assertTrue(mgr.configure(pdpConfig));
458
459         verify(controllerFactory).patch(controller3, drools3);
460         verify(controllerFactory).patch(controller4, drools4);
461     }
462
463     @Test
464     public void testCreatePolicyController() throws Exception {
465         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
466
467         verify(contProv1).beforeCreate(MY_NAME, properties);
468         verify(contProv2).beforeCreate(MY_NAME, properties);
469         verify(controller, never()).lock();
470         verify(contProv1).afterCreate(controller);
471         verify(contProv2).afterCreate(controller);
472
473         // first provider throws exceptions - same result
474         setUp();
475         when(contProv1.beforeCreate(MY_NAME, properties)).thenThrow(new RuntimeException(EXPECTED));
476         when(contProv1.afterCreate(controller)).thenThrow(new RuntimeException(EXPECTED));
477         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
478
479         verify(contProv1).beforeCreate(MY_NAME, properties);
480         verify(contProv2).beforeCreate(MY_NAME, properties);
481         verify(controller, never()).lock();
482         verify(contProv1).afterCreate(controller);
483         verify(contProv2).afterCreate(controller);
484
485         // locked - same result, but engine locked
486         setUp();
487         mgr.lock();
488         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
489         verify(contProv1).beforeCreate(MY_NAME, properties);
490         verify(controller, times(2)).lock();
491         verify(contProv2).afterCreate(controller);
492
493         // empty name in properties - same result
494         setUp();
495         properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, "");
496         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
497         verify(contProv1).beforeCreate(MY_NAME, properties);
498
499         // matching name in properties - same result
500         setUp();
501         properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, MY_NAME);
502         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
503         verify(contProv1).beforeCreate(MY_NAME, properties);
504
505         // mismatching name in properties - nothing should happen besides exception
506         setUp();
507         properties.setProperty(DroolsProperties.PROPERTY_CONTROLLER_NAME, "mistmatched-name");
508         assertThatIllegalStateException().isThrownBy(() -> mgr.createPolicyController(MY_NAME, properties));
509         verify(contProv1, never()).beforeCreate(MY_NAME, properties);
510
511         // first provider generates controller - stops after first provider
512         setUp();
513         when(contProv1.beforeCreate(MY_NAME, properties)).thenReturn(controller2);
514         assertEquals(controller2, mgr.createPolicyController(MY_NAME, properties));
515         verify(contProv1).beforeCreate(MY_NAME, properties);
516         verify(contProv2, never()).beforeCreate(MY_NAME, properties);
517         verify(controller, never()).lock();
518         verify(contProv1, never()).afterCreate(controller);
519         verify(contProv2, never()).afterCreate(controller);
520
521         // first provider returns true - stops after first provider afterXxx
522         setUp();
523         when(contProv1.afterCreate(controller)).thenReturn(true);
524         assertEquals(controller, mgr.createPolicyController(MY_NAME, properties));
525         verify(contProv1).beforeCreate(MY_NAME, properties);
526         verify(contProv2).beforeCreate(MY_NAME, properties);
527         verify(contProv1).afterCreate(controller);
528         verify(contProv2, never()).afterCreate(controller);
529     }
530
531     @Test
532     public void testUpdatePolicyControllers() throws Exception {
533         assertEquals(Arrays.asList(controller3, controller4), mgr.updatePolicyControllers(pdpConfig.getControllers()));
534
535         // controller3 was CREATE
536         verify(controllerFactory).patch(controller3, drools3);
537         verify(controller3, never()).lock();
538         verify(controller3, never()).unlock();
539
540         // controller4 was UPDATE
541         verify(controllerFactory).patch(controller4, drools4);
542         verify(controller4, never()).lock();
543         verify(controller4).unlock();
544
545         // invalid args
546         assertTrue(mgr.updatePolicyControllers(null).isEmpty());
547         assertTrue(mgr.updatePolicyControllers(Collections.emptyList()).isEmpty());
548
549         // force exception in the first controller with invalid operation
550         setUp();
551         config3.setOperation("unknown-operation");
552         assertEquals(Arrays.asList(controller4), mgr.updatePolicyControllers(pdpConfig.getControllers()));
553
554         // controller3 should NOT have been done
555         verify(controllerFactory, never()).patch(controller3, drools3);
556
557         // controller4 should still be done
558         verify(controllerFactory).patch(controller4, drools4);
559         verify(controller4, never()).lock();
560         verify(controller4).unlock();
561     }
562
563     @Test
564     public void testUpdatePolicyController() throws Exception {
565         assertEquals(controller3, mgr.updatePolicyController(config3));
566         verify(engine).createPolicyController(CONTROLLER3, properties);
567
568         // invalid parameters
569         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(null));
570
571         // invalid name
572         setUp();
573         config3.setName(null);
574         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
575
576         config3.setName("");
577         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
578
579         // invalid operation
580         setUp();
581         config3.setOperation(null);
582         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
583
584         config3.setOperation("");
585         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
586
587         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK);
588         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
589
590         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK);
591         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
592
593         // exception from get() - should create controller
594         setUp();
595         when(controllerFactory.get(CONTROLLER3)).thenThrow(new IllegalArgumentException(EXPECTED));
596         assertEquals(controller3, mgr.updatePolicyController(config3));
597         verify(engine).createPolicyController(CONTROLLER3, properties);
598
599         // null properties
600         setUp();
601         when(persist.getControllerProperties(CONTROLLER3)).thenReturn(null);
602         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
603
604         // throw linkage error
605         setUp();
606         when(persist.getControllerProperties(CONTROLLER3)).thenThrow(new LinkageError(EXPECTED));
607         assertThatIllegalStateException().isThrownBy(() -> mgr.updatePolicyController(config3));
608
609         /*
610          * For remaining tests, the factory will return the controller instead of creating
611          * one.
612          */
613         setUp();
614         when(controllerFactory.get(CONTROLLER3)).thenReturn(controller3);
615
616         assertEquals(controller3, mgr.updatePolicyController(config3));
617
618         // should NOT have created a new controller
619         verify(engine, never()).createPolicyController(any(), any());
620
621         int countPatch = 0;
622         int countLock = 0;
623         int countUnlock = 0;
624
625         // check different operations
626
627         // CREATE only invokes patch() (note: mgr.update() has already been called)
628         verify(controllerFactory, times(++countPatch)).patch(controller3, drools3);
629         verify(controller3, times(countLock)).lock();
630         verify(controller3, times(countUnlock)).unlock();
631
632         // UPDATE invokes unlock() and patch()
633         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UPDATE);
634         assertEquals(controller3, mgr.updatePolicyController(config3));
635         verify(controllerFactory, times(++countPatch)).patch(controller3, drools3);
636         verify(controller3, times(countLock)).lock();
637         verify(controller3, times(++countUnlock)).unlock();
638
639         // LOCK invokes lock()
640         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_LOCK);
641         assertEquals(controller3, mgr.updatePolicyController(config3));
642         verify(controllerFactory, times(countPatch)).patch(controller3, drools3);
643         verify(controller3, times(++countLock)).lock();
644         verify(controller3, times(countUnlock)).unlock();
645
646         // UNLOCK invokes unlock()
647         config3.setOperation(ControllerConfiguration.CONFIG_CONTROLLER_OPERATION_UNLOCK);
648         assertEquals(controller3, mgr.updatePolicyController(config3));
649         verify(controllerFactory, times(countPatch)).patch(controller3, drools3);
650         verify(controller3, times(countLock)).lock();
651         verify(controller3, times(++countUnlock)).unlock();
652
653         // invalid operation
654         config3.setOperation("invalid-operation");
655         assertThatIllegalArgumentException().isThrownBy(() -> mgr.updatePolicyController(config3));
656     }
657
658     @Test
659     public void testStart() throws Throwable {
660         // normal success case
661         testStart(true, () -> {
662             // arrange for first provider, server, source, and sink to throw exceptions
663             when(prov1.beforeStart(mgr)).thenThrow(new RuntimeException(EXPECTED));
664             when(prov1.afterStart(mgr)).thenThrow(new RuntimeException(EXPECTED));
665             when(server1.waitedStart(anyLong())).thenThrow(new RuntimeException(EXPECTED));
666             when(source1.start()).thenThrow(new RuntimeException(EXPECTED));
667             when(sink1.start()).thenThrow(new RuntimeException(EXPECTED));
668         });
669
670         // servlet wait fails - still does everything
671         testStart(false, () -> when(server1.waitedStart(anyLong())).thenReturn(false));
672
673         // topic source fails to start - still does everything
674         testStart(false, () -> when(source1.start()).thenReturn(false));
675
676         // topic sink fails to start - still does everything
677         testStart(false, () -> when(sink1.start()).thenReturn(false));
678
679         // controller fails to start - still does everything
680         testStart(false, () -> when(controller.start()).thenReturn(false));
681
682         // controller throws an exception - still does everything
683         testStart(false, () -> when(controller.start()).thenThrow(new RuntimeException(EXPECTED)));
684
685         // endpoint manager fails to start - still does everything
686         testStart(false, () -> when(endpoint.start()).thenReturn(false));
687
688         // endpoint manager throws an exception - still does everything AND succeeds
689         testStart(true, () -> when(endpoint.start()).thenThrow(new IllegalStateException(EXPECTED)));
690
691         // locked - nothing other than beforeXxx methods should be invoked
692         setUp();
693         mgr.configure(properties);
694         mgr.lock();
695         assertThatIllegalStateException().isThrownBy(() -> mgr.start());
696         verify(prov2).beforeStart(mgr);
697         verify(server2, never()).waitedStart(anyLong());
698         verify(source2, never()).start();
699         verify(sink1, never()).start();
700         verify(controller, never()).start();
701         verify(endpoint, never()).start();
702         assertFalse(jmxStarted);
703         verify(prov1, never()).afterStart(mgr);
704
705         // other tests
706         checkBeforeAfter(
707             (prov, flag) -> when(prov.beforeStart(mgr)).thenReturn(flag),
708             (prov, flag) -> when(prov.afterStart(mgr)).thenReturn(flag),
709             () -> {
710                 mgr.configure(properties);
711                 assertTrue(mgr.start());
712             },
713             prov -> verify(prov).beforeStart(mgr),
714             () -> assertTrue(jmxStarted),
715             prov -> verify(prov).afterStart(mgr));
716     }
717
718     /**
719      * Tests the start() method, after setting some option.
720      *
721      * @param expectedResult what start() is expected to return
722      * @param setOption function that sets an option
723      * @throws Throwable if an error occurs during setup
724      */
725     private void testStart(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
726         setUp();
727         setOption.run();
728
729         mgr.configure(properties);
730         assertEquals(expectedResult, mgr.start());
731
732         verify(prov1).beforeStart(mgr);
733         verify(prov2).beforeStart(mgr);
734
735         verify(server1).waitedStart(anyLong());
736         verify(server2).waitedStart(anyLong());
737
738         verify(source1).start();
739         verify(source2).start();
740
741         verify(sink1).start();
742         verify(sink2).start();
743
744         verify(controller).start();
745         verify(controller2).start();
746
747         verify(endpoint).start();
748
749         assertTrue(jmxStarted);
750
751         verify(prov1).afterStart(mgr);
752         verify(prov2).afterStart(mgr);
753     }
754
755     @Test
756     public void testStop() throws Throwable {
757         // normal success case
758         testStop(true, () -> {
759             // arrange for first provider, server, source, and sink to throw exceptions
760             when(prov1.beforeStop(mgr)).thenThrow(new RuntimeException(EXPECTED));
761             when(prov1.afterStop(mgr)).thenThrow(new RuntimeException(EXPECTED));
762             when(server1.stop()).thenThrow(new RuntimeException(EXPECTED));
763             when(source1.stop()).thenThrow(new RuntimeException(EXPECTED));
764             when(sink1.stop()).thenThrow(new RuntimeException(EXPECTED));
765         });
766
767         // not alive - shouldn't run anything besides beforeStop()
768         setUp();
769         mgr.configure(properties);
770         assertTrue(mgr.stop());
771         verify(prov1).beforeStop(mgr);
772         verify(prov2).beforeStop(mgr);
773         verify(controller, never()).stop();
774         verify(source1, never()).stop();
775         verify(sink1, never()).stop();
776         verify(endpoint, never()).stop();
777         verify(server1, never()).stop();
778         verify(prov1, never()).afterStop(mgr);
779         verify(prov2, never()).afterStop(mgr);
780
781         // controller fails to stop - still does everything
782         testStop(false, () -> when(controller.stop()).thenReturn(false));
783
784         // controller throws an exception - still does everything
785         testStop(false, () -> when(controller.stop()).thenThrow(new RuntimeException(EXPECTED)));
786
787         // topic source fails to stop - still does everything
788         testStop(false, () -> when(source1.stop()).thenReturn(false));
789
790         // topic sink fails to stop - still does everything
791         testStop(false, () -> when(sink1.stop()).thenReturn(false));
792
793         // endpoint manager fails to stop - still does everything
794         testStop(false, () -> when(endpoint.stop()).thenReturn(false));
795
796         // servlet fails to stop - still does everything
797         testStop(false, () -> when(server1.stop()).thenReturn(false));
798
799         // other tests
800         checkBeforeAfter(
801             (prov, flag) -> when(prov.beforeStop(mgr)).thenReturn(flag),
802             (prov, flag) -> when(prov.afterStop(mgr)).thenReturn(flag),
803             () -> {
804                 mgr.configure(properties);
805                 mgr.start();
806                 assertTrue(mgr.stop());
807             },
808             prov -> verify(prov).beforeStop(mgr),
809             () -> verify(endpoint).stop(),
810             prov -> verify(prov).afterStop(mgr));
811     }
812
813     /**
814      * Tests the stop() method, after setting some option.
815      *
816      * @param expectedResult what stop() is expected to return
817      * @param setOption function that sets an option
818      * @throws Throwable if an error occurs during setup
819      */
820     private void testStop(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
821         setUp();
822         setOption.run();
823
824         mgr.configure(properties);
825         mgr.start();
826         assertEquals(expectedResult, mgr.stop());
827
828         verify(prov1).beforeStop(mgr);
829         verify(prov2).beforeStop(mgr);
830
831         verify(controller).stop();
832         verify(controller2).stop();
833
834         verify(source1).stop();
835         verify(source2).stop();
836
837         verify(sink1).stop();
838         verify(sink2).stop();
839
840         verify(endpoint).stop();
841
842         verify(server1).stop();
843         verify(server2).stop();
844
845         verify(prov1).afterStop(mgr);
846         verify(prov2).afterStop(mgr);
847     }
848
849     @Test
850     public void testShutdown() throws Throwable {
851         // normal success case
852         testShutdown(() -> {
853             // arrange for first provider, source, and sink to throw exceptions
854             when(prov1.beforeShutdown(mgr)).thenThrow(new RuntimeException(EXPECTED));
855             when(prov1.afterShutdown(mgr)).thenThrow(new RuntimeException(EXPECTED));
856             doThrow(new RuntimeException(EXPECTED)).when(source1).shutdown();
857             doThrow(new RuntimeException(EXPECTED)).when(sink1).shutdown();
858         });
859
860         assertNotNull(shutdownThread);
861         assertTrue(threadStarted);
862         assertTrue(threadInterrupted);
863
864         // other tests
865         checkBeforeAfter(
866             (prov, flag) -> when(prov.beforeShutdown(mgr)).thenReturn(flag),
867             (prov, flag) -> when(prov.afterShutdown(mgr)).thenReturn(flag),
868             () -> {
869                 mgr.configure(properties);
870                 mgr.start();
871                 mgr.shutdown();
872             },
873             prov -> verify(prov).beforeShutdown(mgr),
874             () -> assertTrue(jmxStopped),
875             prov -> verify(prov).afterShutdown(mgr));
876     }
877
878     /**
879      * Tests the shutdown() method, after setting some option.
880      *
881      * @param setOption function that sets an option
882      * @throws Throwable if an error occurs during setup
883      */
884     private void testShutdown(RunnableWithEx setOption) throws Throwable {
885         setUp();
886         setOption.run();
887
888         mgr.configure(properties);
889         mgr.start();
890         mgr.shutdown();
891
892         verify(prov1).beforeShutdown(mgr);
893         verify(prov2).beforeShutdown(mgr);
894
895         verify(source1).shutdown();
896         verify(source2).shutdown();
897
898         verify(sink1).shutdown();
899         verify(sink2).shutdown();
900
901         verify(controllerFactory).shutdown();
902         verify(endpoint).shutdown();
903         verify(serverFactory).destroy();
904
905         assertTrue(jmxStopped);
906
907         verify(prov1).afterShutdown(mgr);
908         verify(prov2).afterShutdown(mgr);
909     }
910
911     @Test
912     public void testShutdownThreadRun() throws Throwable {
913         // arrange for first server to throw exceptions
914         testShutdownThreadRun(() -> doThrow(new RuntimeException(EXPECTED)).when(server1).shutdown());
915
916         // sleep throws an exception
917         testShutdownThreadRun(() -> shouldInterrupt = true);
918     }
919
920     /**
921      * Tests the ShutdownThread.run() method, after setting some option.
922      *
923      * @param setOption function that sets an option
924      * @throws Throwable if an error occurs during setup
925      */
926     private void testShutdownThreadRun(RunnableWithEx setOption) throws Throwable {
927         setUp();
928         setOption.run();
929
930         mgr.configure(properties);
931         mgr.start();
932         mgr.shutdown();
933
934         assertNotNull(shutdownThread);
935
936         shutdownThread.run();
937
938         assertTrue(threadSleepMs >= 0);
939         assertEquals(0, threadExitCode);
940
941         verify(server1).shutdown();
942         verify(server2).shutdown();
943     }
944
945     @Test
946     public void testIsAlive() {
947         mgr.configure(properties);
948         assertFalse(mgr.isAlive());
949
950         mgr.start();
951         assertTrue(mgr.isAlive());
952
953         mgr.stop();
954         assertFalse(mgr.isAlive());
955     }
956
957     @Test
958     public void testLock() throws Throwable {
959         // normal success case
960         testLock(true, () -> {
961             // arrange for first provider to throw exceptions
962             when(prov1.beforeLock(mgr)).thenThrow(new RuntimeException(EXPECTED));
963             when(prov1.afterLock(mgr)).thenThrow(new RuntimeException(EXPECTED));
964         });
965
966         // already locked - shouldn't run anything besides beforeLock()
967         setUp();
968         mgr.configure(properties);
969         mgr.lock();
970         assertTrue(mgr.lock());
971         verify(prov1, times(2)).beforeLock(mgr);
972         verify(prov2, times(2)).beforeLock(mgr);
973         verify(controller).lock();
974         verify(controller2).lock();
975         verify(endpoint).lock();
976         verify(prov1).afterLock(mgr);
977         verify(prov2).afterLock(mgr);
978
979         // controller fails to lock - still does everything
980         testLock(false, () -> when(controller.lock()).thenReturn(false));
981
982         // controller throws an exception - still does everything
983         testLock(false, () -> when(controller.lock()).thenThrow(new RuntimeException(EXPECTED)));
984
985         // endpoint manager fails to lock - still does everything
986         testLock(false, () -> when(endpoint.lock()).thenReturn(false));
987
988         // other tests
989         checkBeforeAfter(
990             (prov, flag) -> when(prov.beforeLock(mgr)).thenReturn(flag),
991             (prov, flag) -> when(prov.afterLock(mgr)).thenReturn(flag),
992             () -> {
993                 mgr.configure(properties);
994                 mgr.start();
995                 assertTrue(mgr.lock());
996             },
997             prov -> verify(prov).beforeLock(mgr),
998             () -> verify(endpoint).lock(),
999             prov -> verify(prov).afterLock(mgr));
1000     }
1001
1002     /**
1003      * Tests the lock() method, after setting some option.
1004      *
1005      * @param expectedResult what lock() is expected to return
1006      * @param setOption function that sets an option
1007      * @throws Throwable if an error occurs during setup
1008      */
1009     private void testLock(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
1010         setUp();
1011         setOption.run();
1012
1013         mgr.configure(properties);
1014         assertEquals(expectedResult, mgr.lock());
1015
1016         verify(prov1).beforeLock(mgr);
1017         verify(prov2).beforeLock(mgr);
1018
1019         verify(controller).lock();
1020         verify(controller2).lock();
1021
1022         verify(endpoint).lock();
1023
1024         verify(prov1).afterLock(mgr);
1025         verify(prov2).afterLock(mgr);
1026     }
1027
1028     @Test
1029     public void testUnlock() throws Throwable {
1030         // normal success case
1031         testUnlock(true, () -> {
1032             // arrange for first provider to throw exceptions
1033             when(prov1.beforeUnlock(mgr)).thenThrow(new RuntimeException(EXPECTED));
1034             when(prov1.afterUnlock(mgr)).thenThrow(new RuntimeException(EXPECTED));
1035         });
1036
1037         // not locked - shouldn't run anything besides beforeUnlock()
1038         setUp();
1039         mgr.configure(properties);
1040         assertTrue(mgr.unlock());
1041         verify(prov1).beforeUnlock(mgr);
1042         verify(prov2).beforeUnlock(mgr);
1043         verify(controller, never()).unlock();
1044         verify(controller2, never()).unlock();
1045         verify(endpoint, never()).unlock();
1046         verify(prov1, never()).afterUnlock(mgr);
1047         verify(prov2, never()).afterUnlock(mgr);
1048
1049         // controller fails to unlock - still does everything
1050         testUnlock(false, () -> when(controller.unlock()).thenReturn(false));
1051
1052         // controller throws an exception - still does everything
1053         testUnlock(false, () -> when(controller.unlock()).thenThrow(new RuntimeException(EXPECTED)));
1054
1055         // endpoint manager fails to unlock - still does everything
1056         testUnlock(false, () -> when(endpoint.unlock()).thenReturn(false));
1057
1058         // other tests
1059         checkBeforeAfter(
1060             (prov, flag) -> when(prov.beforeUnlock(mgr)).thenReturn(flag),
1061             (prov, flag) -> when(prov.afterUnlock(mgr)).thenReturn(flag),
1062             () -> {
1063                 mgr.configure(properties);
1064                 mgr.lock();
1065                 assertTrue(mgr.unlock());
1066             },
1067             prov -> verify(prov).beforeUnlock(mgr),
1068             () -> verify(endpoint).unlock(),
1069             prov -> verify(prov).afterUnlock(mgr));
1070     }
1071
1072     /**
1073      * Tests the unlock() method, after setting some option.
1074      *
1075      * @param expectedResult what unlock() is expected to return
1076      * @param setOption function that sets an option
1077      * @throws Throwable if an error occurs during setup
1078      */
1079     private void testUnlock(boolean expectedResult, RunnableWithEx setOption) throws Throwable {
1080         setUp();
1081         setOption.run();
1082
1083         mgr.configure(properties);
1084         mgr.lock();
1085         assertEquals(expectedResult, mgr.unlock());
1086
1087         verify(prov1).beforeUnlock(mgr);
1088         verify(prov2).beforeUnlock(mgr);
1089
1090         verify(controller).unlock();
1091         verify(controller2).unlock();
1092
1093         verify(endpoint).unlock();
1094
1095         verify(prov1).afterUnlock(mgr);
1096         verify(prov2).afterUnlock(mgr);
1097     }
1098
1099     @Test
1100     public void testIsLocked() {
1101         mgr.configure(properties);
1102         assertFalse(mgr.isLocked());
1103
1104         mgr.lock();
1105         assertTrue(mgr.isLocked());
1106
1107         mgr.unlock();
1108         assertFalse(mgr.isLocked());
1109     }
1110
1111     @Test
1112     public void testRemovePolicyControllerString() {
1113         mgr.removePolicyController(MY_NAME);
1114
1115         verify(controllerFactory).destroy(MY_NAME);
1116     }
1117
1118     @Test
1119     public void testRemovePolicyControllerPolicyController() {
1120         mgr.removePolicyController(controller);
1121
1122         verify(controllerFactory).destroy(controller);
1123     }
1124
1125     @Test
1126     public void testGetPolicyControllers() {
1127         assertEquals(controllers, mgr.getPolicyControllers());
1128     }
1129
1130     @Test
1131     public void testGetPolicyControllerIds() {
1132         assertEquals(Arrays.asList(CONTROLLER1, CONTROLLER2), mgr.getPolicyControllerIds());
1133     }
1134
1135     @Test
1136     public void testGetProperties() {
1137         properties.setProperty("prop-x", "value-x");
1138         properties.setProperty("prop-y", "value-y");
1139
1140         mgr.configure(properties);
1141         assertEquals(properties, mgr.getProperties());
1142     }
1143
1144     @Test
1145     public void testGetSources() {
1146         mgr.configure(properties);
1147         assertEquals(sources, mgr.getSources());
1148     }
1149
1150     @Test
1151     public void testGetSinks() {
1152         mgr.configure(properties);
1153         assertEquals(sinks, mgr.getSinks());
1154     }
1155
1156     @Test
1157     public void testGetHttpServers() {
1158         mgr.configure(properties);
1159         assertEquals(servers, mgr.getHttpServers());
1160     }
1161
1162     @Test
1163     public void testGetFeatures() {
1164         assertEquals(Arrays.asList(FEATURE1, FEATURE2), mgr.getFeatures());
1165     }
1166
1167     @Test
1168     public void testGetFeatureProviders() {
1169         assertEquals(providers, mgr.getFeatureProviders());
1170     }
1171
1172     @Test
1173     public void testGetFeatureProvider() {
1174         assertEquals(prov1, mgr.getFeatureProvider(FEATURE1));
1175         assertEquals(prov2, mgr.getFeatureProvider(FEATURE2));
1176
1177         // null feature
1178         assertThatIllegalArgumentException().isThrownBy(() -> mgr.getFeatureProvider(null));
1179
1180         // empty feature
1181         assertThatIllegalArgumentException().isThrownBy(() -> mgr.getFeatureProvider(""));
1182
1183         // unknown feature
1184         assertThatIllegalArgumentException().isThrownBy(() -> mgr.getFeatureProvider("unknown-feature"));
1185     }
1186
1187     @Test
1188     public void testOnTopicEvent() {
1189         mgr.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, pdpConfigJson);
1190
1191         verify(controllerFactory).patch(controller3, drools3);
1192         verify(controllerFactory).patch(controller4, drools4);
1193
1194         // null json - no additional patches
1195         mgr.onTopicEvent(CommInfrastructure.NOOP, MY_TOPIC, null);
1196
1197         verify(controllerFactory).patch(controller3, drools3);
1198         verify(controllerFactory).patch(controller4, drools4);
1199     }
1200
1201     @Test
1202     public void testDeliverStringObject() throws Exception {
1203         mgr.configure(properties);
1204         mgr.start();
1205
1206         assertTrue(mgr.deliver(MY_TOPIC, MY_EVENT));
1207
1208         verify(sink1).send(MESSAGE);
1209
1210         // invalid parameters
1211         String nullStr = null;
1212         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(nullStr, MY_EVENT));
1213         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver("", MY_EVENT));
1214
1215         Object nullObj = null;
1216         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(MY_TOPIC, nullObj));
1217
1218         // locked
1219         mgr.lock();
1220         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1221         mgr.unlock();
1222
1223         // not running
1224         mgr.stop();
1225         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1226
1227         // issues with topic
1228         setUp();
1229         mgr.configure(properties);
1230         mgr.start();
1231
1232         // null sinks
1233         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(null);
1234         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1235
1236         // empty sinks
1237         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(Collections.emptyList());
1238         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1239
1240         // too many sinks
1241         when(endpoint.getTopicSinks(MY_TOPIC)).thenReturn(sinks);
1242         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(MY_TOPIC, MY_EVENT));
1243     }
1244
1245     @Test
1246     public void testDeliverStringStringObject() {
1247         mgr.configure(properties);
1248         mgr.start();
1249
1250         assertTrue(mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
1251
1252         verify(sink1).send(MESSAGE);
1253
1254         // invalid parameters
1255         String nullStr = null;
1256         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(nullStr, MY_TOPIC, MY_EVENT));
1257         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver("", MY_TOPIC, MY_EVENT));
1258         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver("unknown-bus-type", MY_TOPIC, MY_EVENT));
1259
1260         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(NOOP_STR, nullStr, MY_EVENT));
1261         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(NOOP_STR, "", MY_EVENT));
1262
1263         Object nullObj = null;
1264         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(NOOP_STR, MY_TOPIC, nullObj));
1265
1266         // locked
1267         mgr.lock();
1268         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
1269         mgr.unlock();
1270
1271         // not running
1272         mgr.stop();
1273         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(NOOP_STR, MY_TOPIC, MY_EVENT));
1274     }
1275
1276     @Test
1277     public void testDeliverCommInfrastructureStringObject() throws Exception {
1278         mgr.configure(properties);
1279         mgr.start();
1280
1281         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1282
1283         verify(controller, never()).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
1284
1285         verify(coder).encode(MY_TOPIC, MY_EVENT);
1286         verify(sink1).send(MESSAGE);
1287
1288         // invalid parameters
1289         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, null, MY_EVENT));
1290         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, "", MY_EVENT));
1291
1292         Object nullObj = null;
1293         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, nullObj));
1294
1295         // locked
1296         mgr.lock();
1297         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1298         mgr.unlock();
1299
1300         // not started
1301         mgr.stop();
1302         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1303
1304         // send() throws an exception
1305         setUp();
1306         mgr.configure(properties);
1307         mgr.start();
1308         when(sink1.send(any())).thenThrow(new ArithmeticException(EXPECTED));
1309         assertThatThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT))
1310                         .isInstanceOf(ArithmeticException.class);
1311
1312         /*
1313          * For remaining tests, have the controller handle delivery.
1314          */
1315         setUp();
1316         mgr.configure(properties);
1317         mgr.start();
1318         DroolsController drools = mock(DroolsController.class);
1319         when(coder.getDroolsController(MY_TOPIC, MY_EVENT)).thenReturn(drools);
1320         when(controllerFactory.get(drools)).thenReturn(controller);
1321         when(controller.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT)).thenReturn(true);
1322
1323         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1324
1325         verify(controller).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
1326
1327         verify(coder, never()).encode(MY_TOPIC, MY_EVENT);
1328         verify(sink1, never()).send(MESSAGE);
1329
1330         // controller throws exception, so should drop into regular handling
1331         when(controller.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT)).thenThrow(new RuntimeException(EXPECTED));
1332
1333         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT));
1334
1335         // should have attempted this again
1336         verify(controller, times(2)).deliver(CommInfrastructure.NOOP, MY_TOPIC, MY_EVENT);
1337
1338         // now these should have been called
1339         verify(coder).encode(MY_TOPIC, MY_EVENT);
1340         verify(sink1).send(MESSAGE);
1341     }
1342
1343     @Test
1344     public void testDeliverCommInfrastructureStringString() {
1345         mgr.configure(properties);
1346
1347         // not started yet
1348         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
1349
1350         // start it
1351         mgr.start();
1352
1353         assertTrue(mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
1354         verify(sink1).send(MESSAGE);
1355         verify(sink2, never()).send(any());
1356
1357         // invalid parameters
1358         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, null, MESSAGE));
1359         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, "", MESSAGE));
1360
1361         String nullStr = null;
1362         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, nullStr));
1363         assertThatIllegalArgumentException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, ""));
1364
1365         // unknown topic
1366         assertThatIllegalStateException()
1367                         .isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, "unknown-topic", MESSAGE));
1368
1369         // locked
1370         mgr.lock();
1371         assertThatIllegalStateException().isThrownBy(() -> mgr.deliver(CommInfrastructure.NOOP, MY_TOPIC, MESSAGE));
1372         mgr.unlock();
1373     }
1374
1375     @Test
1376     public void testActivate() throws Throwable {
1377         // normal success case
1378         testActivate(() -> {
1379             // arrange for first provider and controller to throw exceptions
1380             when(prov1.beforeActivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1381             when(prov1.afterActivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1382             when(controller.start()).thenThrow(new RuntimeException(EXPECTED));
1383         });
1384
1385         // controller generates linkage error
1386         testActivate(() -> when(controller.start()).thenThrow(new LinkageError(EXPECTED)));
1387
1388         // other tests
1389         checkBeforeAfter(
1390             (prov, flag) -> when(prov.beforeActivate(mgr)).thenReturn(flag),
1391             (prov, flag) -> when(prov.afterActivate(mgr)).thenReturn(flag),
1392             () -> {
1393                 mgr.configure(properties);
1394                 mgr.lock();
1395                 mgr.activate();
1396             },
1397             prov -> verify(prov).beforeActivate(mgr),
1398             () -> assertFalse(mgr.isLocked()),
1399             prov -> verify(prov).afterActivate(mgr));
1400     }
1401
1402     /**
1403      * Tests the activate() method, after setting some option.
1404      *
1405      * @param setOption function that sets an option
1406      * @throws Throwable if an error occurs during setup
1407      */
1408     private void testActivate(RunnableWithEx setOption) throws Throwable {
1409         setUp();
1410         setOption.run();
1411
1412         mgr.configure(properties);
1413         mgr.lock();
1414         mgr.activate();
1415
1416         verify(prov1).beforeActivate(mgr);
1417         verify(prov2).beforeActivate(mgr);
1418
1419         // unlocked by activate() AND by unlock() (which is invoked by activate())
1420         verify(controller, times(2)).unlock();
1421         verify(controller2, times(2)).unlock();
1422
1423         verify(controller).start();
1424         verify(controller2).start();
1425
1426         assertFalse(mgr.isLocked());
1427
1428         verify(prov1).afterActivate(mgr);
1429         verify(prov2).afterActivate(mgr);
1430     }
1431
1432     @Test
1433     public void testDeactivate() throws Throwable {
1434         // normal success case
1435         testDeactivate(() -> {
1436             // arrange for first provider and controller to throw exceptions
1437             when(prov1.beforeDeactivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1438             when(prov1.afterDeactivate(mgr)).thenThrow(new RuntimeException(EXPECTED));
1439             when(controller.stop()).thenThrow(new RuntimeException(EXPECTED));
1440         });
1441
1442         // controller generates linkage error
1443         testDeactivate(() -> when(controller.stop()).thenThrow(new LinkageError(EXPECTED)));
1444
1445         // other tests
1446         checkBeforeAfter(
1447             (prov, flag) -> when(prov.beforeDeactivate(mgr)).thenReturn(flag),
1448             (prov, flag) -> when(prov.afterDeactivate(mgr)).thenReturn(flag),
1449             () -> {
1450                 mgr.configure(properties);
1451                 mgr.deactivate();
1452             },
1453             prov -> verify(prov).beforeDeactivate(mgr),
1454             () -> assertTrue(mgr.isLocked()),
1455             prov -> verify(prov).afterDeactivate(mgr));
1456     }
1457
1458     /**
1459      * Tests the deactivate() method, after setting some option.
1460      *
1461      * @param setOption function that sets an option
1462      * @throws Throwable if an error occurs during setup
1463      */
1464     private void testDeactivate(RunnableWithEx setOption) throws Throwable {
1465         setUp();
1466         setOption.run();
1467
1468         mgr.configure(properties);
1469         mgr.deactivate();
1470
1471         verify(prov1).beforeDeactivate(mgr);
1472         verify(prov2).beforeDeactivate(mgr);
1473
1474         verify(controller).lock();
1475         verify(controller2).lock();
1476
1477         verify(controller).stop();
1478         verify(controller2).stop();
1479
1480         assertTrue(mgr.isLocked());
1481
1482         verify(prov1).afterDeactivate(mgr);
1483         verify(prov2).afterDeactivate(mgr);
1484     }
1485
1486     @Test
1487     public void testControllerConfig() throws Exception {
1488         mgr.configure(properties);
1489         assertTrue(mgr.configure(pdpConfig));
1490
1491         verify(controllerFactory).patch(controller3, drools3);
1492         verify(controllerFactory).patch(controller4, drools4);
1493
1494         // empty controllers
1495         pdpConfig.getControllers().clear();
1496         assertFalse(mgr.configure(pdpConfig));
1497
1498         // null controllers
1499         pdpConfig.setControllers(null);
1500         assertFalse(mgr.configure(pdpConfig));
1501
1502         // arrange for controller3 to fail
1503         setUp();
1504         config3.setOperation("fail-3");
1505         assertFalse(mgr.configure(pdpConfig));
1506
1507         verify(controllerFactory, never()).patch(controller3, drools3);
1508         verify(controllerFactory).patch(controller4, drools4);
1509
1510         // arrange for both controllers to fail
1511         setUp();
1512         config3.setOperation("fail-3");
1513         config4.setOperation("fail-4");
1514         assertFalse(mgr.configure(pdpConfig));
1515     }
1516
1517     @Test
1518     public void testToString() {
1519         assertTrue(mgr.toString().startsWith("PolicyEngineManager ["));
1520     }
1521
1522     /**
1523      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries
1524      * combinations where beforeXxx and afterXxx return {@code true} and {@code false}.
1525      *
1526      * @param setBefore function to set the return value of a provider's beforeXxx method
1527      * @param setAfter function to set the return value of a provider's afterXxx method
1528      * @param action invokes the operation
1529      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1530      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1531      *        and the afterXxx loop was invoked
1532      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1533      * @throws Exception if an error occurs while calling {@link #setUp()}
1534      */
1535     private void checkBeforeAfter(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1536                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1537                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1538                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1539
1540         checkBeforeAfter_FalseFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
1541         checkBeforeAfter_FalseTrue(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
1542         checkBeforeAfter_TrueFalse(setBefore, setAfter, action, verifyBefore, verifyMiddle, verifyAfter);
1543
1544         // don't need to test true-true, as it's behavior is a subset of true-false
1545     }
1546
1547     /**
1548      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
1549      * case where both the beforeXxx and afterXxx methods return {@code false}.
1550      *
1551      * @param setBefore function to set the return value of a provider's beforeXxx method
1552      * @param setAfter function to set the return value of a provider's afterXxx method
1553      * @param action invokes the operation
1554      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1555      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1556      *        and the afterXxx loop was invoked
1557      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1558      * @throws Exception if an error occurs while calling {@link #setUp()}
1559      */
1560     private void checkBeforeAfter_FalseFalse(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1561                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1562                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1563                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1564
1565         setUp();
1566
1567         // configure for the test
1568         setBefore.accept(prov1, false);
1569         setBefore.accept(prov2, false);
1570
1571         setAfter.accept(prov1, false);
1572         setAfter.accept(prov2, false);
1573
1574         // run the action
1575         action.run();
1576
1577         // verify that various methods were invoked
1578         verifyBefore.accept(prov1);
1579         verifyBefore.accept(prov2);
1580
1581         verifyMiddle.run();
1582
1583         verifyAfter.accept(prov1);
1584         verifyAfter.accept(prov2);
1585     }
1586
1587     /**
1588      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
1589      * case where the first provider's afterXxx returns {@code true}, while the others
1590      * return {@code false}.
1591      *
1592      * @param setBefore function to set the return value of a provider's beforeXxx method
1593      * @param setAfter function to set the return value of a provider's afterXxx method
1594      * @param action invokes the operation
1595      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1596      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1597      *        and the afterXxx loop was invoked
1598      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1599      * @throws Exception if an error occurs while calling {@link #setUp()}
1600      */
1601     private void checkBeforeAfter_FalseTrue(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1602                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1603                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1604                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1605
1606         setUp();
1607
1608         // configure for the test
1609         setBefore.accept(prov1, false);
1610         setBefore.accept(prov2, false);
1611
1612         setAfter.accept(prov1, true);
1613         setAfter.accept(prov2, false);
1614
1615         // run the action
1616         action.run();
1617
1618         // verify that various methods were invoked
1619         verifyBefore.accept(prov1);
1620         verifyBefore.accept(prov2);
1621
1622         verifyMiddle.run();
1623
1624         verifyAfter.accept(prov1);
1625         assertThatThrownBy(() -> verifyAfter.accept(prov2)).isInstanceOf(AssertionError.class);
1626     }
1627
1628     /**
1629      * Performs an operation that has a beforeXxx method and an afterXxx method. Tries the
1630      * case where the first provider's beforeXxx returns {@code true}, while the others
1631      * return {@code false}.
1632      *
1633      * @param setBefore function to set the return value of a provider's beforeXxx method
1634      * @param setAfter function to set the return value of a provider's afterXxx method
1635      * @param action invokes the operation
1636      * @param verifyBefore verifies that a provider's beforeXxx method was invoked
1637      * @param verifyMiddle verifies that the action occurring between the beforeXxx loop
1638      *        and the afterXxx loop was invoked
1639      * @param verifyAfter verifies that a provider's afterXxx method was invoked
1640      * @throws Exception if an error occurs while calling {@link #setUp()}
1641      */
1642     private void checkBeforeAfter_TrueFalse(BiConsumer<PolicyEngineFeatureAPI, Boolean> setBefore,
1643                     BiConsumer<PolicyEngineFeatureAPI, Boolean> setAfter, Runnable action,
1644                     Consumer<PolicyEngineFeatureAPI> verifyBefore, Runnable verifyMiddle,
1645                     Consumer<PolicyEngineFeatureAPI> verifyAfter) throws Exception {
1646
1647         setUp();
1648
1649         // configure for the test
1650         setBefore.accept(prov1, true);
1651         setBefore.accept(prov2, false);
1652
1653         setAfter.accept(prov1, false);
1654         setAfter.accept(prov2, false);
1655
1656         // run the action
1657         action.run();
1658
1659         // verify that various methods were invoked
1660         verifyBefore.accept(prov1);
1661
1662         // remaining methods should not have been invoked
1663         assertThatThrownBy(() -> verifyBefore.accept(prov2)).isInstanceOf(AssertionError.class);
1664
1665         assertThatThrownBy(() -> verifyMiddle.run()).isInstanceOf(AssertionError.class);
1666
1667         assertThatThrownBy(() -> verifyAfter.accept(prov1)).isInstanceOf(AssertionError.class);
1668         assertThatThrownBy(() -> verifyAfter.accept(prov2)).isInstanceOf(AssertionError.class);
1669     }
1670
1671     /**
1672      * Manager with overrides.
1673      */
1674     private class PolicyEngineManagerImpl extends PolicyEngineManager {
1675
1676         @Override
1677         protected List<PolicyEngineFeatureAPI> getEngineProviders() {
1678             return providers;
1679         }
1680
1681         @Override
1682         protected List<PolicyControllerFeatureAPI> getControllerProviders() {
1683             return contProviders;
1684         }
1685
1686         @Override
1687         protected void globalInitContainer(String[] cliArgs) {
1688             globalInitArgs = cliArgs;
1689         }
1690
1691         @Override
1692         protected TopicEndpoint getTopicEndpointManager() {
1693             return endpoint;
1694         }
1695
1696         @Override
1697         protected HttpServletServerFactory getServletFactory() {
1698             return serverFactory;
1699         }
1700
1701         @Override
1702         protected PolicyControllerFactory getControllerFactory() {
1703             return controllerFactory;
1704         }
1705
1706         @Override
1707         protected void startPdpJmxListener() {
1708             jmxStarted = true;
1709         }
1710
1711         @Override
1712         protected void stopPdpJmxListener() {
1713             jmxStopped = true;
1714         }
1715
1716         @Override
1717         protected Thread makeShutdownThread() {
1718             shutdownThread = new MyShutdown();
1719             return shutdownThread;
1720         }
1721
1722         @Override
1723         protected EventProtocolCoder getProtocolCoder() {
1724             return coder;
1725         }
1726
1727         @Override
1728         protected SystemPersistence getPersistenceManager() {
1729             return persist;
1730         }
1731
1732         @Override
1733         protected PolicyEngine getPolicyEngine() {
1734             return engine;
1735         }
1736
1737         /**
1738          * Shutdown thread with overrides.
1739          */
1740         private class MyShutdown extends ShutdownThread {
1741
1742             @Override
1743             protected void doSleep(long sleepMs) throws InterruptedException {
1744                 threadSleepMs = sleepMs;
1745
1746                 if (shouldInterrupt) {
1747                     throw new InterruptedException(EXPECTED);
1748                 }
1749             }
1750
1751             @Override
1752             protected void doExit(int code) {
1753                 threadExitCode = code;
1754             }
1755
1756             @Override
1757             public synchronized void start() {
1758                 threadStarted = true;
1759             }
1760
1761             @Override
1762             public void interrupt() {
1763                 threadInterrupted = true;
1764             }
1765         }
1766     }
1767
1768     @FunctionalInterface
1769     private static interface RunnableWithEx {
1770         void run() throws Exception;
1771     }
1772 }