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