org.snowfk.web.RequestContext.java Source code

Java tutorial

Introduction

Here is the source code for org.snowfk.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 org.snowfk.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.ArrayList;
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 org.snowfk.util.HttpRequestUtil;
import org.snowfk.util.MapUtil;
import org.snowfk.util.ObjectUtil;
import org.snowfk.web.auth.Auth;

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 Map<String, WebStateHandle> webStateMap;

    private String resourcePath;
    private String framePath;

    //this is the roo model for the container the "r" map (for the request) and "m" for the model (i.e. WebMap)
    private Map rootModel = new HashMap();
    //this is the HashMap for the "m" model map
    private Map webMap = new HashMap();

    private Map<Class, Object> beanMap = new HashMap<Class, Object>();

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

    //optional
    private Writer writer;
    private String pathInfo;

    //set by AuthService.authRequest
    private Auth<?> auth;

    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() {
        rootModel.put("m", webMap);
    }

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

    public Auth<?> getAuth() {
        return auth;
    }

    public void setAuth(Auth<?> auth) {
        this.auth = auth;
    }

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

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

    // --------- Bean Methods --------- //
    /**
     * Add a bean for a given class.
     * 
     * @param <T>
     * @param bean
     * @return the bean for chainability
     */
    public <T> T addBean(T bean) {
        if (bean != null) {
            beanMap.put(bean.getClass(), bean);
        }
        return bean;
    }

    /**
     * Return a bean by Class.
     * 
     * @param <T>
     * @param beanClass
     * @return
     */
    public <T> T getBean(Class<T> beanClass) {
        return (T) beanMap.get(beanClass);
    }

    // --------- /Bean Methods --------- //    

    /*--------- 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 getParam(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 getParam(String name, Class<T> cls) {
        return getParam(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 getParam(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;
            }

        }
    }

    /**
     * Get the param from the request, and if not found, from the cookie. TODO:
     * need to support the # params as well.
     * 
     * @param <T>
     * @param name
     * @param cls
     * @param defaultValue
     * @return
     */
    public <T> T getOmniParam(String name, Class<T> cls, T defaultValue) {
        T value = getParam(name, cls, null);
        if (value == null) {
            value = getCookie(name, cls, defaultValue);
        }
        return value;
    }

    @SuppressWarnings("unchecked")
    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 String getFramePath() {
        return framePath;
    }

    public void setFramePath(String framePath) {
        this.framePath = framePath;
    }
    // --------- /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);
    }

    /*--------- Writer ---------*/
    public void setWriter(Writer writer) {
        this.writer = writer;
    }

    public Writer getWriter() {
        if (writer != null) {
            return writer;
        } else if (res != null) {
            try {
                return res.getWriter();
            } catch (IOException e) {
                logger.error(e.getMessage());
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }
    }
    /*--------- /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.getNestedValue(webMap, namePath, cls, defaultValue);
    }

    public Map getRootModel() {
        return rootModel;
    }

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

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