Package org.g4studio.core.mvc.xstruts.action

Source Code of org.g4studio.core.mvc.xstruts.action.RequestProcessor

package org.g4studio.core.mvc.xstruts.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.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.g4studio.core.mvc.xstruts.Globals;
import org.g4studio.core.mvc.xstruts.config.ActionConfig;
import org.g4studio.core.mvc.xstruts.config.ExceptionConfig;
import org.g4studio.core.mvc.xstruts.config.ForwardConfig;
import org.g4studio.core.mvc.xstruts.config.ModuleConfig;
import org.g4studio.core.mvc.xstruts.upload.MultipartRequestWrapper;
import org.g4studio.core.mvc.xstruts.util.MessageResources;
import org.g4studio.core.mvc.xstruts.util.RequestUtils;

/**
* <p>
* <strong>RequestProcessor</strong> contains the processing logic that the
* {@link ActionServlet} performs as it receives each servlet request from the
* container. You can customize the request processing behavior by subclassing
* this class and overriding the method(s) whose behavior you are interested in
* changing.
* </p>
*
* @version $Rev: 421119 $ $Date: 2006-07-11 21:49:11 -0700 (Tue, 11 Jul 2006) $
* @since Struts 1.1
*/
public class RequestProcessor {
  // ----------------------------------------------------- Manifest Constants

  /**
   * <p>
   * The request attribute under which the path information is stored for
   * processing during a <code>RequestDispatcher.include</code> call.
   * </p>
   */
  public static final String INCLUDE_PATH_INFO = "javax.servlet.include.path_info";

  /**
   * <p>
   * The request attribute under which the servlet path information is stored
   * for processing during a <code>RequestDispatcher.include</code> call.
   * </p>
   */
  public static final String INCLUDE_SERVLET_PATH = "javax.servlet.include.servlet_path";

  /**
   * <p>
   * Commons Logging instance.
   * </p>
   */
  protected static Log log = LogFactory.getLog(RequestProcessor.class);

  // ----------------------------------------------------- Instance Variables

  /**
   * <p>
   * The set of <code>Action</code> instances that have been created and
   * initialized, keyed by the fully qualified Java class name of the
   * <code>Action</code> class.
   * </p>
   */
  protected HashMap actions = new HashMap();

  /**
   * <p>
   * The <code>ModuleConfiguration</code> with which we are associated.
   * </p>
   */
  protected ModuleConfig moduleConfig = null;

  /**
   * <p>
   * The servlet with which we are associated.
   * </p>
   */
  protected ActionServlet servlet = null;

  // --------------------------------------------------------- Public Methods

  /**
   * <p>
   * Clean up in preparation for a shutdown of this application.
   * </p>
   */
  public void destroy() {
    synchronized (this.actions) {
      Iterator actions = this.actions.values().iterator();

      while (actions.hasNext()) {
        Action action = (Action) actions.next();

        action.setServlet(null);
      }

      this.actions.clear();
    }

    this.servlet = null;
  }

  /**
   * <p>
   * Initialize this request processor instance.
   * </p>
   *
   * @param servlet
   *            The ActionServlet we are associated with
   * @param moduleConfig
   *            The ModuleConfig we are associated with.
   * @throws ServletException
   *             If an error occor during initialization
   */
  public void init(ActionServlet servlet, ModuleConfig moduleConfig) throws ServletException {
    synchronized (actions) {
      actions.clear();
    }

    this.servlet = servlet;
    this.moduleConfig = moduleConfig;
  }

  /**
   * <p>
   * Process an <code>HttpServletRequest</code> and create the corresponding
   * <code>HttpServletResponse</code> or dispatch to another resource.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a processing exception occurs
   */
  public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    // Wrap multipart requests with a special wrapper
    request = processMultipart(request);

    // Identify the path component we will use to select a mapping
    String path = processPath(request, response);

    if (path == null) {
      return;
    }

    if (log.isDebugEnabled()) {
      log.debug("Processing a '" + request.getMethod() + "' for path '" + path + "'");
    }

    // Select a Locale for the current user if requested
    processLocale(request, response);

    // Set the content type and no-caching headers if requested
    processContent(request, response);
    processNoCache(request, response);

    // General purpose preprocessing hook
    if (!processPreprocess(request, response)) {
      return;
    }

    this.processCachedMessages(request, response);

    // Identify the mapping for this request
    ActionMapping mapping = processMapping(request, response, path);

    if (mapping == null) {
      return;
    }

