/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.util.rest;
import java.lang.reflect.Method;
import java.net.URI;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import org.fudgemsg.FudgeMsg;
import org.fudgemsg.FudgeMsgEnvelope;
import org.joda.beans.Bean;
import com.opengamma.transport.jaxrs.FudgeResponse;
/**
* Abstract base class for RESTful resources.
*/
public abstract class AbstractDataResource {
/**
* Creates the empty RESTful "ok" response object - 200.
* <p>
* This is normally used as a response to a ping.
*
* @return the response, not null
*/
protected Response responseOk() {
return Response.ok().build();
}
/**
* Creates the empty RESTful "no-content" response object - 204.
* <p>
* This is the correct form of response if there is no entity.
*
* @return the response, not null
*/
protected Response responseOkNoContent() {
return Response.noContent().build();
}
/**
* Creates the RESTful "created" response object.
*
* @param uri the URI that was created, may be null if value is null
* @return the response, not null
*/
protected Response responseCreated(final URI uri) {
return Response.created(uri).build();
}
/**
* Creates the RESTful "ok" response object, converting null to a 404.
* <p>
* The response will only go via Fudge if the value if a Fudge recognized type.
*
* @param value the value to contain in the response, or null to trigger a 404
* @return the response, not null
*/
protected Response responseOk(final Object value) {
responseNullTo404(value);
return Response.ok(value).build();
}
//-------------------------------------------------------------------------
/**
* Creates the RESTful "ok" response object for an object, converting null to a 404.
* <p>
* The response object is suitable for conversion to Fudge or Joda-Beans mime types.
*
* @param value the value to contain in the response, or null to trigger a 404
* @return the response, not null
*/
protected Response responseOkObject(final Object value) {
responseNullTo404(value);
return Response.ok(wrap(value)).build();
}
/**
* Creates the RESTful "created" response object for an object, converting null to a 404.
* <p>
* The response object is suitable for conversion to Fudge or Joda-Beans mime types.
*
* @param uri the URI that was created, may be null if value is null
* @param value the value to contain in the response, or null to trigger a 404
* @return the response, not null
*/
protected Response responseCreatedObject(final URI uri, final Object value) {
responseNullTo404(value);
return Response.created(uri).entity(wrap(value)).build();
}
//-------------------------------------------------------------------------
/**
* Creates the RESTful "ok" response object using Fudge, converting null to a 404.
* <p>
* The response will be converted to XML or JSON formatted Fudge on demand.
*
* @param value the value to contain in the response, or null to trigger a 404
* @return the response, not null
* @deprecated Use {@code responseOkObject}
*/
@Deprecated
protected Response responseOkFudge(final Object value) {
responseNullTo404(value);
return Response.ok(wrap(value)).build();
}
/**
* Creates the RESTful "created" response object using Fudge, converting null to a 404.
* <p>
* The response will be converted to XML or JSON formatted Fudge on demand.
*
* @param uri the URI that was created, may be null if value is null
* @param value the value to contain in the response, or null to trigger a 404
* @return the response, not null
* @deprecated Use {@code responseOkObject}
*/
@Deprecated
protected Response responseCreatedFudge(final URI uri, final Object value) {
responseNullTo404(value);
return Response.created(uri).entity(wrap(value)).build();
}
//-------------------------------------------------------------------------
/**
* Checks if the value is null and throws a 404 exception.
*
* @param value the value to check
* @throws WebApplicationException if the value is null
*/
protected void responseNullTo404(final Object value) throws WebApplicationException {
if (value == null) {
throw new WebApplicationException(Response.Status.NOT_FOUND);
}
}
private Object wrap(Object value) {
if (value instanceof FudgeMsgEnvelope || value instanceof FudgeMsg || value instanceof Bean) {
return value;
}
return FudgeResponse.of(value);
}
//-------------------------------------------------------------------------
/**
* Creates a HATEOAS response for this object.
*
* @param uriInfo the URI info, not null
* @return the response, not null
*/
protected Response hateoasResponse(final UriInfo uriInfo) {
Class<? extends AbstractDataResource> cls = getClass();
StringBuilder buf = new StringBuilder();
buf.append("<p>").append(cls.getName()).append("</p>");
buildHateoas(uriInfo, cls, buf, "");
return Response.ok(buf.toString()).build();
}
private void buildHateoas(final UriInfo uriInfo, final Class<?> cls, final StringBuilder buf, final String basePath) {
try {
//trim any trailing slashes
String uriPath = uriInfo.getRequestUri().getPath();
if (uriPath.endsWith("/")) {
uriPath = uriPath.substring(0, uriPath.length() - 1);
}
Method[] methods = cls.getMethods();
for (Method method : methods) {
String path = basePath;
if (method.isAnnotationPresent(Path.class)) {
path += "/" + method.getAnnotation(Path.class).value();
}
if (method.isAnnotationPresent(GET.class)) {
if (path.length() > 0) {
buf.append("<p>GET ").append(uriPath + path).append(" => \"").append(method.getName()).append("\"</p>");
}
} else if (method.isAnnotationPresent(POST.class)) {
buf.append("<p>POST ").append(uriPath + path).append(" => \"").append(method.getName()).append("\"</p>");
} else if (method.isAnnotationPresent(PUT.class)) {
buf.append("<p>PUT ").append(uriPath + path).append(" => \"").append(method.getName()).append("\"</p>");
} else if (method.isAnnotationPresent(DELETE.class)) {
buf.append("<p>DELETE ").append(uriPath + path).append(" => \"").append(method.getName()).append("\"</p>");
} else if (method.isAnnotationPresent(Path.class) && AbstractDataResource.class.isAssignableFrom(method.getReturnType())) {
//this is a sub-resource:
if (method.getReturnType() == cls) {
buf.append("<p>[SUB_RESOURCE] ").append("...").append(path).append(" => \"").append(method.getName()).append("\"</p>");
} else {
buildHateoas(uriInfo, method.getReturnType(), buf, path);
}
}
}
} catch (Exception ex) {
// ignore
}
}
}