Package: ServiceDAO
ServiceDAO
name | instruction | branch | complexity | line | method | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
ServiceDAO() |
|
|
|
|
|
||||||||||||||||||||
checkGroupPermission(Domain, List, Permission) |
|
|
|
|
|
||||||||||||||||||||
count(EntityManager, Class, Class, KapuaQuery) |
|
|
|
|
|
||||||||||||||||||||
create(EntityManager, KapuaEntity) |
|
|
|
|
|
||||||||||||||||||||
delete(EntityManager, Class, KapuaId, KapuaId) |
|
|
|
|
|
||||||||||||||||||||
extractAttribute(Root, String) |
|
|
|
|
|
||||||||||||||||||||
find(EntityManager, Class, KapuaId, KapuaId) |
|
|
|
|
|
||||||||||||||||||||
findByField(EntityManager, Class, KapuaId, String, Object) |
|
|
|
|
|
||||||||||||||||||||
findByField(EntityManager, Class, String, Object) |
|
|
|
|
|
||||||||||||||||||||
findByName(EntityManager, Class, KapuaId, Object) |
|
|
|
|
|
||||||||||||||||||||
findByName(EntityManager, Class, Object) |
|
|
|
|
|
||||||||||||||||||||
handleAndPredicate(AndPredicate, Map, CriteriaBuilder, Root, EntityType) |
|
|
|
|
|
||||||||||||||||||||
handleAttributePredicate(AttributePredicate, Map, CriteriaBuilder, Root, EntityType) |
|
|
|
|
|
||||||||||||||||||||
handleKapuaQueryGroupPredicate(KapuaQuery, Domain, String) |
|
|
|
|
|
||||||||||||||||||||
handleKapuaQueryGroupPredicate(KapuaSession, KapuaQuery, Domain, String) |
|
|
|
|
|
||||||||||||||||||||
handleKapuaQueryPredicates(QueryPredicate, Map, CriteriaBuilder, Root, EntityType) |
|
|
|
|
|
||||||||||||||||||||
handleOrPredicate(OrPredicate, Map, CriteriaBuilder, Root, EntityType) |
|
|
|
|
|
||||||||||||||||||||
handlePredicate(List, Map, CriteriaBuilder, Root, EntityType) |
|
|
|
|
|
||||||||||||||||||||
isInsertConstraintViolation(PersistenceException) |
|
|
|
|
|
||||||||||||||||||||
lambda$handleKapuaQueryGroupPredicate$0(KapuaSession, KapuaId) |
|
|
|
|
|
||||||||||||||||||||
lambda$handleKapuaQueryGroupPredicate$1(AccessInfo) |
|
|
|
|
|
||||||||||||||||||||
lambda$handleKapuaQueryGroupPredicate$2(AccessInfo) |
|
|
|
|
|
||||||||||||||||||||
lambda$handleKapuaQueryGroupPredicate$3(AccessRole, KapuaId) |
|
|
|
|
|
||||||||||||||||||||
lambda$handleKapuaQueryGroupPredicate$4(Role) |
|
|
|
|
|
||||||||||||||||||||
query(EntityManager, Class, Class, KapuaListResult, KapuaQuery) |
|
|
|
|
|
||||||||||||||||||||
static {...} |
|
|
|
|
|
||||||||||||||||||||
update(EntityManager, Class, KapuaUpdatableEntity) |
|
|
|
|
|
Coverage
1: /*******************************************************************************
2: * Copyright (c) 2016, 2022 Eurotech and/or its affiliates and others
3: *
4: * This program and the accompanying materials are made
5: * available under the terms of the Eclipse Public License 2.0
6: * which is available at https://www.eclipse.org/legal/epl-2.0/
7: *
8: * SPDX-License-Identifier: EPL-2.0
9: *
10: * Contributors:
11: * Eurotech - initial API and implementation
12: *******************************************************************************/
13: package org.eclipse.kapua.commons.service.internal;
14:
15: import com.google.common.base.MoreObjects;
16: import org.apache.commons.lang.ArrayUtils;
17: import org.eclipse.kapua.KapuaEntityExistsException;
18: import org.eclipse.kapua.KapuaEntityNotFoundException;
19: import org.eclipse.kapua.KapuaErrorCodes;
20: import org.eclipse.kapua.KapuaException;
21: import org.eclipse.kapua.commons.jpa.EntityManager;
22: import org.eclipse.kapua.commons.model.AbstractKapuaUpdatableEntity;
23: import org.eclipse.kapua.commons.model.id.KapuaEid;
24: import org.eclipse.kapua.commons.model.query.predicate.AttributePredicateImpl;
25: import org.eclipse.kapua.commons.model.query.predicate.OrPredicateImpl;
26: import org.eclipse.kapua.commons.security.KapuaSecurityUtils;
27: import org.eclipse.kapua.commons.security.KapuaSession;
28: import org.eclipse.kapua.commons.setting.system.SystemSetting;
29: import org.eclipse.kapua.commons.setting.system.SystemSettingKey;
30: import org.eclipse.kapua.locator.KapuaLocator;
31: import org.eclipse.kapua.model.KapuaEntity;
32: import org.eclipse.kapua.model.KapuaEntityAttributes;
33: import org.eclipse.kapua.model.KapuaNamedEntity;
34: import org.eclipse.kapua.model.KapuaNamedEntityAttributes;
35: import org.eclipse.kapua.model.KapuaUpdatableEntity;
36: import org.eclipse.kapua.model.domain.Actions;
37: import org.eclipse.kapua.model.domain.Domain;
38: import org.eclipse.kapua.model.id.KapuaId;
39: import org.eclipse.kapua.model.query.FieldSortCriteria;
40: import org.eclipse.kapua.model.query.KapuaListResult;
41: import org.eclipse.kapua.model.query.KapuaQuery;
42: import org.eclipse.kapua.model.query.SortOrder;
43: import org.eclipse.kapua.model.query.predicate.AndPredicate;
44: import org.eclipse.kapua.model.query.predicate.AttributePredicate;
45: import org.eclipse.kapua.model.query.predicate.AttributePredicate.Operator;
46: import org.eclipse.kapua.model.query.predicate.MatchPredicate;
47: import org.eclipse.kapua.model.query.predicate.OrPredicate;
48: import org.eclipse.kapua.model.query.predicate.QueryPredicate;
49: import org.eclipse.kapua.service.authorization.access.AccessInfo;
50: import org.eclipse.kapua.service.authorization.access.AccessInfoFactory;
51: import org.eclipse.kapua.service.authorization.access.AccessInfoService;
52: import org.eclipse.kapua.service.authorization.access.AccessPermission;
53: import org.eclipse.kapua.service.authorization.access.AccessPermissionListResult;
54: import org.eclipse.kapua.service.authorization.access.AccessPermissionService;
55: import org.eclipse.kapua.service.authorization.access.AccessRole;
56: import org.eclipse.kapua.service.authorization.access.AccessRoleListResult;
57: import org.eclipse.kapua.service.authorization.access.AccessRoleService;
58: import org.eclipse.kapua.service.authorization.group.Group;
59: import org.eclipse.kapua.service.authorization.group.Groupable;
60: import org.eclipse.kapua.service.authorization.permission.Permission;
61: import org.eclipse.kapua.service.authorization.role.Role;
62: import org.eclipse.kapua.service.authorization.role.RolePermission;
63: import org.eclipse.kapua.service.authorization.role.RolePermissionListResult;
64: import org.eclipse.kapua.service.authorization.role.RolePermissionService;
65: import org.eclipse.kapua.service.authorization.role.RoleService;
66:
67: import org.checkerframework.checker.nullness.qual.NonNull;
68: import org.checkerframework.checker.nullness.qual.Nullable;
69: import org.slf4j.Logger;
70: import org.slf4j.LoggerFactory;
71:
72: import javax.persistence.Embedded;
73: import javax.persistence.EntityExistsException;
74: import javax.persistence.NonUniqueResultException;
75: import javax.persistence.PersistenceException;
76: import javax.persistence.TypedQuery;
77: import javax.persistence.criteria.CriteriaBuilder;
78: import javax.persistence.criteria.CriteriaQuery;
79: import javax.persistence.criteria.Expression;
80: import javax.persistence.criteria.JoinType;
81: import javax.persistence.criteria.Order;
82: import javax.persistence.criteria.ParameterExpression;
83: import javax.persistence.criteria.Path;
84: import javax.persistence.criteria.Predicate;
85: import javax.persistence.criteria.Root;
86: import javax.persistence.metamodel.Attribute;
87: import javax.persistence.metamodel.EntityType;
88: import javax.validation.constraints.Null;
89: import java.sql.SQLException;
90: import java.util.ArrayList;
91: import java.util.HashMap;
92: import java.util.List;
93: import java.util.Map;
94:
95: /**
96: * {@link ServiceDAO} utility methods.
97: *
98: * @since 1.0.0
99: */
100: public class ServiceDAO {
101:
102: private static final Logger LOG = LoggerFactory.getLogger(ServiceDAO.class);
103:
104: private static final AccessInfoService ACCESS_INFO_SERVICE;
105: private static final AccessInfoFactory ACCESS_INFO_FACTORY;
106:
107: private static final AccessPermissionService ACCESS_PERMISSION_SERVICE;
108: private static final AccessRoleService ACCESS_ROLE_SERVICE;
109:
110: private static final RoleService ROLE_SERVICE;
111: private static final RolePermissionService ROLE_PERMISSION_SERVICE;
112:
113: private static final String SQL_ERROR_CODE_CONSTRAINT_VIOLATION = "23505";
114:
115: private static final SystemSetting SYSTEM_SETTING = SystemSetting.getInstance();
116:
117: private static final String ESCAPE = SYSTEM_SETTING.getString(SystemSettingKey.DB_CHARACTER_ESCAPE, "\\");
118: private static final String LIKE = SYSTEM_SETTING.getString(SystemSettingKey.DB_CHARACTER_WILDCARD_ANY, "%");
119: private static final String ANY = SYSTEM_SETTING.getString(SystemSettingKey.DB_CHARACTER_WILDCARD_SINGLE, "_");
120:
121: private static final String ATTRIBUTE_SEPARATOR = ".";
122: private static final String ATTRIBUTE_SEPARATOR_ESCAPED = "\\.";
123:
124: private static final String COMPARE_ERROR_MESSAGE = "Trying to compare a non-comparable value";
125:
126: static {
127: KapuaLocator locator = null;
128: try {
129: locator = KapuaLocator.getInstance();
130: } catch (ExceptionInInitializerError kre) {
131: LOG.warn("KapuaLocator not available! Access Group feature may be not supported!", kre);
132: }
133:
134:• if (locator != null) {
135: ACCESS_INFO_SERVICE = KapuaLocator.getInstance().getService(AccessInfoService.class);
136: ACCESS_INFO_FACTORY = KapuaLocator.getInstance().getFactory(AccessInfoFactory.class);
137:
138: ACCESS_PERMISSION_SERVICE = KapuaLocator.getInstance().getService(AccessPermissionService.class);
139: ACCESS_ROLE_SERVICE = KapuaLocator.getInstance().getService(AccessRoleService.class);
140:
141: ROLE_SERVICE = KapuaLocator.getInstance().getService(RoleService.class);
142: ROLE_PERMISSION_SERVICE = KapuaLocator.getInstance().getService(RolePermissionService.class);
143: } else {
144: ACCESS_INFO_SERVICE = null;
145: ACCESS_INFO_FACTORY = null;
146:
147: ACCESS_PERMISSION_SERVICE = null;
148: ACCESS_ROLE_SERVICE = null;
149:
150: ROLE_SERVICE = null;
151: ROLE_PERMISSION_SERVICE = null;
152: }
153: }
154:
155: protected ServiceDAO() {
156: }
157:
158: /**
159: * Create {@link KapuaEntity} utility method.<br>
160: * This method checks for the constraint violation and, in this case, it throws a specific exception ({@link KapuaEntityExistsException}).
161: *
162: * @param em The {@link EntityManager} that holds the transaction.
163: * @param entity The {@link KapuaEntity} to be created.
164: * @return The persisted {@link KapuaEntity}.
165: * @since 1.0.0
166: */
167: public static <E extends KapuaEntity> E create(@NonNull EntityManager em, @NonNull E entity) {
168: try {
169: em.persist(entity);
170: em.flush();
171: em.refresh(entity);
172: } catch (EntityExistsException e) {
173: throw new KapuaEntityExistsException(e, entity.getId());
174: } catch (PersistenceException e) {
175:• if (isInsertConstraintViolation(e)) {
176: KapuaEntity entityFound = em.find(entity.getClass(), entity.getId());
177:• if (entityFound == null) {
178: throw e;
179: }
180: throw new KapuaEntityExistsException(e, entity.getId());
181: } else {
182: throw e;
183: }
184: }
185:
186: return entity;
187: }
188:
189: /**
190: * Update {@link KapuaUpdatableEntity} utility method.
191: *
192: * @param em The {@link EntityManager} that holds the transaction.
193: * @param clazz The {@link KapuaUpdatableEntity} class. This must be the implementing {@code class}.
194: * @param entity The {@link KapuaUpdatableEntity} to be updated
195: * @return The updated {@link KapuaUpdatableEntity}.
196: * @throws KapuaEntityNotFoundException If the {@link KapuaEntity} does not exists.
197: * @since 1.0.0
198: */
199: public static <E extends KapuaUpdatableEntity> E update(@NonNull EntityManager em, @NonNull Class<E> clazz, @NonNull E entity) throws KapuaEntityNotFoundException {
200: //
201: // Checking existence
202: E entityToUpdate = em.find(clazz, entity.getId());
203:
204: //
205: // Updating if not null
206:• if (entityToUpdate != null) {
207: AbstractKapuaUpdatableEntity updatableEntity = (AbstractKapuaUpdatableEntity) entity;
208: updatableEntity.setCreatedOn(entityToUpdate.getCreatedOn());
209: updatableEntity.setCreatedBy(entityToUpdate.getCreatedBy());
210:
211: em.merge(entity);
212: em.flush();
213: em.refresh(entityToUpdate);
214: } else {
215: throw new KapuaEntityNotFoundException(clazz.getSimpleName(), entity.getId());
216: }
217:
218: return entityToUpdate;
219: }
220:
221: /**
222: * Finds a {@link KapuaEntity}.
223: *
224: * @param em The {@link EntityManager} that holds the transaction.
225: * @param clazz The {@link KapuaEntity} class. This must be the implementing {@code class}.
226: * @param scopeId The {@link KapuaEntity#getScopeId()} the entity to be found.
227: * @param entityId The {@link KapuaEntity#getId()} of the entity to be found.
228: * @since 1.0.0
229: */
230: public static <E extends KapuaEntity> E find(@NonNull EntityManager em, @NonNull Class<E> clazz, @Null KapuaId scopeId, @NonNull KapuaId entityId) {
231: //
232: // Checking existence
233: E entityToFind = em.find(clazz, entityId);
234:
235: // If 'null' ScopeId has been requested, it means that we need to look for ANY ScopeId.
236:• KapuaId scopeIdToMatch = scopeId != null ? scopeId : KapuaId.ANY;
237:
238: //
239: // Return if not null and ScopeIds matches
240:• if (entityToFind != null) {
241:• if (KapuaId.ANY.equals(scopeIdToMatch)) { // If requested ScopeId is ANY, return whatever Entity has been found
242: return entityToFind;
243:• } else if (scopeIdToMatch.equals(entityToFind.getScopeId())) { // If a specific ScopeId is requested, return Entity if given ScopeId matches Entity.scopeId
244: return entityToFind;
245: } else { // If no match, return no result
246: return null;
247: }
248: } else {
249: return null;
250: }
251: }
252:
253: /**
254: * Finds a {@link KapuaNamedEntity} by {@link KapuaNamedEntity#getName()}.
255: *
256: * @param em The {@link EntityManager} that holds the transaction.
257: * @param clazz The {@link KapuaNamedEntity} class. This must be the implementing {@code class}.
258: * @param value The value of the {@link KapuaNamedEntity#getName()} to search.
259: * @return The {@link KapuaNamedEntity} found, or {@code null} if not found.
260: * @throws NonUniqueResultException When more than one result is returned
261: * @since 2.0.0
262: */
263: @Nullable
264: public static <E extends KapuaNamedEntity> E findByName(@NonNull EntityManager em,
265: @NonNull Class<E> clazz,
266: @NonNull Object value) {
267: return findByName(em, clazz, KapuaId.ANY, value);
268: }
269:
270: /**
271: * Finds a {@link KapuaNamedEntity} by {@link KapuaNamedEntity#getName()}.
272: *
273: * @param em The {@link EntityManager} that holds the transaction.
274: * @param clazz The {@link KapuaNamedEntity} class. This must be the implementing {@code class}.
275: * @param scopeId The {@link KapuaNamedEntity#getScopeId()} in which to look for results.
276: * @param value The value of the field from which to search.
277: * @return The {@link KapuaNamedEntity} found, or {@code null} if not found.
278: * @throws NonUniqueResultException When more than one result is returned.
279: * @since 1.0.0
280: */
281: @Nullable
282: public static <E extends KapuaNamedEntity> E findByName(@NonNull EntityManager em,
283: @NonNull Class<E> clazz,
284: @NonNull KapuaId scopeId,
285: @NonNull Object value) {
286: return findByField(em, clazz, scopeId, KapuaNamedEntityAttributes.NAME, value);
287: }
288:
289: /**
290: * Find a {@link KapuaEntity} by one of its fields.
291: *
292: * @param em The {@link EntityManager} that holds the transaction.
293: * @param clazz The {@link KapuaEntity} class. This must be the implementing {@code class}.
294: * @param name The {@link KapuaEntity} name of the field from which to search.
295: * @param value The value of the field from which to search.
296: * @return The {@link KapuaEntity} found, or {@code null} if not found.
297: * @throws NonUniqueResultException When more than one result is returned
298: * @since 1.0.0
299: */
300: @Nullable
301: public static <E extends KapuaEntity> E findByField(@NonNull EntityManager em,
302: @NonNull Class<E> clazz,
303: @NonNull String name,
304: @NonNull Object value) {
305: return findByField(em, clazz, KapuaId.ANY, name, value);
306: }
307:
308: /**
309: * Find by fields {@link KapuaEntity} utility method
310: *
311: * @param em The {@link EntityManager} that holds the transaction.
312: * @param clazz The {@link KapuaEntity} class. This must be the implementing {@code class}.
313: * @param scopeId The Scope ID in which to look for results.
314: * @param name The {@link KapuaEntity} name of the field from which to search.
315: * @param value The value of the field from which to search.
316: * @return The {@link KapuaEntity} found, or {@code null} if not found.
317: * @throws NonUniqueResultException When more than one result is returned
318: * @since 1.0.0
319: */
320: @Nullable
321: public static <E extends KapuaEntity> E findByField(@NonNull EntityManager em,
322: @NonNull Class<E> clazz,
323: @NonNull KapuaId scopeId,
324: @NonNull String name,
325: @NonNull Object value) {
326: CriteriaBuilder cb = em.getCriteriaBuilder();
327: CriteriaQuery<E> criteriaSelectQuery = cb.createQuery(clazz);
328:
329: //
330: // FROM
331: Root<E> entityRoot = criteriaSelectQuery.from(clazz);
332:
333: //
334: // SELECT
335: criteriaSelectQuery.select(entityRoot);
336:
337: // name
338: ParameterExpression<String> pName = cb.parameter(String.class, name);
339: Predicate namePredicate = cb.equal(entityRoot.get(name), pName);
340:
341: ParameterExpression<KapuaId> pScopeId = null;
342:
343:• if (!KapuaId.ANY.equals(scopeId)) {
344: pScopeId = cb.parameter(KapuaId.class, KapuaEntityAttributes.SCOPE_ID);
345: Predicate scopeIdPredicate = cb.equal(entityRoot.get(KapuaEntityAttributes.SCOPE_ID), pScopeId);
346:
347: Predicate andPredicate = cb.and(namePredicate, scopeIdPredicate);
348: criteriaSelectQuery.where(andPredicate);
349: } else {
350: criteriaSelectQuery.where(namePredicate);
351: }
352:
353: TypedQuery<E> query = em.createQuery(criteriaSelectQuery);
354: query.setParameter(pName.getName(), value);
355:
356:• if (pScopeId != null) {
357: query.setParameter(pScopeId.getName(), scopeId);
358: }
359:
360: //
361: // QUERY!
362: List<E> result = query.getResultList();
363: E entity;
364:• switch (result.size()) {
365: case 0:
366: entity = null;
367: break;
368: case 1:
369: entity = result.get(0);
370: break;
371: default:
372: throw new NonUniqueResultException(String.format("Multiple %s results found for field %s with value %s", clazz.getName(), pName, value.toString()));
373: }
374:
375: return entity;
376: }
377:
378: /**
379: * Query {@link KapuaEntity} utility method.
380: *
381: * @param em The {@link EntityManager} that holds the transaction.
382: * @param interfaceClass {@link KapuaQuery} result entity interface class
383: * @param implementingClass {@link KapuaQuery} result entity implementation class
384: * @param resultContainer The {@link KapuaListResult} in which load the result. It must be empty.
385: * @param kapuaQuery The {@link KapuaQuery} to perform.
386: * @return The reference of the {@code resultContainer} parameter. Results are added to the given {@code resultContainer} parameter.
387: * @throws KapuaException If filter predicates in the {@link KapuaQuery} are incorrect. See {@link #handleKapuaQueryPredicates(QueryPredicate, Map, CriteriaBuilder, Root, EntityType)}.
388: * @since 1.0.0
389: */
390: public static <I extends KapuaEntity, E extends I, L extends KapuaListResult<I>> L query(@NonNull EntityManager em,
391: @NonNull Class<I> interfaceClass,
392: @NonNull Class<E> implementingClass,
393: @NonNull L resultContainer,
394: @NonNull KapuaQuery kapuaQuery)
395: throws KapuaException {
396: CriteriaBuilder cb = em.getCriteriaBuilder();
397: CriteriaQuery<E> criteriaSelectQuery = cb.createQuery(implementingClass);
398:
399: //
400: // FROM
401: Root<E> entityRoot = criteriaSelectQuery.from(implementingClass);
402: EntityType<E> entityType = entityRoot.getModel();
403:
404: //
405: // SELECT
406: criteriaSelectQuery.select(entityRoot).distinct(true);
407:
408: // Fetch LAZY attributes if necessary
409:• for (String fetchAttribute : kapuaQuery.getFetchAttributes()) {
410:• if (entityType.getAttribute(fetchAttribute).isAssociation()) {
411: entityRoot.fetch(entityType.getSingularAttribute(fetchAttribute), JoinType.LEFT);
412: } else {
413: entityRoot.fetch(fetchAttribute);
414: }
415: }
416:
417: //
418: // WHERE
419: QueryPredicate kapuaPredicates = kapuaQuery.getPredicate();
420: // Add ScopeId to query if has been defined one specific
421:• if (kapuaQuery.getScopeId() != null && // Support for old method of querying for all ScopeIds (e.g.: query.setScopeId(null)
422:• !kapuaQuery.getScopeId().equals(KapuaId.ANY)) {// Support for new method of querying for all ScopeIds (e.g.: query.setScopeId(KapuaId.ANY)
423:
424: AndPredicate scopedAndPredicate = kapuaQuery.andPredicate(
425: kapuaQuery.attributePredicate(KapuaEntityAttributes.SCOPE_ID, kapuaQuery.getScopeId())
426: );
427:
428: // Add existing query predicates
429:• if (kapuaQuery.getPredicate() != null) {
430: scopedAndPredicate.and(kapuaQuery.getPredicate());
431: }
432:
433: kapuaPredicates = scopedAndPredicate;
434: }
435:
436: // Manage kapua query predicates to build the where clause.
437: Map<ParameterExpression, Object> binds = new HashMap<>();
438: Expression<Boolean> expr = handleKapuaQueryPredicates(kapuaPredicates,
439: binds,
440: cb,
441: entityRoot,
442: entityRoot.getModel());
443:
444:• if (expr != null) {
445: criteriaSelectQuery.where(expr);
446: }
447:
448: //
449: // ORDER BY
450: // Default to the KapuaEntity id if no ordering is specified.
451: Order order;
452:• if (kapuaQuery.getSortCriteria() != null || kapuaQuery.getDefaultSortCriteria() != null) {
453: FieldSortCriteria sortCriteria = (FieldSortCriteria) MoreObjects.firstNonNull(kapuaQuery.getSortCriteria(), kapuaQuery.getDefaultSortCriteria());
454:
455:• if (SortOrder.DESCENDING.equals(sortCriteria.getSortOrder())) {
456: order = cb.desc(extractAttribute(entityRoot, sortCriteria.getAttributeName()));
457: } else {
458: order = cb.asc(extractAttribute(entityRoot, sortCriteria.getAttributeName()));
459: }
460: } else {
461: order = cb.asc(entityRoot.get(entityType.getSingularAttribute(KapuaEntityAttributes.ENTITY_ID)));
462: }
463: criteriaSelectQuery.orderBy(order);
464:
465: //
466: // QUERY!
467: TypedQuery<E> query = em.createQuery(criteriaSelectQuery);
468:
469: // Populate query parameters
470: binds.forEach(query::setParameter); // Whoah! This is very magic!
471:
472: // Set offset
473:• if (kapuaQuery.getOffset() != null) {
474: query.setFirstResult(kapuaQuery.getOffset());
475: }
476:
477: // Set limit
478:• if (kapuaQuery.getLimit() != null) {
479: query.setMaxResults(kapuaQuery.getLimit() + 1);
480: }
481:
482: // Finally querying!
483: List<E> result = query.getResultList();
484:
485: // Check limit exceeded
486:• if (kapuaQuery.getLimit() != null &&
487:• result.size() > kapuaQuery.getLimit()) {
488: result.remove(kapuaQuery.getLimit().intValue());
489: resultContainer.setLimitExceeded(true);
490: }
491:
492:• if (Boolean.TRUE.equals(kapuaQuery.getAskTotalCount())) {
493: resultContainer.setTotalCount(count(em, interfaceClass, implementingClass, kapuaQuery));
494: }
495:
496: // Set results
497: resultContainer.addItems(result);
498: return resultContainer;
499: }
500:
501: /**
502: * Count {@link KapuaEntity} utility method.
503: *
504: * @param em The {@link EntityManager} that holds the transaction.
505: * @param interfaceClass {@link KapuaQuery} result entity interface class
506: * @param implementingClass {@link KapuaQuery} result entity implementation class
507: * @param kapuaQuery The {@link KapuaQuery} to perform.
508: * @return The number of {@link KapuaEntity}es that matched the filter predicates.
509: * @throws KapuaException If filter predicates in the {@link KapuaQuery} are incorrect. See {@link #handleKapuaQueryPredicates(QueryPredicate, Map, CriteriaBuilder, Root, EntityType)}.
510: * @since 1.0.0
511: */
512: public static <I extends KapuaEntity, E extends I> long count(@NonNull EntityManager em,
513: @NonNull Class<I> interfaceClass,
514: @NonNull Class<E> implementingClass,
515: @NonNull KapuaQuery kapuaQuery)
516: throws KapuaException {
517: CriteriaBuilder cb = em.getCriteriaBuilder();
518: CriteriaQuery<Long> criteriaSelectQuery = cb.createQuery(Long.class);
519:
520: //
521: // FROM
522: Root<E> entityRoot = criteriaSelectQuery.from(implementingClass);
523:
524: //
525: // SELECT
526: criteriaSelectQuery.select(cb.countDistinct(entityRoot));
527:
528: //
529: // WHERE
530: QueryPredicate kapuaPredicates = kapuaQuery.getPredicate();
531: // Add ScopeId to query if has been defined one specific
532:• if (kapuaQuery.getScopeId() != null && // Support for old method of querying for all ScopeIds (e.g.: query.setScopeId(null)
533:• !kapuaQuery.getScopeId().equals(KapuaId.ANY)) {// Support for new method of querying for all ScopeIds (e.g.: query.setScopeId(KapuaId.ANY)
534:
535: AndPredicate scopedAndPredicate = kapuaQuery.andPredicate();
536:
537: AttributePredicate<KapuaId> scopeId = kapuaQuery.attributePredicate(KapuaEntityAttributes.SCOPE_ID, kapuaQuery.getScopeId());
538: scopedAndPredicate.and(scopeId);
539:
540:• if (kapuaQuery.getPredicate() != null) {
541: scopedAndPredicate.and(kapuaQuery.getPredicate());
542: }
543:
544: kapuaPredicates = scopedAndPredicate;
545: }
546:
547: Map<ParameterExpression, Object> binds = new HashMap<>();
548: Expression<Boolean> expr = handleKapuaQueryPredicates(kapuaPredicates,
549: binds,
550: cb,
551: entityRoot,
552: entityRoot.getModel());
553:
554:• if (expr != null) {
555: criteriaSelectQuery.where(expr);
556: }
557:
558: //
559: // COUNT!
560: TypedQuery<Long> query = em.createQuery(criteriaSelectQuery);
561:
562: // Populate query parameters
563: binds.forEach(query::setParameter); // Whoah! This is very magic!
564:
565: return query.getSingleResult();
566: }
567:
568: /**
569: * Deletes a {@link KapuaEntity}.
570: *
571: * @param em The {@link EntityManager} that holds the transaction.
572: * @param clazz The {@link KapuaEntity} class. This must be the implementing {@code class}.
573: * @param scopeId The {@link KapuaEntity#getScopeId()} of the entity to be deleted.
574: * @param entityId The {@link KapuaEntity#getId()} of the entity to be deleted.
575: * @return The deleted {@link KapuaEntity}.
576: * @throws KapuaEntityNotFoundException If the {@link KapuaEntity} does not exists.
577: * @since 1.0.0
578: */
579: public static <E extends KapuaEntity> E delete(@NonNull EntityManager em, @NonNull Class<E> clazz, @NonNull KapuaId scopeId, @NonNull KapuaId entityId)
580: throws KapuaEntityNotFoundException {
581: //
582: // Checking existence
583: E entityToDelete = find(em, clazz, scopeId, entityId);
584:
585: //
586: // Deleting if found
587:• if (entityToDelete != null) {
588: em.remove(entityToDelete);
589: em.flush();
590: } else {
591: throw new KapuaEntityNotFoundException(clazz.getSimpleName(), entityId);
592: }
593:
594: //
595: // Returning deleted entity
596: return entityToDelete;
597: }
598:
599: //
600: // Private Methods
601: //
602:
603: /**
604: * Handles {@link QueryPredicate} contained of a {@link KapuaQuery}.
605: * <p>
606: * It manages different types of {@link QueryPredicate} like:
607: * <ul>
608: * <li>{@link AttributePredicate}</li>
609: * <li>{@link AndPredicate}</li>
610: * <li>{@link OrPredicate}</li>
611: * </ol>
612: * <p>
613: * It can be invoked recursively (i.e. to handle {@link AttributePredicate}s of the {@link AndPredicate}.
614: *
615: * @param queryPredicate The {@link QueryPredicate} to handle.
616: * @param binds The {@link Map}≶{@link String}, {@link Object}> of the query values.
617: * @param cb The JPA {@link CriteriaBuilder} of the {@link javax.persistence.Query}.
618: * @param userPermissionRoot The JPA {@link Root} of the {@link javax.persistence.Query}.
619: * @param entityType The JPA {@link EntityType} of the {@link javax.persistence.Query}.
620: * @return The handled {@link Predicate}
621: * @throws KapuaException If any problem occurs.
622: */
623: private static <E> Predicate handleKapuaQueryPredicates(@NonNull QueryPredicate queryPredicate,
624: @NonNull Map<ParameterExpression, Object> binds,
625: @NonNull CriteriaBuilder cb,
626: @NonNull Root<E> userPermissionRoot,
627: @NonNull EntityType<E> entityType)
628: throws KapuaException {
629: Predicate predicate = null;
630:• if (queryPredicate instanceof AttributePredicate) {
631: AttributePredicate<?> attributePredicate = (AttributePredicate<?>) queryPredicate;
632: predicate = handleAttributePredicate(attributePredicate, binds, cb, userPermissionRoot, entityType);
633:• } else if (queryPredicate instanceof AndPredicate) {
634: AndPredicate andPredicate = (AndPredicate) queryPredicate;
635: predicate = handleAndPredicate(andPredicate, binds, cb, userPermissionRoot, entityType);
636:• } else if (queryPredicate instanceof OrPredicate) {
637: OrPredicate orPredicate = (OrPredicate) queryPredicate;
638: predicate = handleOrPredicate(orPredicate, binds, cb, userPermissionRoot, entityType);
639:• } else if (queryPredicate instanceof MatchPredicate) {
640: MatchPredicate<?> matchPredicate = (MatchPredicate<?>) queryPredicate;
641: OrPredicate orPredicate = new OrPredicateImpl();
642:• for (String attributeName : matchPredicate.getAttributeNames()) {
643: orPredicate.getPredicates().add(new AttributePredicateImpl<>(attributeName, matchPredicate.getMatchTerm(), Operator.STARTS_WITH_IGNORE_CASE));
644: }
645: predicate = handleOrPredicate(orPredicate, binds, cb, userPermissionRoot, entityType);
646: }
647: return predicate;
648: }
649:
650: private static <E> Predicate handleAndPredicate(@NonNull AndPredicate andPredicate,
651: @NonNull Map<ParameterExpression, Object> binds,
652: @NonNull CriteriaBuilder cb,
653: @NonNull Root<E> entityRoot,
654: @NonNull EntityType<E> entityType)
655: throws KapuaException {
656:
657: Predicate[] jpaAndPredicates =
658: handlePredicate(
659: andPredicate.getPredicates(),
660: binds,
661: cb,
662: entityRoot,
663: entityType);
664:
665: return cb.and(jpaAndPredicates);
666:
667: }
668:
669: private static <E> Predicate handleOrPredicate(@NonNull OrPredicate orPredicate,
670: @NonNull Map<ParameterExpression, Object> binds,
671: @NonNull CriteriaBuilder cb,
672: @NonNull Root<E> entityRoot,
673: @NonNull EntityType<E> entityType)
674: throws KapuaException {
675:
676: Predicate[] jpaOrPredicates =
677: handlePredicate(
678: orPredicate.getPredicates(),
679: binds,
680: cb,
681: entityRoot,
682: entityType);
683:
684: return cb.or(jpaOrPredicates);
685: }
686:
687: private static <E> Predicate[] handlePredicate(@NonNull List<QueryPredicate> orPredicates,
688: @NonNull Map<ParameterExpression, Object> binds,
689: @NonNull CriteriaBuilder cb,
690: @NonNull Root<E> entityRoot,
691: @NonNull EntityType<E> entityType) throws KapuaException {
692: Predicate[] jpaOrPredicates = new Predicate[orPredicates.size()];
693:
694:• for (int i = 0; i < orPredicates.size(); i++) {
695: Predicate expr = handleKapuaQueryPredicates(orPredicates.get(i), binds, cb, entityRoot, entityType);
696: jpaOrPredicates[i] = expr;
697: }
698: return jpaOrPredicates;
699: }
700:
701: private static <E> Predicate handleAttributePredicate(@NonNull AttributePredicate<?> attrPred,
702: @NonNull Map<ParameterExpression, Object> binds,
703: @NonNull CriteriaBuilder cb,
704: @NonNull Root<E> entityRoot,
705: @NonNull EntityType<E> entityType)
706: throws KapuaException {
707: Predicate expr;
708: String attrName = attrPred.getAttributeName();
709:
710: // Parse attributes
711: Object attrValue = attrPred.getAttributeValue();
712:• if (attrValue instanceof KapuaId && !(attrValue instanceof KapuaEid)) {
713: attrValue = KapuaEid.parseKapuaId((KapuaId) attrValue);
714: }
715:
716: // Fields to query properties of sub attributes of the root entity
717: Attribute<?, ?> attribute;
718:• if (attrName.contains(ATTRIBUTE_SEPARATOR)) {
719: attribute = entityType.getAttribute(attrName.split(ATTRIBUTE_SEPARATOR_ESCAPED)[0]);
720: } else {
721: attribute = entityType.getAttribute(attrName);
722: }
723:
724:• if (attrValue instanceof Object[]) {
725: Object[] attrValues = (Object[]) attrValue;
726: Expression<?> orPredicate = extractAttribute(entityRoot, attrName);
727:
728: Predicate[] orPredicates = new Predicate[attrValues.length];
729:• for (int i = 0; i < attrValues.length; i++) {
730: Object value = attrValues[i];
731:
732:• if (value instanceof KapuaId && !(value instanceof KapuaEid)) {
733: value = new KapuaEid((KapuaId) value);
734: }
735:
736: orPredicates[i] = cb.equal(orPredicate, value);
737: }
738:
739: expr = cb.and(cb.or(orPredicates));
740: } else {
741: String strAttrValue;
742:• switch (attrPred.getOperator()) {
743: case LIKE:
744: strAttrValue = attrValue.toString().replace(LIKE, ESCAPE + LIKE).replace(ANY, ESCAPE + ANY);
745: ParameterExpression<String> pl = cb.parameter(String.class);
746: binds.put(pl, LIKE + strAttrValue + LIKE);
747: expr = cb.like(extractAttribute(entityRoot, attrName), pl);
748: break;
749:
750: case LIKE_IGNORE_CASE:
751: strAttrValue = attrValue.toString().replace(LIKE, ESCAPE + LIKE).replace(ANY, ESCAPE + ANY).toLowerCase();
752: ParameterExpression<String> plci = cb.parameter(String.class);
753: binds.put(plci, LIKE + strAttrValue + LIKE);
754: expr = cb.like(cb.lower(extractAttribute(entityRoot, attrName)), plci);
755: break;
756:
757: case STARTS_WITH:
758: strAttrValue = attrValue.toString().replace(LIKE, ESCAPE + LIKE).replace(ANY, ESCAPE + ANY);
759: ParameterExpression<String> psw = cb.parameter(String.class);
760: binds.put(psw, strAttrValue + LIKE);
761: expr = cb.like(extractAttribute(entityRoot, attrName), psw);
762: break;
763:
764: case STARTS_WITH_IGNORE_CASE:
765: strAttrValue = attrValue.toString().replace(LIKE, ESCAPE + LIKE).replace(ANY, ESCAPE + ANY).toLowerCase();
766: ParameterExpression<String> pswci = cb.parameter(String.class);
767: binds.put(pswci, strAttrValue + LIKE);
768: expr = cb.like(cb.lower(extractAttribute(entityRoot, attrName)), pswci);
769: break;
770:
771: case IS_NULL:
772: expr = cb.isNull(extractAttribute(entityRoot, attrName));
773: break;
774:
775: case NOT_NULL:
776: expr = cb.isNotNull(extractAttribute(entityRoot, attrName));
777: break;
778:
779: case NOT_EQUAL:
780: expr = cb.notEqual(extractAttribute(entityRoot, attrName), attrValue);
781: break;
782:
783: case GREATER_THAN:
784:• if (attrValue instanceof Comparable && ArrayUtils.contains(attribute.getJavaType().getInterfaces(), Comparable.class)) {
785: Comparable comparableAttrValue = (Comparable<?>) attrValue;
786: Expression<? extends Comparable> comparableExpression = extractAttribute(entityRoot, attrName);
787: expr = cb.greaterThan(comparableExpression, comparableAttrValue);
788: } else {
789: throw new KapuaException(KapuaErrorCodes.ILLEGAL_ARGUMENT, COMPARE_ERROR_MESSAGE);
790: }
791: break;
792:
793: case GREATER_THAN_OR_EQUAL:
794:• if (attrValue instanceof Comparable && ArrayUtils.contains(attribute.getJavaType().getInterfaces(), Comparable.class)) {
795: Expression<? extends Comparable> comparableExpression = extractAttribute(entityRoot, attrName);
796: Comparable comparableAttrValue = (Comparable<?>) attrValue;
797: expr = cb.greaterThanOrEqualTo(comparableExpression, comparableAttrValue);
798: } else {
799: throw new KapuaException(KapuaErrorCodes.ILLEGAL_ARGUMENT, COMPARE_ERROR_MESSAGE);
800: }
801: break;
802:
803: case LESS_THAN:
804:• if (attrValue instanceof Comparable && ArrayUtils.contains(attribute.getJavaType().getInterfaces(), Comparable.class)) {
805: Expression<? extends Comparable> comparableExpression = extractAttribute(entityRoot, attrName);
806: Comparable comparableAttrValue = (Comparable<?>) attrValue;
807: expr = cb.lessThan(comparableExpression, comparableAttrValue);
808: } else {
809: throw new KapuaException(KapuaErrorCodes.ILLEGAL_ARGUMENT, COMPARE_ERROR_MESSAGE);
810: }
811: break;
812: case LESS_THAN_OR_EQUAL:
813:• if (attrValue instanceof Comparable && ArrayUtils.contains(attribute.getJavaType().getInterfaces(), Comparable.class)) {
814: Expression<? extends Comparable> comparableExpression = extractAttribute(entityRoot, attrName);
815: Comparable comparableAttrValue = (Comparable<?>) attrValue;
816: expr = cb.lessThanOrEqualTo(comparableExpression, comparableAttrValue);
817: } else {
818: throw new KapuaException(KapuaErrorCodes.ILLEGAL_ARGUMENT, COMPARE_ERROR_MESSAGE);
819: }
820: break;
821:
822: case EQUAL:
823: default:
824: expr = cb.equal(extractAttribute(entityRoot, attrName), attrValue);
825: }
826: }
827: return expr;
828: }
829:
830: /**
831: * Utility method that selects the correct {@link Root} attribute.
832: * <p>
833: * This method handles {@link Embedded} attributes and nested {@link KapuaEntity}es.
834: * <p>
835: * Filter predicates takes advantage of the dot notation to access {@link Embedded} attributes and nested {@link KapuaEntity}es.
836: *
837: * @param entityRoot The {@link Root} entity from which extract the attribute.
838: * @param attributeName The full attribute name.
839: * @return The {@link Path} expression that matches the given {@code attributeName} parameter.
840: * @since 1.0.0
841: */
842: private static <E, P> Path<P> extractAttribute(@NonNull Root<E> entityRoot, @NonNull String attributeName) {
843: Path<P> expressionPath = null;
844:• if (attributeName.contains(ATTRIBUTE_SEPARATOR)) {
845: String[] pathComponents = attributeName.split(ATTRIBUTE_SEPARATOR_ESCAPED);
846: Path<P> rootPath = entityRoot.get(pathComponents[0]);
847:• for (int i = 1; i < pathComponents.length; i++) {
848: expressionPath = rootPath.get(pathComponents[i]);
849: rootPath = expressionPath;
850: }
851: } else {
852: expressionPath = entityRoot.get(attributeName);
853: }
854: return expressionPath;
855: }
856:
857: /**
858: * Handles the {@link Groupable} property of the {@link KapuaEntity}.
859: *
860: * @param query The {@link KapuaQuery} to manage.
861: * @param domain The {@link Domain} inside which the {@link KapuaQuery} param targets.
862: * @param groupPredicateName The name of the {@link Group} id field.
863: * @since 1.0.0
864: */
865: protected static void handleKapuaQueryGroupPredicate(@NonNull KapuaQuery query, @NonNull Domain domain, @NonNull String groupPredicateName) throws KapuaException {
866: KapuaSession kapuaSession = KapuaSecurityUtils.getSession();
867:• if (ACCESS_INFO_FACTORY != null) {
868:• if (kapuaSession != null && !kapuaSession.isTrustedMode()) {
869: handleKapuaQueryGroupPredicate(kapuaSession, query, domain, groupPredicateName);
870: }
871: } else {
872: LOG.warn("'Access Group Permission' feature is disabled");
873: }
874: }
875:
876: private static void handleKapuaQueryGroupPredicate(KapuaSession kapuaSession, KapuaQuery query, Domain domain, String groupPredicateName) throws KapuaException {
877: try {
878: KapuaId userId = kapuaSession.getUserId();
879:
880: AccessInfo accessInfo = KapuaSecurityUtils.doPrivileged(() -> ACCESS_INFO_SERVICE.findByUserId(kapuaSession.getScopeId(), userId));
881:
882: List<Permission> groupPermissions = new ArrayList<>();
883:• if (accessInfo != null) {
884:
885: AccessPermissionListResult accessPermissions = KapuaSecurityUtils.doPrivileged(() -> ACCESS_PERMISSION_SERVICE.findByAccessInfoId(accessInfo.getScopeId(), accessInfo.getId()));
886:
887:• for (AccessPermission ap : accessPermissions.getItems()) {
888:• if (checkGroupPermission(domain, groupPermissions, ap.getPermission())) {
889: break;
890: }
891: }
892:
893: AccessRoleListResult accessRoles = KapuaSecurityUtils.doPrivileged(() -> ACCESS_ROLE_SERVICE.findByAccessInfoId(accessInfo.getScopeId(), accessInfo.getId()));
894:
895:• for (AccessRole ar : accessRoles.getItems()) {
896: KapuaId roleId = ar.getRoleId();
897:
898: Role role = KapuaSecurityUtils.doPrivileged(() -> ROLE_SERVICE.find(ar.getScopeId(), roleId));
899:
900: RolePermissionListResult rolePermissions = KapuaSecurityUtils.doPrivileged(() -> ROLE_PERMISSION_SERVICE.findByRoleId(role.getScopeId(), role.getId()));
901:
902:• for (RolePermission rp : rolePermissions.getItems()) {
903:• if (checkGroupPermission(domain, groupPermissions, rp.getPermission())) {
904: break;
905: }
906: }
907: }
908: }
909:
910: AndPredicate andPredicate = query.andPredicate();
911:• if (!groupPermissions.isEmpty()) {
912: int i = 0;
913: KapuaId[] groupsIds = new KapuaEid[groupPermissions.size()];
914:• for (Permission p : groupPermissions) {
915: groupsIds[i++] = p.getGroupId();
916: }
917: andPredicate.and(query.attributePredicate(groupPredicateName, groupsIds));
918: }
919:
920:• if (query.getPredicate() != null) {
921: andPredicate.and(query.getPredicate());
922: }
923:
924: query.setPredicate(andPredicate);
925: } catch (Exception e) {
926: throw KapuaException.internalError(e, "Error while grouping!");
927: }
928: }
929:
930: private static boolean checkGroupPermission(@NonNull Domain domain, @NonNull List<Permission> groupPermissions, @NonNull Permission p) {
931:• if ((p.getDomain() == null || domain.getName().equals(p.getDomain())) &&
932:• (p.getAction() == null || Actions.read.equals(p.getAction()))) {
933:• if (p.getGroupId() == null) {
934: groupPermissions.clear();
935: return true;
936: } else {
937: groupPermissions.add(p);
938: }
939: }
940: return false;
941: }
942:
943: private static boolean isInsertConstraintViolation(@NonNull PersistenceException e) {
944: Throwable cause = e.getCause();
945:• while (cause != null && !(cause instanceof SQLException)) {
946: cause = cause.getCause();
947: }
948:
949:• if (cause == null) {
950: return false;
951: }
952:
953: SQLException innerExc = (SQLException) cause;
954: return SQL_ERROR_CODE_CONSTRAINT_VIOLATION.equals(innerExc.getSQLState());
955: }
956: }