/******************************************************************************* * Copyright (c) 2012, Directors of the Tyndale STEP Project * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * Neither the name of the Tyndale House, Cambridge (www.TyndaleHouse.com) * nor the names of its contributors may be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ package com.tyndalehouse.step.rest.framework; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; import javax.servlet.http.HttpServletRequest; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.tyndalehouse.step.core.exceptions.StepInternalException; /** * A simple class that hold request information, provides various cache keys * <p /> * . * * @author chrisburrell */ public class StepRequest { /** The Constant LOGGER. */ private static final Logger LOGGER = LoggerFactory.getLogger(StepRequest.class); /** The controller name. */ private final String controllerName; /** The method name. */ private final String methodName; /** The args. */ private final String[] args; /** The external. */ private final boolean external; /** The request uri. */ private final String requestURI; /** * Creates a request holder object containing the relevant information about a request. This constructor * is used more for testing and could possibly be removed later * * @param requestURI the request URI that determines the controller, method name, etc. * @param controllerName the controller name * @param methodName the method name * @param args the arguments that should be passed to the method */ public StepRequest(final String requestURI, final String controllerName, final String methodName, final String[] args) { this.requestURI = requestURI; this.controllerName = controllerName; this.methodName = methodName; this.external = false; this.args = args == null ? new String[] {} : args; } /** * Returns the step request object containing the relevant information about the STEP Request. * * @param request the HTTP request * @param encoding the encoding with which to decode the request */ public StepRequest(final HttpServletRequest request, final String encoding) { this.requestURI = request.getRequestURI(); this.external = request.getAttribute("external_request") != null; LOGGER.debug("Parsing {}", this.requestURI); final int requestStart = getPathLength(request) + 1; final int endOfControllerName = this.requestURI.indexOf('/', requestStart); final int startOfMethodName = endOfControllerName + 1; final int endOfMethodNameSlash = this.requestURI.indexOf('/', startOfMethodName); // now we can set the controllerName and methodNme this.controllerName = this.requestURI.substring(requestStart, endOfControllerName); this.methodName = this.requestURI.substring(startOfMethodName, endOfMethodNameSlash == -1 ? this.requestURI.length() : endOfMethodNameSlash); LOGGER.debug("Request parsed as controller: [{}], method [{}]", this.controllerName, this.methodName); final int endOfMethodName = startOfMethodName + this.methodName.length(); final String[] calculatedArguments = parseArguments(endOfMethodName + 1, encoding); this.args = calculatedArguments == null ? new String[] {} : calculatedArguments; } /** * gets the arguments out of the requestURI String. * * @param parameterStart the location at which the parameters start * @param encoding the encoding with which to decode the arguments * @return a list of arguments */ private String[] parseArguments(final int parameterStart, final String encoding) { final List<String> arguments = new ArrayList<String>(); int argStart = parameterStart; int nextArgStop = this.requestURI.indexOf('/', argStart); try { while (nextArgStop != -1) { arguments.add(URLDecoder.decode(this.requestURI.substring(argStart, nextArgStop), encoding)); argStart = nextArgStop + 1; nextArgStop = this.requestURI.indexOf('/', argStart); } } catch (final UnsupportedEncodingException e) { throw new StepInternalException(e.getMessage(), e); } // add the last argument if (argStart < this.requestURI.length()) { try { arguments.add(URLDecoder.decode(this.requestURI.substring(argStart), encoding)); } catch (final UnsupportedEncodingException e) { throw new StepInternalException("Unable to decode last argument", e); } } return arguments.toArray(new String[arguments.size()]); } /** * Retrieves the path from the request. * * @param req the request * @return the concatenated request */ private int getPathLength(final HttpServletRequest req) { return req.getServletPath().length() + req.getContextPath().length(); } /** * returns the cache key to resolve from the cache. * * @return the key to the method as expected in the cache. */ public ControllerCacheKey getCacheKey() { final String methodKey; // generate the shorter key final StringBuilder cacheKeyBuffer = new StringBuilder(this.controllerName.length() + this.methodName.length()); cacheKeyBuffer.append(this.controllerName); cacheKeyBuffer.append(this.methodName); cacheKeyBuffer.append(this.args.length); // get the shorter key now methodKey = cacheKeyBuffer.toString(); return new ControllerCacheKey(methodKey, this.requestURI); } /** * Gets the controller name. * * @return the controllerName */ public String getControllerName() { return this.controllerName; } /** * Gets the method name. * * @return the methodName */ public String getMethodName() { return this.methodName; } /** * Gets the args. * * @return the args */ public String[] getArgs() { return this.args; } /** * To string. * * @return an message describibg the step request */ @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append(this.controllerName); sb.append('.'); sb.append(this.methodName); sb.append('('); for (int ii = 0; ii < this.args.length; ii++) { sb.append(this.args.length); sb.append(','); } return sb.toString(); } /** * Checks if request is external. * * @return true, if is external */ public boolean isExternal() { return this.external; } }