27ac2cc14da66c250e68c66557e806f3ee03306d
[policy/drools-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * feature-session-persistence
4  * ================================================================================
5  * Copyright (C) 2017-2018 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.persistence;
22
23 import static org.junit.Assert.assertEquals;
24 import static org.junit.Assert.assertFalse;
25 import static org.junit.Assert.assertNotNull;
26 import static org.junit.Assert.assertNull;
27 import static org.junit.Assert.assertTrue;
28 import static org.junit.Assert.fail;
29 import static org.mockito.Matchers.any;
30 import static org.mockito.Matchers.anyLong;
31 import static org.mockito.Matchers.anyString;
32 import static org.mockito.Mockito.doThrow;
33 import static org.mockito.Mockito.mock;
34 import static org.mockito.Mockito.never;
35 import static org.mockito.Mockito.times;
36 import static org.mockito.Mockito.verify;
37 import static org.mockito.Mockito.when;
38
39 import java.io.FileNotFoundException;
40 import java.io.FileReader;
41 import java.io.IOException;
42 import java.sql.Connection;
43 import java.sql.DriverManager;
44 import java.sql.PreparedStatement;
45 import java.sql.ResultSet;
46 import java.sql.SQLException;
47 import java.sql.Timestamp;
48 import java.util.ArrayList;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Properties;
52 import java.util.concurrent.CountDownLatch;
53 import java.util.concurrent.TimeUnit;
54
55 import javax.persistence.EntityManager;
56 import javax.persistence.EntityManagerFactory;
57 import javax.transaction.TransactionManager;
58 import javax.transaction.TransactionSynchronizationRegistry;
59 import javax.transaction.UserTransaction;
60
61 import org.apache.commons.dbcp2.BasicDataSource;
62 import org.junit.After;
63 import org.junit.Before;
64 import org.junit.BeforeClass;
65 import org.junit.Test;
66 import org.kie.api.KieBase;
67 import org.kie.api.KieServices;
68 import org.kie.api.persistence.jpa.KieStoreServices;
69 import org.kie.api.runtime.Environment;
70 import org.kie.api.runtime.EnvironmentName;
71 import org.kie.api.runtime.KieContainer;
72 import org.kie.api.runtime.KieSession;
73 import org.kie.api.runtime.KieSessionConfiguration;
74 import org.mockito.ArgumentCaptor;
75 import org.onap.policy.drools.core.PolicyContainer;
76 import org.onap.policy.drools.core.PolicySession;
77 import org.onap.policy.drools.core.PolicySession.ThreadModel;
78 import org.onap.policy.drools.persistence.PersistenceFeature.PersistenceFeatureException;
79 import org.onap.policy.drools.persistence.PersistenceFeature.PersistentThreadModel;
80 import org.onap.policy.drools.system.PolicyController;
81 import org.slf4j.Logger;
82 import org.slf4j.LoggerFactory;
83
84 public class PersistenceFeatureTest {
85
86     private static final Logger logger = LoggerFactory.getLogger(PersistenceFeatureTest.class);
87
88     private static final String JDBC_DRIVER = "fake.driver";
89     private static final String JDBC_URL = "fake.url";
90     private static final String JDBC_USER = "fake.user";
91     private static final String JDBC_PASSWD = "fake.password";
92     private static final String JTA_OSDIR = "target";
93     private static final String SRC_TEST_RESOURCES = "src/test/resources";
94
95     private static Properties stdprops;
96
97     private JpaDroolsSessionConnector jpa;
98     private DroolsSession sess;
99     private KieSession kiesess;
100     private BasicDataSource bds;
101     private EntityManagerFactory emf;
102     private Connection conn;
103     private Properties props;
104     private KieServices kiesvc;
105     private Environment kieenv;
106     private KieSessionConfiguration kiecfg;
107     private KieBase kiebase;
108     private KieStoreServices kiestore;
109     private KieContainer kiecont;
110     private TransactionManager transmgr;
111     private UserTransaction usertrans;
112     private TransactionSynchronizationRegistry transreg;
113     private PolicyController polctlr;
114     private PolicyContainer polcont;
115     private PolicySession polsess;
116     private PersistenceFeature.Factory fact;
117
118     private PersistenceFeature feat;
119
120     /**
121      * Setup before class.
122      * 
123      * @throws Exception exception
124      */
125     @BeforeClass
126     public static void setUpBeforeClass() throws Exception {
127         stdprops = new Properties();
128
129         stdprops.put(DroolsPersistenceProperties.DB_DRIVER, JDBC_DRIVER);
130         stdprops.put(DroolsPersistenceProperties.DB_URL, JDBC_URL);
131         stdprops.put(DroolsPersistenceProperties.DB_USER, JDBC_USER);
132         stdprops.put(DroolsPersistenceProperties.DB_PWD, JDBC_PASSWD);
133         stdprops.put(DroolsPersistenceProperties.JTA_OBJECTSTORE_DIR, JTA_OSDIR);
134         stdprops.put(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT, "50");
135
136         System.setProperty("com.arjuna.ats.arjuna.objectstore.objectStoreDir", "target/tm");
137         System.setProperty("ObjectStoreEnvironmentBean.objectStoreDir", "target/tm");
138     }
139
140     /**
141      * Setup.
142      * 
143      * @throws Exception exception
144      */
145     @Before
146     public void setUp() throws Exception {
147         jpa = mock(JpaDroolsSessionConnector.class);
148         sess = mock(DroolsSession.class);
149         bds = mock(BasicDataSource.class);
150         emf = mock(EntityManagerFactory.class);
151         kiesess = mock(KieSession.class);
152         conn = null;
153         props = new Properties();
154         kiesvc = mock(KieServices.class);
155         kieenv = mock(Environment.class);
156         kiecfg = mock(KieSessionConfiguration.class);
157         kiebase = mock(KieBase.class);
158         kiestore = mock(KieStoreServices.class);
159         kiecont = mock(KieContainer.class);
160         transmgr = mock(TransactionManager.class);
161         usertrans = mock(UserTransaction.class);
162         transreg = mock(TransactionSynchronizationRegistry.class);
163         polcont = mock(PolicyContainer.class);
164         polctlr = mock(PolicyController.class);
165         polsess = mock(PolicySession.class);
166         fact = mock(PersistenceFeature.Factory.class);
167
168         feat = new PersistenceFeature();
169         feat.setFactory(fact);
170
171         props.putAll(stdprops);
172
173         System.setProperty("com.arjuna.ats.arjuna.objectstore.objectStoreDir", "target/tm");
174         System.setProperty("ObjectStoreEnvironmentBean.objectStoreDir", "target/tm");
175
176         when(fact.getKieServices()).thenReturn(kiesvc);
177         when(fact.getTransMgr()).thenReturn(transmgr);
178         when(fact.getUserTrans()).thenReturn(usertrans);
179         when(fact.getTransSyncReg()).thenReturn(transreg);
180         when(fact.loadProperties(anyString())).thenReturn(props);
181
182         when(kiesvc.newEnvironment()).thenReturn(kieenv);
183         when(kiesvc.getStoreServices()).thenReturn(kiestore);
184         when(kiesvc.newKieSessionConfiguration()).thenReturn(kiecfg);
185
186         when(polcont.getKieContainer()).thenReturn(kiecont);
187
188         when(polsess.getPolicyContainer()).thenReturn(polcont);
189
190         when(kiecont.getKieBase(anyString())).thenReturn(kiebase);
191     }
192
193     /**
194      * Tear down.
195      */
196     @After
197     public void tearDown() {
198         // this will cause the in-memory test DB to be dropped
199         if (conn != null) {
200             try {
201                 conn.close();
202             } catch (SQLException e) {
203                 logger.warn("failed to close connection", e);
204             }
205         }
206
207         if (emf != null) {
208             try {
209                 emf.close();
210             } catch (IllegalArgumentException e) {
211                 logger.trace("ignored exception", e);
212             }
213         }
214     }
215
216     @Test
217     public void testGetContainerAdjunct_New() throws Exception {
218
219         feat.globalInit(null, SRC_TEST_RESOURCES);
220
221         mockDbConn(5);
222         setUpKie("myname", 999L, true);
223
224         // force getContainerAdjunct() to be invoked
225         feat.activatePolicySession(polcont, "myname", "mybase");
226
227         ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
228                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
229
230         verify(polcont, times(1)).setAdjunct(any(), adjcap.capture());
231
232         assertNotNull(adjcap.getValue());
233     }
234
235     @Test
236     public void testGetContainerAdjunct_Existing() throws Exception {
237
238         feat.globalInit(null, SRC_TEST_RESOURCES);
239
240         mockDbConn(5);
241         setUpKie("myname", 999L, true);
242
243         // force getContainerAdjunct() to be invoked
244         feat.activatePolicySession(polcont, "myname", "mybase");
245
246         ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
247                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
248
249         verify(polcont, times(1)).setAdjunct(any(), adjcap.capture());
250
251         // return adjunct on next call
252         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
253
254         // force getContainerAdjunct() to be invoked again
255         setUpKie("myname2", 999L, true);
256         feat.activatePolicySession(polcont, "myname2", "mybase");
257
258         // ensure it isn't invoked again
259         verify(polcont, times(1)).setAdjunct(any(), any());
260     }
261
262     @Test
263     public void testGetContainerAdjunct_WrongType() throws Exception {
264
265         feat.globalInit(null, SRC_TEST_RESOURCES);
266
267         mockDbConn(5);
268         setUpKie("myname", 999L, true);
269
270         // return false adjunct on next call
271         when(polcont.getAdjunct(any())).thenReturn("not-a-real-adjunct");
272
273         // force getContainerAdjunct() to be invoked
274         setUpKie("myname2", 999L, true);
275         feat.activatePolicySession(polcont, "myname2", "mybase");
276
277         ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
278                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
279
280         verify(polcont, times(1)).setAdjunct(any(), adjcap.capture());
281
282         assertNotNull(adjcap.getValue());
283     }
284
285     @Test
286     public void testGetSequenceNumber() {
287         assertEquals(1, feat.getSequenceNumber());
288     }
289
290     @Test
291     public void testGlobalInit() throws Exception {
292
293         feat.globalInit(null, SRC_TEST_RESOURCES);
294
295         // verify that various factory methods were invoked
296         verify(fact).getKieServices();
297         verify(fact).loadProperties("src/test/resources/feature-session-persistence.properties");
298     }
299
300     @Test(expected = NullPointerException.class)
301     public void testGlobalInitIoEx() throws Exception {
302
303         when(fact.loadProperties(anyString())).thenThrow(new IOException("expected exception"));
304
305         feat.globalInit(null, SRC_TEST_RESOURCES);
306     }
307
308     @Test
309     public void testActivatePolicySession() throws Exception {
310         final PreparedStatement ps = mockDbConn(5);
311         setUpKie("myname", 999L, true);
312
313         feat.globalInit(null, SRC_TEST_RESOURCES);
314         feat.beforeActivate(null);
315
316         KieSession session = feat.activatePolicySession(polcont, "myname", "mybase");
317
318         verify(kiestore).loadKieSession(anyLong(), any(), any(), any());
319         verify(kiestore, never()).newKieSession(any(), any(), any());
320
321         assertEquals(session, kiesess);
322
323         verify(ps).executeUpdate();
324
325         verify(kieenv, times(4)).set(anyString(), any());
326
327         verify(jpa).get("myname");
328         verify(jpa).replace(any());
329     }
330
331     @Test
332     public void testActivatePolicySession_NoPersistence() throws Exception {
333         feat.globalInit(null, SRC_TEST_RESOURCES);
334
335         final PreparedStatement ps = mockDbConn(5);
336         setUpKie("myname", 999L, true);
337
338         props.remove("persistence.type");
339
340         feat.beforeStart(null);
341
342         assertNull(feat.activatePolicySession(polcont, "myname", "mybase"));
343
344         verify(ps, never()).executeUpdate();
345         verify(kiestore, never()).loadKieSession(anyLong(), any(), any(), any());
346         verify(kiestore, never()).newKieSession(any(), any(), any());
347     }
348
349     /** Verifies that a new KIE session is created when there is no existing session entity. */
350     @Test
351     public void testActivatePolicySession_New() throws Exception {
352         feat.globalInit(null, SRC_TEST_RESOURCES);
353
354         mockDbConn(5);
355         setUpKie("noName", 999L, true);
356
357         KieSession session = feat.activatePolicySession(polcont, "myname", "mybase");
358
359         verify(kiestore, never()).loadKieSession(anyLong(), any(), any(), any());
360         verify(kiestore).newKieSession(any(), any(), any());
361
362         assertEquals(session, kiesess);
363
364         verify(kieenv, times(4)).set(anyString(), any());
365
366         verify(jpa).get("myname");
367         verify(jpa).replace(any());
368     }
369
370     /**
371      * Verifies that a new KIE session is created when there KIE fails to load an existing session.
372      */
373     @Test
374     public void testActivatePolicySession_LoadFailed() throws Exception {
375         feat.globalInit(null, SRC_TEST_RESOURCES);
376
377         mockDbConn(5);
378         setUpKie("myname", 999L, false);
379
380         KieSession session = feat.activatePolicySession(polcont, "myname", "mybase");
381
382         verify(kiestore).loadKieSession(anyLong(), any(), any(), any());
383         verify(kiestore).newKieSession(any(), any(), any());
384
385         assertEquals(session, kiesess);
386
387         verify(kieenv, times(4)).set(anyString(), any());
388
389         verify(jpa).get("myname");
390
391         ArgumentCaptor<DroolsSession> drools = ArgumentCaptor.forClass(DroolsSession.class);
392         verify(jpa).replace(drools.capture());
393
394         assertEquals("myname", drools.getValue().getSessionName());
395         assertEquals(100L, drools.getValue().getSessionId());
396     }
397
398     @Test
399     public void testLoadDataSource() throws Exception {
400         feat.globalInit(null, SRC_TEST_RESOURCES);
401
402         mockDbConn(5);
403         setUpKie("myname", 999L, false);
404
405         feat.activatePolicySession(polcont, "myname", "mybase");
406
407         verify(fact).makeEntMgrFact(any());
408     }
409
410     @Test
411     public void testConfigureSysProps() throws Exception {
412         feat.globalInit(null, SRC_TEST_RESOURCES);
413
414         mockDbConn(5);
415         setUpKie("myname", 999L, false);
416
417         feat.activatePolicySession(polcont, "myname", "mybase");
418
419         assertEquals("60", System.getProperty("com.arjuna.ats.arjuna.coordinator.defaultTimeout"));
420         assertEquals(JTA_OSDIR, System.getProperty("com.arjuna.ats.arjuna.objectstore.objectStoreDir"));
421         assertEquals(JTA_OSDIR, System.getProperty("ObjectStoreEnvironmentBean.objectStoreDir"));
422     }
423
424     @Test
425     public void testConfigureKieEnv() throws Exception {
426         feat.globalInit(null, SRC_TEST_RESOURCES);
427
428         mockDbConn(5);
429         setUpKie("myname", 999L, false);
430
431         feat.activatePolicySession(polcont, "myname", "mybase");
432
433         verify(kieenv, times(4)).set(any(), any());
434
435         verify(kieenv).set(EnvironmentName.ENTITY_MANAGER_FACTORY, emf);
436         verify(kieenv).set(EnvironmentName.TRANSACTION, usertrans);
437         verify(kieenv).set(EnvironmentName.TRANSACTION_MANAGER, transmgr);
438         verify(kieenv).set(EnvironmentName.TRANSACTION_SYNCHRONIZATION_REGISTRY, transreg);
439
440         verify(bds, times(1)).close();
441     }
442
443     @Test
444     public void testConfigureKieEnv_RtEx() throws Exception {
445         feat.globalInit(null, SRC_TEST_RESOURCES);
446
447         mockDbConn(5);
448         setUpKie("myname", 999L, false);
449
450         when(fact.getUserTrans()).thenThrow(new IllegalArgumentException("expected exception"));
451
452         try {
453             feat.activatePolicySession(polcont, "myname", "mybase");
454             fail("missing exception");
455
456         } catch (IllegalArgumentException ex) {
457             logger.trace("expected exception", ex);
458         }
459
460         verify(bds, times(2)).close();
461     }
462
463     @Test
464     public void testLoadKieSession() throws Exception {
465         feat.globalInit(null, SRC_TEST_RESOURCES);
466
467         mockDbConn(5);
468         setUpKie("myname", 999L, true);
469
470         KieSession session = feat.activatePolicySession(polcont, "myname", "mybase");
471
472         verify(kiestore).loadKieSession(999L, kiebase, kiecfg, kieenv);
473         verify(kiestore, never()).newKieSession(any(), any(), any());
474
475         assertEquals(session, kiesess);
476     }
477
478     /*
479      * Verifies that loadKieSession() returns null (thus causing newKieSession()
480      * to be called) when an Exception occurs.
481      */
482     @Test
483     public void testLoadKieSession_Ex() throws Exception {
484         feat.globalInit(null, SRC_TEST_RESOURCES);
485
486         mockDbConn(5);
487         setUpKie("myname", 999L, false);
488
489         when(kiestore.loadKieSession(anyLong(), any(), any(), any()))
490             .thenThrow(new IllegalArgumentException("expected exception"));
491
492         KieSession session = feat.activatePolicySession(polcont, "myname", "mybase");
493
494         verify(kiestore).loadKieSession(anyLong(), any(), any(), any());
495         verify(kiestore).newKieSession(any(), any(), any());
496
497         assertEquals(session, kiesess);
498     }
499
500     @Test
501     public void testNewKieSession() throws Exception {
502         feat.globalInit(null, SRC_TEST_RESOURCES);
503
504         mockDbConn(5);
505         setUpKie("myname", 999L, false);
506
507         KieSession session = feat.activatePolicySession(polcont, "myname", "mybase");
508
509         verify(kiestore).newKieSession(kiebase, null, kieenv);
510
511         assertEquals(session, kiesess);
512     }
513
514     @Test
515     public void testLoadDataSource_DiffSession() throws Exception {
516         feat.globalInit(null, SRC_TEST_RESOURCES);
517
518         mockDbConn(5);
519         setUpKie("myname", 999L, false);
520         feat.activatePolicySession(polcont, "myname", "mybase");
521
522         ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
523                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
524
525         verify(polcont).setAdjunct(any(), adjcap.capture());
526
527         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
528
529         setUpKie("myname2", 999L, false);
530
531         // invoke it again
532         feat.activatePolicySession(polcont, "myname2", "mybase");
533
534         verify(fact, times(2)).makeEntMgrFact(any());
535     }
536
537     @Test
538     public void testSelectThreadModel_Persistent() throws Exception {
539         setUpKie("myname", 999L, true);
540
541         ThreadModel model = feat.selectThreadModel(polsess);
542         assertNotNull(model);
543         assertTrue(model instanceof PersistentThreadModel);
544     }
545
546     @Test
547     public void testSelectThreadModel_NotPersistent() throws Exception {
548         when(fact.getPolicyController(any())).thenReturn(polctlr);
549         assertNull(feat.selectThreadModel(polsess));
550     }
551
552     @Test
553     public void testSelectThreadModel_Start__Run_Update_Stop() throws Exception {
554         setUpKie("myname", 999L, true);
555
556         ThreadModel model = feat.selectThreadModel(polsess);
557         assertNotNull(model);
558         assertTrue(model instanceof PersistentThreadModel);
559
560         when(polsess.getKieSession()).thenReturn(kiesess);
561
562         model.start();
563         new CountDownLatch(1).await(10, TimeUnit.MILLISECONDS);
564         model.updated();
565         model.stop();
566     }
567
568     @Test
569     public void testDisposeKieSession() throws Exception {
570         feat.globalInit(null, SRC_TEST_RESOURCES);
571
572         final ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
573                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
574
575         mockDbConn(5);
576         setUpKie("myname", 999L, false);
577
578         feat.activatePolicySession(polcont, "myname", "mybase");
579
580         verify(emf, never()).close();
581         verify(polcont).setAdjunct(any(), adjcap.capture());
582
583         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
584
585         feat.disposeKieSession(polsess);
586
587         // call twice to ensure it isn't re-closed
588         feat.disposeKieSession(polsess);
589
590         verify(emf, times(1)).close();
591     }
592
593     @Test
594     public void testDisposeKieSession_NoAdjunct() throws Exception {
595         feat.globalInit(null, SRC_TEST_RESOURCES);
596
597         feat.disposeKieSession(polsess);
598     }
599
600     @Test
601     public void testDisposeKieSession_NoPersistence() throws Exception {
602         feat.globalInit(null, SRC_TEST_RESOURCES);
603
604         final ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
605                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
606
607         mockDbConn(5);
608         setUpKie("myname", 999L, false);
609
610         feat.activatePolicySession(polcont, "myname", "mybase");
611
612         verify(emf, never()).close();
613         verify(polcont).setAdjunct(any(), adjcap.capture());
614
615         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
616
617         // specify a session that was never loaded
618         when(polsess.getName()).thenReturn("anotherName");
619
620         feat.disposeKieSession(polsess);
621
622         verify(emf, never()).close();
623     }
624
625     @Test
626     public void testDestroyKieSession() throws Exception {
627         feat.globalInit(null, SRC_TEST_RESOURCES);
628
629         final ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
630                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
631
632         mockDbConn(5);
633         setUpKie("myname", 999L, false);
634
635         feat.activatePolicySession(polcont, "myname", "mybase");
636
637         verify(emf, never()).close();
638         verify(polcont).setAdjunct(any(), adjcap.capture());
639
640         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
641
642         feat.destroyKieSession(polsess);
643
644         // call twice to ensure it isn't re-closed
645         feat.destroyKieSession(polsess);
646
647         verify(emf, times(1)).close();
648     }
649
650     @Test
651     public void testDestroyKieSession_NoAdjunct() throws Exception {
652         feat.globalInit(null, SRC_TEST_RESOURCES);
653
654         feat.destroyKieSession(polsess);
655     }
656
657     @Test
658     public void testDestroyKieSession_NoPersistence() throws Exception {
659         feat.globalInit(null, SRC_TEST_RESOURCES);
660
661         final ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
662                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
663
664         mockDbConn(5);
665         setUpKie("myname", 999L, false);
666
667         feat.activatePolicySession(polcont, "myname", "mybase");
668
669         verify(emf, never()).close();
670         verify(polcont).setAdjunct(any(), adjcap.capture());
671
672         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
673
674         // specify a session that was never loaded
675         when(polsess.getName()).thenReturn("anotherName");
676
677         feat.destroyKieSession(polsess);
678
679         verify(emf, never()).close();
680     }
681
682     @Test
683     public void testAfterStart() {
684         assertFalse(feat.afterStart(null));
685     }
686
687     @Test
688     public void testBeforeStart() {
689         assertFalse(feat.beforeStart(null));
690     }
691
692     @Test
693     public void testBeforeShutdown() {
694         assertFalse(feat.beforeShutdown(null));
695     }
696
697     @Test
698     public void testAfterShutdown() {
699         assertFalse(feat.afterShutdown(null));
700     }
701
702     @Test
703     public void testBeforeConfigure() {
704         assertFalse(feat.beforeConfigure(null, null));
705     }
706
707     @Test
708     public void testAfterConfigure() {
709         assertFalse(feat.afterConfigure(null));
710     }
711
712     @Test
713     public void testBeforeActivate() {
714         assertFalse(feat.beforeActivate(null));
715     }
716
717     @Test
718     public void testAfterActivate() {
719         assertFalse(feat.afterActivate(null));
720     }
721
722     @Test
723     public void testBeforeDeactivate() {
724         assertFalse(feat.beforeDeactivate(null));
725     }
726
727     @Test
728     public void testAfterDeactivate() {
729         assertFalse(feat.afterDeactivate(null));
730     }
731
732     @Test
733     public void testBeforeStop() {
734         assertFalse(feat.beforeStop(null));
735     }
736
737     @Test
738     public void testAfterStop() {
739         assertFalse(feat.afterStop(null));
740     }
741
742     @Test
743     public void testBeforeLock() {
744         assertFalse(feat.beforeLock(null));
745     }
746
747     @Test
748     public void testAfterLock() {
749         assertFalse(feat.afterLock(null));
750     }
751
752     @Test
753     public void testBeforeUnlock() {
754         assertFalse(feat.beforeUnlock(null));
755     }
756
757     @Test
758     public void testAfterUnlock() {
759         assertFalse(feat.afterUnlock(null));
760     }
761
762     @Test
763     public void testGetPersistenceTimeout_Valid() throws Exception {
764         final PreparedStatement statement = mockDbConn(5);
765
766         feat.globalInit(null, SRC_TEST_RESOURCES);
767
768         setUpKie("myname", 999L, true);
769
770         feat.activatePolicySession(polcont, "myname", "mybase");
771
772         verify(statement).executeUpdate();
773     }
774
775     @Test
776     public void testGetPersistenceTimeout_Missing() throws Exception {
777
778         props.remove(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT);
779
780         final PreparedStatement statement = mockDbConn(0);
781
782         feat.globalInit(null, SRC_TEST_RESOURCES);
783
784         setUpKie("myname", 999L, true);
785
786         feat.activatePolicySession(polcont, "myname", "mybase");
787
788         verify(statement, never()).executeUpdate();
789     }
790
791     @Test
792     public void testGetPersistenceTimeout_Invalid() throws Exception {
793         props.setProperty(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT, "abc");
794         final PreparedStatement s = mockDbConn(0);
795
796         feat.globalInit(null, SRC_TEST_RESOURCES);
797
798         setUpKie("myname", 999L, true);
799
800         feat.activatePolicySession(polcont, "myname", "mybase");
801
802         verify(s, never()).executeUpdate();
803     }
804
805     @Test
806     public void testCleanUpSessionInfo() throws Exception {
807         setUpKie("myname", 999L, true);
808
809         // use a real DB so we can verify that the "delete" works correctly
810         fact = new PartialFactory();
811         feat.setFactory(fact);
812
813         makeSessionInfoTbl(20000);
814
815         // create mock entity manager for use by JPA connector
816         EntityManager em = mock(EntityManager.class);
817         when(emf.createEntityManager()).thenReturn(em);
818
819         feat.globalInit(null, SRC_TEST_RESOURCES);
820
821         feat.beforeStart(null);
822         feat.activatePolicySession(polcont, "myname", "mybase");
823
824         assertEquals("[1, 4, 5]", getSessions().toString());
825     }
826
827     @Test
828     public void testCleanUpSessionInfo_WithBeforeStart() throws Exception {
829         final PreparedStatement statement = mockDbConn(0);
830
831         feat.globalInit(null, SRC_TEST_RESOURCES);
832
833         setUpKie("myname", 999L, true);
834
835         // reset
836         feat.beforeStart(null);
837
838         feat.activatePolicySession(polcont, "myname", "mybase");
839         verify(statement, times(1)).executeUpdate();
840
841         // should not clean-up again
842         feat.activatePolicySession(polcont, "myname", "mybase");
843         feat.activatePolicySession(polcont, "myname", "mybase");
844         verify(statement, times(1)).executeUpdate();
845
846         // reset
847         feat.beforeStart(null);
848
849         feat.activatePolicySession(polcont, "myname", "mybase");
850         verify(statement, times(2)).executeUpdate();
851
852         // should not clean-up again
853         feat.activatePolicySession(polcont, "myname", "mybase");
854         feat.activatePolicySession(polcont, "myname", "mybase");
855         verify(statement, times(2)).executeUpdate();
856     }
857
858     @Test
859     public void testCleanUpSessionInfo_WithBeforeActivate() throws Exception {
860         final PreparedStatement statement = mockDbConn(0);
861
862         feat.globalInit(null, SRC_TEST_RESOURCES);
863
864         setUpKie("myname", 999L, true);
865
866         // reset
867         feat.beforeActivate(null);
868
869         feat.activatePolicySession(polcont, "myname", "mybase");
870         verify(statement, times(1)).executeUpdate();
871
872         // should not clean-up again
873         feat.activatePolicySession(polcont, "myname", "mybase");
874         feat.activatePolicySession(polcont, "myname", "mybase");
875         verify(statement, times(1)).executeUpdate();
876
877         // reset
878         feat.beforeActivate(null);
879
880         feat.activatePolicySession(polcont, "myname", "mybase");
881         verify(statement, times(2)).executeUpdate();
882
883         // should not clean-up again
884         feat.activatePolicySession(polcont, "myname", "mybase");
885         feat.activatePolicySession(polcont, "myname", "mybase");
886         verify(statement, times(2)).executeUpdate();
887     }
888
889     @Test
890     public void testCleanUpSessionInfo_NoTimeout() throws Exception {
891
892         props.remove(DroolsPersistenceProperties.DB_SESSIONINFO_TIMEOUT);
893
894         final PreparedStatement statement = mockDbConn(0);
895
896         feat.globalInit(null, SRC_TEST_RESOURCES);
897
898         setUpKie("myname", 999L, true);
899
900         feat.activatePolicySession(polcont, "myname", "mybase");
901
902         verify(statement, never()).executeUpdate();
903     }
904
905     @Test
906     public void testCleanUpSessionInfo_NoUrl() throws Exception {
907         final PreparedStatement statement = mockDbConn(0);
908
909         props.remove(DroolsPersistenceProperties.DB_URL);
910
911         feat.globalInit(null, SRC_TEST_RESOURCES);
912
913         setUpKie("myname", 999L, true);
914
915         try {
916             feat.activatePolicySession(polcont, "myname", "mybase");
917             fail("missing exception");
918         } catch (RuntimeException e) {
919             logger.trace("expected exception", e);
920         }
921
922         verify(statement, never()).executeUpdate();
923     }
924
925     @Test
926     public void testCleanUpSessionInfo_NoUser() throws Exception {
927         final PreparedStatement statement = mockDbConn(0);
928
929         props.remove(DroolsPersistenceProperties.DB_USER);
930
931         feat.globalInit(null, SRC_TEST_RESOURCES);
932
933         setUpKie("myname", 999L, true);
934
935         try {
936             feat.activatePolicySession(polcont, "myname", "mybase");
937             fail("missing exception");
938         } catch (RuntimeException e) {
939             logger.trace("expected exception", e);
940         }
941
942         verify(statement, never()).executeUpdate();
943     }
944
945     @Test
946     public void testCleanUpSessionInfo_NoPassword() throws Exception {
947         final PreparedStatement statement = mockDbConn(0);
948
949         props.remove(DroolsPersistenceProperties.DB_PWD);
950
951         feat.globalInit(null, SRC_TEST_RESOURCES);
952
953         setUpKie("myname", 999L, true);
954
955         try {
956             feat.activatePolicySession(polcont, "myname", "mybase");
957             fail("missing exception");
958         } catch (RuntimeException e) {
959             logger.trace("expected exception", e);
960         }
961
962         verify(statement, never()).executeUpdate();
963     }
964
965     @Test
966     public void testCleanUpSessionInfo_SqlEx() throws Exception {
967         final PreparedStatement statement = mockDbConn(-1);
968
969         feat.globalInit(null, SRC_TEST_RESOURCES);
970
971         setUpKie("myname", 999L, true);
972
973         feat.activatePolicySession(polcont, "myname", "mybase");
974
975         verify(statement).executeUpdate();
976     }
977
978     @Test
979     public void testGetDroolsSessionConnector() throws Exception {
980         feat.globalInit(null, SRC_TEST_RESOURCES);
981
982         mockDbConn(5);
983         setUpKie("myname", 999L, true);
984
985         feat.activatePolicySession(polcont, "myname", "mybase");
986
987         verify(fact).makeJpaConnector(emf);
988     }
989
990     @Test
991     public void testReplaceSession() throws Exception {
992         feat.globalInit(null, SRC_TEST_RESOURCES);
993
994         final ArgumentCaptor<DroolsSession> sesscap = ArgumentCaptor.forClass(DroolsSession.class);
995
996         mockDbConn(5);
997         setUpKie("myname", 999L, true);
998
999         feat.activatePolicySession(polcont, "myname", "mybase");
1000
1001         verify(jpa).replace(sesscap.capture());
1002
1003         assertEquals("myname", sesscap.getValue().getSessionName());
1004         assertEquals(999L, sesscap.getValue().getSessionId());
1005     }
1006
1007     @Test
1008     public void testIsPersistenceEnabled_Auto() throws Exception {
1009         feat.globalInit(null, SRC_TEST_RESOURCES);
1010
1011         mockDbConn(5);
1012         setUpKie("myname", 999L, true);
1013
1014         props.setProperty("persistence.type", "auto");
1015
1016         assertNotNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1017     }
1018
1019     @Test
1020     public void testIsPersistenceEnabled_Native() throws Exception {
1021         feat.globalInit(null, SRC_TEST_RESOURCES);
1022
1023         mockDbConn(5);
1024         setUpKie("myname", 999L, true);
1025
1026         props.setProperty("persistence.type", "native");
1027
1028         assertNotNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1029     }
1030
1031     @Test
1032     public void testIsPersistenceEnabled_None() throws Exception {
1033         feat.globalInit(null, SRC_TEST_RESOURCES);
1034
1035         mockDbConn(5);
1036         setUpKie("myname", 999L, true);
1037
1038         props.remove("persistence.type");
1039
1040         assertNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1041     }
1042
1043     @Test
1044     public void testGetProperties_Ex() throws Exception {
1045         feat.globalInit(null, SRC_TEST_RESOURCES);
1046
1047         mockDbConn(5);
1048         setUpKie("myname", 999L, true);
1049
1050         when(fact.getPolicyController(polcont))
1051             .thenThrow(new IllegalArgumentException("expected exception"));
1052
1053         assertNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1054     }
1055
1056     @Test
1057     public void testGetProperty_Specific() throws Exception {
1058         feat.globalInit(null, SRC_TEST_RESOURCES);
1059
1060         mockDbConn(5);
1061         setUpKie("myname", 999L, true);
1062
1063         props.remove("persistence.type");
1064         props.setProperty("persistence.myname.type", "auto");
1065
1066         assertNotNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1067     }
1068
1069     @Test
1070     public void testGetProperty_Specific_None() throws Exception {
1071         feat.globalInit(null, SRC_TEST_RESOURCES);
1072
1073         mockDbConn(5);
1074         setUpKie("myname", 999L, true);
1075
1076         props.remove("persistence.type");
1077         props.setProperty("persistence.xxx.type", "auto");
1078
1079         assertNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1080     }
1081
1082     @Test
1083     public void testGetProperty_Both_SpecificOn() throws Exception {
1084         feat.globalInit(null, SRC_TEST_RESOURCES);
1085
1086         mockDbConn(5);
1087         setUpKie("myname", 999L, true);
1088
1089         props.setProperty("persistence.type", "other");
1090         props.setProperty("persistence.myname.type", "auto");
1091
1092         assertNotNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1093     }
1094
1095     @Test
1096     public void testGetProperty_Both_SpecificDisabledOff() throws Exception {
1097         feat.globalInit(null, SRC_TEST_RESOURCES);
1098
1099         mockDbConn(5);
1100         setUpKie("myname", 999L, true);
1101
1102         props.setProperty("persistence.type", "auto");
1103         props.setProperty("persistence.myname.type", "other");
1104
1105         assertNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1106     }
1107
1108     @Test
1109     public void testGetProperty_None() throws Exception {
1110         feat.globalInit(null, SRC_TEST_RESOURCES);
1111
1112         mockDbConn(5);
1113         setUpKie("myname", 999L, true);
1114
1115         props.remove("persistence.type");
1116
1117         assertNull(feat.activatePolicySession(polcont, "myname", "mybase"));
1118     }
1119
1120     @Test
1121     public void testPersistenceFeatureException() {
1122         SecurityException secex = new SecurityException("expected exception");
1123         PersistenceFeatureException ex = new PersistenceFeatureException(secex);
1124
1125         assertEquals(secex, ex.getCause());
1126     }
1127
1128     @Test
1129     public void testDsEmf_RtEx() throws Exception {
1130         feat.globalInit(null, SRC_TEST_RESOURCES);
1131
1132         mockDbConn(5);
1133         setUpKie("myname", 999L, false);
1134
1135         when(fact.makeEntMgrFact(any())).thenThrow(new IllegalArgumentException("expected exception"));
1136
1137         try {
1138             feat.activatePolicySession(polcont, "myname", "mybase");
1139             fail("missing exception");
1140
1141         } catch (IllegalArgumentException ex) {
1142             logger.trace("expected exception", ex);
1143         }
1144
1145         verify(bds, times(2)).close();
1146     }
1147
1148     @Test
1149     public void testDsEmf_Close_RtEx() throws Exception {
1150         feat.globalInit(null, SRC_TEST_RESOURCES);
1151
1152         mockDbConn(5);
1153         setUpKie("myname", 999L, false);
1154
1155         feat.activatePolicySession(polcont, "myname", "mybase");
1156
1157         ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
1158                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
1159
1160         verify(polcont, times(1)).setAdjunct(any(), adjcap.capture());
1161
1162         // return adjunct on next call
1163         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
1164
1165         try {
1166             doThrow(new IllegalArgumentException("expected exception")).when(emf).close();
1167
1168             feat.destroyKieSession(polsess);
1169             fail("missing exception");
1170
1171         } catch (IllegalArgumentException ex) {
1172             logger.trace("expected exception", ex);
1173         }
1174
1175         verify(bds, times(2)).close();
1176     }
1177
1178     @Test
1179     public void testDsEmf_CloseDataSource_RtEx() throws Exception {
1180         feat.globalInit(null, SRC_TEST_RESOURCES);
1181
1182         mockDbConn(5);
1183         setUpKie("myname", 999L, false);
1184
1185         feat.activatePolicySession(polcont, "myname", "mybase");
1186
1187         ArgumentCaptor<PersistenceFeature.ContainerAdjunct> adjcap =
1188                 ArgumentCaptor.forClass(PersistenceFeature.ContainerAdjunct.class);
1189
1190         verify(polcont, times(1)).setAdjunct(any(), adjcap.capture());
1191
1192         // return adjunct on next call
1193         when(polcont.getAdjunct(any())).thenReturn(adjcap.getValue());
1194
1195         try {
1196             doThrow(new SQLException("expected exception")).when(bds).close();
1197
1198             feat.destroyKieSession(polsess);
1199             fail("missing exception");
1200
1201         } catch (PersistenceFeatureException ex) {
1202             logger.trace("expected exception", ex);
1203         }
1204     }
1205
1206     /**
1207      * Gets an ordered list of ids of the current SessionInfo records.
1208      *
1209      * @return ordered list of SessInfo IDs
1210      * @throws SQLException sql exception
1211      * @throws IOException io exception
1212      */
1213     private List<Integer> getSessions() throws SQLException, IOException {
1214         attachDb();
1215
1216         ArrayList<Integer> lst = new ArrayList<>(5);
1217
1218         try (PreparedStatement stmt = conn.prepareStatement("SELECT id from sessioninfo order by id");
1219                 ResultSet rs = stmt.executeQuery()) {
1220
1221             while (rs.next()) {
1222                 lst.add(rs.getInt(1));
1223             }
1224         }
1225
1226         return lst;
1227     }
1228
1229     /**
1230      * Sets up for doing invoking the newKieSession() method.
1231      *
1232      * @param sessnm name to which JPA should respond with a session
1233      * @param sessid session id to be returned by the session
1234      * @param loadOk {@code true} if loadKieSession() should return a value, {@code false} to return
1235      *     null
1236      * @throws Exception exception
1237      */
1238     private void setUpKie(String sessnm, long sessid, boolean loadOk) throws Exception {
1239
1240         when(fact.makeJpaConnector(emf)).thenReturn(jpa);
1241         when(fact.makeEntMgrFact(any())).thenReturn(emf);
1242         when(fact.getPolicyController(polcont)).thenReturn(polctlr);
1243
1244         props.setProperty("persistence.type", "auto");
1245
1246         when(polctlr.getProperties()).thenReturn(props);
1247
1248         when(jpa.get(sessnm)).thenReturn(sess);
1249
1250         when(sess.getSessionId()).thenReturn(sessid);
1251
1252         when(polsess.getPolicyContainer()).thenReturn(polcont);
1253         when(polsess.getName()).thenReturn(sessnm);
1254
1255         if (loadOk) {
1256             when(kiesess.getIdentifier()).thenReturn(sessid);
1257             when(kiestore.loadKieSession(anyLong(), any(), any(), any())).thenReturn(kiesess);
1258
1259         } else {
1260             // use an alternate id for the new session
1261             when(kiesess.getIdentifier()).thenReturn(100L);
1262             when(kiestore.loadKieSession(anyLong(), any(), any(), any())).thenReturn(null);
1263         }
1264
1265         when(kiestore.newKieSession(any(), any(), any())).thenReturn(kiesess);
1266     }
1267
1268     /**
1269      * Creates the SessionInfo DB table and populates it with some data.
1270      *
1271      * @param expMs number of milli-seconds for expired sessioninfo records
1272      * @throws SQLException exception
1273      * @throws IOException exception
1274      */
1275     private void makeSessionInfoTbl(int expMs) throws SQLException, IOException {
1276
1277         attachDb();
1278
1279         try (PreparedStatement stmt =
1280                 conn.prepareStatement("CREATE TABLE sessioninfo(id int, lastmodificationdate timestamp)")) {
1281
1282             stmt.executeUpdate();
1283         }
1284
1285         try (PreparedStatement stmt =
1286                 conn.prepareStatement("INSERT into sessioninfo(id, lastmodificationdate) values(?, ?)")) {
1287
1288             Timestamp ts;
1289
1290             // current data
1291             ts = new Timestamp(System.currentTimeMillis());
1292             stmt.setTimestamp(2, ts);
1293
1294             stmt.setInt(1, 1);
1295             stmt.executeUpdate();
1296
1297             stmt.setInt(1, 4);
1298             stmt.executeUpdate();
1299
1300             stmt.setInt(1, 5);
1301             stmt.executeUpdate();
1302
1303             // expired data
1304             ts = new Timestamp(System.currentTimeMillis() - expMs);
1305             stmt.setTimestamp(2, ts);
1306
1307             stmt.setInt(1, 2);
1308             stmt.executeUpdate();
1309
1310             stmt.setInt(1, 3);
1311             stmt.executeUpdate();
1312         }
1313     }
1314
1315     /**
1316      * Attaches {@link #conn} to the DB, if it isn't already attached.
1317      *
1318      * @throws SQLException sql exception
1319      * @throws IOException if the property file cannot be read
1320      */
1321     private void attachDb() throws SQLException, IOException {
1322         if (conn == null) {
1323             Properties props = loadDbProps();
1324
1325             conn =
1326                     DriverManager.getConnection(
1327                             props.getProperty(DroolsPersistenceProperties.DB_URL),
1328                             props.getProperty(DroolsPersistenceProperties.DB_USER),
1329                             props.getProperty(DroolsPersistenceProperties.DB_PWD));
1330             conn.setAutoCommit(true);
1331         }
1332     }
1333
1334     /**
1335      * Loads the DB properties from the file, <i>feature-session-persistence.properties</i>.
1336      *
1337      * @return the properties that were loaded
1338      * @throws IOException if the property file cannot be read
1339      * @throws FileNotFoundException if the property file does not exist
1340      */
1341     private Properties loadDbProps() throws IOException, FileNotFoundException {
1342
1343         Properties props = new Properties();
1344
1345         try (FileReader rdr =
1346                 new FileReader("src/test/resources/feature-session-persistence.properties")) {
1347             props.load(rdr);
1348         }
1349
1350         return props;
1351     }
1352
1353     /**
1354      * Create a mock DB connection and statement.
1355      *
1356      * @param retval value to be returned when the statement is executed, or negative to throw an
1357      *     exception
1358      * @return the statement that will be returned by the connection
1359      * @throws SQLException sql exception
1360      */
1361     private PreparedStatement mockDbConn(int retval) throws SQLException {
1362         Connection connection = mock(Connection.class);
1363         PreparedStatement statement = mock(PreparedStatement.class);
1364
1365         when(bds.getConnection()).thenReturn(connection);
1366         when(fact.makeDataSource(any())).thenReturn(bds);
1367         when(connection.prepareStatement(anyString())).thenReturn(statement);
1368
1369         if (retval < 0) {
1370             // should throw an exception
1371             when(statement.executeUpdate()).thenThrow(new SQLException("expected exception"));
1372
1373         } else {
1374             // should return the value
1375             when(statement.executeUpdate()).thenReturn(retval);
1376         }
1377
1378         return statement;
1379     }
1380
1381     /** A partial factory, which exports a few of the real methods, but overrides the rest. */
1382     private class PartialFactory extends PersistenceFeature.Factory {
1383
1384         @Override
1385         public TransactionManager getTransMgr() {
1386             return transmgr;
1387         }
1388
1389         @Override
1390         public UserTransaction getUserTrans() {
1391             return usertrans;
1392         }
1393
1394         @Override
1395         public TransactionSynchronizationRegistry getTransSyncReg() {
1396             return transreg;
1397         }
1398
1399         @Override
1400         public KieServices getKieServices() {
1401             return kiesvc;
1402         }
1403
1404         @Override
1405         public EntityManagerFactory makeEntMgrFact(Map<String, Object> props) {
1406             return emf;
1407         }
1408
1409         @Override
1410         public PolicyController getPolicyController(PolicyContainer container) {
1411             return polctlr;
1412         }
1413     }
1414 }