org.ajax4jsf.webapp.BaseFilter.java Source code

Java tutorial

Introduction

Here is the source code for org.ajax4jsf.webapp.BaseFilter.java

Source

/**
 * License Agreement.
 *
 * Rich Faces - Natural Ajax for Java Server Faces (JSF)
 *
 * Copyright (C) 2007 Exadel, Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1 as published by the Free Software Foundation.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
 */

package org.ajax4jsf.webapp;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;

import javax.faces.application.ViewHandler;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
import javax.servlet.http.HttpSession;

import org.ajax4jsf.Messages;
import org.ajax4jsf.context.AjaxContext;
import org.ajax4jsf.renderkit.AjaxContainerRenderer;
import org.ajax4jsf.request.MultipartRequest;
import org.ajax4jsf.resource.InternetResourceService;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.richfaces.component.FileUploadConstants;

/**
 * Base class for request processing filters, with convert Htmp content to XML
 * for ajax requests, and serve request to application off-page resources
 * 
 * @author shura (latest modification by $Author: alexsmirnov $)
 * @version $Revision: 1.1.2.1 $ $Date: 2007/01/09 18:58:21 $
 * 
 */
public abstract class BaseFilter implements Filter {

    public static final String AJAX_PUSH_READY = "READY";

    public static final String AJAX_PUSH_STATUS_HEADER = "Ajax-Push-Status";

    public static final String AJAX_PUSH_KEY_HEADER = "Ajax-Push-Key";

    private static final Log log = LogFactory.getLog(BaseFilter.class);

    public static final boolean DEBUG = true;

    private FilterConfig filterConfig;

    private static final String FUNCTION_NAME_PARAMETER = "function";

    private String function = "alert('Data received');JSHttpRequest.dataReady";

    private String attributesNames;

    private boolean rewriteid = false;

    public static final String REWRITEID_PARAMETER = "rewriteid";

    public static final String STYLESHEET_PARAMETER = "xsl";

    public static final String ABSOLUTE_TAGS_PARAMETER = "absolute-attributes";

    // private WebXml webXml;
    // private String xsl;
    // private Templates xslTemplates;
    /**
     * 
     */
    private static final long serialVersionUID = -2295534611886142935L;

    public static final String DATA_PARAMETER = "DATA";

    public static final String DEFAULT_SERVLET_PATH = "/resource";

    public static final String RENDERER_PREFIX = "/renderer";

    public static final String CACHEABLE_PREFIX = "/cache";

    // private static final Pattern rendererPattern =
    // Pattern.compile(RENDERER_PREFIX+"/([^/]+)/([^/]+)/([^/]+)/(.*)");
    // private static final Pattern builderPattern =
    // Pattern.compile(CACHEABLE_PREFIX+"/(.*)");
    public static final String FILTER_PERFORMED = "com.exade.vcp.Filter.done";

    public static final String RESPONSE_WRAPPER_ATTRIBUTE = "com.exade.vcp.Filter.ResponseWrapper";

    protected BaseXMLFilter xmlFilter = null;

    protected InternetResourceService resourceService = null;

    protected PollEventsManager eventsManager;

    /**
     * Flag indicating whether a temporary file should be used to cache the
     * uploaded file
     */
    private boolean createTempFiles = false;

    /**
     * The maximum size of a file upload request. 0 means no limit.
     */
    private int maxRequestSize = 0;

    /** Multipart request start */
    public static final String MULTIPART = "multipart/";

    /**
     * Request parameter that allow to send HTTP error instead of html message
     */
    public static final String SEND_HTTP_ERROR = "_richfaces_send_http_error";

    /**
     * Initialize the filter.
     */
    public void init(FilterConfig config) throws ServletException {
        if (log.isDebugEnabled()) {
            log.debug("Init ajax4jsf filter with nane: " + config.getFilterName());
            Enumeration<String> parameterNames = config.getInitParameterNames();
            StringBuffer parameters = new StringBuffer("Init parameters :\n");
            while (parameterNames.hasMoreElements()) {
                String name = parameterNames.nextElement();
                parameters.append(name).append(" : '").append(config.getInitParameter(name)).append('\n');
            }
            log.debug(parameters);
            // log.debug("Stack Trace", new Exception());
        }
        // Save config
        filterConfig = config;
        setFunction((String) nz(filterConfig.getInitParameter(FUNCTION_NAME_PARAMETER), getFunction()));
        setAttributesNames(filterConfig.getInitParameter(ABSOLUTE_TAGS_PARAMETER));
        xmlFilter.init(config);
        if ("true".equalsIgnoreCase(filterConfig.getInitParameter(REWRITEID_PARAMETER))) {
            this.setRewriteid(true);
        }
        resourceService = new InternetResourceService();
        // Caching initialization.
        resourceService.init(filterConfig);
        eventsManager = new PollEventsManager();
        eventsManager.init(filterConfig.getServletContext());

        String param = filterConfig.getInitParameter("createTempFiles");
        if (param != null) {
            this.createTempFiles = Boolean.parseBoolean(param);
        } else {
            this.createTempFiles = true;
        }
        param = filterConfig.getInitParameter("maxRequestSize");
        if (param != null) {
            this.maxRequestSize = Integer.parseInt(param);
        }
    }

