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