/**
* Copyright 2005-2014 Restlet
*
* The contents of this file are subject to the terms of one of the following
* open source licenses: Apache 2.0 or or EPL 1.0 (the "Licenses"). You can
* select the license that you prefer but you may not use this file except in
* compliance with one of these Licenses.
*
* You can obtain a copy of the Apache 2.0 license at
* http://www.opensource.org/licenses/apache-2.0
*
* You can obtain a copy of the EPL 1.0 license at
* http://www.opensource.org/licenses/eclipse-1.0
*
* See the Licenses for the specific language governing permissions and
* limitations under the Licenses.
*
* Alternatively, you can obtain a royalty free commercial license with less
* limitations, transferable or non-transferable, directly at
* http://restlet.com/products/restlet-framework
*
* Restlet is a registered trademark of Restlet S.A.S.
*/
package org.restlet.routing;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.Restlet;
import org.restlet.data.Cookie;
import org.restlet.data.Form;
import org.restlet.representation.Representation;
import org.restlet.util.Series;
/**
* Filter extracting attributes from a call. Multiple extractions can be
* defined, based on the query string of the resource reference, on the request
* form (ex: posted from a browser) or on cookies.<br>
* <br>
* Concurrency note: instances of this class or its subclasses can be invoked by
* several threads at the same time and therefore must be thread-safe. You
* should be especially careful when storing state in member variables.
*
* @author Jerome Louvel
*/
public class Extractor extends Filter {
/** Internal class holding extraction information. */
private static final class ExtractInfo {
/** Target attribute name. */
protected String attribute;
/** Indicates how to handle repeating values. */
protected boolean first;
/** Name of the parameter to look for. */
protected String parameter;
/**
* Constructor.
*
* @param attribute
* Target attribute name.
* @param parameter
* Name of the parameter to look for.
* @param first
* Indicates how to handle repeating values.
*/
public ExtractInfo(String attribute, String parameter, boolean first) {
this.attribute = attribute;
this.parameter = parameter;
this.first = first;
}
}
/** The list of cookies to extract. */
private volatile List<ExtractInfo> cookieExtracts;
/** The list of request entity parameters to extract. */
private volatile List<ExtractInfo> entityExtracts;
/** The list of query parameters to extract. */
private volatile List<ExtractInfo> queryExtracts;
/**
* Constructor.
*/
public Extractor() {
this(null);
}
/**
* Constructor.
*
* @param context
* The context.
*/
public Extractor(Context context) {
this(context, null);
}
/**
* Constructor.
*
* @param context
* The context.
* @param next
* The next Restlet.
*/
public Extractor(Context context, Restlet next) {
super(context, next);
}
/**
* Allows filtering before its handling by the target Restlet. By default it
* extracts the attributes from form parameters (query, cookies, entity) and
* finally puts them in the request's attributes (
* {@link Request#getAttributes()}).
*
* @param request
* The request to filter.
* @param response
* The response to filter.
* @return The continuation status.
*/
@Override
protected int beforeHandle(Request request, Response response) {
// Extract the query parameters
if (!getQueryExtracts().isEmpty()) {
Form form = request.getResourceRef().getQueryAsForm();
if (form != null) {
for (ExtractInfo ei : getQueryExtracts()) {
if (ei.first) {
String value = form.getFirstValue(ei.parameter);
if (value != null) {
request.getAttributes().put(ei.attribute, value);
}
} else {
request.getAttributes().put(ei.attribute,
form.subList(ei.parameter));
}
}
}
}
// Extract the request entity parameters
if (!getEntityExtracts().isEmpty()) {
Representation entity = request.getEntity();
if (entity != null) {
Form form = new Form(entity);
for (ExtractInfo ei : getEntityExtracts()) {
if (ei.first) {
String value = form.getFirstValue(ei.parameter);
if (value != null) {
request.getAttributes().put(ei.attribute, value);
}
} else {
request.getAttributes().put(ei.attribute,
form.subList(ei.parameter));
}
}
}
}
// Extract the cookie parameters
if (!getCookieExtracts().isEmpty()) {
Series<Cookie> cookies = request.getCookies();
if (cookies != null) {
for (ExtractInfo ei : getCookieExtracts()) {
if (ei.first) {
String value = cookies.getFirstValue(ei.parameter);
if (value != null) {
request.getAttributes().put(ei.attribute, value);
}
} else {
request.getAttributes().put(ei.attribute,
cookies.subList(ei.parameter));
}
}
}
}
return CONTINUE;
}
/**
* Extracts an attribute from the request cookies.
*
* @param attribute
* The name of the request attribute to set.
* @param cookieName
* The name of the cookies to extract.
* @param first
* Indicates if only the first cookie should be set. Otherwise as
* a List instance might be set in the attribute value.
*/
public void extractFromCookie(String attribute, String cookieName,
boolean first) {
getCookieExtracts().add(new ExtractInfo(attribute, cookieName, first));
}
/**
* Extracts an attribute from the request entity form.
*
* @param attribute
* The name of the request attribute to set.
* @param parameter
* The name of the entity form parameter to extract.
* @param first
* Indicates if only the first cookie should be set. Otherwise as
* a List instance might be set in the attribute value.
*/
public void extractFromEntity(String attribute, String parameter,
boolean first) {
getEntityExtracts().add(new ExtractInfo(attribute, parameter, first));
}
/**
* Extracts an attribute from the query string of the resource reference.
*
* @param attribute
* The name of the request attribute to set.
* @param parameter
* The name of the query string parameter to extract.
* @param first
* Indicates if only the first cookie should be set. Otherwise as
* a List instance might be set in the attribute value.
*/
public void extractFromQuery(String attribute, String parameter,
boolean first) {
getQueryExtracts().add(new ExtractInfo(attribute, parameter, first));
}
/**
* Returns the list of query extracts.
*
* @return The list of query extracts.
*/
private List<ExtractInfo> getCookieExtracts() {
// Lazy initialization with double-check.
List<ExtractInfo> ce = this.cookieExtracts;
if (ce == null) {
synchronized (this) {
ce = this.cookieExtracts;
if (ce == null) {
this.cookieExtracts = ce = new CopyOnWriteArrayList<ExtractInfo>();
}
}
}
return ce;
}
/**
* Returns the list of query extracts.
*
* @return The list of query extracts.
*/
private List<ExtractInfo> getEntityExtracts() {
// Lazy initialization with double-check.
List<ExtractInfo> ee = this.entityExtracts;
if (ee == null) {
synchronized (this) {
ee = this.entityExtracts;
if (ee == null) {
this.entityExtracts = ee = new CopyOnWriteArrayList<ExtractInfo>();
}
}
}
return ee;
}
/**
* Returns the list of query extracts.
*
* @return The list of query extracts.
*/
private List<ExtractInfo> getQueryExtracts() {
// Lazy initialization with double-check.
List<ExtractInfo> qe = this.queryExtracts;
if (qe == null) {
synchronized (this) {
qe = this.queryExtracts;
if (qe == null) {
this.queryExtracts = qe = new CopyOnWriteArrayList<ExtractInfo>();
}
}
}
return qe;
}
}