com.britesnow.snow.web.RequestContext.java Source code

Java tutorial

Introduction

Here is the source code for com.britesnow.snow.web.RequestContext.java

Source

/* Copyright 2009 Jeremy Chone - Licensed under the Apache License, Version 2.0
 * http://www.apache.org/licenses/LICENSE-2.0
 */
package com.britesnow.snow.web;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Array;
import java.net.URLDecoder;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletContext;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.britesnow.snow.util.HttpRequestUtil;
import com.britesnow.snow.util.MapUtil;
import com.britesnow.snow.util.ObjectUtil;
import com.britesnow.snow.web.auth.AuthToken;
import com.google.common.base.Throwables;

public class RequestContext {
    static private Logger logger = LoggerFactory.getLogger(RequestContext.class);

    HttpServletRequest req;
    HttpServletResponse res;
    ServletContext servletContext;

    private ServletFileUpload fileUploader;
    private boolean isParamInitialized = false;
    private boolean isMultipart = false;
    private List<?> fileItems;

    // built on demand
    private Map<String, Object> paramMap = null;

    // built on demand
    private Map<String, String> cookieMap;

    private String resourcePath;
    private Deque<String> framePathsStack;

    // this is the HashMap for the "m" model map
    private Map webModel = new HashMap();

    // usually set by the WebController.service
    private WebActionResponse webActionResponse;

    // optional
    private String pathInfo;

    // set by AuthService.authRequest
    private AuthToken<?> authToken;

    public RequestContext(HttpServletRequest req, HttpServletResponse res, ServletContext servletContext,
            ServletFileUpload fileUploader) {
        this.req = req;
        this.res = res;
        this.servletContext = servletContext;
        this.fileUploader = fileUploader;
        init();
    }

    protected void init() {
    }

    /*--------- Auth Methods ---------*/

    public AuthToken<?> getAuthToken() {
        return authToken;
    }

    public void setAuthToken(AuthToken<?> auth) {
        this.authToken = auth;
    }

    @SuppressWarnings("unchecked")
    public <T> T getUser(Class<T> userClass) {
        if (authToken != null && userClass.isInstance(authToken.getUser())) {
            return (T) authToken.getUser();
        } else {
            return null;
        }
    }

    /*--------- /Auth Methods ---------*/

    // --------- Attribute Access --------- //
    public void setAttribute(String name, Object obj) {
        req.setAttribute(name, obj);
    }

    public void removeAttribute(String name) {
        req.removeAttribute(name);
    }

    public Object getAttribute(String name) {
        return req.getAttribute(name);
    }

    public <T> T getAttributeAs(String name, Class<T> cls) {
        return getAttributeAs(name, cls, null);
    }

    public <T> T getAttributeAs(String name, Class<T> cls, T defaultValue) {
        Object value = req.getAttribute(name);
        if (value != null) {
            if (cls.isInstance(value)) {
                return (T) value;

            } else {
                // otherwise, get get the toString and try to get it with the ObjectUtil.
                return ObjectUtil.getValue(value.toString(), cls, defaultValue);
            }
        } else {
            return defaultValue;
        }
    }

    // --------- /Attribute Access --------- //

    /*--------- Param Methods ---------*/

    // mostly for Mock objects
    protected void setParamMap(Map<String, Object> paramMap) {
        this.paramMap = paramMap;
        isParamInitialized = true;
    }

    public Map<String, Object> getParamMap() {
        initParamsIfNeeded();
        return paramMap;
    }

    /**
     * A map of all the param starting with the prefix.
     * 
     * @param prefix
     * @return The key is the string after the prefix, and the value, is the value itself. Return null if no param
     *         started with the prefix.
     */
    public Map<String, Object> getParamMap(String prefix) {
        Map<String, Object> allParamMap = getParamMap();
        Map<String, Object> prefixParamMap = null;
        if (allParamMap != null && prefix != null) {
            for (String paramName : allParamMap.keySet()) {

                if (paramName.startsWith(prefix)) {
                    if (prefixParamMap == null) {
                        prefixParamMap = new HashMap<String, Object>();
                    }
                    Object value = allParamMap.get(paramName);
                    prefixParamMap.put(paramName.substring(prefix.length()), value);
                }
            }

        }
        return prefixParamMap;
    }

