/**
* This Source Code Form is subject to the terms of the Mozilla Public License,
* v. 2.0. If a copy of the MPL was not distributed with this file, You can
* obtain one at http://mozilla.org/MPL/2.0/. OpenMRS is also distributed under
* the terms of the Healthcare Disclaimer located at http://openmrs.org/license.
*
* Copyright (C) OpenMRS Inc. OpenMRS is a registered trademark and the OpenMRS
* graphic logo is a trademark of OpenMRS Inc.
*/
package org.openmrs.module.webservices.rest.web.resource.impl;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openmrs.api.context.Context;
import org.openmrs.module.webservices.rest.SimpleObject;
import org.openmrs.module.webservices.rest.web.ConversionUtil;
import org.openmrs.module.webservices.rest.web.RequestContext;
import org.openmrs.module.webservices.rest.web.annotation.RepHandler;
import org.openmrs.module.webservices.rest.web.api.RestService;
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.api.PageableResult;
import org.openmrs.module.webservices.rest.web.resource.api.Resource;
import org.openmrs.module.webservices.rest.web.resource.api.SubResource;
import org.openmrs.module.webservices.rest.web.response.ConversionException;
import org.openmrs.module.webservices.rest.web.response.ObjectMismatchException;
import org.openmrs.module.webservices.rest.web.response.ObjectNotFoundException;
import org.openmrs.module.webservices.rest.web.response.ResourceDoesNotSupportOperationException;
import org.openmrs.module.webservices.rest.web.response.ResponseException;
import org.openmrs.util.OpenmrsUtil;
import org.springframework.util.ReflectionUtils;
/**
* Base implementation of a sub-resource of a DelegatingCrudResource that delegates to a domain
* object
*
* @param <T> type of the domain class we delegate to
* @param <P> type of the parent that T is a sub-resource of
* @param <PR> type of the resource of the parent
*/
public abstract class DelegatingSubResource<T, P, PR extends DelegatingCrudResource<P>> extends BaseDelegatingResource<T> implements SubResource {
protected final Log log = LogFactory.getLog(getClass());
/**
* @param instance
* @return the parent of the given instance of this subresource
*/
public abstract P getParent(T instance);
/**
* Sets the parent property on the given instance of this subresource
*
* @param instance
* @param parent
*/
public abstract void setParent(T instance, P parent);
/**
* Implementations should override this method to return a list of all instances that belong to
* the given parent
*
* @throws ResponseException
*/
public abstract PageableResult doGetAll(P parent, RequestContext context) throws ResponseException;
/**
* @see Resource#getUri(java.lang.Object)
*/
@Override
public String getUri(Object instance) {
org.openmrs.module.webservices.rest.web.annotation.SubResource sub = getClass().getAnnotation(
org.openmrs.module.webservices.rest.web.annotation.SubResource.class);
@SuppressWarnings("unchecked")
T instanceAsT = (T) instance;
String parentUri = getParentUri(instanceAsT);
return parentUri + "/" + sub.path() + "/" + getUniqueId(instanceAsT);
}
/**
* @see SubResource#create(java.lang.String, SimpleObject, RequestContext)
*/
@Override
public Object create(String parentUniqueId, SimpleObject post, RequestContext context) throws ResponseException {
PR parentResource = getParentResource();
P parent = parentResource.getByUniqueId(parentUniqueId);
if (parent == null)
throw new ObjectNotFoundException();
T delegate = newDelegate();
setParent(delegate, parent);
setConvertedProperties(delegate, post, getCreatableProperties(), true);
delegate = save(delegate);
return ConversionUtil.convertToRepresentation(delegate, Representation.DEFAULT);
}
/**
* @see SubResource#retrieve(java.lang.String, java.lang.String, RequestContext)
*/
@Override
public Object retrieve(String parentUniqueId, String uuid, RequestContext context) throws ResponseException {
T delegate = getByUniqueId(uuid);
if (delegate == null)
throw new ObjectNotFoundException();
testParent(delegate, parentUniqueId);
return asRepresentation(delegate, context.getRepresentation());
}
/**
* Ensures that the uuid of the parent of delegate is the same as the passed in parentUniqueId
*
* @param delegate
* @param parentUniqueId
* @throws ObjectMismatchException
*/
private void testParent(T delegate, String parentUniqueId) throws ObjectMismatchException {
P parent = getParent(delegate);
String test = getParentResource().getUniqueId(parent);
if (!OpenmrsUtil.nullSafeEquals(test, parentUniqueId))
throw new ObjectMismatchException(parentUniqueId + " does not match " + parent, null);
}
/**
* @see SubResource#update(java.lang.String, java.lang.String, SimpleObject, RequestContext)
*/
@Override
public Object update(String parentUniqueId, String uuid, SimpleObject propertiesToUpdate, RequestContext context)
throws ResponseException {
T delegate = getByUniqueId(uuid);
if (delegate == null)
throw new ObjectNotFoundException();
testParent(delegate, parentUniqueId);
setConvertedProperties(delegate, propertiesToUpdate, getUpdatableProperties(), false);
delegate = save(delegate);
return ConversionUtil.convertToRepresentation(delegate, Representation.DEFAULT);
}
/**
* @see org.openmrs.module.webservices.rest.web.resource.api.SubResource#delete(java.lang.String,
* java.lang.String, java.lang.String,
* org.openmrs.module.webservices.rest.web.RequestContext)
*/
@Override
public void delete(String parentUniqueId, String uuid, String reason, RequestContext context) throws ResponseException {
T delegate = getByUniqueId(uuid);
if (delegate == null)
throw new ObjectNotFoundException();
testParent(delegate, parentUniqueId);
delete(delegate, reason, context);
}
/**
* @see SubResource#purge(java.lang.String, java.lang.String, RequestContext)
*/
@Override
public void purge(String parentUniqueId, String uuid, RequestContext context) throws ResponseException {
T delegate = getByUniqueId(uuid);
if (delegate == null) {
// HTTP DELETE is idempotent, so if we can't find the object, we assume it's already deleted and return success
return;
}
testParent(delegate, parentUniqueId);
purge(delegate, context);
}
/**
* @see SubResource#getAll(java.lang.String, RequestContext)
*/
@Override
public SimpleObject getAll(String parentUniqueId, RequestContext context) throws ResponseException {
P parent = getParentResource().getByUniqueId(parentUniqueId);
PageableResult result = doGetAll(parent, context);
return result.toSimpleObject(this);
}
private String getParentUri(T instance) {
return getParentResource().getUri(getParent(instance));
}
@SuppressWarnings("unchecked")
private PR getParentResource() {
org.openmrs.module.webservices.rest.web.annotation.SubResource sub = getClass().getAnnotation(
org.openmrs.module.webservices.rest.web.annotation.SubResource.class);
org.openmrs.module.webservices.rest.web.annotation.Resource resource = sub.parent().getAnnotation(
org.openmrs.module.webservices.rest.web.annotation.Resource.class);
return (PR) Context.getService(RestService.class).getResourceByName(resource.name());
}
@RepHandler(RefRepresentation.class)
public SimpleObject asRef(T delegate) throws ConversionException {
DelegatingResourceDescription description = new DelegatingResourceDescription();
description.addProperty("uuid");
description.addProperty("display");
Method method = ReflectionUtils.findMethod(delegate.getClass(), "isVoided");
if (method != null) {
try {
if ((Boolean) method.invoke(delegate))
description.addProperty("voided");
}
catch (IllegalArgumentException e) {
log.debug("unable to get voided status", e);
}
catch (IllegalAccessException e) {
log.debug("unable to get voided status", e);
}
catch (InvocationTargetException e) {
log.debug("unable to get voided status", e);
}
} else {
// couldn't find an "isVoided" method, look for "isRetired"
method = ReflectionUtils.findMethod(delegate.getClass(), "isRetired");
if (method != null) {
try {
if ((Boolean) method.invoke(delegate))
description.addProperty("retired");
}
catch (IllegalArgumentException e) {
log.debug("unable to get retired status", e);
}
catch (IllegalAccessException e) {
log.debug("unable to get retired status", e);
}
catch (InvocationTargetException e) {
log.debug("unable to get retired status", e);
}
}
}
description.addSelfLink();
return convertDelegateToRepresentation(delegate, description);
}
/**
* @see SubResource#p[ut(java.lang.String, SimpleObject, RequestContext)
*/
@Override
public void put(String parentUniqueId, SimpleObject post, RequestContext context) throws ResponseException {
throw new ResourceDoesNotSupportOperationException();
}
}