blob: 7d185e8cb596a5672b3ffe514595348f2749319e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010-2014 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* SAP AG - initial API and implementation
*******************************************************************************/
package org.eclipse.skalli.services.rest;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang.StringUtils;
import org.eclipse.skalli.commons.CollectionUtils;
import org.restlet.Request;
import org.restlet.data.Form;
import org.restlet.data.MediaType;
import org.restlet.data.Preference;
import org.restlet.data.Reference;
import org.restlet.resource.ServerResource;
/**
* Helper class providing information about a request for a REST resource
* like query attributes and request path.
*/
public class RequestContext {
private static final String ACCEPT_ALL = "*/*"; //$NON-NLS-1$
@SuppressWarnings("nls")
private static final Set<String> FORMAT_JSON =
CollectionUtils.asSet("json", "text/json", MediaType.APPLICATION_JSON.toString());
// for backward compatibility and to facilitate browser access,
// we also accept "html" and "text/html", but treat them as "text/xml"
@SuppressWarnings("nls")
private static final Set<String> FORMAT_XML =
CollectionUtils.asSet(
"xml", MediaType.TEXT_XML.toString(), MediaType.APPLICATION_XML.toString(),
"html", MediaType.TEXT_HTML.toString());
private static final String ACCEPT_QUERY_PARAM = "accept"; //$NON-NLS-1$
private String action;
private String path;
private Reference resourceRef;
private Form form;
private Form headers;
private Map<String,String> queryParams;
private MediaType mediaType;
/**
* Creates a request context from a given REST request.
* <p>
* Extracts request parameters like {@link #getPath() path} and
* {@link #getQueryAttributes() query attributes}.
*
* @param request the REST request to evaluate.
*/
public RequestContext(Request request) {
action = request.getMethod().getName();
resourceRef = request.getResourceRef();
path = resourceRef != null ? resourceRef.getPath() : "/"; //$NON-NLS-1$
form = resourceRef != null ? resourceRef.getQueryAsForm() : null;
if (form != null) {
queryParams = form.getValuesMap();
} else {
form = new Form();
queryParams = Collections.emptyMap();
}
mediaType = getMediaType(request);
headers = (Form) request.getAttributes().get("org.restlet.http.headers"); //$NON-NLS-1$
}
/**
* Creates a request context from a given server resource.
* <p>
* This is a convenience method equivalent to
* {@link #RequestContext(Request) RequestContext(serverResource.getRequest())}.
*
* @param serverResource the server resource to evaluate.
*/
public RequestContext(ServerResource serverResource) {
this(serverResource.getRequest());
}
/**
* Returns the request path including the context path
* prefix <code>/api</code>.
*/
public String getPath() {
return path;
}
/**
* Returns the action of this request.
*/
public String getAction() {
return action;
}
/**
* Returns the reference of the requested resource, or <code>null</code>
* if the requested resource could not be determined.
*
* @see org.restlet.Request#getResourceRef().
*/
public Reference getResourceRef() {
return resourceRef;
}
/**
* Returns the host part of the resource's URL including scheme,
* host name and port number, or <code>null</code> if the host
* could not be determined.
*/
public String getHost() {
return resourceRef != null ? resourceRef.getHostIdentifier() : null;
}
/**
* Returns the query as form, which may be an {@link Form#Form() empty form}
* if there is no query.
*/
public Form getQueryAsForm() {
return form;
}
/**
* Returns the query as string, which may be an empty string
* if there is no query.
*/
public String getQueryString() {
return form.getQueryString();
}
/**
* Returns the value of a given query attribute.
*
* @param name the name of the attribute.
*
* @return the value of the attribute, which may be
* <code>null</code> either if there is no attribute
* with the given name, or the attribute is a boolean
* attribute.
*/
public String getQueryAttribute(String name) {
return queryParams.get(name);
}
/**
* Returns <code>true</code> if there is a query attribute
* with the given name.
*
* @param name the name of the attribute.
*/
public boolean hasQueryAttribute(String name) {
return queryParams.containsKey(name);
}
/**
* Returns the query attributes as map.
* @return a mapping of attribute names to their respective values,
* or an empty map if there is no query.
*/
public Map<String,String> getQueryAttributes() {
return queryParams;
}
/**
* Returns the value of the header matching the given name
* (ignoring the case). If there are multiple headers with
* the same name, only the first one is matched. If there
* is no matching header, the given default value is returned.
*
* @param name the header name.
* @param defaultValue the default value to return in case
* there is no matching header.
*/
public String getHeader(String name, String defaultValue) {
return headers.getFirstValue(name, true, defaultValue);
}
/**
* Returns the requested media type, never <code>null</code>.
* If no media type can be determined from the request, <tt>"text/xml"</tt>
* is returned.
*/
public MediaType getMediaType() {
return mediaType;
}
/**
* Returns <code>true</code> if the media type is <tt>text/xml</tt>.
*/
public boolean isXML() {
return MediaType.TEXT_XML.equals(mediaType);
}
/**
* Returns <code>true</code> if the media type is <tt>application/json</tt>.
*/
public boolean isJSON() {
return MediaType.APPLICATION_JSON.equals(mediaType);
}
private MediaType getMediaType(Request request) {
MediaType mediaType = null;
if (request.isEntityAvailable()) {
mediaType = request.getEntity().getMediaType();
}
if (mediaType == null) {
String accept = getQueryAttribute(ACCEPT_QUERY_PARAM);
if (StringUtils.isBlank(accept)) {
accept = getMaxQualityAccept(request);
}
if (StringUtils.isNotBlank(accept)) {
if (FORMAT_XML.contains(accept)) {
mediaType = MediaType.TEXT_XML;
} else if (FORMAT_JSON.contains(accept)) {
mediaType = MediaType.APPLICATION_JSON;
} else if (ACCEPT_ALL.equals(accept)) {
mediaType = MediaType.TEXT_XML;
} else {
mediaType = MediaType.valueOf(accept);
}
}
if (mediaType == null) {
mediaType = MediaType.TEXT_XML;
}
}
return mediaType;
}
/**
* Scans the <tt>Accept</tt> header for the media type with the highest <tt>"q"</tt> attribute.
* If the list of accepted media type contains {@value #ACCEPT_ALL}, {@value #ACCEPT_ALL} is returned.
*/
private String getMaxQualityAccept(Request request) {
String maxQualityAccept = null;
float maxQuality = 0;
for (Preference<MediaType> acceptedMediaType: request.getClientInfo().getAcceptedMediaTypes()) {
String mediaType = acceptedMediaType.getMetadata().getName();
if (ACCEPT_ALL.equals(mediaType)) {
return ACCEPT_ALL;
}
float quality = acceptedMediaType.getQuality();
if (quality > maxQuality) {
maxQualityAccept = mediaType;
maxQuality = quality;
}
}
return maxQualityAccept;
}
}