6e45696248361b0263b18f15127adbf1f0a45f26
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * feature-active-standby-management
4  * ================================================================================
5  * Copyright (C) 2017-2020 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.activestandby;
22
23 import java.util.Collection;
24 import java.util.Date;
25 import java.util.LinkedList;
26 import java.util.List;
27
28 import javax.persistence.EntityManager;
29 import javax.persistence.EntityManagerFactory;
30 import javax.persistence.FlushModeType;
31 import javax.persistence.LockModeType;
32 import javax.persistence.Query;
33 import org.onap.policy.common.im.MonitorTime;
34 import org.onap.policy.common.utils.time.CurrentTime;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
37
38 public class JpaDroolsPdpsConnector implements DroolsPdpsConnector {
39
40     private static final String SELECT_PDP_BY_ID = "SELECT p FROM DroolsPdpEntity p WHERE p.pdpId=:pdpId";
41     private static final String PDP_ID_PARAM = "pdpId";
42
43     // get an instance of logger
44     private static final Logger  logger = LoggerFactory.getLogger(JpaDroolsPdpsConnector.class);
45     private EntityManagerFactory emf;
46
47     private final CurrentTime currentTime = MonitorTime.getInstance();
48
49
50     //not sure if we want to use the same entity manager factory
51     //for drools session and pass it in here, or create a new one
52     public JpaDroolsPdpsConnector(EntityManagerFactory emf) {
53         this.emf = emf;
54     }
55
56     @Override
57     public Collection<DroolsPdp> getDroolsPdps() {
58         //return a list of all the DroolsPdps in the database
59         EntityManager em = emf.createEntityManager();
60         try {
61             em.getTransaction().begin();
62             Query droolsPdpsListQuery = em.createQuery("SELECT p FROM DroolsPdpEntity p");
63             List<?> droolsPdpsList = droolsPdpsListQuery.setLockMode(LockModeType.NONE)
64                     .setFlushMode(FlushModeType.COMMIT).getResultList();
65             LinkedList<DroolsPdp> droolsPdpsReturnList = new LinkedList<>();
66             for (Object o : droolsPdpsList) {
67                 if (!(o instanceof DroolsPdp)) {
68                     continue;
69                 }
70                 //Make sure it is not a cached version
71                 DroolsPdp droolsPdp = (DroolsPdp)o;
72                 em.refresh(droolsPdp);
73                 droolsPdpsReturnList.add(droolsPdp);
74                 if (logger.isDebugEnabled()) {
75                     logger.debug("getDroolsPdps: PDP= {}"
76                             + ", isDesignated= {}"
77                             + ", updatedDate= {}"
78                             + ", priority= {}", droolsPdp.getPdpId(), droolsPdp.isDesignated(),
79                             droolsPdp.getUpdatedDate(), droolsPdp.getPriority());
80                 }
81             }
82             try {
83                 em.getTransaction().commit();
84             } catch (Exception e) {
85                 logger.error("Cannot commit getDroolsPdps() transaction", e);
86             }
87             return droolsPdpsReturnList;
88         } finally {
89             cleanup(em, "getDroolsPdps");
90         }
91     }
92
93     private boolean nullSafeEquals(Object one, Object two) {
94         if (one == null && two == null) {
95             return true;
96         }
97         if (one != null && two != null) {
98             return one.equals(two);
99         }
100         return false;
101     }
102
103     @Override
104     public void update(DroolsPdp pdp) {
105
106         logger.debug("update: Entering, pdpId={}", pdp.getPdpId());
107
108         //this is to update our own pdp in the database
109         EntityManager em = emf.createEntityManager();
110         try {
111             em.getTransaction().begin();
112             Query droolsPdpsListQuery = em.createQuery(SELECT_PDP_BY_ID);
113             droolsPdpsListQuery.setParameter(PDP_ID_PARAM, pdp.getPdpId());
114             List<?> droolsPdpsList = droolsPdpsListQuery.setLockMode(LockModeType.NONE)
115                     .setFlushMode(FlushModeType.COMMIT).getResultList();
116             DroolsPdpEntity droolsPdpEntity;
117             if (droolsPdpsList.size() == 1 && (droolsPdpsList.get(0) instanceof DroolsPdpEntity)) {
118                 droolsPdpEntity = (DroolsPdpEntity)droolsPdpsList.get(0);
119                 em.refresh(droolsPdpEntity); //Make sure we have current values
120                 Date currentDate = currentTime.getDate();
121                 long difference = currentDate.getTime() - droolsPdpEntity.getUpdatedDate().getTime();
122                 //just set some kind of default here
123                 long pdpTimeout = 15000;
124                 try {
125                     pdpTimeout = Long.parseLong(
126                             ActiveStandbyProperties.getProperty(ActiveStandbyProperties.PDP_TIMEOUT));
127                 } catch (Exception e) {
128                     logger.error("Could not get PDP timeout property, using default.", e);
129                 }
130                 boolean isCurrent = difference < pdpTimeout;
131                 logger.debug("update: PDP= {}, isCurrent={}"
132                                 + " difference= {}"
133                                 + ", pdpTimeout= {}, designated= {}",
134                                 pdp.getPdpId(), isCurrent, difference, pdpTimeout, droolsPdpEntity.isDesignated());
135             } else {
136                 logger.debug("update: For PDP={}"
137                                 + ", instantiating new DroolsPdpEntity", pdp.getPdpId());
138                 droolsPdpEntity = new DroolsPdpEntity();
139                 em.persist(droolsPdpEntity);
140                 droolsPdpEntity.setPdpId(pdp.getPdpId());
141             }
142             if (droolsPdpEntity.getPriority() != pdp.getPriority()) {
143                 droolsPdpEntity.setPriority(pdp.getPriority());
144             }
145             if (!droolsPdpEntity.getUpdatedDate().equals(pdp.getUpdatedDate())) {
146                 droolsPdpEntity.setUpdatedDate(pdp.getUpdatedDate());
147             }
148             if (!nullSafeEquals(droolsPdpEntity.getSite(),pdp.getSite())) {
149                 droolsPdpEntity.setSite(pdp.getSite());
150             }
151
152             if (droolsPdpEntity.isDesignated() != pdp.isDesignated()) {
153                 logger.debug("update: pdpId={}"
154                                 + ", pdp.isDesignated={}"
155                                 + ", droolsPdpEntity.pdpId= {}"
156                                 + ", droolsPdpEntity.isDesignated={}",
157                                 pdp.getPdpId(), pdp.isDesignated(),
158                                 droolsPdpEntity.getPdpId(), droolsPdpEntity.isDesignated());
159                 droolsPdpEntity.setDesignated(pdp.isDesignated());
160                 //The isDesignated value is not the same and the new one == true
161                 if (pdp.isDesignated()) {
162                     droolsPdpEntity.setDesignatedDate(currentTime.getDate());
163                 }
164             }
165             em.getTransaction().commit();
166         } finally {
167             cleanup(em, "update");
168         }
169
170         logger.debug("update: Exiting");
171
172     }
173
174     /*
175      * Note: A side effect of this boolean method is that if the PDP is designated but not current, the
176      * droolspdpentity.DESIGNATED column will be set to false (the PDP will be un-designated, i.e. marked as
177      * being in standby mode)
178      */
179     @Override
180     public boolean isPdpCurrent(DroolsPdp pdp) {
181
182         boolean isCurrent = isCurrent(pdp);
183
184         EntityManager em = emf.createEntityManager();
185         try {
186             if (!isCurrent && pdp.isDesignated()) {
187                 em.getTransaction().begin();
188                 Query droolsPdpsListQuery = em.createQuery(SELECT_PDP_BY_ID);
189                 droolsPdpsListQuery.setParameter(PDP_ID_PARAM, pdp.getPdpId());
190                 List<?> droolsPdpsList = droolsPdpsListQuery.setLockMode(LockModeType.NONE)
191                         .setFlushMode(FlushModeType.COMMIT).getResultList();
192                 if (droolsPdpsList.size() == 1 && droolsPdpsList.get(0) instanceof DroolsPdpEntity) {
193                     logger.debug("isPdpCurrent: PDP={}  designated but not current; setting designated to false",
194                                     pdp.getPdpId());
195                     DroolsPdpEntity droolsPdpEntity = (DroolsPdpEntity)droolsPdpsList.get(0);
196                     droolsPdpEntity.setDesignated(false);
197                     em.getTransaction().commit();
198                 } else {
199                     logger.warn("isPdpCurrent: PDP={} is designated but not current; "
200                             + "however it does not have a DB entry, so cannot set DESIGNATED to false!",
201                             pdp.getPdpId());
202                 }
203             } else {
204                 logger.debug("isPdpCurrent: For PDP= {}, "
205                                 + "designated={}, isCurrent={}", pdp.getPdpId(), pdp.isDesignated(), isCurrent);
206             }
207         } catch (Exception e) {
208             logger.error("Could not update expired record marked as designated in the database", e);
209         } finally {
210             cleanup(em, "isPdpCurrent");
211         }
212         return isCurrent;
213
214     }
215
216     @Override
217     public void setDesignated(DroolsPdp pdp, boolean designated) {
218
219         logger.debug("setDesignated: Entering, pdpId={}"
220                 + ", designated={}", pdp.getPdpId(), designated);
221
222         EntityManager em = null;
223         try {
224             em = emf.createEntityManager();
225             em.getTransaction().begin();
226             Query droolsPdpsListQuery = em
227                     .createQuery(SELECT_PDP_BY_ID);
228             droolsPdpsListQuery.setParameter(PDP_ID_PARAM, pdp.getPdpId());
229             List<?> droolsPdpsList = droolsPdpsListQuery.setLockMode(
230                     LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList();
231             if (droolsPdpsList.size() == 1
232                     && droolsPdpsList.get(0) instanceof DroolsPdpEntity) {
233                 DroolsPdpEntity droolsPdpEntity = (DroolsPdpEntity) droolsPdpsList
234                         .get(0);
235
236                 logger.debug("setDesignated: PDP={}"
237                         + " found, designated= {}"
238                         + ", setting to {}", pdp.getPdpId(), droolsPdpEntity.isDesignated(),
239                         designated);
240                 setPdpDesignation(em, droolsPdpEntity, designated);
241                 em.getTransaction().commit();
242             } else {
243                 logger.error("setDesignated: PDP={}"
244                         + " not in DB; cannot update designation", pdp.getPdpId());
245             }
246         } catch (Exception e) {
247             logger.error("setDesignated: Caught Exception", e);
248         } finally {
249             cleanup(em, "setDesignated");
250         }
251
252         logger.debug("setDesignated: Exiting");
253
254     }
255
256     private void setPdpDesignation(EntityManager em, DroolsPdpEntity droolsPdpEntity, boolean designated) {
257         droolsPdpEntity.setDesignated(designated);
258         if (designated) {
259             em.refresh(droolsPdpEntity); //make sure we get the DB value
260             if (!droolsPdpEntity.isDesignated()) {
261                 droolsPdpEntity.setDesignatedDate(currentTime.getDate());
262             }
263
264         }
265     }
266
267
268     @Override
269     public void standDownPdp(String pdpId) {
270         logger.debug("standDownPdp: Entering, pdpId={}", pdpId);
271
272         EntityManager em = null;
273         try {
274             /*
275              * Start transaction.
276              */
277             em = emf.createEntityManager();
278             em.getTransaction().begin();
279
280             /*
281              * Get droolspdpentity record for this PDP and mark DESIGNATED as
282              * false.
283              */
284             Query droolsPdpsListQuery = em
285                     .createQuery(SELECT_PDP_BY_ID);
286             droolsPdpsListQuery.setParameter(PDP_ID_PARAM, pdpId);
287             List<?> droolsPdpsList = droolsPdpsListQuery.setLockMode(
288                     LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList();
289             DroolsPdpEntity droolsPdpEntity;
290             if (droolsPdpsList.size() == 1
291                     && (droolsPdpsList.get(0) instanceof DroolsPdpEntity)) {
292                 droolsPdpEntity = (DroolsPdpEntity) droolsPdpsList.get(0);
293                 droolsPdpEntity.setDesignated(false);
294                 em.persist(droolsPdpEntity);
295                 logger.debug("standDownPdp: PDP={} persisted as non-designated.", pdpId );
296             } else {
297                 logger.error("standDownPdp: Missing record in droolspdpentity for pdpId={}"
298                         + "; cannot stand down PDP", pdpId);
299             }
300
301             /*
302              * End transaction.
303              */
304             em.getTransaction().commit();
305             cleanup(em, "standDownPdp");
306             em = null;
307
308             // Keep the election handler in sync with the DB
309             DroolsPdpsElectionHandler.setMyPdpDesignated(false);
310
311         } catch (Exception e) {
312             logger.error("standDownPdp: Unexpected Exception attempting to mark "
313                     + "DESIGNATED as false for droolspdpentity, pdpId={}"
314                     + ".  Cannot stand down PDP; message={}", pdpId, e.getMessage(), e);
315         } finally {
316             cleanup(em, "standDownPdp");
317         }
318         logger.debug("standDownPdp: Exiting");
319
320     }
321
322     /*
323      * Determines whether or not a designated PDP has failed.
324      *
325      * Note: The update method, which is run periodically by the
326      * TimerUpdateClass, will un-designate a PDP that is stale.
327      */
328     @Override
329     public boolean hasDesignatedPdpFailed(Collection<DroolsPdp> pdps) {
330
331         logger.debug("hasDesignatedPdpFailed: Entering, pdps.size()={}", pdps.size());
332
333         boolean failed = true;
334         boolean foundDesignatedPdp = false;
335
336         for (DroolsPdp pdp : pdps) {
337
338             /*
339              * Normally, the update method will un-designate any stale PDP, but
340              * we check here to see if the PDP has gone stale since the update
341              * method was run.
342              *
343              * Even if we determine that the designated PDP is current, we keep
344              * going (we don't break), so we can get visibility into the other
345              * PDPs, when in DEBUG mode.
346              */
347             if (pdp.isDesignated() && isCurrent(pdp)) {
348                 logger.debug("hasDesignatedPdpFailed: Designated PDP={} is current", pdp.getPdpId());
349                 failed = false;
350                 foundDesignatedPdp = true;
351             } else if (pdp.isDesignated() && !isCurrent(pdp)) {
352                 logger.error("hasDesignatedPdpFailed: Designated PDP={} has failed", pdp.getPdpId());
353                 foundDesignatedPdp = true;
354             } else {
355                 logger.debug("hasDesignatedPdpFailed: PDP={} is not designated", pdp.getPdpId());
356             }
357         }
358
359         logger.debug("hasDesignatedPdpFailed: Exiting and returning, foundDesignatedPdp={}",
360                 foundDesignatedPdp);
361         return failed;
362     }
363
364
365     private boolean isCurrent(DroolsPdp pdp) {
366
367         logger.debug("isCurrent: Entering, pdpId={}", pdp.getPdpId());
368
369         boolean current = false;
370
371         // Return if the current PDP is considered "current" based on whatever
372         // time box that may be.
373         // If the the PDP is not current, we should mark it as not primary in
374         // the database
375         Date currentDate = currentTime.getDate();
376         long difference = currentDate.getTime()
377                 - pdp.getUpdatedDate().getTime();
378         // just set some kind of default here
379         long pdpTimeout = 15000;
380         try {
381             pdpTimeout = Long.parseLong(ActiveStandbyProperties
382                     .getProperty(ActiveStandbyProperties.PDP_TIMEOUT));
383             logger.debug("isCurrent: pdp.timeout={}", pdpTimeout);
384         } catch (Exception e) {
385             logger.error("isCurrent: Could not get PDP timeout property, using default.", e);
386         }
387         current = difference < pdpTimeout;
388
389         logger.debug("isCurrent: Exiting, difference={}, pdpTimeout={}"
390                 + "; returning current={}", difference, pdpTimeout, current);
391
392         return current;
393     }
394
395
396     /*
397      * Currently this method is only used in a JUnit test environment. Gets a
398      * PDP record from droolspdpentity table.
399      */
400     @Override
401     public DroolsPdpEntity getPdp(String pdpId) {
402
403         logger.debug("getPdp: Entering and getting PDP with pdpId={}", pdpId);
404
405         DroolsPdpEntity droolsPdpEntity = null;
406
407         EntityManager em = null;
408         try {
409             em = emf.createEntityManager();
410             em.getTransaction().begin();
411             Query droolsPdpsListQuery = em
412                     .createQuery(SELECT_PDP_BY_ID);
413             droolsPdpsListQuery.setParameter(PDP_ID_PARAM, pdpId);
414             List<?> droolsPdpsList = droolsPdpsListQuery.setLockMode(
415                     LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList();
416             if (droolsPdpsList.size() == 1
417                     && droolsPdpsList.get(0) instanceof DroolsPdpEntity) {
418                 droolsPdpEntity = (DroolsPdpEntity) droolsPdpsList.get(0);
419                 logger.debug("getPdp: PDP={}"
420                                 + " found, isDesignated={},"
421                                 + " updatedDate={}, "
422                                 + "priority={}", pdpId,
423                                 droolsPdpEntity.isDesignated(), droolsPdpEntity.getUpdatedDate(),
424                                 droolsPdpEntity.getPriority());
425
426                 // Make sure the droolsPdpEntity is not a cached version
427                 em.refresh(droolsPdpEntity);
428
429                 em.getTransaction().commit();
430             } else {
431                 logger.error("getPdp: PDP={} not found!?", pdpId);
432             }
433         } catch (Exception e) {
434             logger.error("getPdp: Caught Exception attempting to get PDP", e);
435         } finally {
436             cleanup(em, "getPdp");
437         }
438
439         logger.debug("getPdp: Returning droolsPdpEntity={}", droolsPdpEntity);
440         return droolsPdpEntity;
441
442     }
443
444     /*
445      * Normally this method should only be used in a JUnit test environment.
446      * Manually inserts a PDP record in droolspdpentity table.
447      */
448     @Override
449     public void insertPdp(DroolsPdp pdp) {
450         logger.debug("insertPdp: Entering and manually inserting PDP");
451
452         /*
453          * Start transaction
454          */
455         EntityManager em = emf.createEntityManager();
456         try {
457             em.getTransaction().begin();
458
459             /*
460              * Insert record.
461              */
462             DroolsPdpEntity droolsPdpEntity = new DroolsPdpEntity();
463             em.persist(droolsPdpEntity);
464             droolsPdpEntity.setPdpId(pdp.getPdpId());
465             droolsPdpEntity.setDesignated(pdp.isDesignated());
466             droolsPdpEntity.setPriority(pdp.getPriority());
467             droolsPdpEntity.setUpdatedDate(pdp.getUpdatedDate());
468             droolsPdpEntity.setSite(pdp.getSite());
469
470             /*
471              * End transaction.
472              */
473             em.getTransaction().commit();
474         } finally {
475             cleanup(em, "insertPdp");
476         }
477         logger.debug("insertPdp: Exiting");
478
479     }
480
481     /*
482      * Normally this method should only be used in a JUnit test environment.
483      * Manually deletes all PDP records in droolspdpentity table.
484      */
485     @Override
486     public void deleteAllPdps() {
487
488         logger.debug("deleteAllPdps: Entering");
489
490         /*
491          * Start transaction
492          */
493         EntityManager em = emf.createEntityManager();
494         try {
495             em.getTransaction().begin();
496
497             Query droolsPdpsListQuery = em
498                     .createQuery("SELECT p FROM DroolsPdpEntity p");
499             @SuppressWarnings("unchecked")
500             List<DroolsPdp> droolsPdpsList = droolsPdpsListQuery.setLockMode(
501                     LockModeType.NONE).setFlushMode(FlushModeType.COMMIT).getResultList();
502             logger.debug("deleteAllPdps: Deleting {} PDPs", droolsPdpsList.size());
503             for (DroolsPdp droolsPdp : droolsPdpsList) {
504                 String pdpId = droolsPdp.getPdpId();
505                 deletePdp(pdpId);
506             }
507
508             /*
509              * End transaction.
510              */
511             em.getTransaction().commit();
512         } finally {
513             cleanup(em, "deleteAllPdps");
514         }
515         logger.debug("deleteAllPdps: Exiting");
516
517     }
518
519     /*
520      * Normally this method should only be used in a JUnit test environment.
521      * Manually deletes a PDP record in droolspdpentity table.
522      */
523     @Override
524     public void deletePdp(String pdpId) {
525         logger.debug("deletePdp: Entering and manually deleting pdpId={}", pdpId);
526
527         /*
528          * Start transaction
529          */
530         EntityManager em = emf.createEntityManager();
531         try {
532             em.getTransaction().begin();
533
534             /*
535              * Delete record.
536              */
537             DroolsPdpEntity droolsPdpEntity = em.find(DroolsPdpEntity.class, pdpId);
538             if (droolsPdpEntity != null) {
539                 logger.debug("deletePdp: Removing PDP");
540                 em.remove(droolsPdpEntity);
541             } else {
542                 logger.debug("deletePdp: PDP with ID={} not currently in DB", pdpId);
543             }
544
545             /*
546              * End transaction.
547              */
548             em.getTransaction().commit();
549         } finally {
550             cleanup(em, "deletePdp");
551         }
552         logger.debug("deletePdp: Exiting");
553
554     }
555
556     /*
557      * Close the specified EntityManager, rolling back any pending transaction
558      *
559      * @param em the EntityManager to close ('null' is OK)
560      * @param method the invoking Java method (used for log messages)
561      */
562     private static void cleanup(EntityManager em, String method) {
563         if (em != null && em.isOpen()) {
564             if (em.getTransaction().isActive()) {
565                 // there is an active EntityTransaction -- roll it back
566                 try {
567                     em.getTransaction().rollback();
568                 } catch (Exception e) {
569                     logger.error("{}: Caught Exception attempting to rollback EntityTransaction", method, e);
570                 }
571             }
572
573             // now, close the EntityManager
574             try {
575                 em.close();
576             } catch (Exception e) {
577                 logger.error("{}: Caught Exception attempting to close EntityManager", method, e);
578             }
579         }
580     }
581 }