    private static final Pattern AMPERSAND = Pattern.compile("&+");

    private Map<String, String> parseQueryString(String queryString) {
        if (queryString != null) {
            Map<String, String> parameters = new HashMap<String, String>();

            String[] nvPairs = AMPERSAND.split(queryString);
            for (String nvPair : nvPairs) {
                if (nvPair.length() == 0) {
                    continue;
                }

                int eqIdx = nvPair.indexOf('=');
                if (eqIdx >= 0) {
                    try {
                        String name = URLDecoder.decode(nvPair.substring(0, eqIdx), "UTF-8");
                        if (!parameters.containsKey(name)) {
                            String value = URLDecoder.decode(nvPair.substring(eqIdx + 1), "UTF-8");

                            parameters.put(name, value);
                        }
                    } catch (UnsupportedEncodingException e) {
                        //log warning and skip this parameter
                        log.warn(e.getLocalizedMessage(), e);
                    }
                }
            }

            return parameters;
        } else {

            return Collections.EMPTY_MAP;
        }
    }

    private boolean isMultipartRequest(HttpServletRequest request) {
        if (!"post".equals(request.getMethod().toLowerCase())) {
            return false;
        }

        String contentType = request.getContentType();
        if (contentType == null) {
            return false;
        }

        if (contentType.toLowerCase().startsWith(MULTIPART)) {
            return true;
        }

        return false;
    }

    private boolean isFileSizeRestricted(ServletRequest request, int maxSize) {
        if (maxSize != 0 && request.getContentLength() > maxSize) {
            return true;
        }
        return false;
    }

    private boolean checkFileCount(HttpServletRequest request, String idParameter) {
        HttpSession session = request.getSession(false);

        if (session != null) {
            Map<String, Integer> map = (Map<String, Integer>) session
                    .getAttribute(FileUploadConstants.UPLOADED_COUNTER);

            if (map != null) {
                String id = idParameter;
                if (id != null) {
                    Integer i = map.get(id);
                    if (i != null && i == 0) {
                        return false;
                    }
                }
            }
        }

        return true;
    }

    private void printResponse(ServletResponse response, String message) throws IOException {
        HttpServletResponse httpResponse = (HttpServletResponse) response;
        httpResponse.setStatus(HttpServletResponse.SC_OK);
        httpResponse.setContentType("text/html");
        PrintWriter writer = httpResponse.getWriter();
        writer.write(message);
        writer.close();
    }

