Save This Page
Home » struts-1.3.9-src » org.apache.struts » chain » [javadoc | source]
    1   /*
    2    * $Id: ComposableRequestProcessor.java 471754 2006-11-06 14:55:09Z husted $
    3    *
    4    * Licensed to the Apache Software Foundation (ASF) under one
    5    * or more contributor license agreements.  See the NOTICE file
    6    * distributed with this work for additional information
    7    * regarding copyright ownership.  The ASF licenses this file
    8    * to you under the Apache License, Version 2.0 (the
    9    * "License"); you may not use this file except in compliance
   10    * with the License.  You may obtain a copy of the License at
   11    *
   12    *  http://www.apache.org/licenses/LICENSE-2.0
   13    *
   14    * Unless required by applicable law or agreed to in writing,
   15    * software distributed under the License is distributed on an
   16    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   17    * KIND, either express or implied.  See the License for the
   18    * specific language governing permissions and limitations
   19    * under the License.
   20    */
   21   package org.apache.struts.chain;
   22   
   23   import org.apache.commons.beanutils.ConstructorUtils;
   24   import org.apache.commons.chain.Catalog;
   25   import org.apache.commons.chain.CatalogFactory;
   26   import org.apache.commons.chain.Command;
   27   import org.apache.commons.logging.Log;
   28   import org.apache.commons.logging.LogFactory;
   29   import org.apache.struts.action.ActionServlet;
   30   import org.apache.struts.action.RequestProcessor;
   31   import org.apache.struts.chain.contexts.ActionContext;
   32   import org.apache.struts.chain.contexts.ServletActionContext;
   33   import org.apache.struts.config.ControllerConfig;
   34   import org.apache.struts.config.ModuleConfig;
   35   import org.apache.struts.upload.MultipartRequestWrapper;
   36   import org.apache.struts.util.RequestUtils;
   37   
   38   import javax.servlet.ServletContext;
   39   import javax.servlet.ServletException;
   40   import javax.servlet.UnavailableException;
   41   import javax.servlet.http.HttpServletRequest;
   42   import javax.servlet.http.HttpServletResponse;
   43   
   44   import java.io.IOException;
   45   
   46   import java.lang.reflect.Constructor;
   47   
   48   /**
   49    * <p> ComposableRequestProcessor uses the Chain Of Resposibility design
   50    * pattern (as implemented by the commons-chain package in Jakarta Commons) to
   51    * support external configuration of command chains to be used.  It is
   52    * configured via the following context initialization parameters: </p>
   53    *
   54    * <ul>
   55    *
   56    * <li>[org.apache.struts.chain.CATALOG_NAME] - Name of the Catalog in which
   57    * we will look up the Command to be executed for each request.  If not
   58    * specified, the default value is struts. </li>
   59    *
   60    * <li> org.apache.struts.chain.COMMAND_NAME - Name of the Command which we
   61    * will execute for each request, to be looked up in the specified Catalog.
   62    * If not specified, the default value is servlet-standard. </li>
   63    *
   64    * </ul>
   65    *
   66    * @version $Rev: 471754 $ $Date: 2005-11-12 13:01:44 -0500 (Sat, 12 Nov 2005)
   67    *          $
   68    * @since Struts 1.1
   69    */
   70   public class ComposableRequestProcessor extends RequestProcessor {
   71       // ------------------------------------------------------ Instance Variables
   72   
   73       /**
   74        * <p> Cache for constructor discovered by setActionContextClass method.
   75        * </p>
   76        */
   77       private static final Class[] SERVLET_ACTION_CONTEXT_CTOR_SIGNATURE =
   78           new Class[] {
   79               ServletContext.class, HttpServletRequest.class,
   80               HttpServletResponse.class
   81           };
   82   
   83       /**
   84        * <p> Token for ActionContext clazss so that it can be stored in the
   85        * ControllerConfig. </p>
   86        */
   87       public static final String ACTION_CONTEXT_CLASS = "ACTION_CONTEXT_CLASS";
   88   
   89       /**
   90        * <p>The <code>Log</code> instance for this class.</p>
   91        */
   92       protected static final Log LOG =
   93           LogFactory.getLog(ComposableRequestProcessor.class);
   94   
   95       /**
   96        * <p>The {@link CatalogFactory} from which catalog containing the the
   97        * base request-processing {@link Command} will be retrieved.</p>
   98        */
   99       protected CatalogFactory catalogFactory = null;
  100   
  101       /**
  102        * <p>The {@link Catalog} containing all of the available command chains
  103        * for this module.
  104        */
  105       protected Catalog catalog = null;
  106   
  107       /**
  108        * <p>The {@link Command} to be executed for each request.</p>
  109        */
  110       protected Command command = null;
  111   
  112       /**
  113        * <p> ActionContext class as cached by createActionContextInstance
  114        * method. </p>
  115        */
  116       private Class actionContextClass;
  117   
  118       /**
  119        * <p> ActionContext constructor as cached by createActionContextInstance
  120        * method. </p>
  121        */
  122       private Constructor servletActionContextConstructor = null;
  123   
  124       // ---------------------------------------------------------- Public Methods
  125   
  126       /**
  127        * <p>Clean up in preparation for a shutdown of this application.</p>
  128        */
  129       public void destroy() {
  130           super.destroy();
  131           catalogFactory = null;
  132           catalog = null;
  133           command = null;
  134           actionContextClass = null;
  135           servletActionContextConstructor = null;
  136       }
  137   
  138       /**
  139        * <p>Initialize this request processor instance.</p>
  140        *
  141        * @param servlet      The ActionServlet we are associated with
  142        * @param moduleConfig The ModuleConfig we are associated with.
  143        * @throws ServletException If an error occurs during initialization
  144        */
  145       public void init(ActionServlet servlet, ModuleConfig moduleConfig)
  146           throws ServletException {
  147           LOG.info(
  148               "Initializing composable request processor for module prefix '"
  149               + moduleConfig.getPrefix() + "'");
  150           super.init(servlet, moduleConfig);
  151   
  152           initCatalogFactory(servlet, moduleConfig);
  153   
  154           ControllerConfig controllerConfig = moduleConfig.getControllerConfig();
  155   
  156           String catalogName = controllerConfig.getCatalog();
  157   
  158           catalog = this.catalogFactory.getCatalog(catalogName);
  159   
  160           if (catalog == null) {
  161               throw new ServletException("Cannot find catalog '" + catalogName
  162                   + "'");
  163           }
  164   
  165           String commandName = controllerConfig.getCommand();
  166   
  167           command = catalog.getCommand(commandName);
  168   
  169           if (command == null) {
  170               throw new ServletException("Cannot find command '" + commandName
  171                   + "'");
  172           }
  173   
  174           this.setActionContextClassName(controllerConfig.getProperty(
  175                   ACTION_CONTEXT_CLASS));
  176       }
  177   
  178       /**
  179        * <p> Set and cache ActionContext class. </p><p> If there is a custom
  180        * class provided and if it uses our "preferred" constructor, cache a
  181        * reference to that constructor rather than looking it up every time.
  182        * </p>
  183        *
  184        * @param actionContextClass The ActionContext class to process
  185        */
  186       private void setActionContextClass(Class actionContextClass) {
  187           this.actionContextClass = actionContextClass;
  188   
  189           if (actionContextClass != null) {
  190               this.servletActionContextConstructor =
  191                   ConstructorUtils.getAccessibleConstructor(actionContextClass,
  192                       SERVLET_ACTION_CONTEXT_CTOR_SIGNATURE);
  193           } else {
  194               this.servletActionContextConstructor = null;
  195           }
  196       }
  197   
  198       /**
  199        * <p>Make sure that the specified <code>className</code> identfies a
  200        * class which can be found and which implements the
  201        * <code>ActionContext</code> interface.</p>
  202        *
  203        * @param className Fully qualified name of
  204        * @throws ServletException     If an error occurs during initialization
  205        * @throws UnavailableException if class does not implement ActionContext
  206        *                              or is not found
  207        */
  208       private void setActionContextClassName(String className)
  209           throws ServletException {
  210           if ((className != null) && (className.trim().length() > 0)) {
  211               if (LOG.isDebugEnabled()) {
  212                   LOG.debug(
  213                       "setActionContextClassName: requested context class: "
  214                       + className);
  215               }
  216   
  217               try {
  218                   Class actionContextClass =
  219                       RequestUtils.applicationClass(className);
  220   
  221                   if (!ActionContext.class.isAssignableFrom(actionContextClass)) {
  222                       throw new UnavailableException("ActionContextClass " + "["
  223                           + className + "]"
  224                           + " must implement ActionContext interface.");
  225                   }
  226   
  227                   this.setActionContextClass(actionContextClass);
  228               } catch (ClassNotFoundException e) {
  229                   throw new UnavailableException("ActionContextClass "
  230                       + className + " not found.");
  231               }
  232           } else {
  233               if (LOG.isDebugEnabled()) {
  234                   LOG.debug("setActionContextClassName: no className specified");
  235               }
  236   
  237               this.setActionContextClass(null);
  238           }
  239       }
  240   
  241       /**
  242        * <p> Establish the CatalogFactory which will be used to look up the
  243        * catalog which has the request processing command. </p><p> The base
  244        * implementation simply calls CatalogFactory.getInstance(), unless the
  245        * catalogFactory property of this object has already been set, in which
  246        * case it is not changed. </p>
  247        *
  248        * @param servlet      The ActionServlet we are processing
  249        * @param moduleConfig The ModuleConfig we are processing
  250        */
  251       protected void initCatalogFactory(ActionServlet servlet,
  252           ModuleConfig moduleConfig) {
  253           if (this.catalogFactory != null) {
  254               return;
  255           }
  256   
  257           this.catalogFactory = CatalogFactory.getInstance();
  258       }
  259   
  260       /**
  261        * <p>Process an <code>HttpServletRequest</code> and create the
  262        * corresponding <code>HttpServletResponse</code>.</p>
  263        *
  264        * @param request  The servlet request we are processing
  265        * @param response The servlet response we are creating
  266        * @throws IOException      if an input/output error occurs
  267        * @throws ServletException if a processing exception occurs
  268        */
  269       public void process(HttpServletRequest request, HttpServletResponse response)
  270           throws IOException, ServletException {
  271           // Wrap the request in the case of a multipart request
  272           request = processMultipart(request);
  273   
  274           // Create and populate a Context for this request
  275           ActionContext context = contextInstance(request, response);
  276   
  277           // Create and execute the command.
  278           try {
  279               if (LOG.isDebugEnabled()) {
  280                   LOG.debug("Using processing chain for this request");
  281               }
  282   
  283               command.execute(context);
  284           } catch (Exception e) {
  285               // Execute the exception processing chain??
  286               throw new ServletException(e);
  287           }
  288   
  289           // Release the context.
  290           context.release();
  291       }
  292   
  293       /**
  294        * <p>Provide the initialized <code>ActionContext</code> instance which
  295        * will be used by this request. Internally, this simply calls
  296        * <code>createActionContextInstance</code> followed by
  297        * <code>initializeActionContext</code>.</p>
  298        *
  299        * @param request  The servlet request we are processing
  300        * @param response The servlet response we are creating
  301        * @return Initiliazed ActionContext
  302        * @throws ServletException if a processing exception occurs
  303        */
  304       protected ActionContext contextInstance(HttpServletRequest request,
  305           HttpServletResponse response)
  306           throws ServletException {
  307           ActionContext context =
  308               createActionContextInstance(getServletContext(), request, response);
  309   
  310           initializeActionContext(context);
  311   
  312           return context;
  313       }
  314   
  315       /**
  316        * <p>Create a new instance of <code>ActionContext</code> according to
  317        * configuration.  If no alternative was specified at initialization, a
  318        * new instance <code>ServletActionContext</code> is returned.  If an
  319        * alternative was specified using the <code>ACTION_CONTEXT_CLASS</code>
  320        * property, then that value is treated as a classname, and an instance of
  321        * that class is created.  If that class implements the same constructor
  322        * that <code>ServletActionContext</code> does, then that constructor will
  323        * be used: <code>ServletContext, HttpServletRequest,
  324        * HttpServletResponse</code>; otherwise, it is assumed that the class has
  325        * a no-arguments constructor.  If these constraints do not suit you,
  326        * simply override this method in a subclass.</p>
  327        *
  328        * @param servletContext The servlet context we are processing
  329        * @param request        The servlet request we are processing
  330        * @param response       The servlet response we are creating
  331        * @return New instance of ActionContext
  332        * @throws ServletException if a processing exception occurs
  333        */
  334       protected ActionContext createActionContextInstance(
  335           ServletContext servletContext, HttpServletRequest request,
  336           HttpServletResponse response)
  337           throws ServletException {
  338           if (this.actionContextClass == null) {
  339               return new ServletActionContext(servletContext, request, response);
  340           }
  341   
  342           try {
  343               if (this.servletActionContextConstructor == null) {
  344                   return (ActionContext) this.actionContextClass.newInstance();
  345               }
  346   
  347               return (ActionContext) this.servletActionContextConstructor
  348               .newInstance(new Object[] { servletContext, request, response });
  349           } catch (Exception e) {
  350               throw new ServletException(
  351                   "Error creating ActionContext instance of type "
  352                   + this.actionContextClass, e);
  353           }
  354       }
  355   
  356       /**
  357        * <p>Set common properties on the given <code>ActionContext</code>
  358        * instance so that commands in the chain can count on their presence.
  359        * Note that while this method does not require that its argument be an
  360        * instance of <code>ServletActionContext</code>, at this time many common
  361        * Struts commands will be expecting to receive an <code>ActionContext</code>
  362        * which is also a <code>ServletActionContext</code>.</p>
  363        *
  364        * @param context The ActionContext we are processing
  365        */
  366       protected void initializeActionContext(ActionContext context) {
  367           if (context instanceof ServletActionContext) {
  368               ((ServletActionContext) context).setActionServlet(this.servlet);
  369           }
  370   
  371           context.setModuleConfig(this.moduleConfig);
  372       }
  373   
  374       /**
  375        * <p>If this is a multipart request, wrap it with a special wrapper.
  376        * Otherwise, return the request unchanged.</p>
  377        *
  378        * @param request The HttpServletRequest we are processing
  379        * @return Original or wrapped request as appropriate
  380        */
  381       protected HttpServletRequest processMultipart(HttpServletRequest request) {
  382           if (!"POST".equalsIgnoreCase(request.getMethod())) {
  383               return (request);
  384           }
  385   
  386           String contentType = request.getContentType();
  387   
  388           if ((contentType != null)
  389               && contentType.startsWith("multipart/form-data")) {
  390               return (new MultipartRequestWrapper(request));
  391           } else {
  392               return (request);
  393           }
  394       }
  395   
  396       /**
  397        * <p>Set the <code>CatalogFactory</code> instance which should be used to
  398        * find the request-processing command.  In the base implementation, if
  399        * this value is not already set, then it will be initialized when {@link
  400        * #initCatalogFactory} is called. </p>
  401        *
  402        * @param catalogFactory Our CatalogFactory instance
  403        */
  404       public void setCatalogFactory(CatalogFactory catalogFactory) {
  405           this.catalogFactory = catalogFactory;
  406       }
  407   }

Save This Page
Home » struts-1.3.9-src » org.apache.struts » chain » [javadoc | source]