/*- * ============LICENSE_START======================================================= * policy-persistence * ================================================================================ * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. * ================================================================================ * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ============LICENSE_END========================================================= */ package org.openecomp.policy.drools.persistence; import java.io.IOException; import java.util.Date; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; import org.eclipse.persistence.config.PersistenceUnitProperties; import org.kie.api.KieServices; import org.kie.api.runtime.Environment; import org.kie.api.runtime.EnvironmentName; import org.kie.api.runtime.KieSession; import org.kie.api.runtime.KieSessionConfiguration; import org.openecomp.policy.common.ia.IntegrityAudit; import org.openecomp.policy.common.ia.IntegrityAuditProperties; import org.openecomp.policy.common.im.StateManagement; import org.openecomp.policy.common.logging.eelf.MessageCodes; import org.openecomp.policy.common.logging.flexlogger.FlexLogger; import org.openecomp.policy.common.logging.flexlogger.Logger; import org.openecomp.policy.common.logging.flexlogger.PropertyUtil; import org.openecomp.policy.drools.core.DroolsPDPIntegrityMonitor; import org.openecomp.policy.drools.core.FeatureAPI; import org.openecomp.policy.drools.core.IntegrityMonitorProperties; import org.openecomp.policy.drools.core.PolicyContainer; import org.openecomp.policy.drools.core.PolicySession; import org.openecomp.policy.drools.im.PMStandbyStateChangeNotifier; import org.openecomp.policy.drools.system.PolicyEngine; import bitronix.tm.Configuration; import bitronix.tm.TransactionManagerServices; import bitronix.tm.resource.jdbc.PoolingDataSource; /** * If this feature is supported, there is a single instance of it. * It adds persistence to Drools sessions, but it is also intertwined with * active/standby state management and IntegrityMonitor. For now, they are * all treated as a single feature, but it would be nice to separate them. * * The bulk of the code here was once in other classes, such as * 'PolicyContainer' and 'Main'. It was moved here as part of making this * a separate optional feature. */ public class PersistenceFeature implements FeatureAPI { // get an instance of logger private static Logger logger = FlexLogger.getLogger(PersistenceFeature.class); // 'KieServices' singleton static private KieServices kieServices = KieServices.Factory.get(); private static DroolsPdp myPdp; private static Object myPdpSync = new Object(); private static DroolsPdpsElectionHandler electionHandler; // indicates whether persistence has been disabled private static boolean persistenceDisabled = false; /* * Used by JUnit testing to verify whether or not audit is running. */ private static IntegrityAudit integrityAudit = null; /** * Lookup the adjunct for this feature that is associated with the * specified PolicyContainer. If not found, create one. * * @param policyContainer the container whose adjunct we are looking up, * and possibly creating * @return the associated 'ContainerAdjunct' instance, which may be new */ private ContainerAdjunct getContainerAdjunct(PolicyContainer policyContainer) { Object rval = policyContainer.getAdjunct(this); if (rval == null || ! (rval instanceof ContainerAdjunct)) { // adjunct does not exist, or has the wrong type (should never happen) rval = new ContainerAdjunct(policyContainer); policyContainer.setAdjunct(this, rval); } return((ContainerAdjunct)rval); } /**************************/ /* 'FeatureAPI' interface */ /**************************/ /** * {@inheritDoc} */ @Override public int getSequenceNumber() { return(1); } /** * {@inheritDoc} */ @Override public void globalInit(String args[], String configDir) { // Initialization code associated with 'PolicyContainer' DroolsPDPIntegrityMonitor droolsPdpIntegrityMonitor = null; try { droolsPdpIntegrityMonitor = DroolsPDPIntegrityMonitor.init(configDir); } catch (Exception e) { logger.error(MessageCodes.EXCEPTION_ERROR, e, "main", "DroolsPDPIntegrityMonitor.init()"); } initializePersistence(configDir, droolsPdpIntegrityMonitor); // 1. Start Integrity Monitor (unless it was specifically disabled in the CORE layer if (persistenceDisabled) { System.out.println("WARNING: Starting Engine with Persistance disabled"); logger.warn("Starting Engine with Persistance disabled"); } else { DroolsPDPIntegrityMonitor im = null; //At this point the DroolsPDPIntegrityMonitor instance must exist try { im = DroolsPDPIntegrityMonitor.getInstance(); } catch (Exception e1) { String msg = "policy-core startup failed to get DroolsPDPIntegrityMonitor instance: \n" + e1; System.out.println(msg); e1.printStackTrace(); } //Now get the StateManagement instance so we can register our observer StateManagement sm = im.getStateManager(); //Create an instance of the Observer PMStandbyStateChangeNotifier pmNotifier = new PMStandbyStateChangeNotifier(); //Register the PMStandbyStateChangeNotifier Observer sm.addObserver(pmNotifier); } } /** * This is a hook to create a new persistent KieSession. * * {@inheritDoc} */ @Override public KieSession activatePolicySession (PolicyContainer policyContainer, String name, String kieBaseName) { return(getContainerAdjunct(policyContainer) .newPersistentKieSession(name, kieBaseName)); } /** * {@inheritDoc} */ @Override public void disposeKieSession(PolicySession policySession) { // TODO: There should be one data source per session getContainerAdjunct(policySession.getPolicyContainer()) .disposeKieSession(); } /** * {@inheritDoc} */ @Override public void destroyKieSession(PolicySession policySession) { // TODO: There should be one data source per session getContainerAdjunct(policySession.getPolicyContainer()) .destroyKieSession(); } /** * {@inheritDoc} */ @Override public void beforeStartEngine() { return; } /** * {@inheritDoc} */ @Override public void afterStartEngine() { PolicyEngine.manager.lock(); } /** * {@inheritDoc} */ @Override public void beforeShutdownEngine() { return; } /** * {@inheritDoc} */ @Override public void beforeCreateController(String name, Properties properties) { return; } /** * {@inheritDoc} */ @Override public void afterCreateController(String name) { return; } /** * {@inheritDoc} */ @Override public void afterShutdownEngine() { return; } /** * {@inheritDoc} */ @Override public void beforeStartController(String name) { return; } /** * {@inheritDoc} */ @Override public void afterStartController(String name) { return; } /** * {@inheritDoc} */ @Override public boolean isPersistenceEnabled() { return(!persistenceDisabled); } /**************************/ /** * @return 'true' if Drools persistence is disabled, and 'false' if not */ static public boolean getPersistenceDisabled() { return(persistenceDisabled); } /** * Read in the persistence properties, determine whether persistence is * enabled or disabled, and initialize persistence if enabled. */ private static void initializePersistence(String configDir, DroolsPDPIntegrityMonitor droolsPdpIntegrityMonitor) { try { Properties pDrools = PropertyUtil.getProperties(configDir + "/droolsPersistence.properties"); DroolsPersistenceProperties.initProperties(pDrools); Properties pXacml = PropertyUtil.getProperties(configDir + "/xacmlPersistence.properties"); XacmlPersistenceProperties.initProperties(pXacml); if ("true".equals(pDrools.getProperty("persistenceDisabled"))) { // 'persistenceDisabled' only relates to the 'drools' // database. The fact that integrityMonitor/xacml depends upon // persistence is an implementation detail there (which can't // currently be disabled), and doesn't directly affect // 'policy-core'. persistenceDisabled = true; } } catch (IOException e1) { logger.error(MessageCodes.MISS_PROPERTY_ERROR, e1, "initializePersistence"); } /* * Might as well handle the Integrity Monitor properties here, too. */ try { Properties pIm = PropertyUtil.getProperties(configDir + "/IntegrityMonitor.properties"); IntegrityMonitorProperties.initProperties(pIm); logger.info("initializePersistence: resourceName=" + IntegrityMonitorProperties.getProperty(IntegrityMonitorProperties.PDP_INSTANCE_ID)); } catch (IOException e1) { logger.error(MessageCodes.MISS_PROPERTY_ERROR, e1, "initializePersistence"); } if (persistenceDisabled) { // The persistence design is tied to 'DroolsPdpsElectionHandler', // so we should bypass that as well. This also means that we // won't get active/standby notifications, so we need to go // into the 'active' state in order to have any 'PolicySession' // instances. return; } DroolsPdpsConnector conn = getDroolsPdpsConnector("ncompPU"); String uniquePdpId = IntegrityMonitorProperties.getProperty(IntegrityMonitorProperties.PDP_INSTANCE_ID); if(uniquePdpId == null){ throw new NullPointerException(); } /* * In a JUnit test environment, one or more PDPs may already have been * inserted in the DB, so we need to check for this. */ DroolsPdp existingPdp = conn.getPdp(uniquePdpId); if (existingPdp != null) { System.out.println("Found existing PDP record, pdpId=" + existingPdp.getPdpId() + ", isDesignated=" + existingPdp.isDesignated() + ", updatedDate=" + existingPdp.getUpdatedDate()); myPdp = existingPdp; } /* * Kick off integrity audit for Drools DB. */ startIntegrityAudit(configDir); synchronized(myPdpSync){ if(myPdp == null){ myPdp = new DroolsPdpImpl(uniquePdpId,false,4,new Date()); } if(myPdp != null){ String site_name = ""; site_name = IntegrityMonitorProperties.getProperty(IntegrityMonitorProperties.SITE_NAME); if (site_name == null) { site_name = ""; }else{ site_name = site_name.trim(); } myPdp.setSiteName(site_name); } if(electionHandler == null){ electionHandler = new DroolsPdpsElectionHandler(conn,myPdp,droolsPdpIntegrityMonitor); } } Configuration bitronixConfiguration = TransactionManagerServices.getConfiguration(); bitronixConfiguration.setJournal(null); bitronixConfiguration.setServerId(uniquePdpId); System.out.println("\n\nThis controller is a standby, waiting to be chosen as primary...\n\n"); logger.info("\n\nThis controller is a standby, waiting to be chosen as primary...\n\n"); } private static void startIntegrityAudit(String configDir) { logger.info("startIntegrityAudit: Entering, configDir='" + configDir + "'"); /* * Initialize Integrity Audit properties. file. */ try { String resourceName = IntegrityMonitorProperties .getProperty(IntegrityMonitorProperties.PDP_INSTANCE_ID); /* * Load properties for auditing of Drools DB. */ Properties droolsPia = PropertyUtil.getProperties(configDir + "/IntegrityMonitor.properties"); /* * Supplement properties specific to the IntegrityMonitor (e.g. * site_name, node_type, resource.name) with properties specific to * persisting Drools DB entities (see * ../policy-core/src/main/resources/persistence.xml) * * Note: integrity_audit_period_seconds is defined in * IntegrityMonitor.properties, rather than creating a whole new * "IntegrityAudit.properties" file for just one property. */ droolsPia .setProperty( IntegrityAuditProperties.DB_DRIVER, DroolsPersistenceProperties .getProperty(DroolsPersistenceProperties.DB_DRIVER)); droolsPia.setProperty(IntegrityAuditProperties.DB_PWD, DroolsPersistenceProperties .getProperty(DroolsPersistenceProperties.DB_PWD)); droolsPia.setProperty(IntegrityAuditProperties.DB_URL, DroolsPersistenceProperties .getProperty(DroolsPersistenceProperties.DB_URL)); droolsPia.setProperty(IntegrityAuditProperties.DB_USER, DroolsPersistenceProperties .getProperty(DroolsPersistenceProperties.DB_USER)); /* * Start audit for Drools DB. */ integrityAudit = new IntegrityAudit( resourceName, "ncompPU", droolsPia); integrityAudit.startAuditThread(); } catch (IOException e1) { logger.error( MessageCodes.MISS_PROPERTY_ERROR, e1, "initializePersistence: IntegrityAuditProperties: " + e1.getMessage()); } catch (Exception e2) { logger.error( MessageCodes.EXCEPTION_ERROR, e2, "initializePersistence: IntegrityAuditProperties: " + e2.getMessage()); } logger.debug("startIntegrityAudit: Exiting"); } /* * Moved code to instantiate a JpaDroolsPdpsConnector object from main() to * this method, so it can also be accessed from StandbyStateChangeNotifier * class. */ public static DroolsPdpsConnector getDroolsPdpsConnector(String pu) { Map propMap = new HashMap(); propMap.put("javax.persistence.jdbc.driver", DroolsPersistenceProperties .getProperty(DroolsPersistenceProperties.DB_DRIVER)); propMap.put("javax.persistence.jdbc.url", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_URL)); propMap.put("javax.persistence.jdbc.user", DroolsPersistenceProperties .getProperty(DroolsPersistenceProperties.DB_USER)); propMap.put("javax.persistence.jdbc.password", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_PWD)); EntityManagerFactory emf = Persistence.createEntityManagerFactory( pu, propMap); DroolsPdpsConnector conn = new JpaDroolsPdpsConnector(emf); return conn; } /* * IntegrityAudit instance is needed by JUnit testing to ascertain whether * or not audit is running. */ public static IntegrityAudit getIntegrityAudit() { return integrityAudit; } /* ============================================================ */ /** * Each instance of this class is a logical extension of a 'PolicyContainer' * instance. It's reference is stored in the 'adjuncts' table within the * 'PolicyContainer', and will be garbage-collected with the container. */ class ContainerAdjunct { // this is the 'PolicyContainer' instance that this adjunct is extending private PolicyContainer policyContainer; private PoolingDataSource ds = null; /** * Constructor - initialize a new 'ContainerAdjunct' * * @param policyContainer the 'PolicyContainer' instance this adjunct * is extending */ ContainerAdjunct(PolicyContainer policyContainer) { this.policyContainer = policyContainer; } /** * Create a new persistent KieSession. If there is already a corresponding * entry in the database, it is used to initialize the KieSession. If not, * a completely new session is created. * * @param name the name of the KieSession (which is also the name of * the associated PolicySession) * @param kieBaseName the name of the 'KieBase' instance containing * this session * @return a new KieSession with persistence enabled (if persistence is * disabled, 'null' is returned */ private KieSession newPersistentKieSession(String name, String kieBaseName) { if (persistenceDisabled) { return(null); } long desiredSessionId = -1; synchronized (myPdpSync) { for(DroolsSession droolsSession : electionHandler.getSessions()){ if(droolsSession.getSessionName().equals(name)){ desiredSessionId = droolsSession.getSessionId(); } } } System.out.println("\n\nThis controller is primary... coming up with session "+desiredSessionId+"\n\n"); logger.info("\n\nThis controller is primary... coming up with session "+desiredSessionId+"\n\n"); Map props = new HashMap(); props.put("URL", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_URL)); props.put("user", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_USER)); props.put("password", DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_PWD)); props.put("dataSource",DroolsPersistenceProperties.getProperty(DroolsPersistenceProperties.DB_DATA_SOURCE)); logger.info("getPolicySession:session does not exist -- attempt to create one with name " + name); // session does not exist -- attempt to create one System.getProperties().put("java.naming.factory.initial","bitronix.tm.jndi.BitronixInitialContextFactory"); Environment env = kieServices.newEnvironment(); //kContainer.newKieBase(null); ds = new PoolingDataSource(); ds.setUniqueName("jdbc/BitronixJTADataSource"+name); ds.setClassName( (String)props.remove("dataSource")); //ds.setClassName( "org.h2.Driver" ); ds.setMaxPoolSize( 3 ); ds.setIsolationLevel("SERIALIZABLE"); ds.setAllowLocalTransactions( true ); //ds.getDriverProperties().put( "user", "sa" ); //ds.getDriverProperties().put( "password", "" ); //ds.getDriverProperties().put( "URL", "jdbc:h2:tcp://localhost/drools" ); ds.getDriverProperties().putAll(props); ds.init(); Properties emfProperties = new Properties(); emfProperties.setProperty(PersistenceUnitProperties.JTA_DATASOURCE, "jdbc/BitronixJTADataSource"+name); env.set(EnvironmentName.ENTITY_MANAGER_FACTORY, Persistence.createEntityManagerFactory("ncompsessionsPU",emfProperties)); env.set(EnvironmentName.TRANSACTION_MANAGER,TransactionManagerServices.getTransactionManager()); KieSessionConfiguration kConf = KieServices.Factory.get().newKieSessionConfiguration(); KieSession kieSession; try{ kieSession = kieServices.getStoreServices().loadKieSession(desiredSessionId, policyContainer.getKieContainer().getKieBase(kieBaseName), kConf, env); System.out.println("LOADING We can load session "+desiredSessionId+", going to create a new one"); logger.info("LOADING We can load session "+desiredSessionId+", going to create a new one"); }catch(Exception e){ System.out.println("LOADING We cannot load session "+desiredSessionId+", going to create a new one"); logger.info("LOADING We cannot load session "+desiredSessionId+", going to create a new one"); kieSession = kieServices.getStoreServices().newKieSession(policyContainer.getKieContainer().getKieBase(kieBaseName), null, env); System.out.println("LOADING CREATED "+kieSession.getIdentifier()); logger.info("LOADING CREATED "+kieSession.getIdentifier()); } synchronized (myPdpSync) { myPdp.setSessionId(name,kieSession.getIdentifier()); electionHandler.updateMyPdp(); } return(kieSession); } private void disposeKieSession() { if (ds != null) { ds.close(); ds = null; } } private void destroyKieSession() { // does the same thing as 'dispose' disposeKieSession(); } } }