    /**
     * @param <T>
     * @param prefix
     * @param cls
     * @return The list of values given a param prefix
     */
    public <T> List<T> getParamMapValues(String prefix, Class<T> cls) {
        List<T> list = new ArrayList<T>();
        Map<String, Object> prefixParamMap = getParamMap(prefix);
        if (prefixParamMap != null) {
            for (Object valueObj : prefixParamMap.values()) {
                if (valueObj instanceof String) {
                    T value = ObjectUtil.getValue((String) valueObj, cls, null);
                    if (value != null) {
                        list.add(value);
                    }
                }

            }
        }
        return list;
    }

    /**
     * Simple method to get the Request value for a given parameter name. Return null if the value is null or empty.
     * 
     * @param name
     *            parameter value
     * @return
     */
    public String getParam(String name) {
        return getParamAs(name, String.class, null);
    }

    /**
     * Return the param value as "cls" class object. If the value is null (or empty string) return null.
     * 
     * @param <T>
     * @param name
     * @param cls
     * @return the http param value if exist or valid, otherwise, null.
     */
    public <T> T getParamAs(String name, Class<T> cls) {
        return getParamAs(name, cls, null);
    }

    /**
     * Return the param value as "cls" class object. If the value is null (or empty string) return the defaultValue.<br>
     * 
     * Note: For the first call, this method will parse the request (in case of a multipart).<br />
     * 
     * <div class="issues"> <strong>Issues</strong>
     * <ul>
     * <li>FIXME: Need to fix the multipart handling. Can be simplified</li>
     * </ul>
     * </div>
     * 
     * @param <T>
     *            Class of the return element
     * @param name
     *            of the parameter
     * @param cls
     *            Class of the return element
     * @param defaultValue
     *            Default value in case of an error or null/empty value
     * @return
     */
    @SuppressWarnings("unchecked")
    public <T> T getParamAs(String name, Class<T> cls, T defaultValue) {
        Map<String, Object> paramMap = getParamMap();
        if (paramMap == null) {
            return defaultValue;
        }
        // if we have a primitive type or array, then, just get the single value and convert it to the appropriate type
        if (ObjectUtil.isPrimitive(cls) || cls.isArray() || cls == FileItem.class || cls.isEnum()) {
            // first, try to get it from the paramMap
            Object valueObject = paramMap.get(name);

            if (isMultipart) {
                // HACK
                // if not found, try to get it from the regular HttpServletRequest
                // (in the case of a multiPart post,
                // HttpServletRequest.getParameter still have the URL params)
                if (valueObject == null) {
                    valueObject = getReq().getParameter(name);
                }
            }

            if (valueObject == null) {
                return defaultValue;
            } else if (valueObject instanceof String) {
                return (T) ObjectUtil.getValue((String) valueObject, cls, defaultValue);
            } else if (valueObject instanceof String[]) {
                return (T) ObjectUtil.getValue((String[]) valueObject, cls, defaultValue);
            } else {
                // hope for the best (should be a fileItem)
                return (T) valueObject;
            }
        }
        // otherwise, if it is not a primitive type, attempt to create the targeted object with the corresponding
        // paramMap
        else {
            Map subParamMap = getParamMap(name + "."); // i.e., "product."
            if (subParamMap != null) {
                try {
                    T value = cls.newInstance();
                    ObjectUtil.populate(value, subParamMap);
                    return value;
                } catch (Exception e) {
                    logger.warn(e.getMessage());
                    return defaultValue;
                }
            } else {
                return defaultValue;
            }

        }
    }

