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