/******************************************************************************* * 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; } }