    private void initParamsIfNeeded() {
        if (!isParamInitialized) {
            isMultipart = ServletFileUpload.isMultipartContent(getReq());
            if (isMultipart) {
                try {
                    fileItems = fileUploader.parseRequest(getReq());
                    paramMap = new HashMap<String, Object>();

                    Map<String, Class> paramBaseClasses = new HashMap<String, Class>();
                    boolean hasMultivalues = false;

                    for (Object item : fileItems) {
                        FileItem fileItem = (FileItem) item;
                        String paramName = fileItem.getFieldName();

                        // in case of normal fields, take the string value. otherwise
                        // put the whole file item into the map.
                        Object value;
                        Class paramBaseClass;
                        if (fileItem.isFormField()) {
                            try {
                                value = fileItem.getString("UTF-8");
                            } catch (UnsupportedEncodingException e) {
                                value = fileItem.getString();
                            }
                            paramBaseClass = String.class;
                        } else {
                            value = fileItem;
                            paramBaseClass = FileItem.class;
                        }

                        // make sure that the client isn't calling something that is mixing and
                        // matching parameter types...
                        // todo - could support this as Object arrays or arrays of most specific shared super class.
                        Class prevBaseClass = paramBaseClasses.put(paramName, paramBaseClass);
                        if (prevBaseClass != null && !prevBaseClass.equals(paramBaseClass)) {
                            throw new IllegalArgumentException("parameter " + paramName
                                    + " has mixed parameter types (expected all file or all string)");
                        }

                        // if there is already a value, then, create a list
                        if (paramMap.containsKey(paramName)) {
                            hasMultivalues = true;
                            Object prevValue = paramMap.get(paramName);
                            if (prevValue instanceof List) {
                                ((List) prevValue).add(value);
                            } else {
                                List values = new ArrayList(2);
                                values.add(prevValue);
                                values.add(value);
                                paramMap.put(paramName, values);
                            }
                        } else {
                            paramMap.put(paramName, value);
                        }
                    }

                    // if we had multivalues, need to change the list to arrays
                    if (hasMultivalues) {
                        for (String name : paramMap.keySet()) {
                            Object value = paramMap.get(name);
                            if (value instanceof List) {
                                List valueList = (List) value;
                                Object[] valueArray = (Object[]) Array.newInstance(paramBaseClasses.get(name),
                                        valueList.size());
                                valueList.toArray(valueArray);
                                paramMap.put(name, valueArray);
                            }
                        }
                    }
                } catch (FileUploadException e) {
                    // TODO Auto-generated catch block
                    logger.error(e.getMessage());
                }
            } else {
                paramMap = new HashMap<String, Object>();
                // By the httpServletRequest spect, we can assume the type of
                // the return Map (name and values)
                Map<String, String[]> reqMap = getReq().getParameterMap();
                // now, simplify the map, by replacing single string array to
                // the string itself.
                for (String paramName : reqMap.keySet()) {
                    String[] values = reqMap.get(paramName);
                    if (values.length == 1) {
                        paramMap.put(paramName, values[0]);
                    } else {
                        paramMap.put(paramName, values);
                    }
                }
            }
            isParamInitialized = true;
        }
    }

    /*--------- /Param Methods ---------*/

    /*--------- Cookie Methods ---------*/
    public Map<String, String> getCookieMap() {
        if (cookieMap == null) {
            cookieMap = new HashMap<String, String>();
            Cookie[] cookies = getReq().getCookies();

            if (cookies != null) {
                for (Cookie c : cookies) {
                    String value = c.getValue();
                    try {
                        value = URLDecoder.decode(value, "UTF-8");
                    } catch (Exception e) {
                        // YES, ignore for now. If failed, the raw value will be in the cookie.
                    }
                    cookieMap.put(c.getName(), value);
                }
            }
        }
        return cookieMap;
    }

    public String getCookie(String name) {
        return getCookieMap().get(name);
    }

    public <T> T getCookie(String name, Class<T> cls, T defaultValue) {
        return ObjectUtil.getValue(getCookie(name), cls, defaultValue);
    }

    public void setCookie(String name, Object value) {
        // update the response
        HttpRequestUtil.setCookieValue(getRes(), name, value, true);
        // update the cookieMap (making sure that the template gets what we send to the browser)
        if (value != null) {
            getCookieMap().put(name, value.toString());
        } else {
            removeCookie(name);
        }
    }

    public void removeCookie(String name) {
        // update the response
        HttpRequestUtil.removeCookie(getReq(), getRes(), name);
        // update the cookieMap
        getCookieMap().remove(name);
    }

    /*--------- /Cookie Methods ---------*/

    // --------- WebState Methods --------- //
    /**
     * @param stateContext
     * @return return a WebState for a given uiContext. Never return null.
     */
    /*
     * TODO: needs to implement WebStateHandler public WebStateHandle getWebState(String stateContext){ if (webStateMap
     * == null){ webStateMap = new HashMap<String, WebStateHandle>(); } WebStateHandle webState =
     * webStateMap.get(stateContext); if (webState == null){ webState =
     * getWebStateHandleFactory().constructWebStateHandle(stateContext, this); webStateMap.put(stateContext, webState);
     * } return webState; }
     */
    // --------- /WebState Methods --------- //

    // --------- Paths --------- //

    public String[] getResourcePaths() {
        return splitPath(getResourcePath());
    }

    public String getResourcePathAt(int i) {
        return pathAt(getResourcePaths(), i);
    }

