/** * 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 java.util.regex.Pattern; import org.restlet.Context; import org.restlet.Request; import org.restlet.Response; import org.restlet.Restlet; import org.restlet.data.Status; /** * Filter validating attributes from a call. Validation is verified based on * regex pattern matching.<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 * @see Pattern */ public class Validator extends Filter { /** Internal class holding validation information. */ private static final class ValidateInfo { /** Name of the attribute to look for. */ protected String attribute; /** Format of the attribute value, using Regex pattern syntax. */ protected String format; /** Indicates if the attribute presence is required. */ protected boolean required; /** * Constructor. * * @param attribute * Name of the attribute to look for. * @param required * Indicates if the attribute presence is required. * @param format * Format of the attribute value, using Regex pattern syntax. */ public ValidateInfo(String attribute, boolean required, String format) { this.attribute = attribute; this.required = required; this.format = format; } } /** The list of attribute validations. */ private volatile List<ValidateInfo> validations; /** * Constructor. */ public Validator() { this(null); } /** * Constructor. * * @param context * The context. */ public Validator(Context context) { this(context, null); } /** * Constructor. * * @param context * The context. * @param next * The next Restlet. */ public Validator(Context context, Restlet next) { super(context, next); } /** * Allows filtering before its handling by the target Restlet. By default it * parses the template variable, adjust the base reference, then extracts * the attributes from form parameters (query, cookies, entity) and finally * tries to validate the variables as indicated by the * {@link #validate(String, boolean, String)} method. * * @param request * The request to filter. * @param response * The response to filter. * @return The {@link Filter#CONTINUE} status. */ @Override protected int beforeHandle(Request request, Response response) { if (this.validations != null) { for (ValidateInfo validate : getValidations()) { if (validate.required && !request.getAttributes().containsKey( validate.attribute)) { response.setStatus( Status.CLIENT_ERROR_BAD_REQUEST, "Unable to find the \"" + validate.attribute + "\" attribute in the request. Please check your request."); } else if (validate.format != null) { Object value = request.getAttributes().get( validate.attribute); if ((value != null) && !Pattern.matches(validate.format, value.toString())) { response.setStatus( Status.CLIENT_ERROR_BAD_REQUEST, "Unable to validate the value of the \"" + validate.attribute + "\" attribute. The expected format is: " + validate.format + " (Java Regex). Please check your request."); } } } } return CONTINUE; } /** * Returns the list of attribute validations. * * @return The list of attribute validations. */ private List<ValidateInfo> getValidations() { // Lazy initialization with double-check. List<ValidateInfo> v = this.validations; if (v == null) { synchronized (this) { v = this.validations; if (v == null) { this.validations = v = new CopyOnWriteArrayList<ValidateInfo>(); } } } return v; } /** * Checks the request attributes for presence or format. If the check fails, * then a response status CLIENT_ERROR_BAD_REQUEST is returned with the * proper status description. * * @param attribute * Name of the attribute to look for. * @param required * Indicates if the attribute presence is required. * @param format * Format of the attribute value, using Regex pattern syntax. */ public void validate(String attribute, boolean required, String format) { getValidations().add(new ValidateInfo(attribute, required, format)); } /** * Checks the request attributes for format only. If the check fails, then a * response status CLIENT_ERROR_BAD_REQUEST is returned with the proper * status description. * * @param attribute * Name of the attribute to look for. * @param format * Format of the attribute value, using Regex pattern syntax. */ public void validateFormat(String attribute, String format) { getValidations().add(new ValidateInfo(attribute, false, format)); } /** * Checks the request attributes for presence only. If the check fails, then * a response status CLIENT_ERROR_BAD_REQUEST is returned with the proper * status description. * * @param attribute * Name of the attribute to look for. */ public void validatePresence(String attribute) { getValidations().add(new ValidateInfo(attribute, true, null)); } }