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