/*
* The contents of this file are subject to the OpenMRS Public License
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://license.openmrs.org
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* Copyright (C) OpenMRS, LLC. All Rights Reserved.
*/
package org.openmrs.module.openhmis.commons.api.entity.impl;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projection;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.impl.CriteriaImpl;
import org.hibernate.transform.ResultTransformer;
import org.openmrs.OpenmrsObject;
import org.openmrs.api.context.Context;
import org.openmrs.api.impl.BaseOpenmrsService;
import org.openmrs.module.openhmis.commons.api.PagingInfo;
import org.openmrs.module.openhmis.commons.api.Utility;
import org.openmrs.module.openhmis.commons.api.entity.IObjectDataService;
import org.openmrs.module.openhmis.commons.api.entity.db.hibernate.IHibernateRepository;
import org.openmrs.module.openhmis.commons.api.entity.security.IObjectAuthorizationPrivileges;
import org.openmrs.module.openhmis.commons.api.f.Action1;
import org.openmrs.module.openhmis.commons.api.util.PrivilegeUtil;
import org.springframework.transaction.annotation.Transactional;
/**
* The base type for object services. Provides the core implementation for the common {@link org.openmrs.OpenmrsObject}
* operations.
* @param <E> The {@link org.openmrs.OpenmrsObject} model type.
*/
@Transactional
public abstract class BaseObjectDataServiceImpl<E extends OpenmrsObject, P extends IObjectAuthorizationPrivileges>
extends BaseOpenmrsService implements IObjectDataService<E> {
private IHibernateRepository repository;
private Class entityClass = null;
/**
* Gets the privileges to use for this service or {@code null} if none are needed.
* @return The service privileges.
*/
protected abstract P getPrivileges();
/**
* Validates the specified object, throwing an exception in the validation fails.
* @param object The object to validate.
* @should not throw an exception for valid objects
* @should throw IllegalArgumentException with a null object
* @should throw an exception for invalid objects
*/
protected abstract void validate(E object);
/**
* Gets a list of all related objects for the specified entity.
* @param entity The parent entity.
* @return The list of the related objects or {@code null} if none.
*/
protected Collection<? extends OpenmrsObject> getRelatedObjects(E entity) {
return null;
}
protected Order[] getDefaultSort() {
return null;
}
/**
* Gets the {@link org.openmrs.module.openhmis.commons.api.entity.db.hibernate.IHibernateRepository} for this data
* service.
* @return The repository.
*/
public IHibernateRepository getRepository() {
return this.repository;
}
/**
* Sets the {@link org.openmrs.module.openhmis.commons.api.entity.db.hibernate.IHibernateRepository} for this data
* service.
* @param repository The data repository object that the service will use.
*/
public void setRepository(IHibernateRepository repository) {
this.repository = repository;
}
@Override
@Transactional
public E save(E object) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getSavePrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getSavePrivilege());
}
if (object == null) {
throw new NullPointerException("The object to save cannot be null.");
}
validate(object);
return repository.save(object);
}
@Override
@Transactional
public E saveAll(E object, Collection<? extends OpenmrsObject> related) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getSavePrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getSavePrivilege());
}
if (object == null) {
throw new NullPointerException("The object to save cannot be null.");
}
validate(object);
Collection<OpenmrsObject> saveAll = new ArrayList<OpenmrsObject>();
saveAll.add(object);
saveAll(related);
repository.saveAll(saveAll);
return object;
}
@Override
@Transactional
public void saveAll(Collection<? extends OpenmrsObject> collection) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getSavePrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getSavePrivilege());
}
repository.saveAll(collection);
}
@Override
@Transactional
public void purge(E object) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getPurgePrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getPurgePrivilege());
}
if (object == null) {
throw new NullPointerException("The object to purge cannot be null.");
}
repository.delete(object);
}
@Override
@Transactional(readOnly = true)
public List<E> getAll() {
return getAll(null);
}
@Override
@Transactional(readOnly = true)
public List<E> getAll(PagingInfo pagingInfo) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getGetPrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getGetPrivilege());
}
return executeCriteria(getEntityClass(), pagingInfo, null, getDefaultSort());
}
@Override
@Transactional(readOnly = true)
public E getById(int entityId) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getGetPrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getGetPrivilege());
}
return repository.selectSingle(getEntityClass(), entityId);
}
@Override
@Transactional(readOnly = true)
public E getByUuid(String uuid) {
P privileges = getPrivileges();
if (privileges != null && !StringUtils.isEmpty(privileges.getGetPrivilege())) {
PrivilegeUtil.requirePrivileges(Context.getAuthenticatedUser(), privileges.getGetPrivilege());
}
if (StringUtils.isEmpty(uuid)) {
throw new IllegalArgumentException("The UUID must be defined.");
}
Criteria criteria = repository.createCriteria(getEntityClass());
criteria.add(Restrictions.eq("uuid", uuid));
return repository.selectSingle(getEntityClass(), criteria);
}
/**
* Gets a usable instance of the actual class of the generic type E defined by the implementing sub-class.
* @return The class object for the entity.
*/
@SuppressWarnings("unchecked")
protected Class<E> getEntityClass() {
if (entityClass == null) {
ParameterizedType parameterizedType = (ParameterizedType)getClass().getGenericSuperclass();
entityClass = (Class<E>)parameterizedType.getActualTypeArguments()[0];
}
return entityClass;
}
/**
* Functional method to create, prepare, and execute a query with {@link Criteria}.
* @param action The {@link Action1} to prepare the {@link Criteria} predicates.
* @return The result of the query.
*/
protected <T extends OpenmrsObject> List<T> executeCriteria(Class<T> clazz, Action1<Criteria> action) {
return executeCriteria(clazz, null, action, (Order[])null);
}
/**
* Functional method to create, prepare, and execute a paged query with {@link Criteria}.
* @param pagingInfo The paging information.
* @param action The {@link Action1} to prepare the {@link Criteria} predicates.
* @return
*/
protected <T extends OpenmrsObject> List<T> executeCriteria(Class<T> clazz, PagingInfo pagingInfo,
Action1<Criteria> action) {
return executeCriteria(clazz, pagingInfo, action, (Order[])null);
}
protected <T extends OpenmrsObject> List<T> executeCriteria(Class<T> clazz, PagingInfo pagingInfo,
Action1<Criteria> action, Order... orderBy) {
Criteria criteria = repository.createCriteria(clazz);
if (action != null) {
action.apply(criteria);
}
loadPagingTotal(pagingInfo, criteria);
if (orderBy != null && orderBy.length > 0) {
for (Order order : orderBy) {
criteria.addOrder(order);
}
}
return repository.select(clazz, createPagingCriteria(pagingInfo, criteria));
}
/**
* Functional method to apply an action to the related objects, returning the updated objects.
* @param clazz The class of related objects to perform the action on.
* @param entity The parent entity.
* @param action The {@link Action1} to apply.
* @param <T> The class of the related objects.
* @return A list containing the updated (applied) related objects.
*/
protected <T extends OpenmrsObject> List<T> executeOnRelatedObjects(Class<T> clazz, E entity, Action1<T> action) {
List<T> updatedObjects = new ArrayList<T>();
Collection<? extends OpenmrsObject> relatedObjects = getRelatedObjects(entity);
if (relatedObjects != null && !relatedObjects.isEmpty()) {
for (OpenmrsObject object : relatedObjects) {
T data = Utility.as(clazz, object);
if (data != null) {
action.apply(data);
updatedObjects.add(data);
}
}
}
return updatedObjects;
}
/**
* Loads the total number of records for the specified object type into the specified paging object.
* @param pagingInfo The {@link PagingInfo} object to load with the record count.
*/
protected void loadPagingTotal(PagingInfo pagingInfo) {
loadPagingTotal(pagingInfo, null);
}
/**
* Loads the record count for the specified criteria into the specified paging object.
* @param pagingInfo The {@link PagingInfo} object to load with the record count.
* @param criteria The {@link Criteria} to execute against the hibernate data source or {@code null} to create a new one.
*/
protected void loadPagingTotal(PagingInfo pagingInfo, Criteria criteria) {
if (pagingInfo != null && pagingInfo.getPage() > 0 && pagingInfo.getPageSize() > 0) {
if (criteria == null) {
criteria = repository.createCriteria(getEntityClass());
}
if (pagingInfo.shouldLoadRecordCount()) {
// Copy the current projection and transformer which requires getting access to the underlying criteria
// implementation
Projection projection = null;
ResultTransformer transformer = null;
CriteriaImpl impl = Utility.as(CriteriaImpl.class, criteria);
if (impl != null) {
projection = impl.getProjection();
transformer = impl.getResultTransformer();
}
try {
criteria.setProjection(Projections.rowCount());
Long count = repository.<Long>selectValue(criteria);
pagingInfo.setTotalRecordCount(count == null ? 0 : count);
pagingInfo.setLoadRecordCount(false);
} finally {
// Reset the criteria projection and transformer to return the result rather than the row count
criteria.setProjection(projection);
criteria.setResultTransformer(transformer);
}
}
}
}
/**
* Creates a new {@link Criteria} to retrieve the data specified by the {@link PagingInfo} object.
* @param pagingInfo The {@link PagingInfo} object that specifies which data should be retrieved.
* @return A new {@link Criteria} with the paging settings.
*/
protected Criteria createPagingCriteria(PagingInfo pagingInfo) {
return createPagingCriteria(pagingInfo, null);
}
/**
* Updates the specified {@link Criteria} object to retrieve the data specified by the {@link PagingInfo} object.
* @param pagingInfo The {@link PagingInfo} object that specifies which data should be retrieved.
* @param criteria The {@link Criteria} to add the paging settings to, or {@code null} to create a new one.
* @return The {@link Criteria} object with the paging settings applied.
*/
protected Criteria createPagingCriteria(PagingInfo pagingInfo, Criteria criteria) {
if (pagingInfo != null && pagingInfo.getPage() > 0 && pagingInfo.getPageSize() > 0) {
if (criteria == null) {
criteria = repository.createCriteria(getEntityClass());
}
criteria.setFirstResult((pagingInfo.getPage() - 1) * pagingInfo.getPageSize());
criteria.setMaxResults(pagingInfo.getPageSize());
criteria.setFetchSize(pagingInfo.getPageSize());
}
return criteria;
}
protected Query createPagingQuery(PagingInfo pagingInfo, Query query) {
if (query == null) {
throw new IllegalArgumentException("The query must be defined.");
}
if (pagingInfo != null && pagingInfo.getPage() > 0 && pagingInfo.getPageSize() > 0) {
query.setFirstResult((pagingInfo.getPage() - 1) * pagingInfo.getPageSize());
query.setMaxResults(pagingInfo.getPageSize());
query.setFetchSize(pagingInfo.getPageSize());
}
return query;
}
}