d0681e1e477ba3515f82902fefeefed1773d85a8
[policy/models.git] / models-dao / src / main / java / org / onap / policy / models / dao / impl / ProxyDao.java
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation.
4  *  Modifications Copyright (C) 2022 Bell Canada. 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.List;
27 import javax.persistence.EntityManager;
28 import javax.persistence.Persistence;
29 import javax.persistence.TypedQuery;
30 import lombok.RequiredArgsConstructor;
31 import org.apache.commons.lang3.StringUtils;
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.PfReferenceKey;
36 import org.onap.policy.models.base.PfReferenceTimestampKey;
37 import org.onap.policy.models.base.PfTimestampKey;
38 import org.onap.policy.models.base.PfUtils;
39 import org.onap.policy.models.dao.DaoParameters;
40 import org.onap.policy.models.dao.PfDao;
41 import org.onap.policy.models.dao.PfFilter;
42 import org.onap.policy.models.dao.PfFilterFactory;
43 import org.onap.policy.models.dao.PfFilterParametersIntfc;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47 /**
48  * The Class ProxyDao is an JPA implementation of the {@link ProxyDao} class for Policy Framework concepts
49  * ({@link PfConcept}). It uses the default JPA implementation in the javax {@link Persistence} class.
50  */
51 @RequiredArgsConstructor
52 public class ProxyDao implements PfDao {
53     private static final Logger LOGGER = LoggerFactory.getLogger(ProxyDao.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_BY        = " ORDER BY c.";
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 PARENT_NAME_FILTER     = "c.key.parentKeyName = :parentname";
77     private static final String PARENT_VERSION_FILTER  = "c.key.parentKeyVersion = :parentversion";
78     private static final String LOCAL_NAME_FILTER      = "c.key.localName = :localname";
79
80     private static final String CLONE_ERR_MSG = "Could not clone object of class \"{}\"";
81
82     private static final String DELETE_BY_CONCEPT_KEY =
83             DELETE_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER;
84
85     private static final String DELETE_BY_TIMESTAMP_KEY =
86             DELETE_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER  + AND + TIMESTAMP_FILTER;
87
88     private static final String DELETE_BY_REFERENCE_KEY =
89             DELETE_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER + AND + LOCAL_NAME_FILTER;
90
91     private static final String SELECT_ALL_FOR_PARENT =
92             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER;
93
94     private static final String SELECT_ALL_VERSIONS_FOR_PARENT =
95             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER;
96
97     private static final String SELECT_ALL_VERSIONS = SELECT_FROM_TABLE + WHERE + NAME_FILTER;
98
99     private static final String SELECT_BY_CONCEPT_KEY =
100             SELECT_FROM_TABLE + WHERE + NAME_FILTER + AND + VERSION_FILTER;
101
102     private static final String SELECT_BY_REFERENCE_KEY =
103             SELECT_FROM_TABLE + WHERE + PARENT_NAME_FILTER + AND + PARENT_VERSION_FILTER + AND + LOCAL_NAME_FILTER;
104     // @formatter:on
105
106     // Entity manager for JPA
107     private final EntityManager mg;
108
109     @Override
110     public void init(final DaoParameters daoParameters) throws PfModelException {
111         // Entity manager for JPA should be created at Service level
112     }
113
114     @Override
115     public final void close() {
116         // Entity manager for JPA should be close at Service level
117     }
118
119     @Override
120     public <T extends PfConcept> void create(final T obj) {
121         if (obj == null) {
122             return;
123         }
124         mg.merge(obj);
125         mg.flush();
126     }
127
128     @Override
129     public <T extends PfConcept> void delete(final T obj) {
130         if (obj == null) {
131             return;
132         }
133         mg.remove(mg.contains(obj) ? obj : mg.merge(obj));
134     }
135
136     @Override
137     public <T extends PfConcept> void delete(final Class<T> someClass, final PfConceptKey key) {
138         if (key == null) {
139             return;
140         }
141         // @formatter:off
142         mg.createQuery(setQueryTable(DELETE_BY_CONCEPT_KEY, someClass), someClass)
143             .setParameter(NAME,    key.getName())
144             .setParameter(VERSION, key.getVersion())
145             .executeUpdate();
146         // @formatter:on
147     }
148
149     @Override
150     public <T extends PfConcept> void delete(final Class<T> someClass, final PfReferenceKey key) {
151         if (key == null) {
152             return;
153         }
154         // @formatter:off
155         mg.createQuery(setQueryTable(DELETE_BY_REFERENCE_KEY, someClass), someClass)
156             .setParameter(PARENT_NAME,    key.getParentKeyName())
157             .setParameter(PARENT_VERSION, key.getParentKeyVersion())
158             .setParameter(LOCAL_NAME,     key.getLocalName())
159             .executeUpdate();
160         // @formatter:on
161     }
162
163     @Override
164     public <T extends PfConcept> void delete(final Class<T> someClass, final PfTimestampKey key) {
165         if (key == null) {
166             return;
167         }
168
169         // @formatter:off
170         mg.createQuery(setQueryTable(DELETE_BY_TIMESTAMP_KEY, someClass), someClass)
171                 .setParameter(NAME,    key.getName())
172                 .setParameter(VERSION, key.getVersion())
173                 .setParameter(TIMESTAMP, key.getTimeStamp())
174                 .executeUpdate();
175         // @formatter:on
176     }
177
178     @Override
179     public <T extends PfConcept> void createCollection(final Collection<T> objs) {
180         if (objs == null || objs.isEmpty()) {
181             return;
182         }
183
184         for (final T t : objs) {
185             mg.merge(t);
186         }
187     }
188
189     @Override
190     public <T extends PfConcept> void deleteCollection(final Collection<T> objs) {
191         if (objs == null || objs.isEmpty()) {
192             return;
193         }
194
195         for (final T t : objs) {
196             mg.remove(mg.contains(t) ? t : mg.merge(t));
197         }
198     }
199
200     @Override
201     public <T extends PfConcept> int deleteByConceptKey(final Class<T> someClass, final Collection<PfConceptKey> keys) {
202         if (keys == null || keys.isEmpty()) {
203             return 0;
204         }
205         var deletedCount = 0;
206
207         for (final PfConceptKey key : keys) {
208             // @formatter:off
209             deletedCount += mg.createQuery(setQueryTable(DELETE_BY_CONCEPT_KEY, someClass), someClass)
210                 .setParameter(NAME,    key.getName())
211                 .setParameter(VERSION, key.getVersion())
212                 .executeUpdate();
213             // @formatter:on
214         }
215
216         return deletedCount;
217     }
218
219     @Override
220     public <T extends PfConcept> int deleteByReferenceKey(final Class<T> someClass,
221             final Collection<PfReferenceKey> keys) {
222         if (keys == null || keys.isEmpty()) {
223             return 0;
224         }
225         var deletedCount = 0;
226
227         for (final PfReferenceKey key : keys) {
228             // @formatter:off
229             deletedCount += mg.createQuery(setQueryTable(DELETE_BY_REFERENCE_KEY, someClass), someClass)
230                 .setParameter(PARENT_NAME,    key.getParentKeyName())
231                 .setParameter(PARENT_VERSION, key.getParentKeyVersion())
232                 .setParameter(LOCAL_NAME,     key.getLocalName())
233                 .executeUpdate();
234             // @formatter:on
235         }
236         return deletedCount;
237     }
238
239     @Override
240     public <T extends PfConcept> void deleteAll(final Class<T> someClass) {
241         mg.createQuery(setQueryTable(DELETE_FROM_TABLE, someClass), someClass).executeUpdate();
242     }
243
244     @Override
245     public <T extends PfConcept> List<T> getFiltered(final Class<T> someClass, final String name,
246             final String version) {
247         if (name == null) {
248             return getAll(someClass);
249         }
250
251         if (version == null) {
252             return getAllVersions(someClass, name);
253         }
254
255         var foundConcept = get(someClass, new PfConceptKey(name, version));
256
257         return (foundConcept == null ? Collections.emptyList() : Collections.singletonList(foundConcept));
258     }
259
260     @Override
261     public <T extends PfConcept> List<T> getFiltered(final Class<T> someClass, PfFilterParametersIntfc filterParams) {
262
263         PfFilter filter = new PfFilterFactory().createFilter(someClass);
264         var filterQueryString =
265                 SELECT_FROM_TABLE + filter.genWhereClause(filterParams) + filter.genOrderClause(filterParams);
266
267         TypedQuery<T> query = mg.createQuery(setQueryTable(filterQueryString, someClass), someClass);
268         filter.setParams(query, filterParams);
269
270         LOGGER.debug("filterQueryString is  \"{}\"", filterQueryString);
271         return query.getResultList();
272     }
273
274     @Override
275     public <T extends PfConcept> T get(final Class<T> someClass, final PfConceptKey key) {
276         return genericGet(someClass, key);
277     }
278
279     @Override
280     public <T extends PfConcept> T get(final Class<T> someClass, final PfReferenceKey key) {
281         return genericGet(someClass, key);
282     }
283
284     @Override
285     public <T extends PfConcept> T get(final Class<T> someClass, final PfTimestampKey key) {
286         return genericGet(someClass, key);
287     }
288
289     @Override
290     public <T extends PfConcept> T get(final Class<T> someClass, final PfReferenceTimestampKey key) {
291         return genericGet(someClass, key);
292     }
293
294     private <T extends PfConcept> T genericGet(final Class<T> someClass, final Object key) {
295         if (someClass == null) {
296             return null;
297         }
298
299         final var t = mg.find(someClass, key);
300         return checkAndReturn(someClass, t);
301     }
302
303     @Override
304     public <T extends PfConcept> List<T> getAll(final Class<T> someClass) {
305         if (someClass == null) {
306             return Collections.emptyList();
307         }
308
309         return mg.createQuery(setQueryTable(SELECT_FROM_TABLE, someClass), someClass).getResultList();
310     }
311
312     @Override
313     public <T extends PfConcept> List<T> getAll(final Class<T> someClass, final PfConceptKey parentKey) {
314         if (someClass == null) {
315             return Collections.emptyList();
316         }
317
318         // @formatter:off
319         return mg.createQuery(setQueryTable(SELECT_ALL_FOR_PARENT, someClass), someClass)
320                 .setParameter(PARENT_NAME,    parentKey.getName())
321                 .setParameter(PARENT_VERSION, parentKey.getVersion())
322                 .getResultList();
323         // @formatter:on
324     }
325
326     @Override
327     public <T extends PfConcept> List<T> getAll(Class<T> someClass, String orderBy, Integer numRecords) {
328
329         if (someClass == null) {
330             return Collections.emptyList();
331         }
332
333         String query = setQueryTable(SELECT_FROM_TABLE, someClass);
334
335         if (StringUtils.isNotBlank(orderBy)) {
336             query = query.concat(ORDER_BY).concat(orderBy);
337         }
338
339         return mg.createQuery(query, someClass).setMaxResults(numRecords).getResultList();
340     }
341
342     @Override
343     public <T extends PfConcept> List<T> getAllVersionsByParent(final Class<T> someClass, final String parentKeyName) {
344         if (someClass == null || parentKeyName == null) {
345             return Collections.emptyList();
346         }
347
348         // @formatter:off
349         return mg.createQuery(setQueryTable(SELECT_ALL_VERSIONS_FOR_PARENT, someClass), someClass)
350                 .setParameter(PARENT_NAME, parentKeyName)
351                 .getResultList();
352         // @formatter:on
353     }
354
355     @Override
356     public <T extends PfConcept> List<T> getAllVersions(final Class<T> someClass, final String conceptName) {
357         if (someClass == null || conceptName == null) {
358             return Collections.emptyList();
359         }
360
361         // @formatter:off
362         return mg.createQuery(setQueryTable(SELECT_ALL_VERSIONS, someClass), someClass)
363                 .setParameter(NAME, conceptName)
364                 .getResultList();
365         // @formatter:on
366     }
367
368     @Override
369     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfConceptKey key) {
370         if (someClass == null || key == null) {
371             return null;
372         }
373
374         // @formatter:off
375         var ret = mg.createQuery(setQueryTable(SELECT_BY_CONCEPT_KEY, someClass), someClass)
376                 .setParameter(NAME,    key.getName())
377                 .setParameter(VERSION, key.getVersion())
378                 .getResultList();
379         // @formatter:on
380
381         return getSingleResult(someClass, key.getId(), ret);
382     }
383
384     @Override
385     public <T extends PfConcept> T getConcept(final Class<T> someClass, final PfReferenceKey key) {
386         if (someClass == null || key == null) {
387             return null;
388         }
389
390         // @formatter:off
391         var ret = mg.createQuery(setQueryTable(SELECT_BY_REFERENCE_KEY, someClass), someClass)
392                 .setParameter(PARENT_NAME,    key.getParentKeyName())
393                 .setParameter(PARENT_VERSION, key.getParentKeyVersion())
394                 .setParameter(LOCAL_NAME,     key.getLocalName())
395                 .getResultList();
396         // @formatter:on
397
398         return getSingleResult(someClass, key.getId(), ret);
399     }
400
401     @Override
402     public <T extends PfConcept> T update(final T obj) {
403         var ret = mg.merge(obj);
404         mg.flush();
405         return ret;
406     }
407
408     @Override
409     public <T extends PfConcept> long size(final Class<T> someClass) {
410         if (someClass == null) {
411             return 0;
412         }
413
414         long size = 0;
415         /*
416          * The invoking code only passes well-known classes into this method, thus
417          * disabling the sonar about SQL injection.
418          */
419         size = mg.createQuery("SELECT COUNT(c) FROM " + someClass.getSimpleName() + " c", Long.class) // NOSONAR
420                 .getSingleResult();
421         return size;
422     }
423
424     /**
425      * Add the table to a query string.
426      *
427      * @param queryString the query string
428      * @param tableClass the class name of the table
429      * @return the updated query string
430      */
431     private <T extends PfConcept> String setQueryTable(final String queryString, final Class<T> tableClass) {
432         return queryString.replace(TABLE_TOKEN, tableClass.getSimpleName());
433     }
434
435     /**
436      * Check that a query returned one and only one entry and return that entry.
437      *
438      * @param someClass the class being searched for
439      * @param searchFilter the search filter
440      * @param resultList the result list returned by the query
441      * @return the single unique result
442      */
443     private <T extends PfConcept> T getSingleResult(final Class<T> someClass, final String searchFilter,
444             List<T> resultList) {
445         if (resultList == null || resultList.isEmpty()) {
446             return null;
447         }
448         if (resultList.size() > 1) {
449             throw new IllegalArgumentException("More than one result was returned query on " + someClass
450                     + " with filter " + searchFilter + ": " + resultList);
451         }
452         return resultList.get(0);
453     }
454
455     /**
456      * check the result get from database and return the object.
457      *
458      * @param <T> the type of the object to get, a subclass of {@link PfConcept}
459      * @param someClass the class of the object to get, a subclass of {@link PfConcept}
460      * @param objToCheck the object that was retrieved from the database
461      * @return the checked object or null
462      */
463     private <T extends PfConcept> T checkAndReturn(final Class<T> someClass, final T objToCheck) {
464         if (objToCheck != null) {
465             try {
466                 return PfUtils.makeCopy(objToCheck);
467             } catch (final Exception e) {
468                 LOGGER.warn(CLONE_ERR_MSG, someClass.getName(), e);
469             }
470         }
471         return null;
472     }
473 }