c29c4bf2d50e23d69f4d0faf8fb8ba5b2c373749
[policy/models.git] / models-dao / src / main / java / org / onap / policy / models / dao / impl / DefaultPfDao.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019-2021 Nordix Foundation.
4  *  Modifications Copyright (C) 2019-2021 AT&T Intellectual Property. All rights reserved.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.models.dao.impl;
23
24 import java.sql.Timestamp;
25 import java.time.Instant;
26 import java.util.Collection;
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Map;
30 import javax.persistence.EntityManager;
31 import javax.persistence.EntityManagerFactory;
32 import javax.persistence.Persistence;
33 import javax.persistence.TypedQuery;
34 import javax.ws.rs.core.Response;
35 import org.onap.policy.models.base.PfConcept;
36 import org.onap.policy.models.base.PfConceptKey;
37 import org.onap.policy.models.base.PfModelException;
38 import org.onap.policy.models.base.PfModelRuntimeException;
39 import org.onap.policy.models.base.PfReferenceKey;
40 import org.onap.policy.models.base.PfTimestampKey;
41 import org.onap.policy.models.base.PfUtils;
42 import org.onap.policy.models.dao.DaoParameters;
43 import org.onap.policy.models.dao.PfDao;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * The Class DefaultPfDao is an JPA implementation of the {@link PfDao} class for Policy Framework concepts
49  * ({@link PfConcept}). It uses the default JPA implementation in the javax {@link Persistence} class.
50  */
51 public class DefaultPfDao implements PfDao {
52     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPfDao.class);
53
54     // @formatter:off
55     private static final String NAME           = "name";
56     private static final String VERSION        = "version";
57     private static final String TIMESTAMP      = "timeStamp";
58     private static final String PARENT_NAME    = "parentname";
59     private static final String PARENT_VERSION = "parentversion";
60     private static final String LOCAL_NAME     = "localname";
61
62     private static final String TABLE_TOKEN = "__TABLE__";
63
64     private static final String DELETE_FROM_TABLE = "DELETE FROM __TABLE__ c";
65
66     private static final String SELECT_FROM_TABLE = "SELECT c FROM __TABLE__ c";
67
68     private static final String WHERE      = " WHERE ";
69     private static final String AND        = " AND ";
70     private static final String ORDER      = " ORDER BY ";
71
72     private static final String NAME_FILTER            = "c.key.name = :name";
73     private static final String VERSION_FILTER         = "c.key.version = :version";
74     private static final String TIMESTAMP_FILTER       = "c.key.timeStamp = :timeStamp";
75     private static final String TIMESTAMP_START_FILTER = "c.key.timeStamp >= :startTime";
76     private static final String TIMESTAMP_END_FILTER   = "c.key.timeStamp <= :endTime";
77     private static final String PARENT_NAME_FILTER     = "c.key.parentKeyName = :parentname";
78     private static final String PARENT_VERSION_FILTER  = "c.key.parentKeyVersion = :parentversion";
79     private static final String LOCAL_NAME_FILTER      = "c.key.localName = :localname";
80
81     private static final String CLONE_ERR_MSG = "Could not clone object of class \"{}\"";
82
83     private static final String DELETE_BY_CONCEPT_KEY =
84             DELETE_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER;
85
86     private static final String DELETE_BY_TIMESTAMP_KEY =
87             DELETE_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER  + AND + TIMESTAMP_FILTER;
88
89     private static final String DELETE_BY_REFERENCE_KEY =
90             DELETE_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER + AND + LOCAL_NAME_FILTER;
91
92     private static final String SELECT_ALL_FOR_PARENT =
93             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER;
94
95     private static final String SELECT_ALL_VERSIONS_FOR_PARENT =
96             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER;
97
98     private static final String SELECT_ALL_VERSIONS = SELECT_FROM_TABLE + WHERE + NAME_FILTER;
99
100     private static final String SELECT_BY_CONCEPT_KEY =
101             SELECT_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER;
102
103     private static final String SELECT_BY_REFERENCE_KEY =
104             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER + AND + LOCAL_NAME_FILTER;
105     // @formatter:on
106
107     // Entity manager for JPA
108     private EntityManagerFactory emf = null;
109
110     @Override
111     public void init(final DaoParameters daoParameters) throws PfModelException {
112         if (daoParameters == null || daoParameters.getPersistenceUnit() == null) {
113             LOGGER.error("Policy Framework persistence unit parameter not set");
114             throw new PfModelException(Response.Status.INTERNAL_SERVER_ERROR,
115                     "Policy Framework persistence unit parameter not set");
116         }
117
118         LOGGER.debug("Creating Policy Framework persistence unit \"{}\" . . .", daoParameters.getPersistenceUnit());
119         try {
120             emf = Persistence.createEntityManagerFactory(daoParameters.getPersistenceUnit(),
121                     daoParameters.getJdbcProperties());
122         } catch (final Exception ex) {
123             String errorMessage = "Creation of Policy Framework persistence unit \""
124                     + daoParameters.getPersistenceUnit() + "\" failed";
125             LOGGER.warn(errorMessage);
126             throw new PfModelException(Response.Status.INTERNAL_SERVER_ERROR, errorMessage, ex);
127         }
128         LOGGER.debug("Created Policy Framework persistence unit \"{}\"", daoParameters.getPersistenceUnit());
129     }
130
131     /**
132      * Gets the entity manager for this DAO.
133      *
134      * @return the entity manager
135      */
136     protected final synchronized EntityManager getEntityManager() {
137         if (emf == null) {
138             LOGGER.warn("Policy Framework DAO has not been initialized");
139             throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR,
140                     "Policy Framework DAO has not been initialized");
141         }
142
143         return emf.createEntityManager();
144     }
145
146     @Override
147     public final void close() {
148         if (emf != null) {
149             emf.close();
150         }
151     }
152
153     @Override
154     public <T extends PfConcept> void create(final T obj) {
155         if (obj == null) {
156             return;
157         }
158         final EntityManager mg = getEntityManager();
159         try {
160             mg.getTransaction().begin();
161             mg.merge(obj);
162             mg.getTransaction().commit();
163         } finally {
164             mg.close();
165         }
166     }
167
168     @Override
169     public <T extends PfConcept> void delete(final T obj) {
170         if (obj == null) {
171             return;
172         }
173         final EntityManager mg = getEntityManager();
174         try {
175             mg.getTransaction().begin();
176             mg.remove(mg.contains(obj) ? obj : mg.merge(obj));
177             mg.getTransaction().commit();
178         } finally {
179             mg.close();
180         }
181     }
182
183     @Override
184     public <T extends PfConcept> void delete(final Class<T> someClass, final PfConceptKey key) {
185         if (key == null) {
186             return;
187         }
188         final EntityManager mg = getEntityManager();
189         try {
190             // @formatter:off
191             mg.getTransaction().begin();
192             mg.createQuery(setQueryTable(DELETE_BY_CONCEPT_KEY, someClass), someClass)
193                 .setParameter(NAME,    key.getName())
194                 .setParameter(VERSION, key.getVersion())
195                 .executeUpdate();
196             mg.getTransaction().commit();
197             // @formatter:on
198         } finally {
199             mg.close();
200         }
201     }
202
203     @Override
204     public <T extends PfConcept> void delete(final Class<T> someClass, final PfReferenceKey key) {
205         if (key == null) {
206             return;
207         }
208         final EntityManager mg = getEntityManager();
209         try {
210             // @formatter:off
211             mg.getTransaction().begin();
212             mg.createQuery(setQueryTable(DELETE_BY_REFERENCE_KEY, someClass), someClass)
213                 .setParameter(PARENT_NAME,    key.getParentKeyName())
214                 .setParameter(PARENT_VERSION, key.getParentKeyVersion())
215                 .setParameter(LOCAL_NAME,     key.getLocalName())
216                 .executeUpdate();
217             mg.getTransaction().commit();
218             // @formatter:on
219         } finally {
220             mg.close();
221         }
222     }
223
224     @Override
225     public <T extends PfConcept> void delete(final Class<T> someClass, final PfTimestampKey key) {
226         if (key == null) {
227             return;
228         }
229         final EntityManager mg = getEntityManager();
230         try {
231             // @formatter:off
232             mg.getTransaction().begin();
233             mg.createQuery(setQueryTable(DELETE_BY_TIMESTAMP_KEY, someClass), someClass)
234                     .setParameter(NAME,    key.getName())
235                     .setParameter(VERSION, key.getVersion())
236                     .setParameter(TIMESTAMP, key.getTimeStamp())
237                     .executeUpdate();
238             mg.getTransaction().commit();
239             // @formatter:on
240         } finally {
241             mg.close();
242         }
243     }
244
245     @Override
246     public <T extends PfConcept> void createCollection(final Collection<T> objs) {
247         if (objs == null || objs.isEmpty()) {
248             return;
249         }
250         final EntityManager mg = getEntityManager();
251         try {
252             mg.getTransaction().begin();
253             for (final T t : objs) {
254                 mg.merge(t);
255             }
256             mg.getTransaction().commit();
257         } finally {
258             mg.close();
259         }
260     }
261
262     @Override
263     public <T extends PfConcept> void deleteCollection(final Collection<T> objs) {
264         if (objs == null || objs.isEmpty()) {
265             return;
266         }
267         final EntityManager mg = getEntityManager();
268         try {
269             mg.getTransaction().begin();
270             for (final T t : objs) {
271                 mg.remove(mg.contains(t) ? t : mg.merge(t));
272             }
273             mg.getTransaction().commit();
274         } finally {
275             mg.close();
276         }
277     }
278
279     @Override
280     public <T extends PfConcept> int deleteByConceptKey(final Class<T> someClass, final Collection<PfConceptKey> keys) {
281         if (keys == null || keys.isEmpty()) {
282             return 0;
283         }
284         int deletedCount = 0;
285         final EntityManager mg = getEntityManager();
286         try {
287             // @formatter:off
288             mg.getTransaction().begin();
289             for (final PfConceptKey key : keys) {
290                 deletedCount += mg.createQuery(setQueryTable(DELETE_BY_CONCEPT_KEY, someClass), someClass)
291                     .setParameter(NAME,    key.getName())
292                     .setParameter(VERSION, key.getVersion())
293                     .executeUpdate();
294             }
295             mg.getTransaction().commit();
296             // @formatter:on
297         } finally {
298             mg.close();
299         }
300         return deletedCount;
301     }
302
303     @Override
304     public <T extends PfConcept> int deleteByReferenceKey(final Class<T> someClass,
305             final Collection<PfReferenceKey> keys) {
306         if (keys == null || keys.isEmpty()) {
307             return 0;
308         }
309         int deletedCount = 0;
310         final EntityManager mg = getEntityManager();
311         try {
312             // @formatter:off
313             mg.getTransaction().begin();
314             for (final PfReferenceKey key : keys) {
315                 deletedCount += mg.createQuery(setQueryTable(DELETE_BY_REFERENCE_KEY, someClass), someClass)
316                     .setParameter(PARENT_NAME,    key.getParentKeyName())
317                     .setParameter(PARENT_VERSION, key.getParentKeyVersion())
318                     .setParameter(LOCAL_NAME,     key.getLocalName())
319                     .executeUpdate();
320             }
321             mg.getTransaction().commit();
322             // @formatter:on
323         } finally {
324             mg.close();
325         }
326         return deletedCount;
327     }
328
329     @Override
330     public <T extends PfConcept> void deleteAll(final Class<T> someClass) {
331         final EntityManager mg = getEntityManager();
332         try {
333             mg.getTransaction().begin();
334             mg.createQuery(setQueryTable(DELETE_FROM_TABLE, someClass), someClass).executeUpdate();
335             mg.getTransaction().commit();
336         } finally {
337             mg.close();
338         }
339     }
340
341     @Override
342     public <T extends PfConcept> List<T> getFiltered(final Class<T> someClass, final String name,
343             final String version) {
344         if (name == null) {
345             return getAll(someClass);
346         }
347
348         if (version == null) {
349             return getAllVersions(someClass, name);
350         }
351
352         T foundConcept = get(someClass, new PfConceptKey(name, version));
353
354         return (foundConcept == null ? Collections.emptyList() : Collections.singletonList(foundConcept));
355     }
356
357     @Override
358     public <T extends PfConcept> List<T> getFiltered(final Class<T> someClass, final String name, final String version,
359             final Instant startTime, final Instant endTime, final Map<String, Object> filterMap, final String sortOrder,
360             final int getRecordNum) {
361         final EntityManager mg = getEntityManager();
362
363         String filterQueryString = SELECT_FROM_TABLE + WHERE;
364
365         try {
366             if (filterMap != null) {
367                 filterQueryString = buildFilter(filterMap, filterQueryString);
368             }
369             filterQueryString = addKeyFilterString(filterQueryString, name, startTime, endTime);
370             if (getRecordNum > 0) {
371                 filterQueryString += ORDER + " c.key.timeStamp " + sortOrder;
372             }
373             TypedQuery<T> query = mg.createQuery(setQueryTable(filterQueryString, someClass), someClass);
374
375             if (filterMap != null) {
376                 for (Map.Entry<String, Object> entry : filterMap.entrySet()) {
377                     query.setParameter(entry.getKey(), entry.getValue());
378                 }
379             }
380             if (name != null) {
381                 query.setParameter("name", name);
382             }
383             if (startTime != null) {
384                 if (endTime != null) {
385                     query.setParameter("startTime", Timestamp.from(startTime));
386                     query.setParameter("endTime", Timestamp.from(endTime));
387                 } else {
388                     query.setParameter("startTime", Timestamp.from(startTime));
389                 }
390             } else {
391                 if (endTime != null) {
392                     query.setParameter("endTime", Timestamp.from(endTime));
393                 }
394             }
395             if (getRecordNum > 0) {
396                 query.setMaxResults(getRecordNum);
397             }
398
399             LOGGER.debug("filterQueryString is  \"{}\"", filterQueryString);
400             return query.getResultList();
401         } finally {
402             mg.close();
403         }
404     }
405
406     private String buildFilter(final Map<String, Object> filterMap, String filterQueryString) {
407         StringBuilder bld = new StringBuilder(filterQueryString);
408         for (String key : filterMap.keySet()) {
409             bld.append("c." + key + "= :" + key + AND);
410         }
411         return bld.toString();
412     }
413
414     @Override
415     public <T extends PfConcept> T get(final Class<T> someClass, final PfConceptKey key) {
416         return genericGet(someClass, key);
417     }
418
419     @Override
420     public <T extends PfConcept> T get(final Class<T> someClass, final PfReferenceKey key) {
421         return genericGet(someClass, key);
422     }
423
424     @Override
425     public <T extends PfConcept> T get(final Class<T> someClass, final PfTimestampKey key) {
426         return genericGet(someClass, key);
427     }
428
429     private <T extends PfConcept> T genericGet(final Class<T> someClass, final Object key) {
430         if (someClass == null) {
431             return null;
432         }
433         final EntityManager mg = getEntityManager();
434         try {
435             final T t = mg.find(someClass, key);
436             if (t != null) {
437                 mg.refresh(t);
438             }
439             return checkAndReturn(someClass, t);
440         } finally {
441             mg.close();
442         }
443     }
444
445     @Override
446     public <T extends PfConcept> List<T> getAll(final Class<T> someClass) {
447         if (someClass == null) {
448             return Collections.emptyList();
449         }
450         final EntityManager mg = getEntityManager();
451         try {
452             return mg.createQuery(setQueryTable(SELECT_FROM_TABLE, someClass), someClass).getResultList();
453         } finally {
454             mg.close();
455         }
456     }
457
458     @Override
459     public <T extends PfConcept> List<T> getAll(final Class<T> someClass, final PfConceptKey parentKey) {
460         if (someClass == null) {
461             return Collections.emptyList();
462         }
463         final EntityManager mg = getEntityManager();
464         try {
465             // @formatter:off
466             return mg.createQuery(setQueryTable(SELECT_ALL_FOR_PARENT, someClass), someClass)
467                     .setParameter(PARENT_NAME,    parentKey.getName())
468                     .setParameter(PARENT_VERSION, parentKey.getVersion())
469                     .getResultList();
470             // @formatter:on
471         } finally {
472             mg.close();
473         }
474     }
475
476     @Override
477     public <T extends PfConcept> List<T> getAllVersionsByParent(final Class<T> someClass, final String parentKeyName) {
478         if (someClass == null || parentKeyName == null) {
479             return Collections.emptyList();
480         }
481         final EntityManager mg = getEntityManager();
482         try {
483             // @formatter:off
484             return mg.createQuery(setQueryTable(SELECT_ALL_VERSIONS_FOR_PARENT, someClass), someClass)
485                     .setParameter(PARENT_NAME, parentKeyName)
486                     .getResultList();
487             // @formatter:on
488         } finally {
489             mg.close();
490         }
491     }
492
493     @Override
494     public <T extends PfConcept> List<T> getAllVersions(final Class<T> someClass, final String conceptName) {
495         if (someClass == null || conceptName == null) {
496             return Collections.emptyList();
497         }
498         final EntityManager mg = getEntityManager();
499         try {
500             // @formatter:off
501             return mg.createQuery(setQueryTable(SELECT_ALL_VERSIONS, someClass), someClass)
502                     .setParameter(NAME, conceptName)
503                     .getResultList();
504             // @formatter:on
505         } finally {
506             mg.close();
507         }
508     }
509
510     @Override
511     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfConceptKey key) {
512         if (someClass == null || key == null) {
513             return null;
514         }
515         final EntityManager mg = getEntityManager();
516         List<T> ret;
517         try {
518             // @formatter:off
519             ret = mg.createQuery(setQueryTable(SELECT_BY_CONCEPT_KEY, someClass), someClass)
520                     .setParameter(NAME,    key.getName())
521                     .setParameter(VERSION, key.getVersion())
522                     .getResultList();
523             // @formatter:on
524         } finally {
525             mg.close();
526         }
527
528         return getSingleResult(someClass, key.getId(), ret);
529     }
530
531     @Override
532     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfReferenceKey key) {
533         if (someClass == null || key == null) {
534             return null;
535         }
536         final EntityManager mg = getEntityManager();
537         List<T> ret;
538         try {
539             // @formatter:off
540             ret = mg.createQuery(setQueryTable(SELECT_BY_REFERENCE_KEY, someClass), someClass)
541                     .setParameter(PARENT_NAME,    key.getParentKeyName())
542                     .setParameter(PARENT_VERSION, key.getParentKeyVersion())
543                     .setParameter(LOCAL_NAME,     key.getLocalName())
544                     .getResultList();
545             // @formatter:on
546         } finally {
547             mg.close();
548         }
549
550         return getSingleResult(someClass, key.getId(), ret);
551     }
552
553     @Override
554     public <T extends PfConcept> T update(final T obj) {
555         final EntityManager mg = getEntityManager();
556         T ret;
557         try {
558             mg.getTransaction().begin();
559             ret = mg.merge(obj);
560             mg.flush();
561             mg.getTransaction().commit();
562         } finally {
563             mg.close();
564         }
565         return ret;
566     }
567
568     @Override
569     public <T extends PfConcept> long size(final Class<T> someClass) {
570         if (someClass == null) {
571             return 0;
572         }
573         final EntityManager mg = getEntityManager();
574         long size = 0;
575         try {
576             size = mg.createQuery("SELECT COUNT(c) FROM " + someClass.getSimpleName() + " c", Long.class)
577                     .getSingleResult();
578         } finally {
579             mg.close();
580         }
581         return size;
582     }
583
584     /**
585      * Add the table to a query string.
586      *
587      * @param queryString the query string
588      * @param tableClass the class name of the table
589      * @return the updated query string
590      */
591     private <T extends PfConcept> String setQueryTable(final String queryString, final Class<T> tableClass) {
592         return queryString.replace(TABLE_TOKEN, tableClass.getSimpleName());
593     }
594
595     /**
596      * Check that a query returned one and only one entry and return that entry.
597      *
598      * @param someClass the class being searched for
599      * @param conceptName the concept name being searched for
600      * @param resultList the result list returned by the query
601      * @return the single unique result
602      */
603     private <T extends PfConcept> T getSingleResult(final Class<T> someClass, final String searchFilter, List<T> ret) {
604         if (ret == null || ret.isEmpty()) {
605             return null;
606         }
607         if (ret.size() > 1) {
608             throw new IllegalArgumentException("More than one result was returned query on " + someClass
609                     + " with filter " + searchFilter + ": " + ret);
610         }
611         return ret.get(0);
612     }
613
614     /**
615      * generate filter string with the filter value in TimestampKey.
616      *
617      * @param inputFilterString current filterString generated from FilterMap
618      * @param name the pdp name the start timeStamp to filter from database, filter rule: startTime <= filteredRecord
619      *        timeStamp <= endTime. null for ignore start time.
620      * @param endTime the end timeStamp to filter from database, filter rule: startTime <= filteredRecord timeStamp <=
621      *        endTime. null for ignore end time
622      * @return the filter string to query database
623      */
624     private String addKeyFilterString(String inputFilterString, final String name, final Instant startTime,
625             final Instant endTime) {
626         String filterQueryString;
627         if (name != null) {
628             inputFilterString += NAME_FILTER + AND;
629         }
630         if (startTime != null) {
631             if (endTime != null) {
632                 filterQueryString = inputFilterString + TIMESTAMP_START_FILTER + AND + TIMESTAMP_END_FILTER;
633             } else {
634                 filterQueryString = inputFilterString + TIMESTAMP_START_FILTER;
635             }
636         } else {
637             if (endTime != null) {
638                 filterQueryString = inputFilterString + TIMESTAMP_END_FILTER;
639             } else {
640                 filterQueryString = inputFilterString.substring(0, inputFilterString.length() - AND.length());
641             }
642         }
643
644         return filterQueryString;
645     }
646
647     /**
648      * check the result get from database and return the object.
649      *
650      * @param <T> the type of the object to get, a subclass of {@link PfConcept}
651      * @param someClass the class of the object to get, a subclass of {@link PfConcept}
652      * @param t the object that was retrieved from the database
653      * @return the checked object or null
654      */
655     private <T extends PfConcept> T checkAndReturn(final Class<T> someClass, final T objToCheck) {
656         if (objToCheck != null) {
657             try {
658                 return PfUtils.makeCopy(objToCheck);
659             } catch (final Exception e) {
660                 LOGGER.warn(CLONE_ERR_MSG, someClass.getName(), e);
661             }
662         }
663         return null;
664     }
665 }