    protected void handleRequest(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
            throws IOException, ServletException {

        // check ajax request parameter
        // TODO - check for JSF page.
        if (true) {
            if (log.isDebugEnabled()) {
                log.debug(Messages.getMessage(Messages.FILTER_XML_OUTPUT));
            }

            // Execute the rest of the filter chain, including the
            // JSP
            xmlFilter.doXmlFilter(chain, request, response);
        } else {
            // normal request, execute chain ...
            if (log.isDebugEnabled()) {
                log.debug(Messages.getMessage(Messages.FILTER_NO_XML_CHAIN));
            }
            chain.doFilter(request, response);

        }

    }

    /**
     * Method catches upload files request. Request parameter
     * <b>org.ajax4jsf.Filter.UPLOAD_FILES_ID</b> indicates if request
     * generated by rich-upload component. If it was detected custom parsing
     * request should be done. Processing information about percent of
     * completion and file size will be put into session scope. 
     * 
     * @param request
     * @param response
     * @return 
     * @throws IOException 
     * @throws ServletException 
     */
    protected void processUploadsAndHandleRequest(HttpServletRequest request, HttpServletResponse response,
            FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;

        Map<String, String> queryParamMap = parseQueryString(httpRequest.getQueryString());
        String uid = queryParamMap.get(FileUploadConstants.UPLOAD_FILES_ID);

        if (uid != null) {

            if (isMultipartRequest(httpRequest)) {
                MultipartRequest multipartRequest = new MultipartRequest(httpRequest, createTempFiles,
                        maxRequestSize, uid);

                Object oldAttributeValue = httpRequest
                        .getAttribute(FileUploadConstants.FILE_UPLOAD_REQUEST_ATTRIBUTE_NAME);
                httpRequest.setAttribute(FileUploadConstants.FILE_UPLOAD_REQUEST_ATTRIBUTE_NAME, multipartRequest);

                try {
                    if (isFileSizeRestricted(request, maxRequestSize)) {

                        boolean sendError = Boolean.parseBoolean(queryParamMap.get(SEND_HTTP_ERROR));
                        if (sendError) {
                            response.sendError(HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE);
                            System.err.println("ERROR " + HttpServletResponse.SC_REQUEST_ENTITY_TOO_LARGE
                                    + "request entity is larger than the server is willing or able to process.");
                            return;
                        } else {
                            printResponse(response, "<html id=\"_richfaces_file_upload_size_restricted\"></html>");
                        }

                    } else if (!checkFileCount(httpRequest, queryParamMap.get("id"))) {
                        printResponse(response, "<html id=\"_richfaces_file_upload_forbidden\"></html>");
                    } else {

                        handleRequest(multipartRequest, multipartRequest.isFormUpload() ? response
                                : new HttpServletResponseWrapper((HttpServletResponse) response) {
                                    @Override
                                    public void setContentType(String type) {
                                        super.setContentType(BaseXMLFilter.TEXT_HTML + ";charset=UTF-8");
                                    }
                                }, chain);

                        if (!multipartRequest.isDone()) {
                            printResponse(response, "<html id=\"_richfaces_file_upload_stopped\"></html>");
                        }
                    }

                } finally {
                    httpRequest.setAttribute(FileUploadConstants.FILE_UPLOAD_REQUEST_ATTRIBUTE_NAME,
                            oldAttributeValue);
                    multipartRequest.clearRequestData();
                }
            } else {
                handleRequest(request, response, chain);
            }
        } else {
            handleRequest(request, response, chain);
        }
    }

    /**
     * @param httpServletRequest
     * @throws UnsupportedEncodingException
     */
    protected void setupRequestEncoding(HttpServletRequest httpServletRequest) throws UnsupportedEncodingException {
        String contentType = httpServletRequest.getHeader("Content-Type");

        String characterEncoding = lookupCharacterEncoding(contentType);

        if (characterEncoding == null) {
            HttpSession session = httpServletRequest.getSession(false);

            if (session != null) {
                characterEncoding = (String) session.getAttribute(ViewHandler.CHARACTER_ENCODING_KEY);
            }

            if (characterEncoding != null) {
                httpServletRequest.setCharacterEncoding(characterEncoding);
            }
        }
    }

    /**
     * Detect request encoding from Content-Type header
     * 
     * @param contentType
     * @return - charset, if present.
     */
    private String lookupCharacterEncoding(String contentType) {
        String characterEncoding = null;

        if (contentType != null) {
            int charsetFind = contentType.indexOf("charset=");
            if (charsetFind != -1) {
                if (charsetFind == 0) {
                    // charset at beginning of Content-Type, curious
                    characterEncoding = contentType.substring(8);
                } else {
                    char charBefore = contentType.charAt(charsetFind - 1);
                    if (charBefore == ';' || Character.isWhitespace(charBefore)) {
                        // Correct charset after mime type
                        characterEncoding = contentType.substring(charsetFind + 8);
                    }
                }
                if (log.isDebugEnabled())
                    log.debug(Messages.getMessage(Messages.CONTENT_TYPE_ENCODING, characterEncoding));
            } else {
                if (log.isDebugEnabled())
                    log.debug(Messages.getMessage(Messages.CONTENT_TYPE_NO_ENCODING, contentType));
            }
        }
        return characterEncoding;
    }

    /**
     * @param initParameter
     * @param function2
     * @return
     */
    private Object nz(Object param, Object def) {
        return param != null ? param : def;
    }

    /**
     * Execute the filter.
     */
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        long startTimeMills = 0;
        // Detect case of request - normal, AJAX, AJAX - JavaScript
        // TODO - detect first processing in filter.
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        HttpServletResponse httpServletResponse = (HttpServletResponse) response;
        if (log.isDebugEnabled()) {
            startTimeMills = System.currentTimeMillis();
            log.debug(Messages.getMessage(Messages.FILTER_START_INFO, new Date(startTimeMills),
                    httpServletRequest.getRequestURI()));
        }

        if (request.getAttribute(FILTER_PERFORMED) != Boolean.TRUE) {
            // mark - and not processing same request twice.
            try {
                request.setAttribute(FILTER_PERFORMED, Boolean.TRUE);
                String ajaxPushHeader = httpServletRequest.getHeader(AJAX_PUSH_KEY_HEADER);
                // check for a push check request.
                if (httpServletRequest.getMethod().equals("HEAD") && null != ajaxPushHeader) {
                    PushEventsCounter listener = eventsManager.getListener(ajaxPushHeader);
                    // To avoid XmlHttpRequest parsing exceptions.
                    httpServletResponse.setContentType("text/plain");
                    if (listener.isPerformed()) {
                        listener.processed();
                        httpServletResponse.setStatus(HttpServletResponse.SC_OK);
                        httpServletResponse.setHeader(AJAX_PUSH_STATUS_HEADER, AJAX_PUSH_READY);
                        if (log.isDebugEnabled()) {
                            log.debug("Occurs event for a id " + ajaxPushHeader);
                        }
                    } else {
                        // Response code - 'No content'
                        httpServletResponse.setStatus(HttpServletResponse.SC_ACCEPTED);
                        if (log.isDebugEnabled()) {
                            log.debug("No event for a id " + ajaxPushHeader);
                        }
                    }
                    httpServletResponse.setContentLength(0);
                } else
                // check for resource request
                if (!getResourceService().serviceResource(httpServletRequest, httpServletResponse)) {
                    // Not request to resource - perform filtering.
                    // first stage - detect/set encoding of request. Same as in
                    // Myfaces External Context.
                    setupRequestEncoding(httpServletRequest);

                    processUploadsAndHandleRequest(httpServletRequest, httpServletResponse, chain);
                }
            } finally {
                // Remove filter marker from response, to enable sequence calls ( for example, forward to error page )
                request.removeAttribute(FILTER_PERFORMED);
                Object ajaxContext = request.getAttribute(AjaxContext.AJAX_CONTEXT_KEY);
                if (null != ajaxContext && ajaxContext instanceof AjaxContext) {
                    ((AjaxContext) ajaxContext).release();
                    request.removeAttribute(AjaxContext.AJAX_CONTEXT_KEY);
                }
            }
        } else {
            if (log.isDebugEnabled()) {
                log.debug(Messages.getMessage(Messages.FILTER_NO_XML_CHAIN_2));
            }
            chain.doFilter(request, response);

        }
        if (log.isDebugEnabled()) {
            startTimeMills = System.currentTimeMillis() - startTimeMills;
            log.debug(Messages.getMessage(Messages.FILTER_STOP_INFO, "" + startTimeMills,
                    httpServletRequest.getRequestURI()));
        }
    }

