craigmcc    01/03/23 14:21:34

  Modified:    conf/share struts-config_1_0.dtd
               src/share/org/apache/struts/action ActionMapping.java
  Added:       src/share/org/apache/struts/action DispatchAction.java
                        LocalStrings.properties
  Log:
  Add support for the "dispatch via method reflection" mechanism that was
  discussed on STRUTS-DEV a couple of weeks ago.  A detailed usage example
  is found in the Javadoc comments of the new DispatchAction class.
  
  This code is based on code proposed by Niall Pemberton, with the following
  changes:
  * Added a general-purpose configuration parameter attribute in the
    ActionMapping class that implements a standard <action> element.
  * Use this configuration attribute to identify the name of the request
    parameter whose value will be the name of the method to be called.
  * Used a request parameter (rather than extra path info) to represent
    the method name, because extra path info is not available when you
    are using extension mapping.
  * Cached previously introspected Method objects so that the lookup
    need be done only once for each method name.
  * Created error responses for all possible processing problems (as well
    as logging them) so that the user doesn't see just a blank page.
  
  Submitted by: Niall Pemberton <[EMAIL PROTECTED]>
   and many others who liked the idea (apparently I'm not quite
   brilliant enough to have thought of this one :-)
  
  Revision  Changes    Path
  1.2       +6 -1      jakarta-struts/conf/share/struts-config_1_0.dtd
  
  Index: struts-config_1_0.dtd
  ===================================================================
  RCS file: /home/cvs/jakarta-struts/conf/share/struts-config_1_0.dtd,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- struts-config_1_0.dtd     2001/03/18 17:58:51     1.1
  +++ struts-config_1_0.dtd     2001/03/23 22:21:34     1.2
  @@ -11,7 +11,7 @@
          "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN"
          "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd">
   
  -     $Id: struts-config_1_0.dtd,v 1.1 2001/03/18 17:58:51 vmassol Exp $
  +     $Id: struts-config_1_0.dtd,v 1.2 2001/03/23 22:21:34 craigmcc Exp $
   -->
   
   
  @@ -289,6 +289,10 @@
                        starting with a "/" character, and without the
                        filename extension if extension mapping is used.
   
  +     parameter       General purpose configuration parameter that can be used
  +                     to pass extra information to the Action selected by this
  +                     mapping.
  +
        prefix          Prefix used to match request parameter names to form bean
                        property names, if any.  Optional if "name" is specified,
                        else not allowed.
  @@ -324,6 +328,7 @@
   <!ATTLIST action         include        %RequestPath;   #IMPLIED>
   <!ATTLIST action         input          %RequestPath;   #IMPLIED>
   <!ATTLIST action         name           %BeanName;      #IMPLIED>
  +<!ATTLIST action         parameter      CDATA           #IMPLIED>
   <!ATTLIST action         path           %RequestPath;   #REQUIRED>
   <!ATTLIST action         prefix         CDATA           #IMPLIED>
   <!ATTLIST action         scope          %RequestScope;  #IMPLIED>
  
  
  
  1.17      +35 -4     
