Java tutorial
/** * 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; } }