/* * ============LICENSE_START======================================================= * feature-active-standby-management * ================================================================================ * Copyright (C) 2017-2018 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.onap.policy.drools.activestandby; import java.util.Collection; import java.util.Date; import java.util.LinkedList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; import javax.persistence.Query; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class JpaDroolsPdpsConnector implements DroolsPdpsConnector { // get an instance of logger private static final Logger logger = LoggerFactory.getLogger(JpaDroolsPdpsConnector.class); private EntityManagerFactory emf; //not sure if we want to use the same entity manager factory //for drools session and pass it in here, or create a new one public JpaDroolsPdpsConnector(EntityManagerFactory emf) { this.emf = emf; } @Override public Collection getDroolsPdps() { //return a list of all the DroolsPdps in the database EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); Query droolsPdpsListQuery = em.createQuery("SELECT p FROM DroolsPdpEntity p"); List droolsPdpsList = droolsPdpsListQuery.setLockMode(LockModeType.NONE) .setFlushMode(FlushModeType.COMMIT).getResultList(); LinkedList droolsPdpsReturnList = new LinkedList<>(); for (Object o : droolsPdpsList) { if (o instanceof DroolsPdp) { //Make sure it is not a cached version em.refresh((DroolsPdpEntity)o); droolsPdpsReturnList.add((DroolsPdp)o); if (logger.isDebugEnabled()) { DroolsPdp droolsPdp = (DroolsPdp)o; logger.debug("getDroolsPdps: PDP= {}" + ", isDesignated= {}" + ", updatedDate= {}" + ", priority= {}", droolsPdp.getPdpId(), droolsPdp.isDesignated(), droolsPdp.getUpdatedDate(), droolsPdp.getPriority()); } } } try { em.getTransaction().commit(); } catch (Exception e) { logger.error("Cannot commit getDroolsPdps() transaction", e); } return droolsPdpsReturnList; } finally { cleanup(em, "getDroolsPdps"); } } private boolean nullSafeEquals(Object one, Object two) { if (one == null && two == null) { return true; } if (one != null && two != null) { return one.equals(two); } return false; } @Override public void update(DroolsPdp pdp) { if (logger.isDebugEnabled()) { logger.debug("update: Entering, pdpId={}", pdp.getPdpId()); } //this is to update our own pdp in the database EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); Query droolsPdpsListQuery = em.createQuery("SELECT p FROM DroolsPdpEntity p WHERE p.pdpId=:pdpId"); droolsPdpsListQuery.setParameter("pdpId", pdp.getPdpId()); List droolsPdpsList = droolsPdpsListQuery.setLockMode(LockModeType.NONE) .setFlushMode(FlushModeType.COMMIT).getResultList(); DroolsPdpEntity droolsPdpEntity; if (droolsPdpsList.size() == 1 && (droolsPdpsList.get(0) instanceof DroolsPdpEntity)) { droolsPdpEntity = (DroolsPdpEntity)droolsPdpsList.get(0); em.refresh(droolsPdpEntity); //Make sure we have current values Date currentDate = new Date(); long difference = currentDate.getTime() - droolsPdpEntity.getUpdatedDate().getTime(); //just set some kind of default here long pdpTimeout = 15000; try { pdpTimeout = Long.parseLong( ActiveStandbyProperties.getProperty(ActiveStandbyProperties.PDP_TIMEOUT)); } catch (Exception e) { logger.error("Could not get PDP timeout property, using default.", e); } boolean isCurrent = difference < pdpTimeout; if (logger.isDebugEnabled()) { logger.debug("update: PDP= {}, isCurrent={}" + " difference= {}" + ", pdpTimeout= {}, designated= {}", pdp.getPdpId(), isCurrent, difference, pdpTimeout, droolsPdpEntity.isDesignated()); } } else { if (logger.isDebugEnabled()) { logger.debug("update: For PDP={}" + ", instantiating new DroolsPdpEntity", pdp.getPdpId()); } droolsPdpEntity = new DroolsPdpEntity(); em.persist(droolsPdpEntity); droolsPdpEntity.setPdpId(pdp.getPdpId()); } if (droolsPdpEntity.getPriority() != pdp.getPriority()) { droolsPdpEntity.setPriority(pdp.getPriority()); } if (!droolsPdpEntity.getUpdatedDate().equals(pdp.getUpdatedDate())) { droolsPdpEntity.setUpdatedDate(pdp.getUpdatedDate()); } if (!nullSafeEquals(droolsPdpEntity.getSiteName(),pdp.getSiteName())) { droolsPdpEntity.setSiteName(pdp.getSiteName()); } if (droolsPdpEntity.isDesignated() != pdp.isDesignated()) { if (logger.isDebugEnabled()) { logger.debug("update: pdpId={}" + ", pdp.isDesignated={}" + ", droolsPdpEntity.pdpId= {}" + ", droolsPdpEntity.isDesignated={}", pdp.getPdpId(), pdp.isDesignated(), droolsPdpEntity.getPdpId(), droolsPdpEntity.isDesignated()); } droolsPdpEntity.setDesignated(pdp.isDesignated()); //The isDesignated value is not the same and the new one == true if (pdp.isDesignated()) { droolsPdpEntity.setDesignatedDate(new Date()); } } em.getTransaction().commit(); } finally { cleanup(em, "update"); } if (logger.isDebugEnabled()) { logger.debug("update: Exiting"); } } /* * Note: A side effect of this boolean method is that if the PDP is designated but not current, the * droolspdpentity.DESIGNATED column will be set to false (the PDP will be un-designated, i.e. marked as * being in standby mode) */ @Override public boolean isPdpCurrent(DroolsPdp pdp) { boolean isCurrent = isCurrent(pdp); EntityManager em = emf.createEntityManager(); try { if (!isCurrent && pdp.isDesignated()) { em.getTransaction().begin(); Query droolsPdpsListQuery = em.createQuery("SELECT p FROM DroolsPdpEntity p WHERE p.pdpId=:pdpId"); droolsPdpsListQuery.setParameter("pdpId", pdp.getPdpId()); List droolsPdpsList = droolsPdpsListQuery.setLockMode(LockModeType.NONE) .setFlushMode(FlushModeType.COMMIT).getResultList(); if (droolsPdpsList.size() == 1 && droolsPdpsList.get(0) instanceof DroolsPdpEntity) { if (logger.isDebugEnabled()) { logger.debug("isPdpCurrent: PDP={} designated but not current; setting designated to false", pdp.getPdpId()); } DroolsPdpEntity droolsPdpEntity = (DroolsPdpEntity)droolsPdpsList.get(0); droolsPdpEntity.setDesignated(false); em.getTransaction().commit(); } else { logger.warn("isPdpCurrent: PDP={} is designated but not current; " + "however it does not have a DB entry, so cannot set DESIGNATED to false!", pdp.getPdpId()); } } else { if (logger.isDebugEnabled()) { logger.debug("isPdpCurrent: For PDP= {}, " + "designated={}, isCurrent={}", pdp.getPdpId(), pdp.isDesignated(), isCurrent); } } } catch (Exception e) { logger.error("Could not update expired record marked as designated in the database", e); } finally { cleanup(em, "isPdpCurrent"); } return isCurrent; } @Override public void setDesignated(DroolsPdp pdp, boolean designated) { if (logger.isDebugEnabled()) { logger.debug("setDesignated: Entering, pdpId={}" + ", designated={}", pdp.getPdpId(), designated); } EntityManager em = null; try { em = emf.createEntityManager(); em.getTransaction().begin(); Query droolsPdpsListQuery = em .createQuery("SELECT p FROM DroolsPdpEntity p WHERE p.pdpId=:pdpId"); droolsPdpsListQuery.setParameter("pdpId", pdp.getPdpId()); List droolsPdpsList = droolsPdpsListQuery.setLockMode( LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList(); if (droolsPdpsList.size() == 1 && droolsPdpsList.get(0) instanceof DroolsPdpEntity) { DroolsPdpEntity droolsPdpEntity = (DroolsPdpEntity) droolsPdpsList .get(0); if (logger.isDebugEnabled()) { logger.debug("setDesignated: PDP={}" + " found, designated= {}" + ", setting to {}", pdp.getPdpId(), droolsPdpEntity.isDesignated(), designated); } droolsPdpEntity.setDesignated(designated); if (designated) { em.refresh(droolsPdpEntity); //make sure we get the DB value if (!droolsPdpEntity.isDesignated()) { droolsPdpEntity.setDesignatedDate(new Date()); } } em.getTransaction().commit(); } else { logger.error("setDesignated: PDP={}" + " not in DB; cannot update designation", pdp.getPdpId()); } } catch (Exception e) { logger.error("setDesignated: Caught Exception", e); } finally { cleanup(em, "setDesignated"); } if (logger.isDebugEnabled()) { logger.debug("setDesignated: Exiting"); } } @Override public void standDownPdp(String pdpId) { if (logger.isDebugEnabled()) { logger.debug("standDownPdp: Entering, pdpId={}", pdpId); } EntityManager em = null; try { /* * Start transaction. */ em = emf.createEntityManager(); em.getTransaction().begin(); /* * Get droolspdpentity record for this PDP and mark DESIGNATED as * false. */ Query droolsPdpsListQuery = em .createQuery("SELECT p FROM DroolsPdpEntity p WHERE p.pdpId=:pdpId"); droolsPdpsListQuery.setParameter("pdpId", pdpId); List droolsPdpsList = droolsPdpsListQuery.setLockMode( LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList(); DroolsPdpEntity droolsPdpEntity; if (droolsPdpsList.size() == 1 && (droolsPdpsList.get(0) instanceof DroolsPdpEntity)) { droolsPdpEntity = (DroolsPdpEntity) droolsPdpsList.get(0); droolsPdpEntity.setDesignated(false); em.persist(droolsPdpEntity); if (logger.isDebugEnabled()) { logger.debug("standDownPdp: PDP={} persisted as non-designated.", pdpId ); } } else { logger.error("standDownPdp: Missing record in droolspdpentity for pdpId={}" + "; cannot stand down PDP", pdpId); } /* * End transaction. */ em.getTransaction().commit(); cleanup(em, "standDownPdp"); em = null; // Keep the election handler in sync with the DB DroolsPdpsElectionHandler.setMyPdpDesignated(false); } catch (Exception e) { logger.error("standDownPdp: Unexpected Exception attempting to mark " + "DESIGNATED as false for droolspdpentity, pdpId={}" + ". Cannot stand down PDP; message={}", pdpId, e.getMessage(), e); } finally { cleanup(em, "standDownPdp"); } if (logger.isDebugEnabled()) { logger.debug("standDownPdp: Exiting"); } } /* * Determines whether or not a designated PDP has failed. * * Note: The update method, which is run periodically by the * TimerUpdateClass, will un-designate a PDP that is stale. */ @Override public boolean hasDesignatedPdpFailed(Collection pdps) { if (logger.isDebugEnabled()) { logger.debug("hasDesignatedPdpFailed: Entering, pdps.size()={}", pdps.size()); } boolean failed = true; boolean foundDesignatedPdp = false; for (DroolsPdp pdp : pdps) { /* * Normally, the update method will un-designate any stale PDP, but * we check here to see if the PDP has gone stale since the update * method was run. * * Even if we determine that the designated PDP is current, we keep * going (we don't break), so we can get visibility into the other * PDPs, when in DEBUG mode. */ if (pdp.isDesignated() && isCurrent(pdp)) { if (logger.isDebugEnabled()) { logger.debug("hasDesignatedPdpFailed: Designated PDP={} is current", pdp.getPdpId()); } failed = false; foundDesignatedPdp = true; } else if (pdp.isDesignated() && !isCurrent(pdp)) { logger.error("hasDesignatedPdpFailed: Designated PDP={} has failed", pdp.getPdpId()); foundDesignatedPdp = true; } else { if (logger.isDebugEnabled()) { logger.debug("hasDesignatedPdpFailed: PDP={} is not designated", pdp.getPdpId()); } } } if (logger.isDebugEnabled()) { logger.debug("hasDesignatedPdpFailed: Exiting and returning, foundDesignatedPdp={}", foundDesignatedPdp); } return failed; } private boolean isCurrent(DroolsPdp pdp) { if (logger.isDebugEnabled()) { logger.debug("isCurrent: Entering, pdpId={}", pdp.getPdpId()); } boolean current = false; // Return if the current PDP is considered "current" based on whatever // time box that may be. // If the the PDP is not current, we should mark it as not primary in // the database Date currentDate = new Date(); long difference = currentDate.getTime() - pdp.getUpdatedDate().getTime(); // just set some kind of default here long pdpTimeout = 15000; try { pdpTimeout = Long.parseLong(ActiveStandbyProperties .getProperty(ActiveStandbyProperties.PDP_TIMEOUT)); if (logger.isDebugEnabled()) { logger.debug("isCurrent: pdp.timeout={}", pdpTimeout); } } catch (Exception e) { logger.error("isCurrent: Could not get PDP timeout property, using default.", e); } current = difference < pdpTimeout; if (logger.isDebugEnabled()) { logger.debug("isCurrent: Exiting, difference={}, pdpTimeout={}" + "; returning current={}", difference, pdpTimeout, current); } return current; } /* * Currently this method is only used in a JUnit test environment. Gets a * PDP record from droolspdpentity table. */ @Override public DroolsPdpEntity getPdp(String pdpId) { if (logger.isDebugEnabled()) { logger.debug("getPdp: Entering and getting PDP with pdpId={}", pdpId); } DroolsPdpEntity droolsPdpEntity = null; EntityManager em = null; try { em = emf.createEntityManager(); em.getTransaction().begin(); Query droolsPdpsListQuery = em .createQuery("SELECT p FROM DroolsPdpEntity p WHERE p.pdpId=:pdpId"); droolsPdpsListQuery.setParameter("pdpId", pdpId); List droolsPdpsList = droolsPdpsListQuery.setLockMode( LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList(); if (droolsPdpsList.size() == 1 && droolsPdpsList.get(0) instanceof DroolsPdpEntity) { droolsPdpEntity = (DroolsPdpEntity) droolsPdpsList.get(0); if (logger.isDebugEnabled()) { logger.debug("getPdp: PDP={}" + " found, isDesignated={}," + " updatedDate={}, " + "priority={}", pdpId, droolsPdpEntity.isDesignated(), droolsPdpEntity.getUpdatedDate(), droolsPdpEntity.getPriority()); } // Make sure the droolsPdpEntity is not a cached version em.refresh(droolsPdpEntity); em.getTransaction().commit(); } else { logger.error("getPdp: PDP={} not found!?", pdpId); } } catch (Exception e) { logger.error("getPdp: Caught Exception attempting to get PDP", e); } finally { cleanup(em, "getPdp"); } if (logger.isDebugEnabled()) { logger.debug("getPdp: Returning droolsPdpEntity={}", droolsPdpEntity); } return droolsPdpEntity; } /* * Normally this method should only be used in a JUnit test environment. * Manually inserts a PDP record in droolspdpentity table. */ @Override public void insertPdp(DroolsPdp pdp) { if (logger.isDebugEnabled()) { logger.debug("insertPdp: Entering and manually inserting PDP"); } /* * Start transaction */ EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); /* * Insert record. */ DroolsPdpEntity droolsPdpEntity = new DroolsPdpEntity(); em.persist(droolsPdpEntity); droolsPdpEntity.setPdpId(pdp.getPdpId()); droolsPdpEntity.setDesignated(pdp.isDesignated()); droolsPdpEntity.setPriority(pdp.getPriority()); droolsPdpEntity.setUpdatedDate(pdp.getUpdatedDate()); droolsPdpEntity.setSiteName(pdp.getSiteName()); /* * End transaction. */ em.getTransaction().commit(); } finally { cleanup(em, "insertPdp"); } if (logger.isDebugEnabled()) { logger.debug("insertPdp: Exiting"); } } /* * Normally this method should only be used in a JUnit test environment. * Manually deletes all PDP records in droolspdpentity table. */ @Override public void deleteAllPdps() { if (logger.isDebugEnabled()) { logger.debug("deleteAllPdps: Entering"); } /* * Start transaction */ EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); Query droolsPdpsListQuery = em .createQuery("SELECT p FROM DroolsPdpEntity p"); @SuppressWarnings("unchecked") List droolsPdpsList = droolsPdpsListQuery.setLockMode( LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList(); if (logger.isDebugEnabled()) { logger.debug("deleteAllPdps: Deleting {} PDPs", droolsPdpsList.size()); } for (DroolsPdp droolsPdp : droolsPdpsList) { String pdpId = droolsPdp.getPdpId(); deletePdp(pdpId); } /* * End transaction. */ em.getTransaction().commit(); } finally { cleanup(em, "deleteAllPdps"); } if (logger.isDebugEnabled()) { logger.debug("deleteAllPdps: Exiting"); } } /* * Normally this method should only be used in a JUnit test environment. * Manually deletes a PDP record in droolspdpentity table. */ @Override public void deletePdp(String pdpId) { if (logger.isDebugEnabled()) { logger.debug("deletePdp: Entering and manually deleting pdpId={}", pdpId); } /* * Start transaction */ EntityManager em = emf.createEntityManager(); try { em.getTransaction().begin(); /* * Delete record. */ DroolsPdpEntity droolsPdpEntity = em.find(DroolsPdpEntity.class, pdpId); if (droolsPdpEntity != null) { if (logger.isDebugEnabled()) { logger.debug("deletePdp: Removing PDP"); } em.remove(droolsPdpEntity); } else { if (logger.isDebugEnabled()) { logger.debug("deletePdp: PDP with ID={} not currently in DB", pdpId); } } /* * End transaction. */ em.getTransaction().commit(); } finally { cleanup(em, "deletePdp"); } if (logger.isDebugEnabled()) { logger.debug("deletePdp: Exiting"); } } /* * Close the specified EntityManager, rolling back any pending transaction * * @param em the EntityManager to close ('null' is OK) * @param method the invoking Java method (used for log messages) */ private static void cleanup(EntityManager em, String method) { if (em != null && em.isOpen()) { if (em.getTransaction().isActive()) { // there is an active EntityTransaction -- roll it back try { em.getTransaction().rollback(); } catch (Exception e) { logger.error(method + ": Caught Exception attempting to rollback EntityTransaction,", e); } } // now, close the EntityManager try { em.close(); } catch (Exception e) { logger.error(method + ": Caught Exception attempting to close EntityManager, ", e); } } } }