    /**
     * @param request
     * @return
     */
    protected boolean isAjaxRequest(ServletRequest request) {
        try {
            return null != request.getParameter(AjaxContainerRenderer.AJAX_PARAMETER_NAME);
        } catch (Exception e) {
            // OCJ 10 - throw exception for static resources.
            return false;
        }
    }

    /**
     * Destroy the filter.
     */
    public void destroy() {
    }

    /**
     * @return Returns the servletContext.
     */
    ServletContext getServletContext() {
        return filterConfig.getServletContext();
    }

    /**
     * @return the resourceService
     * @throws ServletException
     */
    protected synchronized InternetResourceService getResourceService() throws ServletException {
        // if (resourceService == null) {
        // resourceService = new InternetResourceService();
        // // Caching initialization.
        // resourceService.init(filterConfig);
        //
        // }
        return resourceService;
    }

    /**
     * @param function
     *            The function to set.
     */
    protected void setFunction(String function) {
        this.function = function;
    }

    /**
     * @return Returns the function.
     */
    protected String getFunction() {
        return function;
    }

    /**
     * @param rewriteid
     *            The rewriteid to set.
     */
    protected void setRewriteid(boolean rewriteid) {
        this.rewriteid = rewriteid;
    }

    /**
     * @return Returns the rewriteid.
     */
    protected boolean isRewriteid() {
        return rewriteid;
    }

    /**
     * @param attributesNames
     *            The attributesNames to set.
     */
    protected void setAttributesNames(String attributesNames) {
        this.attributesNames = attributesNames;
    }

    /**
     * @return Returns the attributesNames.
     */
    protected String getAttributesNames() {
        return attributesNames;
    }
}