b3d72a3fd4d2dca3132025fee69d38c93f6bb711
[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 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, ex);
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                 StringBuilder bld = new StringBuilder(filterQueryString);
364                 for (String key : filterMap.keySet()) {
365                     bld.append("c." + key + "= :" + key + AND);
366                 }
367                 filterQueryString = bld.toString();
368             }
369             filterQueryString = addKeyFilterString(filterQueryString, name, startTime, endTime);
370             if (getRecordNum > 0) {
371                 filterQueryString += ORDER + " c.key.timeStamp " + sortOrder;
372             }
373             TypedQuery<T> query = mg.createQuery(setQueryTable(filterQueryString, someClass), someClass);
374
375             if (filterMap != null) {
376                 for (Map.Entry<String, Object> entry : filterMap.entrySet()) {
377                     query.setParameter(entry.getKey(), entry.getValue());
378                 }
379             }
380             if (name != null) {
381                 query.setParameter("name", name);
382             }
383             if (startTime != null) {
384                 if (endTime != null) {
385                     query.setParameter("startTime", startTime);
386                     query.setParameter("endTime", endTime);
387                 } else {
388                     query.setParameter("startTime", startTime);
389                 }
390             } else {
391                 if (endTime != null) {
392                     query.setParameter("endTime", endTime);
393                 }
394             }
395             if (getRecordNum > 0) {
396                 query.setMaxResults(getRecordNum);
397             }
398
399             LOGGER.error("filterQueryString is  \"{}\"", filterQueryString);
400             return query.getResultList();
401         } finally {
402             mg.close();
403         }
404     }
405
406     @Override
407     public <T extends PfConcept> T get(final Class<T> someClass, final PfConceptKey key) {
408         if (someClass == null) {
409             return null;
410         }
411         final EntityManager mg = getEntityManager();
412         try {
413             final T t = mg.find(someClass, key);
414             return checkAndReturn(someClass, t);
415         } finally {
416             mg.close();
417         }
418     }
419
420     @Override
421     public <T extends PfConcept> T get(final Class<T> someClass, final PfReferenceKey key) {
422         if (someClass == null) {
423             return null;
424         }
425         final EntityManager mg = getEntityManager();
426         try {
427             final T t = mg.find(someClass, key);
428             return checkAndReturn(someClass, t);
429         } finally {
430             mg.close();
431         }
432     }
433
434     @Override
435     public <T extends PfConcept> T get(final Class<T> someClass, final PfTimestampKey key) {
436         if (someClass == null) {
437             return null;
438         }
439         final EntityManager mg = getEntityManager();
440         try {
441             final T t = mg.find(someClass, key);
442             return checkAndReturn(someClass, t);
443         } finally {
444             mg.close();
445         }
446     }
447
448     @Override
449     public <T extends PfConcept> List<T> getAll(final Class<T> someClass) {
450         if (someClass == null) {
451             return Collections.emptyList();
452         }
453         final EntityManager mg = getEntityManager();
454         try {
455             return mg.createQuery(setQueryTable(SELECT_FROM_TABLE, someClass), someClass).getResultList();
456         } finally {
457             mg.close();
458         }
459     }
460
461     @Override
462     public <T extends PfConcept> List<T> getAll(final Class<T> someClass, final PfConceptKey parentKey) {
463         if (someClass == null) {
464             return Collections.emptyList();
465         }
466         final EntityManager mg = getEntityManager();
467         try {
468             // @formatter:off
469             return mg.createQuery(setQueryTable(SELECT_ALL_FOR_PARENT, someClass), someClass)
470                     .setParameter(PARENT_NAME,    parentKey.getName())
471                     .setParameter(PARENT_VERSION, parentKey.getVersion())
472                     .getResultList();
473             // @formatter:on
474         } finally {
475             mg.close();
476         }
477     }
478
479     @Override
480     public <T extends PfConcept> List<T> getAllVersions(final Class<T> someClass, final String conceptName) {
481         if (someClass == null || conceptName == null) {
482             return Collections.emptyList();
483         }
484         final EntityManager mg = getEntityManager();
485         try {
486             // @formatter:off
487             return mg.createQuery(setQueryTable(SELECT_ALL_VERSIONS, someClass), someClass)
488                     .setParameter(NAME, conceptName)
489                     .getResultList();
490             // @formatter:on
491         } finally {
492             mg.close();
493         }
494     }
495
496     @Override
497     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfConceptKey key) {
498         if (someClass == null || key == null) {
499             return null;
500         }
501         final EntityManager mg = getEntityManager();
502         List<T> ret;
503         try {
504             // @formatter:off
505             ret = mg.createQuery(setQueryTable(SELECT_BY_CONCEPT_KEY, someClass), someClass)
506                     .setParameter(NAME,    key.getName())
507                     .setParameter(VERSION, key.getVersion())
508                     .getResultList();
509             // @formatter:on
510         } finally {
511             mg.close();
512         }
513
514         return getSingleResult(someClass, key.getId(), ret);
515     }
516
517     @Override
518     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfReferenceKey key) {
519         if (someClass == null || key == null) {
520             return null;
521         }
522         final EntityManager mg = getEntityManager();
523         List<T> ret;
524         try {
525             // @formatter:off
526             ret = mg.createQuery(setQueryTable(SELECT_BY_REFERENCE_KEY, someClass), someClass)
527                     .setParameter(PARENT_NAME,    key.getParentKeyName())
528                     .setParameter(PARENT_VERSION, key.getParentKeyVersion())
529                     .setParameter(LOCAL_NAME,     key.getLocalName())
530                     .getResultList();
531             // @formatter:on
532         } finally {
533             mg.close();
534         }
535
536         return getSingleResult(someClass, key.getId(), ret);
537     }
538
539     @Override
540     public <T extends PfConcept> T update(final T obj) {
541         final EntityManager mg = getEntityManager();
542         T ret;
543         try {
544             mg.getTransaction().begin();
545             ret = mg.merge(obj);
546             mg.flush();
547             mg.getTransaction().commit();
548         } finally {
549             mg.close();
550         }
551         return ret;
552     }
553
554     @Override
555     public <T extends PfConcept> long size(final Class<T> someClass) {
556         if (someClass == null) {
557             return 0;
558         }
559         final EntityManager mg = getEntityManager();
560         long size = 0;
561         try {
562             size = mg.createQuery("SELECT COUNT(c) FROM " + someClass.getSimpleName() + " c", Long.class)
563                     .getSingleResult();
564         } finally {
565             mg.close();
566         }
567         return size;
568     }
569
570     /**
571      * Add the table to a query string.
572      *
573      * @param queryString the query string
574      * @param tableClass the class name of the table
575      * @return the updated query string
576      */
577     private <T extends PfConcept> String setQueryTable(final String queryString, final Class<T> tableClass) {
578         return queryString.replaceAll(TABLE_TOKEN, tableClass.getSimpleName());
579     }
580
581     /**
582      * Check that a query returned one and only one entry and return that entry.
583      *
584      * @param someClass the class being searched for
585      * @param conceptName the concept name being searched for
586      * @param resultList the result list returned by the query
587      * @return the single unique result
588      */
589     private <T extends PfConcept> T getSingleResult(final Class<T> someClass, final String searchFilter, List<T> ret) {
590         if (ret == null || ret.isEmpty()) {
591             return null;
592         }
593         if (ret.size() > 1) {
594             throw new IllegalArgumentException("More than one result was returned query on " + someClass
595                     + " with filter " + searchFilter + ": " + ret);
596         }
597         return ret.get(0);
598     }
599
600     /**
601      * generate filter string with the filter value in TimestampKey.
602      *
603      * @param inputFilterString current filterString generated from FilterMap
604      * @param name the pdp name the start timeStamp to filter from database, filter rule: startTime <= filteredRecord
605      *        timeStamp <= endTime. null for ignore start time.
606      * @param endTime the end timeStamp to filter from database, filter rule: startTime <= filteredRecord timeStamp <=
607      *        endTime. null for ignore end time
608      * @return the filter string to query database
609      */
610     private String addKeyFilterString(String inputFilterString, final String name, final Date startTime,
611             final Date endTime) {
612         String filterQueryString;
613         if (name != null) {
614             inputFilterString += NAME_FILTER + AND;
615         }
616         if (startTime != null) {
617             if (endTime != null) {
618                 filterQueryString = inputFilterString + TIMESTAMP_START_FILTER + AND + TIMESTAMP_END_FILTER;
619             } else {
620                 filterQueryString = inputFilterString + TIMESTAMP_START_FILTER;
621             }
622         } else {
623             if (endTime != null) {
624                 filterQueryString = inputFilterString + TIMESTAMP_END_FILTER;
625             } else {
626                 filterQueryString = inputFilterString.substring(0, inputFilterString.length() - AND.length());
627             }
628         }
629
630         return filterQueryString;
631     }
632
633     /**
634      * check the result get from database and return the object.
635      *
636      * @param <T> the type of the object to get, a subclass of {@link PfConcept}
637      * @param someClass the class of the object to get, a subclass of {@link PfConcept}
638      * @param t the object that was retrieved from the database
639      * @return the checked object or null
640      */
641     private <T extends PfConcept> T checkAndReturn(final Class<T> someClass, final T objToCheck) {
642         if (objToCheck != null) {
643             try {
644                 return PfUtils.makeCopy(objToCheck);
645             } catch (final Exception e) {
646                 LOGGER.warn(CLONE_ERR_MSG, someClass.getName(), e);
647             }
648         }
649         return null;
650     }
651 }