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