package org.apache.struts.action; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Locale; import javax.servlet.RequestDispatcher; 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.HttpSession; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.struts.config.ActionConfig; import org.apache.struts.config.ExceptionConfig; import org.apache.struts.config.ForwardConfig; import org.apache.struts.config.ModuleConfig; import org.apache.struts.upload.MultipartRequestWrapper; import org.apache.struts.util.MessageResources; import org.apache.struts.util.RequestUtils; public class RequestProcessor { public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info"; public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path"; protected HashMap actions = new HashMap(); protected ModuleConfig appConfig = null; protected ModuleConfig moduleConfig = null; protected static Log log = LogFactory.getLog(RequestProcessor.class); protected ActionServlet servlet = null; public void destroy() { synchronized (this.actions) { Iterator actions = this.actions.values().iterator(); while (actions.hasNext()) { Action action = actions.next(); action.setServlet(null); } this.actions.clear(); } this.servlet = null; } public void init(ActionServlet servlet, ModuleConfig moduleConfig) throws ServletException { synchronized (this.actions) { this.actions.clear(); } this.servlet = servlet; this.appConfig = moduleConfig; this.moduleConfig = moduleConfig; } public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { request = processMultipart(request); String path = processPath(request, response); if (path == null) return; if (log.isDebugEnabled()) log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'"); processLocale(request, response); processContent(request, response); processNoCache(request, response); if (!processPreprocess(request, response)) return; ActionMapping mapping = processMapping(request, response, path); if (mapping == null) return; if (!processRoles(request, response, mapping)) return; ActionForm form = processActionForm(request, response, mapping); processPopulate(request, response, form, mapping); if (!processValidate(request, response, form, mapping)) return; if (!processForward(request, response, mapping)) return; if (!processInclude(request, response, mapping)) return; Action action = processActionCreate(request, response, mapping); if (action == null) return; ActionForward forward = processActionPerform(request, response, action, form, mapping); processForwardConfig(request, response, forward); } protected Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException { String className = mapping.getType(); if (log.isDebugEnabled()) log.debug(" Looking for Action instance for class " + className); Action instance = null; synchronized (this.actions) { instance = (Action)this.actions.get(className); if (instance != null) { if (log.isTraceEnabled()) log.trace(" Returning existing Action instance"); return instance; } if (log.isTraceEnabled()) log.trace(" Creating new Action instance"); try { instance = (Action)RequestUtils.applicationInstance(className); } catch (Exception e) { log.error(getInternal().getMessage("actionCreate", mapping.getPath()), e); response.sendError(500, getInternal().getMessage("actionCreate", mapping.getPath())); return null; } instance.setServlet(this.servlet); this.actions.put(className, instance); } return instance; } protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) { ActionForm instance = RequestUtils.createActionForm(request, mapping, this.moduleConfig, this.servlet); if (instance == null) return null; if (log.isDebugEnabled()) log.debug(" Storing ActionForm bean instance in scope '" + mapping.getScope() + "' under attribute key '" + mapping.getAttribute() + "'"); if ("request".equals(mapping.getScope())) { request.setAttribute(mapping.getAttribute(), instance); } else { HttpSession session = request.getSession(); session.setAttribute(mapping.getAttribute(), instance); } return instance; } protected void processActionForward(HttpServletRequest request, HttpServletResponse response, ActionForward forward) throws IOException, ServletException { processForwardConfig(request, response, forward); } protected void processForwardConfig(HttpServletRequest request, HttpServletResponse response, ForwardConfig forward) throws IOException, ServletException { if (forward == null) return; if (log.isDebugEnabled()) log.debug("processForwardConfig(" + forward + ")"); String forwardPath = forward.getPath(); String uri = null; if (forwardPath.startsWith("/")) { uri = RequestUtils.forwardURL(request, forward); } else { uri = forwardPath; } if (forward.getRedirect()) { if (uri.startsWith("/")) uri = request.getContextPath() + uri; response.sendRedirect(response.encodeRedirectURL(uri)); } else { doForward(uri, request, response); } } protected ActionForward processActionPerform(HttpServletRequest request, HttpServletResponse response, Action action, ActionForm form, ActionMapping mapping) throws IOException, ServletException { try { return action.execute(mapping, form, request, response); } catch (Exception e) { return processException(request, response, e, form, mapping); } } protected void processContent(HttpServletRequest request, HttpServletResponse response) { String contentType = this.moduleConfig.getControllerConfig().getContentType(); if (contentType != null) response.setContentType(contentType); } protected ActionForward processException(HttpServletRequest request, HttpServletResponse response, Exception exception, ActionForm form, ActionMapping mapping) throws IOException, ServletException { ExceptionConfig config = mapping.findException(exception.getClass()); if (config == null) { log.warn(getInternal().getMessage("unhandledException", exception.getClass())); if (exception instanceof IOException) throw (IOException)exception; if (exception instanceof ServletException) throw (ServletException)exception; throw new ServletException(exception); } try { ExceptionHandler handler = (ExceptionHandler)RequestUtils.applicationInstance(config.getHandler()); return handler.execute(exception, config, mapping, form, request, response); } catch (Exception e) { throw new ServletException(e); } } protected boolean processForward(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { String forward = mapping.getForward(); if (forward == null) return true; internalModuleRelativeForward(forward, request, response); return false; } protected boolean processInclude(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { String include = mapping.getInclude(); if (include == null) return true; internalModuleRelativeInclude(include, request, response); return false; } protected void processLocale(HttpServletRequest request, HttpServletResponse response) { if (!this.moduleConfig.getControllerConfig().getLocale()) return; HttpSession session = request.getSession(); if (session.getAttribute("org.apache.struts.action.LOCALE") != null) return; Locale locale = request.getLocale(); if (locale != null) { if (log.isDebugEnabled()) log.debug(" Setting user locale '" + locale + "'"); session.setAttribute("org.apache.struts.action.LOCALE", locale); } } protected ActionMapping processMapping(HttpServletRequest request, HttpServletResponse response, String path) throws IOException { ActionMapping mapping = (ActionMapping)this.moduleConfig.findActionConfig(path); if (mapping != null) { request.setAttribute("org.apache.struts.action.mapping.instance", mapping); return mapping; } ActionConfig[] configs = this.moduleConfig.findActionConfigs(); for (int i = 0; i < configs.length; i++) { if (configs[i].getUnknown()) { mapping = (ActionMapping)configs[i]; request.setAttribute("org.apache.struts.action.mapping.instance", mapping); return mapping; } } log.error(getInternal().getMessage("processInvalid", path)); response.sendError(400, getInternal().getMessage("processInvalid", path)); return null; } protected HttpServletRequest processMultipart(HttpServletRequest request) { if (!"POST".equalsIgnoreCase(request.getMethod())) return request; String contentType = request.getContentType(); if (contentType != null && contentType.startsWith("multipart/form-data")) return (HttpServletRequest)new MultipartRequestWrapper(request); return request; } protected void processNoCache(HttpServletRequest request, HttpServletResponse response) { if (this.moduleConfig.getControllerConfig().getNocache()) { response.setHeader("Pragma", "No-cache"); response.setHeader("Cache-Control", "no-cache"); response.setDateHeader("Expires", 1L); } } protected String processPath(HttpServletRequest request, HttpServletResponse response) throws IOException { String path = null; path = (String)request.getAttribute("javax.servlet.include.path_info"); if (path == null) path = request.getPathInfo(); if (path != null && path.length() > 0) return path; path = (String)request.getAttribute("javax.servlet.include.servlet_path"); if (path == null) path = request.getServletPath(); String prefix = this.moduleConfig.getPrefix(); if (!path.startsWith(prefix)) { log.error(getInternal().getMessage("processPath", request.getRequestURI())); response.sendError(400, getInternal().getMessage("processPath", request.getRequestURI())); return null; } path = path.substring(prefix.length()); int slash = path.lastIndexOf("/"); int period = path.lastIndexOf("."); if (period >= 0 && period > slash) path = path.substring(0, period); return path; } protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws ServletException { if (form == null) return; if (log.isDebugEnabled()) log.debug(" Populating bean properties from this request"); form.setServlet(this.servlet); form.reset(mapping, request); if (mapping.getMultipartClass() != null) request.setAttribute("org.apache.struts.action.mapping.multipartclass", mapping.getMultipartClass()); RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(), request); if (request.getParameter("org.apache.struts.taglib.html.CANCEL") != null || request.getParameter("org.apache.struts.taglib.html.CANCEL.x") != null) request.setAttribute("org.apache.struts.action.CANCEL", Boolean.TRUE); } protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) { return true; } protected boolean processRoles(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException, ServletException { String[] roles = mapping.getRoleNames(); if (roles == null || roles.length < 1) return true; for (int i = 0; i < roles.length; i++) { if (request.isUserInRole(roles[i])) { if (log.isDebugEnabled()) log.debug(" User '" + request.getRemoteUser() + "' has role '" + roles[i] + "', granting access"); return true; } } if (log.isDebugEnabled()) log.debug(" User '" + request.getRemoteUser() + "' does not have any required role, denying access"); response.sendError(400, getInternal().getMessage("notAuthorized", mapping.getPath())); return false; } protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form, ActionMapping mapping) throws IOException, ServletException { if (form == null) return true; if (request.getAttribute("org.apache.struts.action.CANCEL") != null) { if (log.isDebugEnabled()) log.debug(" Cancelled transaction, skipping validation"); return true; } if (!mapping.getValidate()) return true; if (log.isDebugEnabled()) log.debug(" Validating input form properties"); ActionErrors errors = form.validate(mapping, request); if (errors == null || errors.isEmpty()) { if (log.isTraceEnabled()) log.trace(" No errors detected, accepting input"); return true; } if (form.getMultipartRequestHandler() != null) { if (log.isTraceEnabled()) log.trace(" Rolling back multipart request"); form.getMultipartRequestHandler().rollback(); } String input = mapping.getInput(); if (input == null) { if (log.isTraceEnabled()) log.trace(" Validation failed but no input form available"); response.sendError(500, getInternal().getMessage("noInput", mapping.getPath())); return false; } if (log.isDebugEnabled()) log.debug(" Validation failed, returning to '" + input + "'"); request.setAttribute("org.apache.struts.action.ERROR", errors); if (this.moduleConfig.getControllerConfig().getInputForward()) { ForwardConfig forward = mapping.findForward(input); processForwardConfig(request, response, forward); } else { internalModuleRelativeForward(input, request, response); } return false; } protected void internalModuleRelativeForward(String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { uri = this.moduleConfig.getPrefix() + uri; if (log.isDebugEnabled()) log.debug(" Delegating via forward to '" + uri + "'"); doForward(uri, request, response); } protected void internalModuleRelativeInclude(String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { uri = this.moduleConfig.getPrefix() + uri; if (log.isDebugEnabled()) log.debug(" Delegating via include to '" + uri + "'"); doInclude(uri, request, response); } protected void doForward(String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (request instanceof MultipartRequestWrapper) request = ((MultipartRequestWrapper)request).getRequest(); RequestDispatcher rd = getServletContext().getRequestDispatcher(uri); if (rd == null) { response.sendError(500, getInternal().getMessage("requestDispatcher", uri)); return; } rd.forward((ServletRequest)request, (ServletResponse)response); } protected void doInclude(String uri, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { if (request instanceof MultipartRequestWrapper) request = ((MultipartRequestWrapper)request).getRequest(); RequestDispatcher rd = getServletContext().getRequestDispatcher(uri); if (rd == null) { response.sendError(500, getInternal().getMessage("requestDispatcher", uri)); return; } rd.include((ServletRequest)request, (ServletResponse)response); } public int getDebug() { return this.servlet.getDebug(); } protected MessageResources getInternal() { return this.servlet.getInternal(); } protected ServletContext getServletContext() { return this.servlet.getServletContext(); } protected void log(String message) { this.servlet.log(message); } protected void log(String message, Throwable exception) { this.servlet.log(message, exception); } }