/* * 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.webservices.rest.resource; import java.lang.reflect.ParameterizedType; import java.util.Collection; import java.util.HashMap; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openmrs.OpenmrsData; import org.openmrs.OpenmrsObject; import org.openmrs.api.context.Context; import org.openmrs.module.openhmis.commons.api.entity.IEntityDataService; import org.openmrs.module.openhmis.commons.api.exception.PrivilegeException; import org.openmrs.module.openhmis.commons.api.f.Action2; import org.openmrs.module.webservices.rest.web.RequestContext; import org.openmrs.module.webservices.rest.web.representation.DefaultRepresentation; import org.openmrs.module.webservices.rest.web.representation.FullRepresentation; import org.openmrs.module.webservices.rest.web.representation.RefRepresentation; import org.openmrs.module.webservices.rest.web.representation.Representation; import org.openmrs.module.webservices.rest.web.resource.impl.AlreadyPaged; import org.openmrs.module.webservices.rest.web.resource.impl.DataDelegatingCrudResource; import org.openmrs.module.webservices.rest.web.resource.impl.DelegatingResourceDescription; import org.openmrs.module.webservices.rest.web.resource.impl.NeedsPaging; import org.openmrs.module.webservices.rest.web.resource.impl.ServiceSearcher; import org.springframework.beans.BeanUtils; /** * REST resource for {@link org.openmrs.OpenmrsData} entities. * @param <E> The model class */ public abstract class BaseRestDataResource<E extends OpenmrsData> extends DataDelegatingCrudResource<E> implements IEntityDataServiceResource<E> { private static final Log LOG = LogFactory.getLog(BaseRestDataResource.class); private Class<E> entityClass = null; /** * Syncs the base collection with the items in the sync collection. This will add any missing items, updating existing * items, and delete any items not found in the sync collection. * @param base The collection to update. * @param sync The collection used to update the base. * @param <E> The {@link OpenmrsObject} stored in the collection. */ public static <E extends OpenmrsObject> void syncCollection(Collection<E> base, Collection<E> sync) { syncCollection(base, sync, new Action2<Collection<E>, E>() { @Override public void apply(Collection<E> collection, E entity) { collection.add(entity); } }, new Action2<Collection<E>, E>() { @Override public void apply(Collection<E> collection, E entity) { collection.remove(entity); } }); } public static <E extends OpenmrsObject> void syncCollection(Collection<E> base, Collection<E> sync, Action2<Collection<E>, E> add, Action2<Collection<E>, E> remove) { Map<String, E> baseMap = new HashMap<String, E>(base.size()); Map<String, E> syncMap = new HashMap<String, E>(sync.size()); for (E item : base) { baseMap.put(item.getUuid(), item); } for (E item : sync) { syncMap.put(item.getUuid(), item); } for (E item : baseMap.values()) { if (syncMap.containsKey(item.getUuid())) { // Update the existing item E syncItem = syncMap.get(item.getUuid()); syncItem.setId(item.getId()); BeanUtils.copyProperties(syncItem, item); } else { // Delete item that is not in the sync collection remove.apply(base, item); } } for (E item : syncMap.values()) { if (!baseMap.containsKey(item.getUuid())) { // Add the item not in the base collection add.apply(base, item); } } } @Override public abstract E newDelegate(); @Override public abstract Class<? extends IEntityDataService<E>> getServiceClass(); @Override public E save(E delegate) { return getService().save(delegate); } @Override public DelegatingResourceDescription getRepresentationDescription(Representation rep) { DelegatingResourceDescription description = new DelegatingResourceDescription(); description.addProperty("uuid"); description.addProperty("display", findMethod("getDisplayString")); if (!(rep instanceof RefRepresentation)) { description.addProperty("voided"); description.addProperty("voidReason"); if (rep instanceof FullRepresentation) { description.addProperty("auditInfo", findMethod("getAuditInfo")); } } return description; } @Override public DelegatingResourceDescription getCreatableProperties() { DelegatingResourceDescription description = getRepresentationDescription(new DefaultRepresentation()); description.removeProperty("uuid"); description.removeProperty("voidReason"); return description; } public String getDisplayString(E instance) { return instance.getClass().getSimpleName(); } @Override public E getByUniqueId(String uniqueId) { if (StringUtils.isEmpty(uniqueId)) { return null; } E result = null; // Ensure that a service is found for this resource. This is a fix for changes to the RESTWS module as of v2.12. IEntityDataService<E> service = getService(); if (service != null) { try { result = service.getByUuid(uniqueId); } catch (PrivilegeException p) { LOG.error("Exception occured when trying to get entity with ID <" + uniqueId + "> as privilege is missing", p); throw new PrivilegeException("Can't get entity with ID <" + uniqueId + "> as privilege is missing"); } } return result; } @Override protected void delete(E delegate, String reason, RequestContext context) { getService().voidEntity(delegate, reason); } @Override public void purge(E delegate, RequestContext context) { getService().purge(delegate); } @Override protected NeedsPaging<E> doGetAll(RequestContext context) { return new NeedsPaging<E>(getService().getAll(), context); } @Override protected AlreadyPaged<E> doSearch(RequestContext context) { String query = context.getRequest().getParameter("q"); return new ServiceSearcher<E>(this.getServiceClass(), "getResources", "getCountOfResources").search(query, context); } /** * 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") public Class<E> getEntityClass() { if (entityClass == null) { ParameterizedType parameterizedType = (ParameterizedType)getClass().getGenericSuperclass(); entityClass = (Class)parameterizedType.getActualTypeArguments()[0]; } return entityClass; } protected IEntityDataService<E> getService() { // Ensure that the service class is not null. This is a fix for changes to the RESTWS module as of v2.12. Class<? extends IEntityDataService<E>> cls = getServiceClass(); if (cls == null) { return null; } else { return Context.getService(cls); } } }