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