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