2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.policy.drools.persistence;
23 import java.io.IOException;
24 import java.sql.Connection;
25 import java.sql.DriverManager;
26 import java.sql.PreparedStatement;
27 import java.sql.SQLException;
28 import java.util.Date;
29 import java.util.HashMap;
31 import java.util.Properties;
33 import javax.persistence.EntityManagerFactory;
34 import javax.persistence.Persistence;
36 import org.eclipse.persistence.config.PersistenceUnitProperties;
37 import org.kie.api.KieServices;
38 import org.kie.api.runtime.Environment;
39 import org.kie.api.runtime.EnvironmentName;
40 import org.kie.api.runtime.KieSession;
41 import org.kie.api.runtime.KieSessionConfiguration;
42 import org.openecomp.policy.common.ia.IntegrityAudit;
43 import org.openecomp.policy.common.ia.IntegrityAuditProperties;
44 import org.openecomp.policy.common.im.StateManagement;
45 import org.openecomp.policy.common.logging.eelf.MessageCodes;
46 import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
47 import org.openecomp.policy.common.logging.flexlogger.Logger;
48 import org.openecomp.policy.common.logging.flexlogger.PropertyUtil;
49 import org.openecomp.policy.drools.core.DroolsPDPIntegrityMonitor;
50 import org.openecomp.policy.drools.core.PolicySessionFeatureAPI;
51 import org.openecomp.policy.drools.core.IntegrityMonitorProperties;
52 import org.openecomp.policy.drools.core.PolicyContainer;
53 import org.openecomp.policy.drools.core.PolicySession;
54 import org.openecomp.policy.drools.features.PolicyEngineFeatureAPI;
55 import org.openecomp.policy.drools.im.PMStandbyStateChangeNotifier;
56 import org.openecomp.policy.drools.system.PolicyEngine;
58 import bitronix.tm.Configuration;
59 import bitronix.tm.TransactionManagerServices;
60 import bitronix.tm.resource.jdbc.PoolingDataSource;
63 * If this feature is supported, there is a single instance of it.
64 * It adds persistence to Drools sessions, but it is also intertwined with
65 * active/standby state management and IntegrityMonitor. For now, they are
66 * all treated as a single feature, but it would be nice to separate them.
68 * The bulk of the code here was once in other classes, such as
69 * 'PolicyContainer' and 'Main'. It was moved here as part of making this
70 * a separate optional feature.
72 public class PersistenceFeature implements PolicySessionFeatureAPI, PolicyEngineFeatureAPI
74 // get an instance of logger
75 private static Logger logger =
76 FlexLogger.getLogger(PersistenceFeature.class);
78 // 'KieServices' singleton
79 static private KieServices kieServices = KieServices.Factory.get();
81 private static DroolsPdp myPdp;
82 private static Object myPdpSync = new Object();
83 private static DroolsPdpsElectionHandler electionHandler;
85 // indicates whether persistence has been disabled
86 private static boolean persistenceDisabled = false;
89 * Used by JUnit testing to verify whether or not audit is running.
91 private static IntegrityAudit integrityAudit = null;
94 * Lookup the adjunct for this feature that is associated with the
95 * specified PolicyContainer. If not found, create one.
97 * @param policyContainer the container whose adjunct we are looking up,
98 * and possibly creating
99 * @return the associated 'ContainerAdjunct' instance, which may be new
101 private ContainerAdjunct getContainerAdjunct(PolicyContainer policyContainer)
103 Object rval = policyContainer.getAdjunct(this);
104 if (rval == null || ! (rval instanceof ContainerAdjunct))
106 // adjunct does not exist, or has the wrong type (should never happen)
107 rval = new ContainerAdjunct(policyContainer);
108 policyContainer.setAdjunct(this, rval);
110 return((ContainerAdjunct)rval);
113 /**************************/
114 /* 'FeatureAPI' interface */
115 /**************************/
121 public int getSequenceNumber()
130 public void globalInit(String args[], String configDir)
132 // Initialization code associated with 'PolicyContainer'
133 DroolsPDPIntegrityMonitor droolsPdpIntegrityMonitor = null;
136 droolsPdpIntegrityMonitor = DroolsPDPIntegrityMonitor.init(configDir);
140 logger.error(MessageCodes.EXCEPTION_ERROR, e,
141 "main", "DroolsPDPIntegrityMonitor.init()");
144 initializePersistence(configDir, droolsPdpIntegrityMonitor);
146 // 1. Start Integrity Monitor (unless it was specifically disabled in the CORE layer
149 if (persistenceDisabled) {
150 System.out.println("WARNING: Starting Engine with Persistance disabled");
151 logger.warn("Starting Engine with Persistance disabled");
153 DroolsPDPIntegrityMonitor im = null;
154 //At this point the DroolsPDPIntegrityMonitor instance must exist
156 im = DroolsPDPIntegrityMonitor.getInstance();
157 } catch (Exception e1) {
158 String msg = "policy-core startup failed to get DroolsPDPIntegrityMonitor instance: \n" + e1;
159 System.out.println(msg);
160 e1.printStackTrace();
162 //Now get the StateManagement instance so we can register our observer
163 StateManagement sm = im.getStateManager();
165 //Create an instance of the Observer
166 PMStandbyStateChangeNotifier pmNotifier = new PMStandbyStateChangeNotifier();
168 //Register the PMStandbyStateChangeNotifier Observer
169 sm.addObserver(pmNotifier);
174 * This is a hook to create a new persistent KieSession.
179 public KieSession activatePolicySession
180 (PolicyContainer policyContainer, String name, String kieBaseName)
182 return(getContainerAdjunct(policyContainer)
183 .newPersistentKieSession(name, kieBaseName));
190 public void disposeKieSession(PolicySession policySession)
192 // TODO: There should be one data source per session
193 getContainerAdjunct(policySession.getPolicyContainer())
194 .disposeKieSession();
201 public void destroyKieSession(PolicySession policySession)
203 // TODO: There should be one data source per session
204 getContainerAdjunct(policySession.getPolicyContainer())
205 .destroyKieSession();
212 public boolean isPersistenceEnabled()
214 return(!persistenceDisabled);
221 public boolean afterStart(PolicyEngine engine)
223 // ASSERTION: engine == PolicyEngine.manager
224 PolicyEngine.manager.lock();
232 public boolean beforeStart(PolicyEngine engine) {return false;}
238 public boolean beforeShutdown(PolicyEngine engine) {return false;}
244 public boolean afterShutdown(PolicyEngine engine) {return false;}
250 public boolean beforeConfigure(PolicyEngine engine, Properties properties) {return false;}
256 public boolean afterConfigure(PolicyEngine engine) {return false;}
262 public boolean beforeActivate(PolicyEngine engine)
264 if (persistenceDisabled)
269 // The following code will remove "old" Drools 'sessioninfo' records, so
270 // they aren't used to restore data to Drools sessions. This also has the
271 // useful side-effect of removing abandoned records as well.
273 // Fetch the timeout value, in seconds. If it is not specified or is
274 // less than or equal to 0, no records are removed.
276 String timeoutString = null;
280 timeoutString = DroolsPersistenceProperties.getProperty
281 ("persistence.sessioninfo.timeout");
282 if (timeoutString != null)
284 // timeout parameter is specified
285 timeout = Integer.valueOf(timeoutString);
288 catch (NumberFormatException e)
290 logger.error("Invalid value for Drools persistence property "
291 + "persistence.sessioninfo.timeout: "
296 // parameter is not specified, is <= 0, or is an invalid number
300 // if we reach this point, we are ready to remove old records from
303 Connection connection = null;
304 PreparedStatement statement = null;
307 // fetch database parameters from properties
309 String url = DroolsPersistenceProperties.getProperty
310 (DroolsPersistenceProperties.DB_URL);
311 String user = DroolsPersistenceProperties.getProperty
312 (DroolsPersistenceProperties.DB_USER);
313 String password = DroolsPersistenceProperties.getProperty
314 (DroolsPersistenceProperties.DB_PWD);
316 if (url != null && user != null && password != null)
319 connection = DriverManager.getConnection(url, user, password);
321 // create statement to delete old records
322 statement = connection.prepareStatement
323 ("DELETE FROM sessioninfo WHERE "
324 + "timestampdiff(second,lastmodificationdate,now()) > ?");
325 statement.setInt(1,timeout);
328 int count = statement.executeUpdate();
329 logger.info("Cleaning up sessioninfo table -- "
330 + count + " records removed");
333 catch (SQLException e)
335 logger.error("Clean up of sessioninfo table failed", e);
340 if (statement != null)
346 catch (SQLException e)
348 logger.error("SQL connection close failed", e);
351 if (connection != null)
357 catch (SQLException e)
359 logger.error("SQL connection close failed", e);
370 public boolean afterActivate(PolicyEngine engine) {return false;}
376 public boolean beforeDeactivate(PolicyEngine engine) {return false;}
382 public boolean afterDeactivate(PolicyEngine engine) {return false;}
388 public boolean beforeStop(PolicyEngine engine) {return false;}
394 public boolean afterStop(PolicyEngine engine) {return false;}
400 public boolean beforeLock(PolicyEngine engine) {return false;}
406 public boolean afterLock(PolicyEngine engine) {return false;}
412 public boolean beforeUnlock(PolicyEngine engine) {return false;}
418 public boolean afterUnlock(PolicyEngine engine) {return false;}
420 /**************************/
423 * @return 'true' if Drools persistence is disabled, and 'false' if not
425 static public boolean getPersistenceDisabled()
427 return(persistenceDisabled);
431 * Read in the persistence properties, determine whether persistence is
432 * enabled or disabled, and initialize persistence if enabled.
434 private static void initializePersistence(String configDir, DroolsPDPIntegrityMonitor droolsPdpIntegrityMonitor)
438 Properties pDrools = PropertyUtil.getProperties(configDir
439 + "/droolsPersistence.properties");
440 DroolsPersistenceProperties.initProperties(pDrools);
441 Properties pXacml = PropertyUtil.getProperties(configDir
442 + "/xacmlPersistence.properties");
443 XacmlPersistenceProperties.initProperties(pXacml);
444 if ("true".equals(pDrools.getProperty("persistenceDisabled"))) {
445 // 'persistenceDisabled' only relates to the 'drools'
446 // database. The fact that integrityMonitor/xacml depends upon
447 // persistence is an implementation detail there (which can't
448 // currently be disabled), and doesn't directly affect
450 persistenceDisabled = true;
452 } catch (IOException e1) {
453 logger.error(MessageCodes.MISS_PROPERTY_ERROR, e1,
454 "initializePersistence");
458 * Might as well handle the Integrity Monitor properties here, too.
462 PropertyUtil.getProperties(configDir + "/IntegrityMonitor.properties");
463 IntegrityMonitorProperties.initProperties(pIm);
464 logger.info("initializePersistence: resourceName=" + IntegrityMonitorProperties.getProperty(IntegrityMonitorProperties.PDP_INSTANCE_ID));
465 } catch (IOException e1) {
466 logger.error(MessageCodes.MISS_PROPERTY_ERROR, e1, "initializePersistence");
470 if (persistenceDisabled) {
471 // The persistence design is tied to 'DroolsPdpsElectionHandler',
472 // so we should bypass that as well. This also means that we
473 // won't get active/standby notifications, so we need to go
474 // into the 'active' state in order to have any 'PolicySession'
479 DroolsPdpsConnector conn = getDroolsPdpsConnector("ncompPU");
480 String uniquePdpId = IntegrityMonitorProperties.getProperty(IntegrityMonitorProperties.PDP_INSTANCE_ID);
481 if(uniquePdpId == null){
482 throw new NullPointerException();
486 * In a JUnit test environment, one or more PDPs may already have been
487 * inserted in the DB, so we need to check for this.
489 DroolsPdp existingPdp = conn.getPdp(uniquePdpId);
490 if (existingPdp != null) {
491 System.out.println("Found existing PDP record, pdpId="
492 + existingPdp.getPdpId() + ", isDesignated="
493 + existingPdp.isDesignated() + ", updatedDate="
494 + existingPdp.getUpdatedDate());
499 * Kick off integrity audit for Drools DB.
501 startIntegrityAudit(configDir);
503 synchronized(myPdpSync){
506 myPdp = new DroolsPdpImpl(uniquePdpId,false,4,new Date());
509 String site_name = "";
510 site_name = IntegrityMonitorProperties.getProperty(IntegrityMonitorProperties.SITE_NAME);
511 if (site_name == null) {
514 site_name = site_name.trim();
516 myPdp.setSiteName(site_name);
518 if(electionHandler == null){
519 electionHandler = new DroolsPdpsElectionHandler(conn,myPdp,droolsPdpIntegrityMonitor);
522 Configuration bitronixConfiguration = TransactionManagerServices.getConfiguration();
523 bitronixConfiguration.setJournal(null);
524 bitronixConfiguration.setServerId(uniquePdpId);
525 System.out.println("\n\nThis controller is a standby, waiting to be chosen as primary...\n\n");
526 logger.info("\n\nThis controller is a standby, waiting to be chosen as primary...\n\n");
529 private static void startIntegrityAudit(String configDir) {
531 logger.info("startIntegrityAudit: Entering, configDir='" + configDir
535 * Initialize Integrity Audit properties. file.
539 String resourceName = IntegrityMonitorProperties
540 .getProperty(IntegrityMonitorProperties.PDP_INSTANCE_ID);
543 * Load properties for auditing of Drools DB.
545 Properties droolsPia = PropertyUtil.getProperties(configDir
546 + "/IntegrityMonitor.properties");
549 * Supplement properties specific to the IntegrityMonitor (e.g.
550 * site_name, node_type, resource.name) with properties specific to
551 * persisting Drools DB entities (see
552 * ../policy-core/src/main/resources/persistence.xml)
554 * Note: integrity_audit_period_seconds is defined in
555 * IntegrityMonitor.properties, rather than creating a whole new
556 * "IntegrityAudit.properties" file for just one property.
560 IntegrityAuditProperties.DB_DRIVER,
561 DroolsPersistenceProperties
562 .getProperty(DroolsPersistenceProperties.DB_DRIVER));
563 droolsPia.setProperty(IntegrityAuditProperties.DB_PWD,
564 DroolsPersistenceProperties
565 .getProperty(DroolsPersistenceProperties.DB_PWD));
566 droolsPia.setProperty(IntegrityAuditProperties.DB_URL,
567 DroolsPersistenceProperties
568 .getProperty(DroolsPersistenceProperties.DB_URL));
569 droolsPia.setProperty(IntegrityAuditProperties.DB_USER,
570 DroolsPersistenceProperties
571 .getProperty(DroolsPersistenceProperties.DB_USER));
574 * Start audit for Drools DB.
576 integrityAudit = new IntegrityAudit(
577 resourceName, "auditDroolsPU", droolsPia);
578 integrityAudit.startAuditThread();
580 } catch (IOException e1) {
582 MessageCodes.MISS_PROPERTY_ERROR,
584 "initializePersistence: IntegrityAuditProperties: "
586 } catch (Exception e2) {
588 MessageCodes.EXCEPTION_ERROR,
590 "initializePersistence: IntegrityAuditProperties: "
594 logger.debug("startIntegrityAudit: Exiting");
599 * Moved code to instantiate a JpaDroolsPdpsConnector object from main() to
600 * this method, so it can also be accessed from StandbyStateChangeNotifier
603 public static DroolsPdpsConnector getDroolsPdpsConnector(String pu) {
605 Map<String, Object> propMap = new HashMap<String, Object>();
606 propMap.put("javax.persistence.jdbc.driver", DroolsPersistenceProperties
607 .getProperty(DroolsPersistenceProperties.DB_DRIVER));
608 propMap.put("javax.persistence.jdbc.url",
609 DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_URL));
610 propMap.put("javax.persistence.jdbc.user", DroolsPersistenceProperties
611 .getProperty(DroolsPersistenceProperties.DB_USER));
612 propMap.put("javax.persistence.jdbc.password",
613 DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_PWD));
615 EntityManagerFactory emf = Persistence.createEntityManagerFactory(
617 DroolsPdpsConnector conn = new JpaDroolsPdpsConnector(emf);
623 * IntegrityAudit instance is needed by JUnit testing to ascertain whether
624 * or not audit is running.
626 public static IntegrityAudit getIntegrityAudit() {
628 return integrityAudit;
632 /* ============================================================ */
635 * Each instance of this class is a logical extension of a 'PolicyContainer'
636 * instance. It's reference is stored in the 'adjuncts' table within the
637 * 'PolicyContainer', and will be garbage-collected with the container.
639 class ContainerAdjunct
641 // this is the 'PolicyContainer' instance that this adjunct is extending
642 private PolicyContainer policyContainer;
643 private PoolingDataSource ds = null;
646 * Constructor - initialize a new 'ContainerAdjunct'
648 * @param policyContainer the 'PolicyContainer' instance this adjunct
651 ContainerAdjunct(PolicyContainer policyContainer)
653 this.policyContainer = policyContainer;
657 * Create a new persistent KieSession. If there is already a corresponding
658 * entry in the database, it is used to initialize the KieSession. If not,
659 * a completely new session is created.
661 * @param name the name of the KieSession (which is also the name of
662 * the associated PolicySession)
663 * @param kieBaseName the name of the 'KieBase' instance containing
665 * @return a new KieSession with persistence enabled (if persistence is
666 * disabled, 'null' is returned
668 private KieSession newPersistentKieSession(String name, String kieBaseName)
670 if (persistenceDisabled)
674 long desiredSessionId = -1;
675 synchronized (myPdpSync) {
679 for(DroolsSession droolsSession : electionHandler.getSessions()){
680 if(droolsSession.getSessionName().equals(name)){
681 desiredSessionId = droolsSession.getSessionId();
685 System.out.println("\n\nThis controller is primary... coming up with session "+desiredSessionId+"\n\n");
686 logger.info("\n\nThis controller is primary... coming up with session "+desiredSessionId+"\n\n");
687 Map<String, Object> props = new HashMap<String, Object>();
688 props.put("URL", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_URL));
689 props.put("user", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_USER));
690 props.put("password", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_PWD));
691 props.put("dataSource",DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_DATA_SOURCE));
692 logger.info("getPolicySession:session does not exist -- attempt to create one with name " + name);
693 // session does not exist -- attempt to create one
694 System.getProperties().put("java.naming.factory.initial","bitronix.tm.jndi.BitronixInitialContextFactory");
695 Environment env = kieServices.newEnvironment();
696 //kContainer.newKieBase(null);
697 ds = new PoolingDataSource();
698 ds.setUniqueName("jdbc/BitronixJTADataSource"+name);
699 ds.setClassName( (String)props.remove("dataSource"));
700 //ds.setClassName( "org.h2.Driver" );
701 ds.setMaxPoolSize( 3 );
702 ds.setIsolationLevel("SERIALIZABLE");
703 ds.setAllowLocalTransactions( true );
704 //ds.getDriverProperties().put( "user", "sa" );
705 //ds.getDriverProperties().put( "password", "" );
706 //ds.getDriverProperties().put( "URL", "jdbc:h2:tcp://localhost/drools" );
707 ds.getDriverProperties().putAll(props);
709 Properties emfProperties = new Properties();
710 emfProperties.setProperty(PersistenceUnitProperties.JTA_DATASOURCE, "jdbc/BitronixJTADataSource"+name);
711 env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, Persistence.createEntityManagerFactory("ncompsessionsPU",emfProperties));
712 env.set(EnvironmentName.TRANSACTION_MANAGER,TransactionManagerServices.getTransactionManager());
713 KieSessionConfiguration kConf = KieServices.Factory.get().newKieSessionConfiguration();
714 KieSession kieSession;
716 kieSession = kieServices.getStoreServices().loadKieSession(desiredSessionId, policyContainer.getKieContainer().getKieBase(kieBaseName), kConf, env);
717 System.out.println("LOADING We can load session "+desiredSessionId+", going to create a new one");
718 logger.info("LOADING We can load session "+desiredSessionId+", going to create a new one");
720 System.out.println("LOADING We cannot load session "+desiredSessionId+", going to create a new one");
722 logger.info("LOADING We cannot load session "+desiredSessionId+", going to create a new one");
723 kieSession = kieServices.getStoreServices().newKieSession(policyContainer.getKieContainer().getKieBase(kieBaseName), null, env);
724 System.out.println("LOADING CREATED "+kieSession.getIdentifier());
725 logger.info("LOADING CREATED "+kieSession.getIdentifier());
727 synchronized (myPdpSync) {
728 myPdp.setSessionId(name,kieSession.getIdentifier());
729 electionHandler.updateMyPdp();
734 private void disposeKieSession()
743 private void destroyKieSession()
745 // does the same thing as 'dispose'