Package org.g4studio.core.mvc.xstruts.chain

Source Code of org.g4studio.core.mvc.xstruts.chain.ComposableRequestProcessor

package org.g4studio.core.mvc.xstruts.chain;

import java.io.IOException;
import java.lang.reflect.Constructor;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.ConstructorUtils;
import org.apache.commons.chain.Catalog;
import org.apache.commons.chain.CatalogFactory;
import org.apache.commons.chain.Command;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.g4studio.core.mvc.xstruts.action.ActionServlet;
import org.g4studio.core.mvc.xstruts.action.RequestProcessor;
import org.g4studio.core.mvc.xstruts.chain.contexts.ActionContext;
import org.g4studio.core.mvc.xstruts.chain.contexts.ServletActionContext;
import org.g4studio.core.mvc.xstruts.config.ControllerConfig;
import org.g4studio.core.mvc.xstruts.config.ModuleConfig;
import org.g4studio.core.mvc.xstruts.upload.MultipartRequestWrapper;
import org.g4studio.core.mvc.xstruts.util.RequestUtils;

public class ComposableRequestProcessor extends RequestProcessor {
  // ------------------------------------------------------ Instance Variables

  /**
   * <p>
   * Cache for constructor discovered by setActionContextClass method.
   * </p>
   */
  private static final Class[] SERVLET_ACTION_CONTEXT_CTOR_SIGNATURE = new Class[] { ServletContext.class,
      HttpServletRequest.class, HttpServletResponse.class };

  /**
   * <p>
   * Token for ActionContext clazss so that it can be stored in the
   * ControllerConfig.
   * </p>
   */
  public static final String ACTION_CONTEXT_CLASS = "ACTION_CONTEXT_CLASS";

  /**
   * <p>
   * The <code>Log</code> instance for this class.
   * </p>
   */
  protected static final Log LOG = LogFactory.getLog(ComposableRequestProcessor.class);

  /**
   * <p>
   * The {@link CatalogFactory} from which catalog containing the the base
   * request-processing {@link Command} will be retrieved.
   * </p>
   */
  protected CatalogFactory catalogFactory = null;

  /**
   * <p>
   * The {@link Catalog} containing all of the available command chains for
   * this module.
   */
  protected Catalog catalog = null;

  /**
   * <p>
   * The {@link Command} to be executed for each request.
   * </p>
   */
  protected Command command = null;

  /**
   * <p>
   * ActionContext class as cached by createActionContextInstance method.
   * </p>
   */
  private Class actionContextClass;

  /**
   * <p>
   * ActionContext constructor as cached by createActionContextInstance
   * method.
   * </p>
   */
  private Constructor servletActionContextConstructor = null;

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

