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