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