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