Merge "Add impl of more PDP persistence"
[policy/models.git] / models-dao / src / main / java / org / onap / policy / models / dao / impl / DefaultPfDao.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2019 Nordix Foundation.
4  * ================================================================================
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  * SPDX-License-Identifier: Apache-2.0
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.models.dao.impl;
22
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.List;
26
27 import javax.persistence.EntityManager;
28 import javax.persistence.EntityManagerFactory;
29 import javax.persistence.Persistence;
30 import javax.ws.rs.core.Response;
31
32 import org.onap.policy.models.base.PfConcept;
33 import org.onap.policy.models.base.PfConceptKey;
34 import org.onap.policy.models.base.PfModelException;
35 import org.onap.policy.models.base.PfModelRuntimeException;
36 import org.onap.policy.models.base.PfReferenceKey;
37 import org.onap.policy.models.dao.DaoParameters;
38 import org.onap.policy.models.dao.PfDao;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42 /**
43  * The Class DefaultPfDao is an JPA implementation of the {@link PfDao} class for Policy Framework concepts
44  * ({@link PfConcept}). It uses the default JPA implementation in the javax {@link Persistence} class.
45  */
46 public class DefaultPfDao implements PfDao {
47     private static final Logger LOGGER = LoggerFactory.getLogger(DefaultPfDao.class);
48
49     // @formatter:off
50     private static final String NAME           = "name";
51     private static final String VERSION        = "version";
52     private static final String PARENT_NAME    = "parentname";
53     private static final String PARENT_VERSION = "parentversion";
54     private static final String LOCAL_NAME     = "localname";
55
56     private static final String TABLE_TOKEN = "__TABLE__";
57
58     private static final String DELETE_FROM_TABLE = "DELETE FROM __TABLE__ c";
59
60     private static final String SELECT_FROM_TABLE = "SELECT c FROM __TABLE__ c";
61
62     private static final String WHERE      = " WHERE ";
63     private static final String AND        = " AND ";
64
65     private static final String NAME_FILTER           = "c.key.name = :name";
66     private static final String VERSION_FILTER        = "c.key.version = :version";
67     private static final String PARENT_NAME_FILTER    = "c.key.parentKeyName = :parentname";
68     private static final String PARENT_VERSION_FILTER = "c.key.parentKeyVersion = :parentversion";
69     private static final String LOCAL_NAME_FILTER     = "c.key.localName = :localname";
70     private static final String MAX_VERISON_FILTER    = "c.key.version = (SELECT MAX(c.key.version) FROM __TABLE__ c)";
71
72     private static final String DELETE_BY_CONCEPT_KEY =
73             DELETE_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER;
74
75     private static final String DELETE_BY_REFERENCE_KEY =
76             DELETE_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER + AND + LOCAL_NAME_FILTER;
77
78     private static final String SELECT_ALL_FOR_PARENT =
79             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER;
80
81     private static final String SELECT_ALL_VERSIONS = SELECT_FROM_TABLE + WHERE + NAME_FILTER;
82
83     private static final String SELECT_LATEST_VERSION =
84             SELECT_FROM_TABLE + WHERE + NAME_FILTER + AND + MAX_VERISON_FILTER;
85
86     private static final String SELECT_LATEST_VERSIONS =
87             "SELECT c FROM __TABLE__ c WHERE c.key.version = (SELECT MAX(c.key.version) FROM __TABLE__ c)";
88
89     private static final String SELECT_BY_CONCEPT_KEY =
90             SELECT_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER;
91
92     private static final String SELECT_BY_REFERENCE_KEY =
93             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER + AND + LOCAL_NAME_FILTER;
94     // @formatter:on
95
96     // Entity manager for JPA
97     private EntityManagerFactory emf = null;
98
99     @Override
100     public void init(final DaoParameters daoParameters) throws PfModelException {
101         if (daoParameters == null || daoParameters.getPersistenceUnit() == null) {
102             LOGGER.error("Policy Framework persistence unit parameter not set");
103             throw new PfModelException(Response.Status.INTERNAL_SERVER_ERROR,
104                     "Policy Framework persistence unit parameter not set");
105         }
106
107         LOGGER.debug("Creating Policy Framework persistence unit \"{}\" . . .", daoParameters.getPersistenceUnit());
108         try {
109             emf = Persistence.createEntityManagerFactory(daoParameters.getPersistenceUnit(),
110                     daoParameters.getJdbcProperties());
111         } catch (final Exception ex) {
112             String errorMessage = "Creation of Policy Framework persistence unit \""
113                     + daoParameters.getPersistenceUnit() + "\" failed";
114             LOGGER.warn(errorMessage, ex);
115             throw new PfModelException(Response.Status.INTERNAL_SERVER_ERROR, errorMessage, ex);
116         }
117         LOGGER.debug("Created Policy Framework persistence unit \"{}\"", daoParameters.getPersistenceUnit());
118     }
119
120     /**
121      * Gets the entity manager for this DAO.
122      *
123      * @return the entity manager
124      */
125     protected final synchronized EntityManager getEntityManager() {
126         if (emf == null) {
127             LOGGER.warn("Policy Framework DAO has not been initialized");
128             throw new PfModelRuntimeException(Response.Status.INTERNAL_SERVER_ERROR,
129                     "Policy Framework DAO has not been initialized");
130         }
131
132         return emf.createEntityManager();
133     }
134
135     @Override
136     public final void close() {
137         if (emf != null) {
138             emf.close();
139         }
140     }
141
142     @Override
143     public <T extends PfConcept> void create(final T obj) {
144         if (obj == null) {
145             return;
146         }
147         final EntityManager mg = getEntityManager();
148         try {
149             mg.getTransaction().begin();
150             mg.merge(obj);
151             mg.getTransaction().commit();
152         } finally {
153             mg.close();
154         }
155     }
156
157     @Override
158     public <T extends PfConcept> void delete(final T obj) {
159         if (obj == null) {
160             return;
161         }
162         final EntityManager mg = getEntityManager();
163         try {
164             mg.getTransaction().begin();
165             mg.remove(mg.contains(obj) ? obj : mg.merge(obj));
166             mg.getTransaction().commit();
167         } finally {
168             mg.close();
169         }
170     }
171
172     @Override
173     public <T extends PfConcept> void delete(final Class<T> someClass, final PfConceptKey key) {
174         if (key == null) {
175             return;
176         }
177         final EntityManager mg = getEntityManager();
178         try {
179             // @formatter:off
180             mg.getTransaction().begin();
181             mg.createQuery(setQueryTable(DELETE_BY_CONCEPT_KEY, someClass), someClass)
182                 .setParameter(NAME,    key.getName())
183                 .setParameter("version", key.getVersion())
184                 .executeUpdate();
185             mg.getTransaction().commit();
186             // @formatter:on
187         } finally {
188             mg.close();
189         }
190     }
191
192     @Override
193     public <T extends PfConcept> void delete(final Class<T> someClass, final PfReferenceKey key) {
194         if (key == null) {
195             return;
196         }
197         final EntityManager mg = getEntityManager();
198         try {
199             // @formatter:off
200             mg.getTransaction().begin();
201             mg.createQuery(setQueryTable(DELETE_BY_REFERENCE_KEY, someClass), someClass)
202                 .setParameter(PARENT_NAME,    key.getParentKeyName())
203                 .setParameter(PARENT_VERSION, key.getParentKeyVersion())
204                 .setParameter(LOCAL_NAME,     key.getLocalName())
205                 .executeUpdate();
206             mg.getTransaction().commit();
207             // @formatter:on
208         } finally {
209             mg.close();
210         }
211     }
212
213     @Override
214     public <T extends PfConcept> void createCollection(final Collection<T> objs) {
215         if (objs == null || objs.isEmpty()) {
216             return;
217         }
218         final EntityManager mg = getEntityManager();
219         try {
220             mg.getTransaction().begin();
221             for (final T t : objs) {
222                 mg.merge(t);
223             }
224             mg.getTransaction().commit();
225         } finally {
226             mg.close();
227         }
228     }
229
230     @Override
231     public <T extends PfConcept> void deleteCollection(final Collection<T> objs) {
232         if (objs == null || objs.isEmpty()) {
233             return;
234         }
235         final EntityManager mg = getEntityManager();
236         try {
237             mg.getTransaction().begin();
238             for (final T t : objs) {
239                 mg.remove(mg.contains(t) ? t : mg.merge(t));
240             }
241             mg.getTransaction().commit();
242         } finally {
243             mg.close();
244         }
245     }
246
247     @Override
248     public <T extends PfConcept> int deleteByConceptKey(final Class<T> someClass, final Collection<PfConceptKey> keys) {
249         if (keys == null || keys.isEmpty()) {
250             return 0;
251         }
252         int deletedCount = 0;
253         final EntityManager mg = getEntityManager();
254         try {
255             // @formatter:off
256             mg.getTransaction().begin();
257             for (final PfConceptKey key : keys) {
258                 deletedCount += mg.createQuery(setQueryTable(DELETE_BY_CONCEPT_KEY, someClass), someClass)
259                     .setParameter(NAME,    key.getName())
260                     .setParameter("version", key.getVersion())
261                     .executeUpdate();
262             }
263             mg.getTransaction().commit();
264             // @formatter:on
265         } finally {
266             mg.close();
267         }
268         return deletedCount;
269     }
270
271     @Override
272     public <T extends PfConcept> int deleteByReferenceKey(final Class<T> someClass,
273             final Collection<PfReferenceKey> keys) {
274         if (keys == null || keys.isEmpty()) {
275             return 0;
276         }
277         int deletedCount = 0;
278         final EntityManager mg = getEntityManager();
279         try {
280             // @formatter:off
281             mg.getTransaction().begin();
282             for (final PfReferenceKey key : keys) {
283                 deletedCount += mg.createQuery(setQueryTable(DELETE_BY_REFERENCE_KEY, someClass), someClass)
284                     .setParameter(PARENT_NAME,    key.getParentKeyName())
285                     .setParameter(PARENT_VERSION, key.getParentKeyVersion())
286                     .setParameter(LOCAL_NAME,     key.getLocalName())
287                     .executeUpdate();
288             }
289             mg.getTransaction().commit();
290             // @formatter:on
291         } finally {
292             mg.close();
293         }
294         return deletedCount;
295     }
296
297     @Override
298     public <T extends PfConcept> void deleteAll(final Class<T> someClass) {
299         final EntityManager mg = getEntityManager();
300         try {
301             mg.getTransaction().begin();
302             mg.createQuery(setQueryTable(DELETE_FROM_TABLE, someClass), someClass).executeUpdate();
303             mg.getTransaction().commit();
304         } finally {
305             mg.close();
306         }
307     }
308
309     @Override
310     public <T extends PfConcept> List<T> getFiltered(Class<T> someClass, PfConceptKey key) {
311         if (key.getName() == null) {
312             return getAll(someClass);
313         }
314
315         if (key.getVersion() == null) {
316             return getAllVersions(someClass, key.getName());
317         }
318
319         T foundConcept = get(someClass, key);
320
321         return (foundConcept == null ? Collections.emptyList() : Collections.singletonList(foundConcept));
322     }
323
324     @Override
325     public <T extends PfConcept> T get(final Class<T> someClass, final PfConceptKey key) {
326         if (someClass == null) {
327             return null;
328         }
329         final EntityManager mg = getEntityManager();
330         try {
331             final T t = mg.find(someClass, key);
332             if (t != null) {
333                 // This clone is created to force the JPA DAO to recurse down through the object
334                 try {
335                     final T clonedT = someClass.newInstance();
336                     t.copyTo(clonedT);
337                     return clonedT;
338                 } catch (final Exception e) {
339                     LOGGER.warn("Could not clone object of class \"" + someClass.getCanonicalName() + "\"", e);
340                     return null;
341                 }
342             } else {
343                 return null;
344             }
345         } finally {
346             mg.close();
347         }
348     }
349
350     @Override
351     public <T extends PfConcept> T get(final Class<T> someClass, final PfReferenceKey key) {
352         if (someClass == null) {
353             return null;
354         }
355         final EntityManager mg = getEntityManager();
356         try {
357             final T t = mg.find(someClass, key);
358             if (t != null) {
359                 try {
360                     final T clonedT = someClass.newInstance();
361                     t.copyTo(clonedT);
362                     return clonedT;
363                 } catch (final Exception e) {
364                     LOGGER.warn("Could not clone object of class \"" + someClass.getCanonicalName() + "\"", e);
365                     return null;
366                 }
367             } else {
368                 return null;
369             }
370         } finally {
371             mg.close();
372         }
373     }
374
375     @Override
376     public <T extends PfConcept> List<T> getAll(final Class<T> someClass) {
377         if (someClass == null) {
378             return Collections.emptyList();
379         }
380         final EntityManager mg = getEntityManager();
381         try {
382             return mg.createQuery(setQueryTable(SELECT_FROM_TABLE, someClass), someClass).getResultList();
383         } finally {
384             mg.close();
385         }
386     }
387
388     @Override
389     public <T extends PfConcept> List<T> getAll(final Class<T> someClass, final PfConceptKey parentKey) {
390         if (someClass == null) {
391             return Collections.emptyList();
392         }
393         final EntityManager mg = getEntityManager();
394         try {
395             // @formatter:off
396             return mg.createQuery(setQueryTable(SELECT_ALL_FOR_PARENT, someClass), someClass)
397                     .setParameter(PARENT_NAME,    parentKey.getName())
398                     .setParameter(PARENT_VERSION, parentKey.getVersion())
399                     .getResultList();
400             // @formatter:on
401         } finally {
402             mg.close();
403         }
404     }
405
406     @Override
407     public <T extends PfConcept> List<T> getAllVersions(final Class<T> someClass, final String conceptName) {
408         if (someClass == null || conceptName == null) {
409             return Collections.emptyList();
410         }
411         final EntityManager mg = getEntityManager();
412         try {
413             // @formatter:off
414             return mg.createQuery(setQueryTable(SELECT_ALL_VERSIONS, someClass), someClass)
415                     .setParameter(NAME, conceptName)
416                     .getResultList();
417             // @formatter:on
418         } finally {
419             mg.close();
420         }
421     }
422
423     @Override
424     public <T extends PfConcept> List<T> getLatestVersions(final Class<T> someClass) {
425         if (someClass == null) {
426             return Collections.emptyList();
427         }
428         final EntityManager mg = getEntityManager();
429         List<T> ret;
430         try {
431             // @formatter:off
432             return mg.createQuery(setQueryTable(SELECT_LATEST_VERSIONS, someClass), someClass)
433                     .getResultList();
434             // @formatter:on
435         } finally {
436             mg.close();
437         }
438     }
439
440     @Override
441     public <T extends PfConcept> T getLatestVersion(final Class<T> someClass, final String conceptName) {
442         if (someClass == null || conceptName == null) {
443             return null;
444         }
445         final EntityManager mg = getEntityManager();
446         List<T> ret;
447         try {
448             // @formatter:off
449             ret = mg.createQuery(setQueryTable(SELECT_LATEST_VERSION, someClass), someClass)
450                     .setParameter(NAME, conceptName)
451                     .getResultList();
452             // @formatter:on
453         } finally {
454             mg.close();
455         }
456
457         return getSingleResult(someClass, conceptName, ret);
458     }
459
460     @Override
461     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfConceptKey key) {
462         if (someClass == null || key == null) {
463             return null;
464         }
465         final EntityManager mg = getEntityManager();
466         List<T> ret;
467         try {
468             // @formatter:off
469             ret = mg.createQuery(setQueryTable(SELECT_BY_CONCEPT_KEY, someClass), someClass)
470                     .setParameter(NAME,    key.getName())
471                     .setParameter(VERSION, key.getVersion())
472                     .getResultList();
473             // @formatter:on
474         } finally {
475             mg.close();
476         }
477
478         return getSingleResult(someClass, key.getId(), ret);
479     }
480
481     @Override
482     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfReferenceKey key) {
483         if (someClass == null || key == null) {
484             return null;
485         }
486         final EntityManager mg = getEntityManager();
487         List<T> ret;
488         try {
489             // @formatter:off
490             ret = mg.createQuery(setQueryTable(SELECT_BY_REFERENCE_KEY, someClass), someClass)
491                     .setParameter(PARENT_NAME,    key.getParentKeyName())
492                     .setParameter(PARENT_VERSION, key.getParentKeyVersion())
493                     .setParameter(LOCAL_NAME,     key.getLocalName())
494                     .getResultList();
495             // @formatter:on
496         } finally {
497             mg.close();
498         }
499
500         return getSingleResult(someClass, key.getId(), ret);
501     }
502
503     @Override
504     public <T extends PfConcept> T update(final T obj) {
505         final EntityManager mg = getEntityManager();
506         T ret;
507         try {
508             mg.getTransaction().begin();
509             ret = mg.merge(obj);
510             mg.flush();
511             mg.getTransaction().commit();
512         } finally {
513             mg.close();
514         }
515         return ret;
516     }
517
518     @Override
519     public <T extends PfConcept> long size(final Class<T> someClass) {
520         if (someClass == null) {
521             return 0;
522         }
523         final EntityManager mg = getEntityManager();
524         long size = 0;
525         try {
526             size = mg.createQuery("SELECT COUNT(c) FROM " + someClass.getSimpleName() + " c", Long.class)
527                     .getSingleResult();
528         } finally {
529             mg.close();
530         }
531         return size;
532     }
533
534     /**
535      * Add the table to a query string.
536      *
537      * @param queryString the query string
538      * @param tableClass the class name of the table
539      * @return the updated query string
540      */
541     private <T extends PfConcept> String setQueryTable(final String queryString, final Class<T> tableClass) {
542         return queryString.replaceAll(TABLE_TOKEN, tableClass.getSimpleName());
543     }
544
545     /**
546      * Check that a query returned one and only one entry and return that entry.
547      *
548      * @param someClass the class being searched for
549      * @param conceptName the concept name being searched for
550      * @param resultList the result list returned by the query
551      * @return the single unique result
552      */
553     private <T extends PfConcept> T getSingleResult(final Class<T> someClass, final String searchFilter, List<T> ret) {
554         if (ret == null || ret.isEmpty()) {
555             return null;
556         }
557         if (ret.size() > 1) {
558             throw new IllegalArgumentException("More than one result was returned query on " + someClass
559                     + " with filter " + searchFilter + ": " + ret);
560         }
561         return ret.get(0);
562     }
563 }