  /**
   * <p>
   * Clean up in preparation for a shutdown of this application.
   * </p>
   */
  public void destroy() {
    super.destroy();
    catalogFactory = null;
    catalog = null;
    command = null;
    actionContextClass = null;
    servletActionContextConstructor = 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 occurs during initialization
   */
  public void init(ActionServlet servlet, ModuleConfig moduleConfig) throws ServletException {
    LOG.info("Initializing composable request processor for module prefix '" + moduleConfig.getPrefix() + "'");
    super.init(servlet, moduleConfig);

    initCatalogFactory(servlet, moduleConfig);

    ControllerConfig controllerConfig = moduleConfig.getControllerConfig();

    String catalogName = controllerConfig.getCatalog();

    catalog = this.catalogFactory.getCatalog(catalogName);

    if (catalog == null) {
      throw new ServletException("Cannot find catalog '" + catalogName + "'");
    }

    String commandName = controllerConfig.getCommand();

    command = catalog.getCommand(commandName);

    if (command == null) {
      throw new ServletException("Cannot find command '" + commandName + "'");
    }

    this.setActionContextClassName(controllerConfig.getProperty(ACTION_CONTEXT_CLASS));
  }

  /**
   * <p>
   * Set and cache ActionContext class.
   * </p>
   * <p>
   * If there is a custom class provided and if it uses our "preferred"
   * constructor, cache a reference to that constructor rather than looking it
   * up every time.
   * </p>
   *
   * @param actionContextClass
   *            The ActionContext class to process
   */
  private void setActionContextClass(Class actionContextClass) {
    this.actionContextClass = actionContextClass;

    if (actionContextClass != null) {
      this.servletActionContextConstructor = ConstructorUtils.getAccessibleConstructor(actionContextClass,
          SERVLET_ACTION_CONTEXT_CTOR_SIGNATURE);
    } else {
      this.servletActionContextConstructor = null;
    }
  }

  /**
   * <p>
   * Make sure that the specified <code>className</code> identfies a class
   * which can be found and which implements the <code>ActionContext</code>
   * interface.
   * </p>
   *
   * @param className
   *            Fully qualified name of
   * @throws ServletException
   *             If an error occurs during initialization
   * @throws UnavailableException
   *             if class does not implement ActionContext or is not found
   */
  private void setActionContextClassName(String className) throws ServletException {
    if ((className != null) && (className.trim().length() > 0)) {
      if (LOG.isDebugEnabled()) {
        LOG.debug("setActionContextClassName: requested context class: " + className);
      }

      try {
        Class actionContextClass = RequestUtils.applicationClass(className);

        if (!ActionContext.class.isAssignableFrom(actionContextClass)) {
          throw new UnavailableException("ActionContextClass " + "[" + className + "]"
              + " must implement ActionContext interface.");
        }

        this.setActionContextClass(actionContextClass);
      } catch (ClassNotFoundException e) {
        throw new UnavailableException("ActionContextClass " + className + " not found.");
      }
    } else {
      if (LOG.isDebugEnabled()) {
        LOG.debug("setActionContextClassName: no className specified");
      }

      this.setActionContextClass(null);
    }
  }

  /**
   * <p>
   * Establish the CatalogFactory which will be used to look up the catalog
   * which has the request processing command.
   * </p>
   * <p>
   * The base implementation simply calls CatalogFactory.getInstance(), unless
   * the catalogFactory property of this object has already been set, in which
   * case it is not changed.
   * </p>
   *
   * @param servlet
   *            The ActionServlet we are processing
   * @param moduleConfig
   *            The ModuleConfig we are processing
   */
  protected void initCatalogFactory(ActionServlet servlet, ModuleConfig moduleConfig) {
    if (this.catalogFactory != null) {
      return;
    }

    this.catalogFactory = CatalogFactory.getInstance();
  }

  /**
   * <p>
   * Process an <code>HttpServletRequest</code> and create the corresponding
   * <code>HttpServletResponse</code>.
   * </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 the request in the case of a multipart request
    request = processMultipart(request);

    // Create and populate a Context for this request
    ActionContext context = contextInstance(request, response);

    // Create and execute the command.
    try {
      if (LOG.isDebugEnabled()) {
        LOG.debug("Using processing chain for this request");
      }

      command.execute(context);
    } catch (Exception e) {
      // Execute the exception processing chain??
      throw new ServletException(e);
    }

    // Release the context.
    context.release();
  }

  /**
   * <p>
   * Provide the initialized <code>ActionContext</code> instance which will be
   * used by this request. Internally, this simply calls
   * <code>createActionContextInstance</code> followed by
   * <code>initializeActionContext</code>.
   * </p>
   *
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @return Initiliazed ActionContext
   * @throws ServletException
   *             if a processing exception occurs
   */
  protected ActionContext contextInstance(HttpServletRequest request, HttpServletResponse response)
      throws ServletException {
    ActionContext context = createActionContextInstance(getServletContext(), request, response);

    initializeActionContext(context);

    return context;
  }

  /**
   * <p>
   * Create a new instance of <code>ActionContext</code> according to
   * configuration. If no alternative was specified at initialization, a new
   * instance <code>ServletActionContext</code> is returned. If an alternative
   * was specified using the <code>ACTION_CONTEXT_CLASS</code> property, then
   * that value is treated as a classname, and an instance of that class is
   * created. If that class implements the same constructor that
   * <code>ServletActionContext</code> does, then that constructor will be
   * used: <code>ServletContext, HttpServletRequest,
   * HttpServletResponse</code>; otherwise, it is assumed that the class has a
   * no-arguments constructor. If these constraints do not suit you, simply
   * override this method in a subclass.
   * </p>
   *
   * @param servletContext
   *            The servlet context we are processing
   * @param request
   *            The servlet request we are processing
   * @param response
   *            The servlet response we are creating
   * @return New instance of ActionContext
   * @throws ServletException
   *             if a processing exception occurs
   */
  protected ActionContext createActionContextInstance(ServletContext servletContext, HttpServletRequest request,
      HttpServletResponse response) throws ServletException {
    if (this.actionContextClass == null) {
      return new ServletActionContext(servletContext, request, response);
    }

    try {
      if (this.servletActionContextConstructor == null) {
        return (ActionContext) this.actionContextClass.newInstance();
      }

      return (ActionContext) this.servletActionContextConstructor.newInstance(new Object[] { servletContext,
          request, response });
    } catch (Exception e) {
      throw new ServletException("Error creating ActionContext instance of type " + this.actionContextClass, e);
    }
  }

  /**
   * <p>
   * Set common properties on the given <code>ActionContext</code> instance so
   * that commands in the chain can count on their presence. Note that while
   * this method does not require that its argument be an instance of
   * <code>ServletActionContext</code>, at this time many common Struts
   * commands will be expecting to receive an <code>ActionContext</code> which
   * is also a <code>ServletActionContext</code>.
   * </p>
   *
   * @param context
   *            The ActionContext we are processing
   */
  protected void initializeActionContext(ActionContext context) {
    if (context instanceof ServletActionContext) {
      ((ServletActionContext) context).setActionServlet(this.servlet);
    }

    context.setModuleConfig(this.moduleConfig);
  }

  /**
   * <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 Original or wrapped request as appropriate
   */
  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 <code>CatalogFactory</code> instance which should be used to find
   * the request-processing command. In the base implementation, if this value
   * is not already set, then it will be initialized when
   * {@link #initCatalogFactory} is called.
   * </p>
   *
   * @param catalogFactory
   *            Our CatalogFactory instance
   */
  public void setCatalogFactory(CatalogFactory catalogFactory) {
    this.catalogFactory = catalogFactory;
  }
}
TOP

Related Classes of org.g4studio.core.mvc.xstruts.chain.ComposableRequestProcessor

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.