com.tinkerpop.rexster.BaseResource Maven / Gradle / Ivy
package com.tinkerpop.rexster;
import com.tinkerpop.rexster.extension.HttpMethod;
import com.tinkerpop.rexster.server.RexsterApplication;
import com.tinkerpop.rexster.util.StatisticsHelper;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.codehaus.jettison.json.JSONArray;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.UriInfo;
import javax.ws.rs.core.Variant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* Base resource from which most other resources extend. Exposes the request/response object and other
* request and context information.
*
* @author Stephen Mallette (http://stephen.genoprime.com)
*/
public abstract class BaseResource {
private static Logger logger = Logger.getLogger(BaseResource.class);
protected final List producesVariantList = Variant.VariantListBuilder.newInstance().mediaTypes(
MediaType.APPLICATION_JSON_TYPE,
RexsterMediaType.APPLICATION_REXSTER_JSON_TYPE,
RexsterMediaType.APPLICATION_REXSTER_TYPED_JSON_TYPE).add().build();
protected final StatisticsHelper sh = new StatisticsHelper();
/**
* This request object goes through the mapping of a URI to JSON.
*/
private JSONObject requestObject = null;
/**
* This request object is just a single layered map of keys/values.
*/
private JSONObject requestObjectFlat = null;
protected JSONObject resultObject = new JSONObject();
@Context
private RexsterApplication rexsterApplication;
@Context
protected HttpServletRequest httpServletRequest;
@Context
protected UriInfo uriInfo;
@Context
protected ServletContext servletContext;
@Context
protected SecurityContext securityContext;
public BaseResource(final RexsterApplication rexsterApplication) {
if (rexsterApplication != null) {
this.rexsterApplication = rexsterApplication;
}
sh.stopWatch();
try {
this.resultObject.put(Tokens.VERSION, Tokens.REXSTER_VERSION);
} catch (JSONException ex) {
JSONObject error = generateErrorObject(ex.getMessage());
throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).entity(error).build());
}
}
public JSONObject generateErrorObject(String message) {
return generateErrorObject(message, null);
}
public JSONObject generateErrorObjectJsonFail(Throwable source) {
return generateErrorObject("An error occurred while generating the response object", source);
}
public JSONObject generateErrorObject(String message, Throwable source) {
Map m = new HashMap();
m.put(Tokens.MESSAGE, message);
if (source != null) {
m.put("error", source.getMessage());
}
// use a hashmap with the constructor so that a JSONException
// will not be thrown
return new JSONObject(m);
}
protected RexsterApplication getRexsterApplication() {
return this.rexsterApplication;
}
protected String getUriPath() {
String baseUri = "";
if (this.uriInfo != null) {
baseUri = this.uriInfo.getAbsolutePath().toString();
if (!baseUri.endsWith("/")) {
baseUri = baseUri + "/";
}
}
return baseUri;
}
/**
* Sets the request object.
*
* If this is set then the any previous call to getRequestObject which instantiated
* the request object from the URI parameters will be overriden.
*
* @param jsonObject The JSON Object.
*/
protected void setRequestObject(final JSONObject jsonObject) {
this.requestObject = jsonObject;
this.requestObjectFlat = jsonObject;
}
protected void setRequestObject(final MultivaluedMap formData) {
final Map m = new HashMap();
for(MultivaluedMap.Entry> entry : formData.entrySet()) {
// only grabs the first item for JSON support
m.put(entry.getKey(), entry.getValue().get(0));
}
this.requestObject = new JSONObject(m);
this.requestObjectFlat = new JSONObject(m);
}
public JSONObject getRequestObject() {
return this.getRequestObject(true);
}
public JSONObject getRequestObjectFlat() {
return this.getRequestObject(false);
}
/**
* Gets the request object.
*
* If it does not exist then an attempt is made to parse the parameter list on
* the URI into a JSON object.
*
* @return The request object.
*/
public JSONObject getRequestObject(final boolean parseToJson) {
if (this.requestObject == null) {
try {
this.requestObject = new JSONObject();
this.requestObjectFlat = new JSONObject();
if (this.httpServletRequest != null && this.httpServletRequest.getParameterNames().hasMoreElements()) {
// unclear if this block of code is still necessary. seems like this is here as a fallback
// for when the request comes through without the parameters being extracted from an entity.
// doesn't seem like it is a likely thing to happen, but can't think of the corner case that
// allows it to happen.
final Map queryParameters = this.httpServletRequest.getParameterMap();
this.buildRequestObject(queryParameters);
}
} catch (JSONException ex) {
logger.error(ex);
final JSONObject error = generateErrorObjectJsonFail(ex);
throw new WebApplicationException(Response.status(Status.INTERNAL_SERVER_ERROR).entity(error).build());
}
}
if (parseToJson) {
return this.requestObject;
} else {
return this.requestObjectFlat;
}
}
private void buildRequestObject(final Map queryParameters) throws JSONException {
final Map flatMap = new HashMap();
for (String key : (Set) queryParameters.keySet()) {
final String[] keys = key.split(Tokens.PERIOD_REGEX);
JSONObject embeddedObject = this.requestObject;
for (int i = 0; i < keys.length - 1; i++) {
JSONObject tempEmbeddedObject = (JSONObject) embeddedObject.opt(keys[i]);
if (null == tempEmbeddedObject) {
tempEmbeddedObject = new JSONObject();
embeddedObject.put(keys[i], tempEmbeddedObject);
}
embeddedObject = tempEmbeddedObject;
}
String rawValue;
final Object val = queryParameters.get(key);
if (val instanceof String) {
rawValue = (String) val;
} else {
// supports multiple parameters on the same key...just take the first?
String[] values = (String[]) val;
rawValue = values[0];
}
flatMap.put(key, rawValue);
try {
if (rawValue.startsWith(Tokens.LEFT_BRACKET) && rawValue.endsWith(Tokens.RIGHT_BRACKET)) {
rawValue = rawValue.substring(1, rawValue.length() - 1);
final JSONArray array = new JSONArray();
for (String value : rawValue.split(Tokens.COMMA)) {
array.put(value.trim());
}
embeddedObject.put(keys[keys.length - 1], array);
} else {
final Object parsedValue = new JSONObject(rawValue);
embeddedObject.put(keys[keys.length - 1], parsedValue);
}
} catch (JSONException e) {
embeddedObject.put(keys[keys.length - 1], rawValue);
}
}
this.requestObjectFlat = new JSONObject(flatMap);
}
protected JSONObject getNonRexsterRequest() throws JSONException {
JSONObject object = new JSONObject();
Iterator keys = this.getRequestObject().keys();
while (keys.hasNext()) {
String key = keys.next().toString();
if (!key.equals(Tokens.REXSTER)) {
object.put(key, this.getRequestObject().opt(key));
}
}
return object;
}
protected List getNonRexsterRequestKeys() throws JSONException {
final List keys = new ArrayList();
final JSONObject request = this.getNonRexsterRequest();
if (request.length() > 0) {
final Iterator itty = request.keys();
while (itty.hasNext()) {
keys.add((String) itty.next());
}
}
return keys;
}
/*
* NOT SURE WHAT THIS CODE WAS EVERY DOING. it was being called within VertexResource and was filtering
* each element for some reason in the getVertexEdges method. wasn't used anywhere else. its only purpose
* seemed to be related to the filtering of elements by label...not sure if this is safe to remove yet.
protected boolean hasPropertyValues(Element element, JSONObject properties) throws JSONException {
Iterator keys = properties.keys();
while (keys.hasNext()) {
String key = keys.next().toString();
Object temp;
if (key.equals(Tokens._ID))
temp = element.getId();
else if (key.equals(Tokens._LABEL))
temp = ((Edge) element).getLabel();
else if (key.equals(Tokens._IN_V))
temp = ((Edge) element).getVertex(Direction.IN).getId();
else if (key.equals(Tokens._OUT_V))
temp = ((Edge) element).getVertex(Direction.OUT).getId();
else if (key.equals(Tokens._TYPE)) {
if (element instanceof Vertex)
temp = Tokens.VERTEX;
else
temp = Tokens.EDGE;
} else
temp = element.getProperty(key);
if (null == temp || !temp.equals(properties.get(key)))
return false;
}
return true;
}
*/
protected String getTimeAlive() {
long timeMillis = System.currentTimeMillis() - this.rexsterApplication.getStartTime();
long timeSeconds = timeMillis / 1000;
long timeMinutes = timeSeconds / 60;
long timeHours = timeMinutes / 60;
long timeDays = timeHours / 24;
String seconds = Integer.toString((int) (timeSeconds % 60));
String minutes = Integer.toString((int) (timeMinutes % 60));
String hours = Integer.toString((int) timeHours % 24);
String days = Integer.toString((int) timeDays);
for (int i = 0; i < 2; i++) {
if (seconds.length() < 2) {
seconds = "0" + seconds;
}
if (minutes.length() < 2) {
minutes = "0" + minutes;
}
if (hours.length() < 2) {
hours = "0" + hours;
}
}
return days + "[d]:" + hours + "[h]:" + minutes + "[m]:" + seconds + "[s]";
}
/**
* Includes all HTTP Methods in allowable options.
*/
protected Response buildOptionsResponse() {
return buildOptionsResponse(HttpMethod.DELETE.toString(),
HttpMethod.GET.toString(),
HttpMethod.POST.toString(),
HttpMethod.PUT.toString());
}
protected Response buildOptionsResponse(final String... methods) {
final String requestHeaders = this.httpServletRequest.getHeader("Access-Control-Request-Headers");
final String allowHeaders = requestHeaders == null ? "*" : requestHeaders;
return Response.ok()
.header("Access-Control-Allow-Methods", "OPTIONS," + StringUtils.join(methods, ","))
.header("Access-Control-Allow-Headers", allowHeaders)
.header("Access-Control-Max-Age", "1728000").build();
}
}