    public <T> T getResourcePathAt(int i, Class<T> cls) {
        return pathAt(getResourcePaths(), i, cls, null);
    }

    public <T> T getResourcePathAt(int i, Class<T> cls, T defaultValue) {
        return pathAt(getResourcePaths(), i, cls, defaultValue);
    }

    public String getResourcePath() {
        return resourcePath;
    }

    public void setResourcePath(String resourcePath) {
        this.resourcePath = resourcePath;
    }

    public void setFramePaths(String[] framePaths) {
        if (framePaths != null) {
            ArrayDeque<String> stack = new ArrayDeque<String>();
            stack.addAll(Arrays.asList(framePaths));
            framePathsStack = stack;
        }
    }

    public String popFramePath() {
        if (framePathsStack != null && !framePathsStack.isEmpty()) {
            return framePathsStack.pop();
        } else {
            return null;
        }
    }

    // --------- /Paths --------- //

    // --------- Utilities for Paths --------- //
    // TODO: probably does not need to be that complicated. Might want to use Guava here.
    static private String[] splitPath(String path) {
        String[] paths = null;
        String[] tmpPaths = path.split("/");
        // remove the first element (always empty since the currentPri
        // start starts with "/")
        if (tmpPaths.length > 1) {
            paths = new String[tmpPaths.length - 1];
            if (tmpPaths.length > 1) {
                System.arraycopy(tmpPaths, 1, paths, 0, paths.length);
            }
        } else {
            paths = new String[0];
        }
        return paths;
    }

    static private String pathAt(String[] paths, int i) {
        if (paths.length > i) {
            return paths[i];
        } else {
            return null;
        }
    }

    public <T> T pathAt(String[] paths, int i, Class<T> cls, T defaultValue) {
        String valueStr = pathAt(paths, i);
        return ObjectUtil.getValue(valueStr, cls, defaultValue);
    }

    // --------- /Utilities for Paths --------- //

    /*--------- Writer ---------*/
    public Writer getWriter() {
        try {
            return res.getWriter();
        } catch (IOException e) {
            throw Throwables.propagate(e);
        }
    }

    /*--------- /Writer ---------*/

    /*--------- RootModel ---------*/
    /**
     * Return the value in the model map (the m.**) with the appropriate type or null if not found.
     * 
     * @see #getModelValue(String, Class, Object)
     */
    public <T> T getModelValue(String namePath, Class<T> cls) {
        return getModelValue(namePath, cls, null);
    }

    /**
     * Return the value in the model map (the m.**) with the appropriate type and fall back value.
     * 
     * @param <T>
     * @param namePath
     *            path deliminated with the ".". Note that the "m." should not be in this namePath.
     * @param cls
     *            The type of the value to be casted to
     * @param defaultValue
     *            The fall back value
     * @return
     */
    public <T> T getModelValue(String namePath, Class<T> cls, T defaultValue) {
        return MapUtil.getDeepValue(webModel, namePath, cls, defaultValue);
    }

    /**
     * @return the Model M (use for the page model "m")
     */
    public Map getWebModel() {
        return webModel;
    }

    /*--------- /RootModel ---------*/

    public WebActionResponse getWebActionResponse() {
        return webActionResponse;
    }

    public void setWebActionResponse(WebActionResponse webActionResponse) {
        this.webActionResponse = webActionResponse;
    }

    /*--------- HttpServlet ---------*/
    public HttpServletRequest getReq() {
        return req;
    }

    public HttpServletResponse getRes() {
        return res;
    }

    public ServletContext getServletContext() {
        return servletContext;
    }

    public String getContextPath() {
        HttpServletRequest request = getReq();
        if (request != null) {
            return request.getContextPath();
        }
        return null;
    }

    public String getPathInfo() {
        if (pathInfo == null) {
            if (req != null) {
                // first try the traditional way
                pathInfo = req.getPathInfo();

                // otherwise build it from the requestURI
                if (pathInfo == null) {
                    // remove the contextPath
                    pathInfo = req.getRequestURI().substring(req.getContextPath().length());

                    try {
                        pathInfo = URLDecoder.decode(pathInfo, "UTF-8");
                    } catch (UnsupportedEncodingException e) {
                        // TODO Auto-generated catch block
                        logger.error(e.getMessage());
                    }
                }
            }
        }
        return pathInfo;
    }

    /*--------- /HttpServlet ---------*/
}