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