    // Check for any role required to perform this action
    if (!processRoles(request, response, mapping)) {
      return;
    }

    // Process any ActionForm bean related to this request
    ActionForm form = processActionForm(request, response, mapping);

    processPopulate(request, response, form, mapping);

    // Validate any fields of the ActionForm bean, if applicable
    try {
      if (!processValidate(request, response, form, mapping)) {
        return;
      }
    } catch (InvalidCancelException e) {
      ActionForward forward = processException(request, response, e, form, mapping);
      processForwardConfig(request, response, forward);
      return;
    } catch (IOException e) {
      throw e;
    } catch (ServletException e) {
      throw e;
    }

    // Process a forward or include specified by this mapping
    if (!processForward(request, response, mapping)) {
      return;
    }

    if (!processInclude(request, response, mapping)) {
      return;
    }

    // Create or acquire the Action instance to process this request
    Action action = processActionCreate(request, response, mapping);

    if (action == null) {
      return;
    }

    // Call the Action instance itself
    ActionForward forward = processActionPerform(request, response, action, form, mapping);

    // Process the returned ActionForward instance
    processForwardConfig(request, response, forward);
  }

  // ----------------------------------------------------- Processing Methods

  /**
   * <p>
   * Return an <code>Action</code> instance that will be used to process the
   * current request, creating a new one if necessary.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param mapping
   *            The mapping we are using
   * @return An <code>Action</code> instance that will be used to process the
   *         current request.
   * @throws IOException
   *             if an input/output error occurs
   */
  protected Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
      throws IOException {
    // Acquire the Action instance we will be using (if there is one)
    String className = mapping.getType();

    if (log.isDebugEnabled()) {
      log.debug(" Looking for Action instance for class " + className);
    }

    // If there were a mapping property indicating whether
    // an Action were a singleton or not ([true]),
    // could we just instantiate and return a new instance here?
    Action instance;

    synchronized (actions) {
      // Return any existing Action instance of this class
      instance = (Action) actions.get(className);

      if (instance != null) {
        if (log.isTraceEnabled()) {
          log.trace("  Returning existing Action instance");
        }

        return (instance);
      }

      // Create and return a new Action instance
      if (log.isTraceEnabled()) {
        log.trace("  Creating new Action instance");
      }

      try {
        instance = (Action) RequestUtils.applicationInstance(className);

        // Maybe we should propagate this exception
        // instead of returning null.
      } catch (Exception e) {
        log.error(getInternal().getMessage("actionCreate", mapping.getPath()), e);

        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
            getInternal().getMessage("actionCreate", mapping.getPath()));

        return (null);
      }

      actions.put(className, instance);
    }

    if (instance.getServlet() == null) {
      instance.setServlet(this.servlet);
    }

    return (instance);
  }

  /**
   * <p>
   * Retrieve and return the <code>ActionForm</code> associated with this
   * mapping, creating and retaining one if necessary. If there is no
   * <code>ActionForm</code> associated with this mapping, return
   * <code>null</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param mapping
   *            The mapping we are using
   * @return The <code>ActionForm</code> associated with this mapping.
   */
  protected ActionForm processActionForm(HttpServletRequest request, HttpServletResponse response,
      ActionMapping mapping) {
    // Create (if necessary) a form bean to use
    ActionForm instance = RequestUtils.createActionForm(request, mapping, moduleConfig, servlet);

    if (instance == null) {
      return (null);
    }

    // Store the new instance in the appropriate scope
    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);
  }

  /**
   * <p>
   * Forward or redirect to the specified destination, by the specified
   * mechanism. This method uses a <code>ForwardConfig</code> object instead
   * an <code>ActionForward</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param forward
   *            The ForwardConfig controlling where we go next
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   */
  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;

    // paths not starting with / should be passed through without any
    // processing (ie. they're absolute)
    if (forwardPath.startsWith("/")) {
      // get module relative uri
      uri = RequestUtils.forwardURL(request, forward, null);
    } else {
      uri = forwardPath;
    }

    if (forward.getRedirect()) {
      // only prepend context path for relative uri
      if (uri.startsWith("/")) {
        uri = request.getContextPath() + uri;
      }

      response.sendRedirect(response.encodeRedirectURL(uri));
    } else {
      doForward(uri, request, response);
    }
  }

  // :FIXME: if Action.execute throws Exception, and Action.process has been
  // removed, should the process* methods still throw IOException,
  // ServletException?

  /**
   * <P>
   * Ask the specified <code>Action</code> instance to handle this request.
   * Return the <code>ActionForward</code> instance (if any) returned by the
   * called <code>Action</code> for further processing.
   * </P>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param action
   *            The Action instance to be used
   * @param form
   *            The ActionForm instance to pass to this Action
   * @param mapping
   *            The ActionMapping instance to pass to this Action
   * @return The <code>ActionForward</code> instance (if any) returned by the
   *         called <code>Action</code>.
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   */
  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));
    }
  }

  /**
   * <p>
   * Removes any <code>ActionMessages</code> object stored in the session
   * under <code>Globals.MESSAGE_KEY</code> and <code>Globals.ERROR_KEY</code>
   * if the messages' <code>isAccessed</code> method returns true. This allows
   * messages to be stored in the session, display one time, and be released
   * here.
   * </p>
   *
   * @param request
   *            The servlet request we are processing.
   * @param response
   *            The servlet response we are creating.
   * @since Struts 1.2
   */
  protected void processCachedMessages(HttpServletRequest request, HttpServletResponse response) {
    HttpSession session = request.getSession(false);

    if (session == null) {
      return;
    }

    // Remove messages as needed
    ActionMessages messages = (ActionMessages) session.getAttribute(Globals.MESSAGE_KEY);

    if (messages != null) {
      if (messages.isAccessed()) {
        session.removeAttribute(Globals.MESSAGE_KEY);
      }
    }

    // Remove error messages as needed
    messages = (ActionMessages) session.getAttribute(Globals.ERROR_KEY);

    if (messages != null) {
      if (messages.isAccessed()) {
        session.removeAttribute(Globals.ERROR_KEY);
      }
    }
  }

  /**
   * <p>
   * Set the default content type (with optional character encoding) for all
   * responses if requested. <strong>NOTE</strong> - This header will be
   * overridden automatically if a <code>RequestDispatcher.forward</code> call
   * is ultimately invoked.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   */
  protected void processContent(HttpServletRequest request, HttpServletResponse response) {
    String contentType = moduleConfig.getControllerConfig().getContentType();

    if (contentType != null) {
      response.setContentType(contentType);
    }
  }

  /**
   * <p>
   * Ask our exception handler to handle the exception. Return the
   * <code>ActionForward</code> instance (if any) returned by the called
   * <code>ExceptionHandler</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are processing
   * @param exception
   *            The exception being handled
   * @param form
   *            The ActionForm we are processing
   * @param mapping
   *            The ActionMapping we are using
   * @return The <code>ActionForward</code> instance (if any) returned by the
   *         called <code>ExceptionHandler</code>.
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   */
  protected ActionForward processException(HttpServletRequest request, HttpServletResponse response,
      Exception exception, ActionForm form, ActionMapping mapping) throws IOException, ServletException {
    // Is there a defined handler for this exception?
    ExceptionConfig config = mapping.findException(exception.getClass());

    if (config == null) {
      log.warn(getInternal().getMessage("unhandledException", exception.getClass()));

      if (exception instanceof IOException) {
        throw (IOException) exception;
      } else if (exception instanceof ServletException) {
        throw (ServletException) exception;
      } else {
        throw new ServletException(exception);
      }
    }

    // Use the configured exception handling
    try {
      ExceptionHandler handler = (ExceptionHandler) RequestUtils.applicationInstance(config.getHandler());

      return (handler.execute(exception, config, mapping, form, request, response));
    } catch (Exception e) {
      throw new ServletException(e);
    }
  }

  /**
   * <p>
   * Process a forward requested by this mapping (if any). Return
   * <code>true</code> if standard processing should continue, or
   * <code>false</code> if we have already handled this request.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param mapping
   *            The ActionMapping we are using
   * @return <code>true</code> to continue normal processing;
   *         <code>false</code> if a response has been created.
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   */
  protected boolean processForward(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
      throws IOException, ServletException {
    // Are we going to processing this request?
    String forward = mapping.getForward();

    if (forward == null) {
      return (true);
    }

    internalModuleRelativeForward(forward, request, response);

    return (false);
  }

  /**
   * <p>
   * Process an include requested by this mapping (if any). Return
   * <code>true</code> if standard processing should continue, or
   * <code>false</code> if we have already handled this request.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param mapping
   *            The ActionMapping we are using
   * @return <code>true</code> to continue normal processing;
   *         <code>false</code> if a response has been created.
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if thrown by invoked methods
   */
  protected boolean processInclude(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
      throws IOException, ServletException {
    // Are we going to processing this request?
    String include = mapping.getInclude();

    if (include == null) {
      return (true);
    }

    internalModuleRelativeInclude(include, request, response);

    return (false);
  }

  /**
   * <p>
   * Automatically select a <code>Locale</code> for the current user, if
   * requested. <strong>NOTE</strong> - configuring Locale selection will
   * trigger the creation of a new <code>HttpSession</code> if necessary.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   */
  protected void processLocale(HttpServletRequest request, HttpServletResponse response) {
    // Are we configured to select the Locale automatically?
    if (!moduleConfig.getControllerConfig().getLocale()) {
      return;
    }

    // Has a Locale already been selected?
    HttpSession session = request.getSession();

    if (session.getAttribute(Globals.LOCALE_KEY) != null) {
      return;
    }

    // Use the Locale returned by the servlet container (if any)
    Locale locale = request.getLocale();

    if (locale != null) {
      if (log.isDebugEnabled()) {
        log.debug(" Setting user locale '" + locale + "'");
      }

      session.setAttribute(Globals.LOCALE_KEY, locale);
    }
  }

  /**
   * <p>
   * Select the mapping used to process the selection path for this request.
   * If no mapping can be identified, create an error response and return
   * <code>null</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param path
   *            The portion of the request URI for selecting a mapping
   * @return The mapping used to process the selection path for this request.
   * @throws IOException
   *             if an input/output error occurs
   */
  protected ActionMapping processMapping(HttpServletRequest request, HttpServletResponse response, String path)
      throws IOException {
    // Is there a mapping for this path?
    ActionMapping mapping = (ActionMapping) moduleConfig.findActionConfig(path);

    // If a mapping is found, put it in the request and return it
    if (mapping != null) {
      request.setAttribute(Globals.MAPPING_KEY, mapping);

      return (mapping);
    }

    // Locate the mapping for unknown paths (if any)
    ActionConfig[] configs = moduleConfig.findActionConfigs();

    for (int i = 0; i < configs.length; i++) {
      if (configs[i].getUnknown()) {
        mapping = (ActionMapping) configs[i];
        request.setAttribute(Globals.MAPPING_KEY, mapping);

        return (mapping);
      }
    }

    // No mapping can be found to process this request
    String msg = getInternal().getMessage("processInvalid");

    log.error(msg + " " + path);
    response.sendError(HttpServletResponse.SC_NOT_FOUND, msg);

    return null;
  }

  /**
   * <p>
   * If this is a multipart request, wrap it with a special wrapper.
   * Otherwise, return the request unchanged.
   * </p>
   *
   * @param request
   *            The HttpServletRequest we are processing
   * @return A wrapped request, if the request is multipart; otherwise the
   *         original request.
   */
  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 (new MultipartRequestWrapper(request));
    } else {
      return (request);
    }
  }

  /**
   * <p>
   * Set the no-cache headers for all responses, if requested.
   * <strong>NOTE</strong> - This header will be overridden automatically if a
   * <code>RequestDispatcher.forward</code> call is ultimately invoked.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   */
  protected void processNoCache(HttpServletRequest request, HttpServletResponse response) {
    if (moduleConfig.getControllerConfig().getNocache()) {
      response.setHeader("Pragma", "No-cache");
      response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
      response.setDateHeader("Expires", 1);
    }
  }

  /**
   * <p>
   * Identify and return the path component (from the request URI) that we
   * will use to select an <code>ActionMapping</code> with which to dispatch.
   * If no such path can be identified, create an error response and return
   * <code>null</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @return The path that will be used to select an action mapping.
   * @throws IOException
   *             if an input/output error occurs
   */
  protected String processPath(HttpServletRequest request, HttpServletResponse response) throws IOException {
    String path;

    // For prefix matching, match on the path info (if any)
    path = (String) request.getAttribute(INCLUDE_PATH_INFO);

    if (path == null) {
      path = request.getPathInfo();
    }

    if ((path != null) && (path.length() > 0)) {
      return (path);
    }

    // For extension matching, strip the module prefix and extension
    path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);

    if (path == null) {
      path = request.getServletPath();
    }

    String prefix = moduleConfig.getPrefix();

    if (!path.startsWith(prefix)) {
      String msg = getInternal().getMessage("processPath");

      log.error(msg + " " + request.getRequestURI());
      response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);

      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);
  }

  /**
   * <p>
   * Populate the properties of the specified <code>ActionForm</code> instance
   * from the request parameters included with this request. In addition,
   * request attribute <code>Globals.CANCEL_KEY</code> will be set if the
   * request was submitted with a button created by <code>CancelTag</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param form
   *            The ActionForm instance we are populating
   * @param mapping
   *            The ActionMapping we are using
   * @throws ServletException
   *             if thrown by RequestUtils.populate()
   */
  protected void processPopulate(HttpServletRequest request, HttpServletResponse response, ActionForm form,
      ActionMapping mapping) throws ServletException {
    if (form == null) {
      return;
    }

    // Populate the bean properties of this ActionForm instance
    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(Globals.MULTIPART_KEY, mapping.getMultipartClass());
    }

    RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(), request);

    // Set the cancellation request attribute if appropriate
    if ((request.getParameter(Globals.CANCEL_PROPERTY) != null)
        || (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) {
      request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
    }
  }

  /**
   * <p>
   * General-purpose preprocessing hook that can be overridden as required by
   * subclasses. Return <code>true</code> if you want standard processing to
   * continue, or <code>false</code> if the response has already been
   * completed. The default implementation does nothing.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @return <code>true</code> to continue normal processing;
   *         <code>false</code> if a response has been created.
   */
  protected boolean processPreprocess(HttpServletRequest request, HttpServletResponse response) {
    return (true);
  }

  /**
   * <p>
   * If this action is protected by security roles, make sure that the current
   * user possesses at least one of them. Return <code>true</code> to continue
   * normal processing, or <code>false</code> if an appropriate response has
   * been created and processing should terminate.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param mapping
   *            The mapping we are using
   * @return <code>true</code> to continue normal processing;
   *         <code>false</code> if a response has been created.
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   */
  protected boolean processRoles(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
      throws IOException, ServletException {
    // Is this action protected by role requirements?
    String[] roles = mapping.getRoleNames();

    if ((roles == null) || (roles.length < 1)) {
      return (true);
    }

    // Check the current user against the list of required roles
    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);
      }
    }

    // The current user is not authorized for this action
    if (log.isDebugEnabled()) {
      log.debug(" User '" + request.getRemoteUser() + "' does not have any required role, denying access");
    }

    response.sendError(HttpServletResponse.SC_FORBIDDEN,
        getInternal().getMessage("notAuthorized", mapping.getPath()));

    return (false);
  }

  /**
   * <p>
   * If this request was not cancelled, and the request's
   * {@link ActionMapping} has not disabled validation, call the
   * <code>validate</code> method of the specified {@link ActionForm}, and
   * forward to the input path if there were any errors. Return
   * <code>true</code> if we should continue processing, or <code>false</code>
   * if we have already forwarded control back to the input form.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @param form
   *            The ActionForm instance we are populating
   * @param mapping
   *            The ActionMapping we are using
   * @return <code>true</code> to continue normal processing;
   *         <code>false</code> if a response has been created.
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   * @throws InvalidCancelException
   *             if a cancellation is attempted without the proper action
   *             configuration.
   */
  protected boolean processValidate(HttpServletRequest request, HttpServletResponse response, ActionForm form,
      ActionMapping mapping) throws IOException, ServletException, InvalidCancelException {
    if (form == null) {
      return (true);
    }

    // Has validation been turned off for this mapping?
    if (!mapping.getValidate()) {
      return (true);
    }

    // Was this request cancelled? If it has been, the mapping also
    // needs to state whether the cancellation is permissable; otherwise
    // the cancellation is considered to be a symptom of a programmer
    // error or a spoof.
    if (request.getAttribute(Globals.CANCEL_KEY) != null) {
      if (mapping.getCancellable()) {
        if (log.isDebugEnabled()) {
          log.debug(" Cancelled transaction, skipping validation");
        }
        return (true);
      } else {
        request.removeAttribute(Globals.CANCEL_KEY);
        throw new InvalidCancelException();
      }
    }

    // Call the form bean's validation method
    if (log.isDebugEnabled()) {
      log.debug(" Validating input form properties");
    }

    ActionMessages errors = form.validate(mapping, request);

    if ((errors == null) || errors.isEmpty()) {
      if (log.isTraceEnabled()) {
        log.trace("  No errors detected, accepting input");
      }

      return (true);
    }

    // Special handling for multipart request
    if (form.getMultipartRequestHandler() != null) {
      if (log.isTraceEnabled()) {
        log.trace("  Rolling back multipart request");
      }

      form.getMultipartRequestHandler().rollback();
    }

    // Was an input path (or forward) specified for this mapping?
    String input = mapping.getInput();

    if (input == null) {
      if (log.isTraceEnabled()) {
        log.trace("  Validation failed but no input form available");
      }

      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          getInternal().getMessage("noInput", mapping.getPath()));

      return (false);
    }

    // Save our error messages and return to the input form if possible
    if (log.isDebugEnabled()) {
      log.debug(" Validation failed, returning to '" + input + "'");
    }

    request.setAttribute(Globals.ERROR_KEY, errors);

    if (moduleConfig.getControllerConfig().getInputForward()) {
      ForwardConfig forward = mapping.findForward(input);

      processForwardConfig(request, response, forward);
    } else {
      internalModuleRelativeForward(input, request, response);
    }

    return (false);
  }

  /**
   * <p>
   * Do a module relative forward to specified URI using request dispatcher.
   * URI is relative to the current module. The real URI is compute by
   * prefixing the module name.
   * </p>
   * <p>
   * This method is used internally and is not part of the public API. It is
   * advised to not use it in subclasses.
   * </p>
   *
   * @param uri
   *            Module-relative URI to forward to
   * @param request
   *            Current page request
   * @param response
   *            Current page response
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   * @since Struts 1.1
   */
  protected void internalModuleRelativeForward(String uri, HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {
    // Construct a request dispatcher for the specified path
    uri = moduleConfig.getPrefix() + uri;

    // Delegate the processing of this request
    // :FIXME: - exception handling?
    if (log.isDebugEnabled()) {
      log.debug(" Delegating via forward to '" + uri + "'");
    }

    doForward(uri, request, response);
  }

  /**
   * <p>
   * Do a module relative include to specified URI using request dispatcher.
   * URI is relative to the current module. The real URI is compute by
   * prefixing the module name.
   * </p>
   * <p>
   * This method is used internally and is not part of the public API. It is
   * advised to not use it in subclasses.
   * </p>
   *
   * @param uri
   *            Module-relative URI to include
   * @param request
   *            Current page request
   * @param response
   *            Current page response
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   * @since Struts 1.1
   */
  protected void internalModuleRelativeInclude(String uri, HttpServletRequest request, HttpServletResponse response)
      throws IOException, ServletException {
    // Construct a request dispatcher for the specified path
    uri = moduleConfig.getPrefix() + uri;

    // Delegate the processing of this request
    // FIXME - exception handling?
    if (log.isDebugEnabled()) {
      log.debug(" Delegating via include to '" + uri + "'");
    }

    doInclude(uri, request, response);
  }

  /**
   * <p>
   * Do a forward to specified URI using a <code>RequestDispatcher</code>.
   * This method is used by all internal method needing to do a forward.
   * </p>
   *
   * @param uri
   *            Context-relative URI to forward to
   * @param request
   *            Current page request
   * @param response
   *            Current page response
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   * @since Struts 1.1
   */
  protected void doForward(String uri, HttpServletRequest request, HttpServletResponse response) throws IOException,
      ServletException {
    RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);

    if (rd == null) {
      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          getInternal().getMessage("requestDispatcher", uri));

      return;
    }

    rd.forward(request, response);
  }

  /**
   * <p>
   * Do an include of specified URI using a <code>RequestDispatcher</code>.
   * This method is used by all internal method needing to do an include.
   * </p>
   *
   * @param uri
   *            Context-relative URI to include
   * @param request
   *            Current page request
   * @param response
   *            Current page response
   * @throws IOException
   *             if an input/output error occurs
   * @throws ServletException
   *             if a servlet exception occurs
   * @since Struts 1.1
   */
  protected void doInclude(String uri, HttpServletRequest request, HttpServletResponse response) throws IOException,
      ServletException {
    RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);

    if (rd == null) {
      response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
          getInternal().getMessage("requestDispatcher", uri));

      return;
    }

    rd.include(request, response);
  }

  // -------------------------------------------------------- Support Methods

  /**
   * <p>
   * Return the <code>MessageResources</code> instance containing our internal
   * message strings.
   * </p>
   *
   * @return The <code>MessageResources</code> instance containing our
   *         internal message strings.
   */
  protected MessageResources getInternal() {
    return (servlet.getInternal());
  }

  /**
   * <p>
   * Return the <code>ServletContext</code> for the web application in which
   * we are running.
   * </p>
   *
   * @return The <code>ServletContext</code> for the web application.
   */
  protected ServletContext getServletContext() {
    return (servlet.getServletContext());
  }
}
TOP

Related Classes of org.g4studio.core.mvc.xstruts.action.RequestProcessor

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.