jakarta-struts/src/share/org/apache/struts/action/ActionMapping.java
  
  Index: ActionMapping.java
  ===================================================================
  RCS file: 
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/ActionMapping.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- ActionMapping.java        2001/03/13 22:31:48     1.16
  +++ ActionMapping.java        2001/03/23 22:21:34     1.17
  @@ -1,7 +1,7 @@
   /*
  - * $Header: 
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/ActionMapping.java,v 1.16 
2001/03/13 22:31:48 craigmcc Exp $
  - * $Revision: 1.16 $
  - * $Date: 2001/03/13 22:31:48 $
  + * $Header: 
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/ActionMapping.java,v 1.17 
2001/03/23 22:21:34 craigmcc Exp $
  + * $Revision: 1.17 $
  + * $Date: 2001/03/23 22:21:34 $
    *
    * ====================================================================
    *
  @@ -130,6 +130,9 @@
    *     collection of which we are a part.
    * <li><strong>name</strong> - Name of the form bean, if any, associated
    *     with this action.
  + * <li><strong>parameter</strong> - General purpose configuration parameter
  + *     that can be used to pass extra information to the <code>Action</code>
  + *     selected by this <code>ActionMapping</code>.
    * <li><strong>path</strong> - Request URI path used to select this mapping.
    *     If extension mapping is used for the controller servlet, the extension
    *     will be stripped before comparisions against this value are made.
  @@ -158,7 +161,7 @@
    * </ul>
    *
    * @author Craig R. McClanahan
  - * @version $Revision: 1.16 $ $Date: 2001/03/13 22:31:48 $
  + * @version $Revision: 1.17 $ $Date: 2001/03/23 22:21:34 $
    */
   
   public class ActionMapping implements Serializable {
  @@ -229,6 +232,12 @@
   
   
       /**
  +     * General purpose configuration parameter for this mapping.
  +     */
  +    protected String parameter = null;
  +
  +
  +    /**
        * The context-relative path of the submitted request, starting with a
        * "/" character, and without the filename extension (if any), that is
        * mapped to this action.
  @@ -616,6 +625,28 @@
       public void setName(String name) {
   
           this.name = name;
  +
  +    }
  +
  +
  +    /**
  +     * Return the general purpose configuation parameter for this mapping.
  +     */
  +    public String getParameter() {
  +
  +        return (this.parameter);
  +
  +    }
  +
  +
  +    /**
  +     * Set the general purpose configuration parameter for this mapping.
  +     *
  +     * @param parameter The new configuration parameter
  +     */
  +    public void setParameter(String parameter) {
  +
  +        this.parameter = parameter;
   
       }
   
  
  
  
  1.1                  
jakarta-struts/src/share/org/apache/struts/action/DispatchAction.java
  
  Index: DispatchAction.java
  ===================================================================
  /*
   * $Header: 
/home/cvs/jakarta-struts/src/share/org/apache/struts/action/DispatchAction.java,v 1.1 
2001/03/23 22:21:34 craigmcc Exp $
   * $Revision: 1.1 $
   * $Date: 2001/03/23 22:21:34 $
   *
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 1999-2001 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowlegement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowlegement may appear in the software itself,
   *    if and wherever such third-party acknowlegements normally appear.
   *
   * 4. The names "The Jakarta Project", "Struts", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact [EMAIL PROTECTED]
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Group.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  
  
  package org.apache.struts.actions;
  
  
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.util.HashMap;
  import javax.servlet.ServletException;
  import javax.servlet.http.HttpServletRequest;
  import javax.servlet.http.HttpServletResponse;
  import javax.servlet.http.HttpSession;
  import org.apache.struts.action.Action;
  import org.apache.struts.action.ActionForm;
  import org.apache.struts.action.ActionForward;
  import org.apache.struts.action.ActionMapping;
  import org.apache.struts.util.MessageResources;
  
  
  /**
   * <p>An abstract <strong>Action</strong> that dispatches to a public
   * method that is named by the request parameter whose name is specified
   * by the <code>parameter</code> property of the corresponding
   * ActionMapping.  This Action is useful for developers who prefer to
   * combine many similar actions into a single Action class, in order to
   * simplify their application design.</p>
   *
   * <p>To configure the use of this action in your
   * <code>struts-config.xml</code> file, create an entry like this:</p>
   *
   * <code>
   *   &lt;action path="/saveSubscription"
   *           type="org.apache.struts.action.DispatchAction"
   *           name="subscriptionForm"
   *          scope="request"
   *          input="/subscription.jsp"
   *      parameter="method"/&gt;
   * </code>
   *
   * <p>which will use the value of the request parameter named "method"
   * to pick the appropriate "perform" method, which must have the same
   * signature (other than method name) of the standard Action.perform()
   * method.  For example, you might have the following three methods in the
   * same action:</p>
   * <ul>
   * <li>public ActionForward delete(ActionMapping mapping, ActionForm form,
   *     HttpServletRequest request, HttpServletResponse response)
   *     throws IOException, ServletException</li>
   * <li>public ActionForward insert(ActionMapping mapping, ActionForm form,
   *     HttpServletRequest request, HttpServletResponse response)
   *     throws IOException, ServletException</li>
   * <li>public ActionForward update(ActionMapping mapping, ActionForm form,
   *     HttpServletRequest request, HttpServletResponse response)
   *     throws IOException, ServletException</li>
   * </ul>
   * <p>and call one of the methods with a URL like this:</p>
   * <code>
   *   http://localhost:8080/myapp/saveSubscription.do?method=update
   * </code>
   *
   * <p><strong>NOTE</strong> - All of the other mapping characteristics of
   * this action must be shared by the various handlers.  This places some
   * constraints over what types of handlers may reasonably be packaged into
   * the same <code>DispatchAction</code> subclass.</p>
   *
   * @author Niall Pemberton <[EMAIL PROTECTED]>
   * @author Craig R. McClanahan
   * @version $Revision: 1.1 $ $Date: 2001/03/23 22:21:34 $
   */
  
  public abstract class DispatchAction extends Action {
  
  
      // ----------------------------------------------------- Instance Variables
  
  
      /**
       * The Class instance of this <code>DispatchAction</code> class.
       */
      protected Class clazz = this.getClass();
  
  
      /**
       * The message resources for this package.
       */
      protected static MessageResources messages =
       MessageResources.getMessageResources
          ("org.apache.struts.action.LocalStrings");
  
  
      /**
       * The set of Method objects we have introspected for this class,
       * keyed by method name.  This collection is populated as different
       * methods are called, so that introspection needs to occur only
       * once per method name.
       */
      protected HashMap methods = new HashMap();
  
  
      /**
       * The set of argument type classes for the reflected method call.  These
       * are the same for all calls, so calculate them only once.
       */
      protected Class types[] = {
          ActionMapping.class, ActionForm.class,
          HttpServletRequest.class, HttpServletResponse.class };
  
  
      // --------------------------------------------------------- Public Methods
  
  
      /**
       * Process the specified HTTP request, and create the corresponding HTTP
       * response (or forward to another web component that will create it).
       * Return an <code>ActionForward</code> instance describing where and how
       * control should be forwarded, or <code>null</code> if the response has
       * already been completed.
       *
       * @param mapping The ActionMapping used to select this instance
       * @param actionForm The optional ActionForm bean for this request (if any)
       * @param request The HTTP request we are processing
       * @param response The HTTP response we are creating
       *
       * @exception IOException if an input/output error occurs
       * @exception ServletException if a servlet exception occurs
       */
      public ActionForward perform(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
        throws IOException, ServletException {
  
          // Identify the request parameter containing the method name
          String parameter = mapping.getParameter();
          if (parameter == null) {
              String message =
                  messages.getMessage("dispatch.handler", mapping.getPath());
              servlet.log(message);
              response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                 message);
              return (null);
          }
  
          // Identify the method name to be dispatched to
          String name = request.getParameter(parameter);
          if (name == null) {
              String message =
                  messages.getMessage("dispatch.parameter", mapping.getPath(),
                                      parameter);
              servlet.log(message);
              response.sendError(HttpServletResponse.SC_BAD_REQUEST,
                                 message);
              return (null);
          }
  
          // Identify the method object to be dispatched to
          Method method = null;
          try {
              method = getMethod(name);
          } catch (NoSuchMethodException e) {
              String message =
                  messages.getMessage("dispatch.method", mapping.getPath(),
                                      name);
              servlet.log(message);
              response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                 message);
              return (null);
          }
  
          // Dispatch to the specified method
          ActionForward forward = null;
          try {
              Object args[] = { mapping, form, request, response };
              forward = (ActionForward) method.invoke(this, args);
          } catch (ClassCastException e) {
              String message =
                  messages.getMessage("dispatch.return", mapping.getPath(),
                                      name);
              servlet.log(message);
              response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                 message);
              return (null);
          } catch (IllegalAccessException e) {
              String message =
                  messages.getMessage("dispatch.error", mapping.getPath(),
                                      name);
              servlet.log(message, e);
              response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                 message);
              return (null);
          } catch (InvocationTargetException e) {
              String message =
                  messages.getMessage("dispatch.error", mapping.getPath(),
                                      name);
              servlet.log(message, e);
              response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                                 message);
              return (null);
          }
  
          // Return the returned ActionForward instance
          return (forward);
  
      }
  
  
      // ----------------------------------------------------- Protected Methods
  
  
      /**
       * Introspect the current class to identify a method of the specified
       * name that accepts the same parameter types as the <code>perform()</code>
       * method does.
       *
       * @param name Name of the method to be introspected
       *
       * @exception NoSuchMethodException if no such method can be found
       */
      protected Method getMethod(String name)
          throws NoSuchMethodException {
  
          synchronized (methods) {
              Method method = (Method) methods.get(name);
              if (method == null) {
                  method = clazz.getMethod(name, types);
                  methods.put(name, method);
              }
              return (method);
          }
  
      }
  
  
  }
  
  
  
  1.1                  
jakarta-struts/src/share/org/apache/struts/action/LocalStrings.properties
  
  Index: LocalStrings.properties
  ===================================================================
  dispatch.error=Dispatch[{0}] to method {1} returned an exception
  dispatch.handler=DispatchMapping[{0}] does not define a handler property
  dispatch.method=Action[{0}] does not contain method named {1}
  dispatch.parameter=Request[{0}] does not contain handler parameter named {1}
  dispatch.return=Action[{0}] invalid return type for method {1}
  
  
  

Reply via email to