You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by la...@apache.org on 2005/11/13 06:30:29 UTC

svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Author: laurieh
Date: Sat Nov 12 21:30:23 2005
New Revision: 332894

URL: http://svn.apache.org/viewcvs?rev=332894&view=rev
Log:
iEnhancing DyanActionForm and friends to remove the need for '.map' when
accessing properties in JSPs, as described in #36794:

http://issues.apache.org/bugzilla/show_bug.cgi?id=36794


Modified:
    struts/build/trunk/project.xml
    struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java
    struts/core/trunk/xdocs/userGuide/building_controller.xml

Modified: struts/build/trunk/project.xml
URL: http://svn.apache.org/viewcvs/struts/build/trunk/project.xml?rev=332894&r1=332893&r2=332894&view=diff
==============================================================================
--- struts/build/trunk/project.xml (original)
+++ struts/build/trunk/project.xml Sat Nov 12 21:30:23 2005
@@ -361,6 +361,16 @@
     </dependency>
     
     <dependency>
+      <groupId>cglib</groupId>
+      <artifactId>cglib-nodep</artifactId>
+      <version>2.1_3</version>
+      <url>http://cglib.sourceforge.net/</url>
+      <properties>
+        <war.bundle>true</war.bundle>
+      </properties>
+    </dependency>
+
+    <dependency>
       <groupId>oro</groupId>
       <artifactId>oro</artifactId>
       <version>2.0.8</version>

Modified: struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java
URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java?rev=332894&r1=332893&r2=332894&view=diff
==============================================================================
--- struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java (original)
+++ struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java Sat Nov 12 21:30:23 2005
@@ -22,13 +22,24 @@
 
 import java.io.Serializable;
 import java.util.HashMap;
+import java.util.Map;
+import java.lang.reflect.Method;
 
 import org.apache.commons.beanutils.DynaBean;
 import org.apache.commons.beanutils.DynaClass;
 import org.apache.commons.beanutils.DynaProperty;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
 import org.apache.struts.config.FormBeanConfig;
 import org.apache.struts.config.FormPropertyConfig;
 import org.apache.struts.util.RequestUtils;
+import net.sf.cglib.proxy.InterfaceMaker;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import net.sf.cglib.asm.Type;
+import net.sf.cglib.core.Signature;
+import net.sf.cglib.core.Constants;
 
 
 /**
@@ -45,6 +56,7 @@
 
 public class DynaActionFormClass implements DynaClass, Serializable {
 
+    private Log log = LogFactory.getLog(DynaActionFormClass.class);
 
     // ----------------------------------------------------------- Constructors
 
@@ -181,10 +193,9 @@
     public DynaBean newInstance()
         throws IllegalAccessException, InstantiationException {
 
-        DynaActionForm dynaBean =
-            (DynaActionForm) getBeanClass().newInstance();
-        dynaBean.setDynaActionFormClass(this);
         FormPropertyConfig[] props = config.findFormPropertyConfigs();
+        DynaActionForm dynaBean = (DynaActionForm) doCreate(props);
+        dynaBean.setDynaActionFormClass(this);
         for (int i = 0; i < props.length; i++) {
             dynaBean.set(props[i].getName(), props[i].initial());
         }
@@ -312,4 +323,112 @@
     }
 
 
+    // -------------------------------------------------------- Private Methods
+
+    private Object doCreate(FormPropertyConfig[] props) {
+        // Build an interface to implement consisting of getter/setter
+        // pairs for each property. Also create a lookup table so we
+        // can map method names back to the corresponding dynamic
+        // property on invocation. This allows us to correctly handle
+        // property names that don't comply with JavaBeans naming
+        // conventions.
+        Map properties = new HashMap(props.length * 2);
+        InterfaceMaker im = new InterfaceMaker();
+        for (int i = 0; i < props.length; i++) {
+            String name = props[i].getName();
+            Class type = props[i].getTypeClass();
+            Type ttype = Type.getType(type);
+
+            if (! name.matches("[\\w]+")) {
+                // Note: this allows leading digits, which is not legal
+                // for an identifier but is valid in a getter/setter
+                // method name. Since you can define such getter/setter
+                // directly, we support doing so dynamically too.
+                if (log.isWarnEnabled()) {
+                    log.warn(
+                        "Dyna property name '" + name +
+                        "' in form bean " + config.getName() +
+                        " is not a legal Java identifier. " +
+                        "No property access methods generated.");
+                }
+            } else {
+                // Capitalize property name appropriately
+                String property;
+                if ((name.length() <= 1) ||
+                    (  Character.isLowerCase(name.charAt(0)) &&
+                    (! Character.isLowerCase(name.charAt(1))))
+                ) {
+                    property = name;
+                } else {
+                    property =
+                        Character.toUpperCase(name.charAt(0)) +
+                        name.substring(1);
+                }
+
+                // Create the getter/setter method pair
+                Signature getter = new Signature("get"+property, ttype, Constants.TYPES_EMPTY);
+                Signature setter = new Signature("set"+property, Type.VOID_TYPE, new Type[] { ttype });
+                im.add(getter, Constants.TYPES_EMPTY);
+                im.add(setter, Constants.TYPES_EMPTY);
+                properties.put("get"+property, name);
+                properties.put("set"+property, name);
+            }
+        }
+        Class beanInterface = im.create();
+
+        // Now generate a proxy for the dyna bean that also implements
+        // the getter/setter methods defined above. We turn off the
+        // Factory interface to preven problems with BeanUtils.copyProperties
+        // when both source and target bean are enhanced (otherwise, the
+        // target bean's callbacks get overwritten with the source beans,
+        // leading to unexpected behaviour).
+        Enhancer e = new Enhancer();
+        e.setSuperclass(beanClass);
+        e.setInterfaces(new Class[] { beanInterface });
+        e.setCallback(new BeanInterceptor(properties));
+        e.setUseFactory(false);
+
+        // Return the generated/enhanced bean
+        return e.create();
+    }
+
+    private static class BeanInterceptor implements MethodInterceptor, Serializable {
+        private Map propertyLookup;
+
+        public BeanInterceptor(Map propertyLookup) {
+            this.propertyLookup = propertyLookup;
+        }
+
+        public Object intercept(Object obj, Method method, Object[] args,
+                                MethodProxy proxy) throws Throwable {
+
+            String methodNm = method.getName();
+            String propertyNm = (String) propertyLookup.get(methodNm);
+            String targetNm = methodNm.substring(0, 3); // get/set
+
+            if (propertyNm == null) {
+                // Not a dyna property access, just pass call along
+                return proxy.invokeSuper(obj, args);
+            }
+
+            // Handle the dyna property access:
+            //  - map getFoo(...) to get("foo", ...)
+            //  - map setFoo(bar, ...) to set("foo", bar, ...)
+            Class[] targetArgTypes = new Class[args.length + 1];
+            Object[] targetArgs = new Object[args.length + 1];
+            targetArgTypes[0] = String.class; // for property name
+            targetArgs[0] = propertyNm;
+
+            System.arraycopy(args, 0, targetArgs, 1, args.length);
+
+            for (int i = 0; i < args.length; i++) {
+                targetArgTypes[i + 1] = args[i].getClass();
+            }
+
+            Method target = obj.getClass().getMethod(targetNm, targetArgTypes);
+
+            // Return the result of mapped get/set
+            return target.invoke(obj, targetArgs);
+        }
+    }
 }

Modified: struts/core/trunk/xdocs/userGuide/building_controller.xml
URL: http://svn.apache.org/viewcvs/struts/core/trunk/xdocs/userGuide/building_controller.xml?rev=332894&r1=332893&r2=332894&view=diff
==============================================================================
--- struts/core/trunk/xdocs/userGuide/building_controller.xml (original)
+++ struts/core/trunk/xdocs/userGuide/building_controller.xml Sat Nov 12 21:30:23 2005
@@ -31,14 +31,14 @@
     <p>
     Now that we understand how to construct the Model and View components
     of your application, it is time to focus on the Controller
-    components.  
+    components.
     Struts Core includes a servlet that implements the primary function of
      mapping a request URI to an Action class.
     Therefore, your primary responsibilities related to the Controller are:
     </p>
 
     <ul>
-    
+
         <li>
         Write an ActionForm class to mediate between the Model
         and the View. (See also <a href="../faqs/actionForm.html">Building
@@ -52,13 +52,13 @@
 
         <li>
         Configure a ActionMapping (in XML) for each logical request that may
-        be submitted. 
-        The XML configuration file is usually named 
+        be submitted.
+        The XML configuration file is usually named
         struts-config.xml.
         </li>
 
     </ul>
-    
+
     <p>
     To deploy your application, you will also need to:
     </p>
@@ -73,79 +73,79 @@
         <li>
         Include the additional components with your application.
         </li>
-        
+
     </ul>
-    
+
     <p>
-    The latter two items are covered in the 
+    The latter two items are covered in the
     "<a href="configuration.html">Configuring Applications</a>" chapter.
     </p>
 
 </subsection>
-    
+
 <a name="action_servlet"/>
 <subsection name="4.2 The ActionServlet">
 
     <p>
     For those of you familiar with MVC architecture, the ActionServlet
-    represents the C - the controller.  
+    represents the C - the controller.
     The job of the controller is to:
     </p>
-    
+
     <ul>
-    
+
     <li>
-    process user requests, 
+    process user requests,
     </li>
-    
+
     <li>
-    determine what the user is trying to achieve according to the request, 
+    determine what the user is trying to achieve according to the request,
     </li>
-    
+
     <li>
-    pull data from the model (if necessary) to be given to the appropriate 
-    view, and 
+    pull data from the model (if necessary) to be given to the appropriate
+    view, and
     </li>
-    
+
     <li>
-    select the proper view to respond to the user.  
+    select the proper view to respond to the user.
     </li>
-    
+
     </ul>
-    
+
     <p>
     Our controller delegates most of this grunt work to the
     <a href="#4_2_1_Request_Processor">Request Processor</a> and Action classes.
     </p>
-    
+
     <p>
     In addition to being the front controller for your application, the
     ActionServlet instance also is responsible for initialization and
-    clean-up of resources.  
-    When the controller initializes, it first loads the application config 
+    clean-up of resources.
+    When the controller initializes, it first loads the application config
     corresponding to the "config" init-param.
     It then goes through an enumeration of all init-param
     elements, looking for those elements who's name starts with
-    config/.  
+    config/.
     For each of these elements, Core loads the configuration file specified
-    by the value of that init-param, and assigns a "prefix" 
+    by the value of that init-param, and assigns a "prefix"
     value to that module's ModuleConfig instance consisting of the piece
-    of the init-param name following "config/".  
-    For example, the module prefix specified by the 
-    init-param config/foo would be "foo". 
-    This is important to know, since this is how the controller determines 
-    which module will be given control of processing the request.  
+    of the init-param name following "config/".
+    For example, the module prefix specified by the
+    init-param config/foo would be "foo".
+    This is important to know, since this is how the controller determines
+    which module will be given control of processing the request.
     To access the module foo, you would use a URL like:
     </p>
-    
+
     <pre>http://localhost:8080/myApp/foo/someAction.do</pre>
-    
+
     <p>
     For each request made of the controller, the method
     process(HttpServletRequest, HttpServletResponse) will be
-    called.  
-    This method simply determines which module should service the request and 
-    then invokes that module's RequestProcessor's process method, passing the 
+    called.
+    This method simply determines which module should service the request and
+    then invokes that module's RequestProcessor's process method, passing the
     same request and response.
     </p>
 
@@ -155,19 +155,19 @@
 <subsection name="4.2.1 Request Processor">
 
     <p>
-    The RequestProcessor is where the majority of the core processing 
+    The RequestProcessor is where the majority of the core processing
     occurs for each request. Since Struts 1.3, the default Request Processor
-    (<code>ComposableRequestProcessor</code>) is composed using 
+    (<code>ComposableRequestProcessor</code>) is composed using
     <a href="http://jakarta.apache.org/commons/chain/">Jakarta Commons Chain</a>,
-    which is an implementation of the <i>Chain of Responsibility</i> pattern (CoR). 
+    which is an implementation of the <i>Chain of Responsibility</i> pattern (CoR).
     It is designed as a drop-in replacement of the Struts 1.2.x behaviour and brings
     greater flexibility and easier customization to the request processing process.
     Each step of the processing is represented as a separate Command in the Chain.
-    By inserting, substituting, or removing Commands, 
+    By inserting, substituting, or removing Commands,
     you can customize Struts to work <i>your</i> way.
     Let's take a look at the <i>default</i> commands for the ComposableRequestProcessor
-    Chain in turn. These Command classes are in either the 
-    <code>org.apache.struts.chain.commands</code> or 
+    Chain in turn. These Command classes are in either the
+    <code>org.apache.struts.chain.commands</code> or
     or <code>org.apache.struts.chain.commands.servlet</code> packages.
     </p>
 
@@ -177,7 +177,7 @@
         SelectLocale
         </td>
         <td>
-        Select a locale for this request, if one hasn't already been 
+        Select a locale for this request, if one hasn't already been
         selected, and place it in the request.
         </td>
     </tr>
@@ -196,7 +196,7 @@
         RequestNoCache
         </td>
         <td>
-        If appropriate, set the following response headers: "Pragma", 
+        If appropriate, set the following response headers: "Pragma",
         "Cache-Control", and "Expires".
         </td>
     </tr>
@@ -206,14 +206,14 @@
         SetContentType
         </td>
         <td>
-        Set the default content type (with optional character encoding) for 
+        Set the default content type (with optional character encoding) for
         all responses if requested.
         </td>
     </tr>
 
     <tr>
         <td>
-        SelectAction 
+        SelectAction
         </td>
         <td>
         Determine the ActionMapping associated with this path.
@@ -222,10 +222,10 @@
 
     <tr>
         <td>
-        AuthorizeAction 
+        AuthorizeAction
         </td>
         <td>
-        If the mapping has a role associated with it, ensure the requesting 
+        If the mapping has a role associated with it, ensure the requesting
         client has the specified role.
         If the client does not, raise an error and stop processing of the request.
         </td>
@@ -233,10 +233,10 @@
 
     <tr>
         <td>
-        CreateActionForm 
+        CreateActionForm
         </td>
         <td>
-        Instantiate (if necessary) the ActionForm associated with this 
+        Instantiate (if necessary) the ActionForm associated with this
         mapping (if any) and place it into the appropriate scope.
         </td>
     </tr>
@@ -252,14 +252,14 @@
 
     <tr>
         <td>
-        ValidateActionForm 
+        ValidateActionForm
         </td>
         <td>
-        Perform validation (if requested) on the ActionForm associated with 
+        Perform validation (if requested) on the ActionForm associated with
         this request (if any).
         </td>
     </tr>
-    
+
     <tr>
         <td>
         SelectInput
@@ -269,7 +269,7 @@
         to the input page.
         </td>
     </tr>
-    
+
     <tr>
         <td>
         ExecuteCommand
@@ -278,48 +278,48 @@
         Lookup and execute a chain command if the current ActionConfig is so-configured.
         </td>
     </tr>
-    
+
     <tr>
         <td>
         SelectForward
         </td>
         <td>
-        If this mapping represents a forward, forward to the path specified 
+        If this mapping represents a forward, forward to the path specified
         by the mapping.
         </td>
     </tr>
-    
+
     <tr>
         <td>
-        SelectInclude 
+        SelectInclude
         </td>
         <td>
         Select the include uri (if any) for the current action mapping.
         </td>
     </tr>
-    
+
     <tr>
         <td>
-        PerformInclude 
+        PerformInclude
         </td>
         <td>
         If selected, include the result of invoking the path in this request.
         </td>
     </tr>
-    
+
     <tr>
         <td>
         CreateAction
         </td>
         <td>
-        Instantiate an instance of the class specified by the current 
+        Instantiate an instance of the class specified by the current
         ActionMapping (if necessary).
         </td>
     </tr>
-    
+
     <tr>
         <td>
-        ExecuteAction 
+        ExecuteAction
         </td>
         <td>
         This is the point at which your Action's execute method
@@ -341,7 +341,7 @@
          PerformForward
         </td>
         <td>
-        Finally, the process method of the RequestProcessor takes the 
+        Finally, the process method of the RequestProcessor takes the
         ActionForward returned by your Action class, and uses it to select the
         next resource (if any).
         Most often the ActionForward leads to the presentation page that
@@ -354,7 +354,7 @@
     <p>
     In Struts 1.2.x, Tiles processing required configuring Struts to use the
     <code>TilesRequestProcessor</code> implementation. In the Struts 1.3
-    <i>Chain</i> based request processor using Tiles simply 
+    <i>Chain</i> based request processor using Tiles simply
     involves configuring it to use an additional Tiles Command, and
     deploying the tiles sub-project jar. The following Command is in the
     <code>org.apache.struts.tiles.commands</code> package.
@@ -379,7 +379,7 @@
 <subsection name="4.2.2 Where did they go?">
 
 <p>
-The Struts 1.2.x RequestProcessor utilized a similar but different set of 
+The Struts 1.2.x RequestProcessor utilized a similar but different set of
 messages. Here we provide a table summarizing the changes.
 </p>
 
@@ -454,16 +454,16 @@
 <subsection name="4.3 ActionForm Classes">
 
     <p>
-    An ActionForm represents an HTML form that the user interacts with over 
-    one or more pages.  
-    You will provide properties to hold the state of the form with getters 
-    and setters to access them.  
-    ActionForms can be stored in either the session (default) or request 
+    An ActionForm represents an HTML form that the user interacts with over
+    one or more pages.
+    You will provide properties to hold the state of the form with getters
+    and setters to access them.
+    ActionForms can be stored in either the session (default) or request
     scope.
-    If they're in the session it's important to implement the form's 
-    reset method to initialize the form before each use.  
+    If they're in the session it's important to implement the form's
+    reset method to initialize the form before each use.
     Core sets the ActionForm's properties from the request parameters and
-    sends the validated form to the appropriate Action's execute 
+    sends the validated form to the appropriate Action's execute
     method.
     </p>
 
@@ -473,21 +473,21 @@
     </p>
 
     <ul>
-    
+
     <li>
     The ActionForm class itself requires no specific
-    methods to be implemented.  
-    It is used to identify the role these particular beans play in the overall 
-    architecture.  
-    Typically, an ActionForm bean will have only property getter 
+    methods to be implemented.
+    It is used to identify the role these particular beans play in the overall
+    architecture.
+    Typically, an ActionForm bean will have only property getter
     and property setter methods, with no business logic.
     </li>
-    
+
     <li>
     The ActionForm object also offers a standard validation mechanism.
     If you override a "stub" method, and provide error messages in the
     standard application resource, Core will automatically validate the
-    input from the form (using your method). 
+    input from the form (using your method).
     See "<a href="building_view.html#3_3_4_Automatic_Form_Validation">Automatic Form
     Validation</a>" for details. Of course, you can also ignore the
     ActionForm validation and provide your own in the Action object.
@@ -496,53 +496,53 @@
     <li>
     Define a property (with associated getXxx and
     setXxx methods) for each field that is present in the
-    form.  
-    The field name and property name must match according to the usual 
-    JavaBeans conventions (see the Javadoc for the 
+    form.
+    The field name and property name must match according to the usual
+    JavaBeans conventions (see the Javadoc for the
     java.beans.Introspector class for a start on information
-    about this).  
-    For example, an input field named username will cause the 
+    about this).
+    For example, an input field named username will cause the
     setUsername method to be called.
     </li>
 
     <li>
     Buttons and other controls on your form can also be defined as properties.
     This can help determine which button or control was selected when the
-    form was submitted. 
-    Remember, the ActionForm is meant to represent your data-entry form, not 
+    form was submitted.
+    Remember, the ActionForm is meant to represent your data-entry form, not
     just the data beans.
     </li>
-    
+
     <li>
     Think of your ActionForm beans as a firewall between HTTP and the Action.
-    Use the validate method to ensure all required properties 
-    are present, and that they contain reasonable values. 
-    An ActionForm that fails validation will not even be presented to the 
+    Use the validate method to ensure all required properties
+    are present, and that they contain reasonable values.
+    An ActionForm that fails validation will not even be presented to the
     Action for handling.
     </li>
-    
+
     <li>
     You may also place a bean instance on your form, and use nested property
-    references. 
-    For example, you might have a "customer" bean on your ActionForm, and 
+    references.
+    For example, you might have a "customer" bean on your ActionForm, and
     then refer to the property "customer.name" in your presentation page.
     This would correspond to the methods customer.getName() and
     customer.setName(String Name) on your customer bean.
     See the Apache Struts Taglib Developer Guides for more about
     using the nested syntax.
     </li>
-    
+
     <li>
     <em>Caution:</em> If you nest an existing bean instance on your form, think
-    about the properties it exposes. 
-    Any public property on an ActionForm that accepts a single String value 
-    can be set with a query string. 
-    It may be useful to place beans that can affect the business state inside 
-    a thin "wrapper" that exposes only the properties required. 
-    This wrapper can also provide a filter to be sure runtime properties are 
+    about the properties it exposes.
+    Any public property on an ActionForm that accepts a single String value
+    can be set with a query string.
+    It may be useful to place beans that can affect the business state inside
+    a thin "wrapper" that exposes only the properties required.
+    This wrapper can also provide a filter to be sure runtime properties are
     not set to inappropriate values.
     </li>
-    
+
     </ul>
 
 </subsection>
@@ -559,27 +559,27 @@
     </p>
     <p>
     This bottleneck can be alleviated through the use of DynaActionForm classes.
-    Instead of creating a new ActionForm subclass and new get/set methods for 
-    each of your bean's properties, you can list its properties, type, and 
+    Instead of creating a new ActionForm subclass and new get/set methods for
+    each of your bean's properties, you can list its properties, type, and
     defaults in the Struts Core configuration file.
     </p>
-    
+
     <p>
-    For example, add the following to struts-config.xml for a UserForm bean 
+    For example, add the following to struts-config.xml for a UserForm bean
     that stores a user's given and family names:
     </p>
-    
+
 <source><![CDATA[
-<form-bean 
-    name="UserForm" 
+<form-bean
+    name="UserForm"
     type="org.apache.struts.action.DynaActionForm">
-    <form-property 
-        name="givenName" 
-        type="java.lang.String" 
+    <form-property
+        name="givenName"
+        type="java.lang.String"
         initial="John"/>
-    <form-property 
-        name="familyName" 
-        type="java.lang.String" 
+    <form-property
+        name="familyName"
+        type="java.lang.String"
         initial="Smith"/>
 </form-bean>
 ]]></source>
@@ -587,9 +587,9 @@
     <p>
     The types supported by DynaActionForm include:
     </p>
-    
+
     <ul>
-    
+
         <li>
         java.math.BigDecimal
         </li>
@@ -649,7 +649,7 @@
         <li>
         java.sql.Timestamp
         </li>
-    
+
     </ul>
 
     <p>
@@ -660,10 +660,10 @@
     </p>
 
     <p>
-    If you do not supply an initial attribute, numbers will be initialized to 
+    If you do not supply an initial attribute, numbers will be initialized to
     0 and objects to null.
     </p>
-    
+
     <p>
     In JSP pages using the Apache Struts Taglib, attributes of
     DynaActionForm objects can be referenced just like ordinary
@@ -676,29 +676,31 @@
     </p>
 
     <p>
-    If you are using the Apache Struts EL taglib for JSTL, the references are
-    different, however.
-    Only properties of ordinary ActionForm objects can be directly
-    accessed through the JSTL expression language syntax.  
-    The DynaActionForm properties must be accessed through a
-    slightly different syntax. 
-    The JSTL EL syntax for referencing a property
-    of an ActionForm goes like this:
+    Since release 1.3.0, JSTL EL works with DynaActionForm properties just
+    like with ordinary ActionForm objects. For example, you can use an
+    expression like this:
     </p>
-    
+
 <source>${formbean.prop}</source>
-    
+
     <p>
-    The syntax for referencing a property of a DynaActionForm
-    would be:
+    Prior to 1.3.0, the references are different, however.
+    Only properties of ordinary ActionForm objects can be directly
+    accessed through the JSTL expression language syntax.
+    The DynaActionForm properties must be accessed through a
+    slightly different syntax.
+    The JSTL EL syntax for referencing a property
+    of a DynaActionForm in Struts 1.2.x and earlier goes like this:
     </p>
 
 <source>${dynabean.map.prop}</source>
 
     <p>
-    The map property is a property of 
+    The map property is a property of
     DynaActionForm which represents the HashMap
     containing the DynaActionForm properties.
+    In Struts 1.3.0 and later, this syntax is still
+    supported, but is no longer required.
     </p>
 
     <p>
@@ -758,7 +760,7 @@
 <subsection name="4.3.2 LazyActionForm Classes">
 
     <p>
-    Struts <i>Lazy</i> ActionForm which <i>wraps</i> a LazyDynaBean (see 
+    Struts <i>Lazy</i> ActionForm which <i>wraps</i> a LazyDynaBean (see
     <a href="http://jakarta.apache.org/commons/beanutils/apidocs/org/apache/commons/beanutils/package-summary.html#dynamic.lazy">
     Commons BeanUtils JavaDoc</a>).
     </p>
@@ -856,21 +858,21 @@
 <subsection name="4.3.3 Map-backed ActionForms">
 
     <p>
-    The DynaActionForm classes offer the ability to create ActionForm beans 
-    at initialization time, based on a list of properties enumerated in the 
+    The DynaActionForm classes offer the ability to create ActionForm beans
+    at initialization time, based on a list of properties enumerated in the
     Struts Core configuration file.
-    However, many HTML forms are generated dynamically at request time. 
-    Since the properties of these forms' ActionForm beans are not all known 
+    However, many HTML forms are generated dynamically at request time.
+    Since the properties of these forms' ActionForm beans are not all known
     ahead of time, we need a new approach.
     </p>
-    
+
     <p>
     Struts Core allows you to make one or more of your ActionForm's
     properties' values a Map instead of a traditional atomic object.
-    You can then store the data from your form's dynamic fields in that Map. 
+    You can then store the data from your form's dynamic fields in that Map.
     Here is an example of a map-backed ActionForm class:
     </p>
-    
+
 <source><![CDATA[public FooForm extends ActionForm {
 
     private final Map values = new HashMap();
@@ -887,27 +889,27 @@
 ]]></source>
 
     <p>
-    In its corresponding JSP page, you can access objects stored in the 
-    values map using a special notation: mapname(keyname). 
+    In its corresponding JSP page, you can access objects stored in the
+    values map using a special notation: mapname(keyname).
     The parentheses in the bean property name indicate that:
     </p>
-    
+
     <ul>
-    
+
         <li>
         The bean property named mapname is indexed using Strings
-        (probably backed by a Map), and that 
+        (probably backed by a Map), and that
         </li>
 
         <li>
         Struts Core should look for get/set methods that take a String key
-        parameter to find the correct sub-property value. 
+        parameter to find the correct sub-property value.
         Core will, of course, use the keyname value from the
         parentheses when it calls the get/set methods.
         </li>
-    
+
     </ul>
-    
+
     <p>
     Here is a simple example:
     </p>
@@ -915,13 +917,13 @@
 <source><![CDATA[<html:text property="value(foo)"/>]]></source>
 
     <p>
-    This will call the getValue method on FooForm with a key 
-    value of "foo" to find the property value. 
+    This will call the getValue method on FooForm with a key
+    value of "foo" to find the property value.
     To create a form with dynamic field names, you could do the following:
     </p>
 
 <source><![CDATA[
-<% 
+<%
 	for (int i = 0; i < 10; i++) {
 		String name = "value(foo-" + i + ")";
 %>
@@ -933,18 +935,18 @@
 ]]></source>
 
     <p>
-    Note that there is nothing special about the name value. 
-    Your map-backed property could instead be named property, 
-    thingy, or any other bean property name you prefer. 
+    Note that there is nothing special about the name value.
+    Your map-backed property could instead be named property,
+    thingy, or any other bean property name you prefer.
     You can even have multiple map-backed properties on the same bean.
     </p>
-    
+
     <p>
-    In addition to map-backed properties, you can also create list-backed 
-    properties. 
+    In addition to map-backed properties, you can also create list-backed
+    properties.
     You do so by creating indexed get/set methods on your bean:
     </p>
-    
+
 <source><![CDATA[public FooForm extends ActionForm {
 
     private final List values = new ArrayList();
@@ -960,11 +962,11 @@
 ]]></source>
 
     <p>
-    In your presentation pages, you access individual entries in a list-backed 
-    property by using a different special notation: 
-    listname[index]. 
-    The braces in the bean property name indicate that the bean property named 
-    listname is indexed (probably backed by a List), and that 
+    In your presentation pages, you access individual entries in a list-backed
+    property by using a different special notation:
+    listname[index].
+    The braces in the bean property name indicate that the bean property named
+    listname is indexed (probably backed by a List), and that
     Core should look for get/set methods that take an index parameter
     in order to find the correct sub-property value.
     </p>
@@ -1003,49 +1005,49 @@
 
     <p>
     Since the majority of teams using Apache Struts are focused on building web
-    applications, most projects will only use the "HttpServletRequest" 
+    applications, most projects will only use the "HttpServletRequest"
     version.
-    A non-HTTP execute() method has been provided for applications that are 
-    not specifically geared towards the HTTP protocol.  
+    A non-HTTP execute() method has been provided for applications that are
+    not specifically geared towards the HTTP protocol.
     </p>
 
     <p>
     The goal of an Action class is to process a request, via
-    its execute method, and return an ActionForward 
-    object that identifies where control should be forwarded (e.g. a JSP, 
-    Tile definition, Velocity template, or another Action) to provide the 
-    appropriate response.  
-    In the <em>MVC/Model 2</em> design pattern, a typical Action 
-    class will often implement logic like the following in its 
+    its execute method, and return an ActionForward
+    object that identifies where control should be forwarded (e.g. a JSP,
+    Tile definition, Velocity template, or another Action) to provide the
+    appropriate response.
+    In the <em>MVC/Model 2</em> design pattern, a typical Action
+    class will often implement logic like the following in its
     execute method:
     </p>
-    
+
     <ul>
-    
+
         <li>
-        Validate the current state of the user's session (for example, 
+        Validate the current state of the user's session (for example,
         checking that the user has successfully logged on).
-        If the Action class finds that no logon exists, the 
-        request can be forwarded to the presentation page that displays the 
-        username and password prompts for logging on.  
-        This could occur because a user tried to enter an application "in the 
-        middle" (say, from a bookmark), or because the session has timed out, 
+        If the Action class finds that no logon exists, the
+        request can be forwarded to the presentation page that displays the
+        username and password prompts for logging on.
+        This could occur because a user tried to enter an application "in the
+        middle" (say, from a bookmark), or because the session has timed out,
         and the servlet container created a new one.
         </li>
 
         <li>
-        If validation is not complete, validate the form bean properties as 
-        needed. 
-        If a problem is found, store the appropriate error message keys as a 
-        request attribute, and forward control back to the input form so that 
+        If validation is not complete, validate the form bean properties as
+        needed.
+        If a problem is found, store the appropriate error message keys as a
+        request attribute, and forward control back to the input form so that
         the errors can be corrected.
         </li>
 
         <li>
         Perform the processing required to deal with this request (such as
-        saving a row into a database).  
-        This <em>can</em> be done by logic code embedded within the 
-        Action class itself, <strong>but</strong> should generally be 
+        saving a row into a database).
+        This <em>can</em> be done by logic code embedded within the
+        Action class itself, <strong>but</strong> should generally be
         performed by calling an appropriate method of a business logic bean.
         </li>
 
@@ -1056,30 +1058,30 @@
         </li>
 
         <li>
-        Return an appropriate ActionForward object that 
-        identifies the presentation page to be used to generate this response, 
-        based on the newly updated beans. 
-        Typically, you will acquire a reference to such an object by calling 
-        findForward on either the ActionMapping 
-        object you received (if you are using a logical name local to this 
-        mapping), or on the controller servlet itself (if you are using a 
+        Return an appropriate ActionForward object that
+        identifies the presentation page to be used to generate this response,
+        based on the newly updated beans.
+        Typically, you will acquire a reference to such an object by calling
+        findForward on either the ActionMapping
+        object you received (if you are using a logical name local to this
+        mapping), or on the controller servlet itself (if you are using a
         logical name global to the application).
         </li>
-    
+
     </ul>
 
     <p>
     In Apache Struts 1.0, Actions called a perform method instead of
-    the now-preferred execute method. 
-    These methods use the same parameters and differ only in which exceptions 
-    they throw. 
-    The elder perform method throws SerlvetException 
-    and IOException. 
-    The new execute method simply throws Exception. 
-    The change was to facilitate the Declarative Exception handling feature 
+    the now-preferred execute method.
+    These methods use the same parameters and differ only in which exceptions
+    they throw.
+    The elder perform method throws SerlvetException
+    and IOException.
+    The new execute method simply throws Exception.
+    The change was to facilitate the Declarative Exception handling feature
     introduced in Apache Struts 1.1.
     </p>
-    
+
     <p>
     The perform method may still be used in Apache Struts 1.1
     but is deprecated.
@@ -1091,89 +1093,89 @@
     </p>
 
    </subsection>
-   
+
    <a name="action_design_guide"/>
    <subsection name="4.4.1 Action Class Design Guidelines">
 
     <p>
-    Remember the following design guidelines when coding Action 
+    Remember the following design guidelines when coding Action
     classes:
     </p>
 
     <ul>
-    
+
         <li>
         <strong>Write code for a multi-threaded environment</strong> -
         Our controller servlet creates <strong>only one instance of your
         Action class</strong>, and uses this one instance to service
-        all requests.  
+        all requests.
         Thus, you need to write thread-safe Action classes.
-        Follow the same guidelines you would use to write thread-safe 
-        Servlets.  
-        Here are two general guidelines that will help you write scalable, 
+        Follow the same guidelines you would use to write thread-safe
+        Servlets.
+        Here are two general guidelines that will help you write scalable,
         thread-safe Action classes:
-        
+
         <ul>
-        
+
             <li>
-            <strong>Only Use Local Variables</strong> - The most important principle 
-            that aids in thread-safe coding is to use only local variables, 
-            <strong>not instance variables</strong>, in your Action class.  
-            Local variables are created on a stack that is assigned (by your 
-            JVM) to each request thread, so there is no need to worry about 
-            sharing them.  
-            An Action can be factored into several local methods, 
-            so  long as all variables needed are passed as method parameters. 
-            This assures thread safety, as the JVM handles such variables 
-            internally using the call stack which is associated with a single 
+            <strong>Only Use Local Variables</strong> - The most important principle
+            that aids in thread-safe coding is to use only local variables,
+            <strong>not instance variables</strong>, in your Action class.
+            Local variables are created on a stack that is assigned (by your
+            JVM) to each request thread, so there is no need to worry about
+            sharing them.
+            An Action can be factored into several local methods,
+            so  long as all variables needed are passed as method parameters.
+            This assures thread safety, as the JVM handles such variables
+            internally using the call stack which is associated with a single
             Thread.
             </li>
-            
+
             <li>
-            <strong>Conserve Resources</strong> - As a general rule, allocating scarce 
-            resources and keeping them across requests from the same user 
-            (in the user's session) can cause scalability problems.  
-            For example, if your application uses JDBC and you  allocate a 
+            <strong>Conserve Resources</strong> - As a general rule, allocating scarce
+            resources and keeping them across requests from the same user
+            (in the user's session) can cause scalability problems.
+            For example, if your application uses JDBC and you  allocate a
             separate JDBC connection for every user, you are probably going
-            to run in some scalability issues when your site suddenly shows 
-            up on Slashdot.  
-            You should strive to use pools and release resources (such as 
-            database connections) prior to forwarding control to the 
-            appropriate View component -- even if a bean method you have 
+            to run in some scalability issues when your site suddenly shows
+            up on Slashdot.
+            You should strive to use pools and release resources (such as
+            database connections) prior to forwarding control to the
+            appropriate View component -- even if a bean method you have
             called throws an exception.
             </li>
-            
+
         </ul>
-    
+
         </li>
 
         <li>
-        <strong>Don't throw it, catch it!</strong> - Ever used a commercial website only to 
-        have a stack trace or exception thrown in your face after you've already 
-        typed in your credit card number and clicked the purchase button?  
-        Let's just say it doesn't inspire confidence.  
-        Now is your chance to deal with these application errors - in the 
-        Action class.  
-        If your application specific code throws expections you should catch these 
+        <strong>Don't throw it, catch it!</strong> - Ever used a commercial website only to
+        have a stack trace or exception thrown in your face after you've already
+        typed in your credit card number and clicked the purchase button?
+        Let's just say it doesn't inspire confidence.
+        Now is your chance to deal with these application errors - in the
+        Action class.
+        If your application specific code throws expections you should catch these
         exceptions  in your Action class, log them in your application's log
-        (servlet.log("Error message", exception)) and return the 
+        (servlet.log("Error message", exception)) and return the
         appropriate ActionForward.
         </li>
-    
+
     </ul>
 
     <p>
     It is wise to avoid creating lengthy and complex Action classes.
-    If you start to embed too much logic in the Action class 
-    itself, you will begin to find the Action class hard to 
-    understand, maintain, and impossible to reuse.  
-    Rather than creating overly complex Action classes, it is generally a 
-    good practice to move most of the persistence, and "business logic" to a 
-    separate application layer.  
-    When an Action class becomes lengthy and procedural, it may be a good time 
+    If you start to embed too much logic in the Action class
+    itself, you will begin to find the Action class hard to
+    understand, maintain, and impossible to reuse.
+    Rather than creating overly complex Action classes, it is generally a
+    good practice to move most of the persistence, and "business logic" to a
+    separate application layer.
+    When an Action class becomes lengthy and procedural, it may be a good time
     to refactor your application architecture and move some of this logic
-    to another conceptual layer; 
-    otherwise, you may be left with an inflexible application which can only 
+    to another conceptual layer;
+    otherwise, you may be left with an inflexible application which can only
     be accessed in a web-application environment.
     Struts Core should be viewed as simply the <strong>foundation</strong>
     for implementing MVC in your applications.
@@ -1182,39 +1184,39 @@
     </p>
 
 </subsection>
-   
+
 <a name="exception_handler"/>
 <subsection name="4.5 Exception Handler">
 
     <p>
-    You can define an ExceptionHandler to execute when an Action's 
-    execute method throws an Exception.  
-    First, you need to subclass 
-    org.apache.struts.action.ExceptionHandler and override the 
+    You can define an ExceptionHandler to execute when an Action's
+    execute method throws an Exception.
+    First, you need to subclass
+    org.apache.struts.action.ExceptionHandler and override the
     execute method.
-    Your execute method should process the Exception and return 
+    Your execute method should process the Exception and return
     an ActionForward object to tell Core where to forward to next.
     Then you configure your handler in struts-config.xml like this:
     </p>
-    
+
 <source><![CDATA[<global-exceptions>
-    <exception 
-      key="some.key" 
-      type="java.io.IOException" 
+    <exception
+      key="some.key"
+      type="java.io.IOException"
       handler="com.yourcorp.ExceptionHandler"/>
 </global-exceptions>
 ]]></source>
-    
+
     <p>
-    This configuration element says that 
-    com.yourcorp.ExceptionHandler.execute will be called when 
-    any IOException is thrown by an Action.  
-    The key is a key into your message resources properties file 
+    This configuration element says that
+    com.yourcorp.ExceptionHandler.execute will be called when
+    any IOException is thrown by an Action.
+    The key is a key into your message resources properties file
     that can be used to retrieve an error message.
     </p>
     <p>
     If the handler attribute is not specified, the default handler
-    stores the exception in the request attribute under the value of the 
+    stores the exception in the request attribute under the value of the
     Globals.EXCEPTION_KEY global key.
     </p>
 
@@ -1226,7 +1228,7 @@
         <th>Attribute</th>
         <th>Description</th>
         <th>Default</th>
-    </tr>    
+    </tr>
     <tr>
         <td>
         bundle
@@ -1259,7 +1261,7 @@
         extends
         </td>
         <td>
-            The name of the exception handler that this 
+            The name of the exception handler that this
             will inherit configuration information from.
         </td>
         <td>
@@ -1328,18 +1330,18 @@
         </td>
     </tr>
     </table>
-    
+
     <p>
-    You can override global exception handlers by defining a handler inside an 
+    You can override global exception handlers by defining a handler inside an
     action definition.
     </p>
-    
+
     <p>
-    A common use of ExceptionHandlers is to configure one for 
-    java.lang.Exception so it's called for any exception and log 
+    A common use of ExceptionHandlers is to configure one for
+    java.lang.Exception so it's called for any exception and log
     the exception to some data store.
     </p>
-    
+
 </subsection>
 
 <a name="plugin_classes"/>
@@ -1347,27 +1349,27 @@
 
     <p>
     The <em>PlugIn</em> interface extends Action and so that applications can
-    easily hook into the ActionServlet lifecycle. 
-    This interface defines two methods, init() and 
-    destroy(), which are called at application startup and 
-    shutdown, respectively. 
-    A common use of a Plugin Action is to configure or load 
+    easily hook into the ActionServlet lifecycle.
+    This interface defines two methods, init() and
+    destroy(), which are called at application startup and
+    shutdown, respectively.
+    A common use of a Plugin Action is to configure or load
     application-specific data as the web application is starting up.
     </p>
-    
+
     <p>
-    At runtime, any resource setup by init would be accessed by 
-    Actions or business tier classes. 
-    The PlugIn interface allows you to setup resources, but does not provide 
-    any special way to access them. 
+    At runtime, any resource setup by init would be accessed by
+    Actions or business tier classes.
+    The PlugIn interface allows you to setup resources, but does not provide
+    any special way to access them.
     Most often, the resource would be stored in application context, under
     a known key, where other components can find it.
     </p>
-    
+
     <p>
     PlugIns are configured using &lt;plug-in&gt; elements within the
     Struts Core configuration file.
-    See <a href="configuration.html#5_2_3_PlugIn_Configuration">PlugIn Configuration</a> 
+    See <a href="configuration.html#5_2_3_PlugIn_Configuration">PlugIn Configuration</a>
     for details.
     </p>
 
@@ -1379,15 +1381,15 @@
     <p>
     In order to operate successfully, our controller servlet needs
     to know several things about how each request URI should be mapped to an
-    appropriate Action class.  
-    The required knowledge has been encapsulated in a Java class named 
+    appropriate Action class.
+    The required knowledge has been encapsulated in a Java class named
     <em>ActionMapping</em>, the most important properties are as follows:
     </p>
 
     <ul>
-    
+
         <li>
-        type - Fully qualified Java class name of the Action 
+        type - Fully qualified Java class name of the Action
         implementation class used by this mapping.
         </li>
 
@@ -1398,28 +1400,28 @@
 
         <li>
         path - The request URI path that is matched to select this
-        mapping.  
-        See below for examples of how matching works and how to use wildcards 
+        mapping.
+        See below for examples of how matching works and how to use wildcards
         to match multiple request URIs.
         </li>
 
         <li>
         unknown - Set to true if this action
         should be configured as the default for this application, to handle
-        all requests not handled by another action.  
+        all requests not handled by another action.
         Only one action can be defined as a default within a single application.
         </li>
 
         <li>
-        validate - Set to true if the 
-        validate method of the action associated with this mapping 
+        validate - Set to true if the
+        validate method of the action associated with this mapping
         should be called.
         </li>
 
         <li>
         forward - The request URI path to which control is passed
-        when this mapping is invoked. 
-        This is an alternative to declaring a type property. 
+        when this mapping is invoked.
+        This is an alternative to declaring a type property.
         </li>
 
     </ul>
@@ -1433,79 +1435,79 @@
     How does the controller servlet learn about the mappings you want?
     It would be possible (but tedious) to write a small Java class that simply
     instantiated new ActionMapping instances, and called all of
-    the appropriate setter methods.  
+    the appropriate setter methods.
     To make this process easier,
     Struts Core uses the Jakarta Commons Digester component
     to parse an XML-based description of the desired mappings
     and create the appropriate objects initialized
     to the appropriate default values.
-    See the <a href="http://jakarta.apache.org/commons">Jakarta Commons 
+    See the <a href="http://jakarta.apache.org/commons">Jakarta Commons
     website</a> for more information about the Digester.
     </p>
 
     <p>
     The developer's responsibility is to create an XML file named
-    struts-config.xml and place it in the WEB-INF directory of 
-    your application. 
+    struts-config.xml and place it in the WEB-INF directory of
+    your application.
     This format of this document is described by the Document Type Definition
     (DTD) maintained at
     <a href="http://struts.apache.org/dtds/struts-config_1_3.dtd">
     http://struts.apache.org/dtds/struts-config_1_3.dtd</a>.
-    This chapter covers the configuration elements that you will typically 
+    This chapter covers the configuration elements that you will typically
     write as part of developing your application.
-    There are several other elements that can be placed in the 
-    struts-config file to customize your application. 
-    See "<a href="configuration.html">Configuring Applications</a>" for more 
+    There are several other elements that can be placed in the
+    struts-config file to customize your application.
+    See "<a href="configuration.html">Configuring Applications</a>" for more
     about the other elements in the Struts Core configuration file.
     </p>
 
     <p>
-    The controller uses an internal copy of this document to parse the 
+    The controller uses an internal copy of this document to parse the
     configuration; an Internet connection is not required for operation.
     </p>
 
     <p>
-    The outermost XML element must be &lt;struts-config&gt;. 
-    Inside of the &lt;struts-config&gt; element, there are three important 
+    The outermost XML element must be &lt;struts-config&gt;.
+    Inside of the &lt;struts-config&gt; element, there are three important
     elements that are used to describe your actions:
     </p>
-    
+
     <ul>
-    
+
         <li>
         &lt;form-beans&gt;
         </li>
-    
+
         <li>
         &lt;global-forwards&gt;
         </li>
-    
+
         <li>
         &lt;action-mappings&gt;
         </li>
 
     </ul>
-    
+
     <p>
     <strong>&lt;form-beans&gt;</strong><br />
-    This section contains your form bean definitions.  
-    Form beans are descriptors that are used to create ActionForm instances 
-    at runtime. 
-    You use a &lt;form-bean&gt; element for each form bean, which has the 
+    This section contains your form bean definitions.
+    Form beans are descriptors that are used to create ActionForm instances
+    at runtime.
+    You use a &lt;form-bean&gt; element for each form bean, which has the
     following important attributes:
     </p>
-    
+
     <ul>
-    
+
         <li>
-        name: A unique identifier for this bean, which will be 
-        used to reference it in corresponding action mappings. 
-        Usually, this is also the name of the request or session attribute 
+        name: A unique identifier for this bean, which will be
+        used to reference it in corresponding action mappings.
+        Usually, this is also the name of the request or session attribute
         under which this form bean will be stored.
         </li>
 
         <li>
-        type: The fully-qualified Java classname of the 
+        type: The fully-qualified Java classname of the
         ActionForm subclass to use with this form bean.
         </li>
 
@@ -1513,22 +1515,22 @@
 
     <p>
     <strong>&lt;global-forwards&gt;</strong><br />
-    This section contains your global forward definitions.  
-    Forwards are instances of the ActionForward class returned from an 
-    Action's execute method. 
-    These map logical names to specific resources (typically JSPs), allowing 
-    you to change the resource without changing references to it throughout 
+    This section contains your global forward definitions.
+    Forwards are instances of the ActionForward class returned from an
+    Action's execute method.
+    These map logical names to specific resources (typically JSPs), allowing
+    you to change the resource without changing references to it throughout
     your application.
-    You use a &lt;forward&gt; element for each forward 
+    You use a &lt;forward&gt; element for each forward
     definition, which has the following important attributes:
     </p>
-    
+
     <ul>
-    
+
         <li>
-        name: The logical name for this forward.  
-        This is used in your Action's execute method to 
-        forward to the next appropriate resource. 
+        name: The logical name for this forward.
+        This is used in your Action's execute method to
+        forward to the next appropriate resource.
         Example: homepage
         </li>
 
@@ -1538,35 +1540,35 @@
         </li>
 
         <li>
-        redirect: True or false 
-        (default).  
+        redirect: True or false
+        (default).
         Should the ActionServlet redirect to the resource instead of forward?
         </li>
 
     </ul>
-    
+
     <p>
     <strong>&lt;action-mappings&gt;</strong><br />
-    This section contains your action definitions.  
-    You use an &lt;action&gt; element for each of the mappings 
-    you would like to define.  
+    This section contains your action definitions.
+    You use an &lt;action&gt; element for each of the mappings
+    you would like to define.
     Most action elements will define at least the following attributes:
     </p>
 
     <ul>
 
         <li>
-        path: The application context-relative path to the 
+        path: The application context-relative path to the
         action.
         </li>
 
         <li>
-        type: The fully qualified java classname of your 
+        type: The fully qualified java classname of your
         Action class.
         </li>
 
         <li>
-        name: The name of your 
+        name: The name of your
         &lt;form-bean&gt; element to use with this action
         </li>
 
@@ -1575,21 +1577,21 @@
     <p>Other often-used attributes include:</p>
 
     <ul>
-    
+
         <li>
-        parameter: A general-purpose attribute often used by 
+        parameter: A general-purpose attribute often used by
         "standard" Actions to pass a required property.
         </li>
-    
+
         <li>
-        roles: A comma-delimited list of the user security roles 
+        roles: A comma-delimited list of the user security roles
         that can access this mapping.
         </li>
-    
+
     </ul>
 
     <p>
-    For a complete description of the elements that can be used with the 
+    For a complete description of the elements that can be used with the
     action element, see the
     <a href="http://struts.apache.org/dtds/struts-config_1_3.dtd">
     Apache Struts Core Configuration DTD</a> and the
@@ -1618,8 +1620,8 @@
      </form-beans>
     <global-forwards
         type="org.apache.struts.action.ActionForward">
-        <forward 
-            name="logon" 
+        <forward
+            name="logon"
             path="/logon.jsp"
             redirect="false" />
     </global-forwards>
@@ -1637,24 +1639,24 @@
 ]]></source>
 
     <p>
-    First the form bean is defined.  
+    First the form bean is defined.
     A basic bean of class "org.apache.struts.webapp.example.LogonForm"
-    is mapped to the logical name "logonForm". 
+    is mapped to the logical name "logonForm".
     This name is used as a request attribute name for the form
     bean.
     </p>
 
     <p>
-    The "global-forwards" section is used to create logical name 
-    mappings for commonly used presentation pages.  
-    Each of these forwards is available through a call to your action mapping 
+    The "global-forwards" section is used to create logical name
+    mappings for commonly used presentation pages.
+    Each of these forwards is available through a call to your action mapping
     instance, i.e. mapping.findForward("logicalName").
     </p>
 
     <p>
-    As you can see, this mapping matches the path /logon 
-    (actually, because the MailReader example application uses extension 
-    mapping, the request URI you specify in a JSP page would end in 
+    As you can see, this mapping matches the path /logon
+    (actually, because the MailReader example application uses extension
+    mapping, the request URI you specify in a JSP page would end in
     /logon.do).
     When a request that matches this path is received, an instance of the
     <em>LogonAction</em> class will be created (the first time only) and used.
@@ -1662,43 +1664,43 @@
     logonForm, creating and saving a bean of the specified class
     if needed.
     </p>
-   
+
     <p>
-    Optional but very useful are the local "forward" elements. 
-    In the MailReader example application, many actions include a local 
+    Optional but very useful are the local "forward" elements.
+    In the MailReader example application, many actions include a local
     "success" and/or "failure" forward as part of an action mapping.
     </p>
 
 <source><![CDATA[<!-- Edit mail subscription -->
-<action    
+<action
     path="/editSubscription"
     type="org.apache.struts.webapp.example.EditSubscriptionAction"
     name="subscriptionForm"
     scope="request"
     validate="false">
-    <forward 
-        name="failure" 
+    <forward
+        name="failure"
         path="/mainMenu.jsp"/>
-    <forward 
-        name="success" 
+    <forward
+        name="success"
         path="/subscription.jsp"/>
 </action>
 ]]></source>
 
     <p>
-    Using just these two extra properties, the Action classes are almost 
-    totally independent of the actual names of the presentation pages. 
-    The pages can be renamed (for example) during a redesign, with negligible 
+    Using just these two extra properties, the Action classes are almost
+    totally independent of the actual names of the presentation pages.
+    The pages can be renamed (for example) during a redesign, with negligible
     impact on the Action classes themselves.
-    If the names of the "next" pages were hard coded into the Action classes, 
-    all of these classes would also need to be modified. 
-    Of course, you can define whatever local forward properties 
+    If the names of the "next" pages were hard coded into the Action classes,
+    all of these classes would also need to be modified.
+    Of course, you can define whatever local forward properties
     makes sense for your own application.
     </p>
 
     <p>
     The Struts Core configuration file includes several other elements that you
-    can use to customize your application. 
+    can use to customize your application.
     See "<a href="configuration.html">Configuring Applications</a>" for details.
     </p>
 
@@ -1708,20 +1710,20 @@
 <subsection name="4.9 Using ActionMappings for Pages">
 
     <p>
-    Fronting your pages with ActionMappings is <em>essential</em> when using 
-    modules, since doing so is the only way you involve the 
-    controller in the request -- and you want to!  
+    Fronting your pages with ActionMappings is <em>essential</em> when using
+    modules, since doing so is the only way you involve the
+    controller in the request -- and you want to!
     The controller puts the application configuration in the request,
-    which makes available all of your module-specific configuration data 
+    which makes available all of your module-specific configuration data
     (including which message resources you are using, request-processor,
     and so forth).
     </p>
-    
+
     <p>
-    The simplest way to do this is to use the forward property 
+    The simplest way to do this is to use the forward property
     of the ActionMapping:
     </p>
-    
+
     <pre>&lt;action path="/view" forward="/view.jsp"/></pre>
 
 </subsection>
@@ -1736,41 +1738,41 @@
     into one more generic mapping.
     </p>
     <p>
-    The best way to explain wildcards is to show an example and walk through 
-    how it works.  This example modifies the previous mapping in the <a 
+    The best way to explain wildcards is to show an example and walk through
+    how it works.  This example modifies the previous mapping in the <a
     href="#4_8_1_ActionMapping_Example">ActionMapping Example</a> section to use
     wildcards to match all pages that start with /edit:
-    </p> 
+    </p>
 
 <source><![CDATA[<!-- Generic edit* mapping -->
-<action    
+<action
     path="/edit*"
     type="org.apache.struts.webapp.example.Edit{1}Action"
     name="{1}Form"
     scope="request"
     validate="false">
-    <forward 
-        name="failure" 
+    <forward
+        name="failure"
         path="/mainMenu.jsp"/>
-    <forward 
-        name="success" 
+    <forward
+        name="success"
         path="/{1}.jsp"/>
 </action>
 ]]></source>
 
     <p>
-    The "*" in the path attribute allows the mapping to match the 
+    The "*" in the path attribute allows the mapping to match the
     request URIs /editSubscription, editRegistration,
-    or any other URI that starts with 
-    /edit, however /editSubscription/add would not be 
-    matched.  The part of 
-    the URI matched by the wildcard will then be substituted into various 
-    attributes of the action mapping and its action forwards replacing 
+    or any other URI that starts with
+    /edit, however /editSubscription/add would not be
+    matched.  The part of
+    the URI matched by the wildcard will then be substituted into various
+    attributes of the action mapping and its action forwards replacing
     {1}.
     For the rest of the request, Core will see the action mapping and its
     action forwards containing the new values.
     </p>
-   
+
     <p>
     Mappings are matched against the request in the order they appear in
     the Struts Core configuration file. If more than one pattern matches the
@@ -1789,7 +1791,7 @@
             *
             </td>
             <td>
-            Matches zero or more characters excluding the 
+            Matches zero or more characters excluding the
             slash ('/') character.
             </td>
         </tr>       <tr>
@@ -1797,7 +1799,7 @@
             **
             </td>
             <td>
-            Matches zero or more characters including the 
+            Matches zero or more characters including the
             slash ('/') character.
             </td>
         </tr>       <tr>
@@ -1806,12 +1808,12 @@
             </td>
             <td>
             The backslash character is used as an escape
-            sequence.  Thus \* matches the character asterisk 
+            sequence.  Thus \* matches the character asterisk
             ('*'), and \\
             matches the character backslash ('\').
             </td>
         </tr>
-    </table>    
+    </table>
 
     <p>
     In the action mapping and action forwards, the wildcard-matched values can
@@ -1844,7 +1846,7 @@
 
     <p>
     Also, the action mapping properties (set using the &lt;set-property key="foo"
-     value="bar"&gt; syntax) will accept wildcard-matched strings in their 
+     value="bar"&gt; syntax) will accept wildcard-matched strings in their
     value attribute.
     </p>
 
@@ -1852,16 +1854,16 @@
     The action forward attributes that will accept wildcard-matched strings
     are:
     </p>
-    
+
     <ul>
         <li>catalog</li>
         <li>command</li>
         <li>path</li>
     </ul>
-    
+
     <p>
-    Like the action mapping, the action forward properties (set using the 
-    &lt;set-property key="foo" value="bar"&gt; syntax) will 
+    Like the action mapping, the action forward properties (set using the
+    &lt;set-property key="foo" value="bar"&gt; syntax) will
     accept wildcard-matched strings in their value attribute.
     </p>
 
@@ -1871,13 +1873,13 @@
 <subsection name="4.11 Commons Logging Interface">
     <p>
     Struts Core doesn't configure logging itself -- it's all done by
-    <a href="http://jakarta.apache.org/commons/">commons-logging</a> 
-    under the covers. 
+    <a href="http://jakarta.apache.org/commons/">commons-logging</a>
+    under the covers.
     The default algorithm is a search:
     </p>
-    
+
     <ul>
-    
+
         <li>
         If Log4J is there, use it.
         </li>
@@ -1889,44 +1891,44 @@
         <li>
         Otherwise, use SimpleLog.
         </li>
-    
+
     </ul>
-    
+
     <p>
-    The commons-logging interface is an <em>ultra-thin</em> bridge to many 
-    different logging implementations.  
-    The intent is to remove compile- and run-time dependencies on any 
-    single logging implementation.  
-    For more information about the currently-supported implementations, 
+    The commons-logging interface is an <em>ultra-thin</em> bridge to many
+    different logging implementations.
+    The intent is to remove compile- and run-time dependencies on any
+    single logging implementation.
+    For more information about the currently-supported implementations,
     please refer to the
     <a href="http://jakarta.apache.org/commons/logging/api/index.html">
-    the description for the org.apache.commons.logging 
+    the description for the org.apache.commons.logging
     package</a>.
     </p>
-    
+
     <p>
     Because Struts Core uses commons-logging and, therefore,
     includes the necessary JAR files,
     you've probably had the occasional fleeting thought,
     <em>"Should I use commons-logging?"</em>
-    The answer (surprise!) depends on the requirements for your particular 
-    project. 
-    If one of your requirements is the ability to easily change logging 
-    implementations with zero impact on your application, then commons-logging 
+    The answer (surprise!) depends on the requirements for your particular
+    project.
+    If one of your requirements is the ability to easily change logging
+    implementations with zero impact on your application, then commons-logging
     is a very good option.
     </p>
-    
+
     <p>
-    <em>"Great!  What do I do to get started using commons-logging in my own 
+    <em>"Great!  What do I do to get started using commons-logging in my own
     code?"</em>
     </p>
-    
+
     <p>
-    Using commons-logging in your own code is very simple - all you need are 
+    Using commons-logging in your own code is very simple - all you need are
     two imports and a declaration for a logger.
     Let's take a look:
     </p>
-    
+
 <source>package com.foo;
 // ...
 import org.apache.commons.logging.Log;
@@ -1937,7 +1939,7 @@
     private static Log log = LogFactory.getLog(Foo.class);
     // ...
     public void setBar(Bar bar) {
-        if (log.isTraceEnabled()) { 
+        if (log.isTraceEnabled()) {
             log.trace("Setting bar to " + bar);
         }
         this.bar = bar;
@@ -1945,7 +1947,7 @@
 // ...
 }
 </source>
-    
+
     <p>
     The general idea is to instantiate a single logger per class and to
     use a name for the logger which reflects where it's being used.  The



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Wendy Smoak <ws...@gmail.com>.
On 11/13/05, Wendy Smoak <ws...@gmail.com> wrote:

> Configurable, how?  Just like commons-fileupload, if you're not using
> it, you don't have to add it to your apps.

Sorry, you probably meant using dynamic forms, but not being
interested in this new behavior, right?  I was thinking about the
build files... you can tell what I've been working on lately. :)

--
Wendy

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Laurie Harper <la...@holoweb.net>.
Wendy Smoak wrote:
> That said, I think this dependency belongs in core/project.xml, not in
> the common build.

That's where I originally had it, then I saw you removed the other 
dependencies that were in there and figured all dependencies should be 
inheritted from build/project.xml. So I guess I should move over then. 
Will do.

L.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Wendy Smoak <ws...@gmail.com>.
On 11/13/05, Hubert Rabago <hr...@gmail.com> wrote:

> Given the size of the new dependency, can this be made configurable so
> that only the users who want to use this feature be required to add
> the 276k jar?  After all, the original reason for the enhancement was
> to support "reusable components that must access form beans".  IMO,
> this would really limit the audience that this feature targets.

Configurable, how?  Just like commons-fileupload, if you're not using
it, you don't have to add it to your apps.

Maven 2 has a scope of 'optional' (but we're not there yet.)

That said, I think this dependency belongs in core/project.xml, not in
the common build.

--
Wendy

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Laurie Harper <la...@holoweb.net>.
Hubert Rabago wrote:
> Several items are "optional" even now.  commons-validator,
> jakarta-oro, commons-fileupload, antlr.  In each case, you'd have to
> use specific features before they become required.  This change
> requires the new jar even if an app decides not to use the new
> feature.  I guess I'm just not convinced that the new dependency is
> worth it, especially to something as basic as using DynaActionForm. 
> This is why I'd +1 Joe's and Niall's suggestions.  In either case, I'm
> hoping I can avoid having to include this in my app if I don't need
> the feature it provides.

OK, that's fair enough. I'll revisit this tomorrow and ensure CGLIB is 
optional, probably by wrapping the enhanced functionality with a test 
for CGLIB availability.

L.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Hubert Rabago <hr...@gmail.com>.
On 11/13/05, Laurie Harper <la...@holoweb.net> wrote:
> Well I initially proposed it back in September and took the lack of
> objections to imply agreement...

At that point, it was an enhancement request from a user.  It got one
response from a committer which suggested an alternative approach.

> > Given the size of the new dependency, can this be made configurable so
> > that only the users who want to use this feature be required to add
> > the 276k jar?  After all, the original reason for the enhancement was
> > to support "reusable components that must access form beans".  IMO,
> > this would really limit the audience that this feature targets.
>
> Well, that's the use case that prompted me but I think it simplifies the
> use of dyna beans in any circumstance where you want to reference its
> properties in a JSP.
>
> I could rework things to check for CGLIB in the classpath and revert to
> the traditional behaviour if it's unavailable, though the documentation
> would then need caveats about downloading/installing CGLIB to get the
> functionality.
>
> L.

Several items are "optional" even now.  commons-validator,
jakarta-oro, commons-fileupload, antlr.  In each case, you'd have to
use specific features before they become required.  This change
requires the new jar even if an app decides not to use the new
feature.  I guess I'm just not convinced that the new dependency is
worth it, especially to something as basic as using DynaActionForm. 
This is why I'd +1 Joe's and Niall's suggestions.  In either case, I'm
hoping I can avoid having to include this in my app if I don't need
the feature it provides.

Hubert

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Laurie Harper <la...@holoweb.net>.
Ted Husted wrote:
> On 11/13/05, Laurie Harper <la...@holoweb.net> wrote:
> 
>>Well I initially proposed it back in September and took the lack of
>>objections to imply agreement...
> 
> I think Niall's and Joe's comments, while not strictly "objections",
> did imply some disagreement, which Hubert has affirmed. If a committer
> comments on a ticket, I think it's important that we address those
> concerns before applyng a patch. Code changes are subject to
> consensus, which means that all the committers (or at least PMC
> members) have to agree, or at least not disagree.
> 
> Why do we all have to agree? Because we are all on the hook to support
> any changes made to the codebase. We all own all of the codebase, not
> just the parts we change ourselves. If one of us is unavailable, the
> rest have to be ready to pick up the slack.
> 
> Of course, Struts is a "commit-then-review" project, so it's perfectly
> all right to make changes first, and then see if people like it. But,
> when we do this, we just have to be prepared to answer any concerns
> afterwards (as we are doing now), which could include reversing the
> change, or implementing the change in an totally different way.
> 
> Meanwhile, I'm setting up the Extras subproject now. One idea would be
> to put a "CGDynaActionForm" class there. We might want to move other
> very optional classes there too, and keep only the "cannonical"
> classes in the Core (or Action Framework) subproject.

Yes, I agree with all of that, I wasn't trying to duck Hubert's comments 
;-) In fact I was pretty much working on the assumption of 
'commit-then-review' and fully expecting commentary.

So, the question now is what to do with the code I committed; I'd 
planned to add a check for CGLIB and not attempt the enhancement if the 
library wasn't present, to address Hubert's concerns. Having just looked 
through Nial's comments on the Bugzilla ticket [1], though, it seems 
like I should go a step further, and make this something you have to 
explicitly enable.

I was planning to look at adding the idea Ryan proposed [2], making it 
possible to specify an interface for the dyna bean to implement. So, how 
about an 'interface' attribute on form-property, which can take either 
the value 'auto' or the FQCN of an actual interface, where 'auto' would 
give you the behaviour I just added.

That way, the existing behaviour doesn't change by default. Or, I can 
roll back the commit and look at creating a seperate (set of) classes to 
be included in the Extras subproject when it's set up.

I'm open to either option, or alternative proposals, whatever gives us 
consensus.

L.

[1] http://issues.apache.org/bugzilla/show_bug.cgi?id=36794
[2] http://issues.apache.org/bugzilla/show_bug.cgi?id=37301


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Ted Husted <te...@gmail.com>.
On 11/13/05, Laurie Harper <la...@holoweb.net> wrote:
> Well I initially proposed it back in September and took the lack of
> objections to imply agreement...

I think Niall's and Joe's comments, while not strictly "objections",
did imply some disagreement, which Hubert has affirmed. If a committer
comments on a ticket, I think it's important that we address those
concerns before applyng a patch. Code changes are subject to
consensus, which means that all the committers (or at least PMC
members) have to agree, or at least not disagree.

Why do we all have to agree? Because we are all on the hook to support
any changes made to the codebase. We all own all of the codebase, not
just the parts we change ourselves. If one of us is unavailable, the
rest have to be ready to pick up the slack.

Of course, Struts is a "commit-then-review" project, so it's perfectly
all right to make changes first, and then see if people like it. But,
when we do this, we just have to be prepared to answer any concerns
afterwards (as we are doing now), which could include reversing the
change, or implementing the change in an totally different way.

Meanwhile, I'm setting up the Extras subproject now. One idea would be
to put a "CGDynaActionForm" class there. We might want to move other
very optional classes there too, and keep only the "cannonical"
classes in the Core (or Action Framework) subproject.

-Ted.

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Laurie Harper <la...@holoweb.net>.
Hubert Rabago wrote:
>>New Revision: 332894
>>
>>     <dependency>
>>+      <groupId>cglib</groupId>
>>+      <artifactId>cglib-nodep</artifactId>
>>+      <version>2.1_3</version>
>>+      <url>http://cglib.sourceforge.net/</url>
>>+      <properties>
>>+        <war.bundle>true</war.bundle>
>>+      </properties>
>>+    </dependency>
> 
> I apologize as well for only commenting now.  I didn't realize an
> agreement had already been made to add a new dependency to support
> this feature.

Well I initially proposed it back in September and took the lack of 
objections to imply agreement...

> Given the size of the new dependency, can this be made configurable so
> that only the users who want to use this feature be required to add
> the 276k jar?  After all, the original reason for the enhancement was
> to support "reusable components that must access form beans".  IMO,
> this would really limit the audience that this feature targets.

Well, that's the use case that prompted me but I think it simplifies the 
use of dyna beans in any circumstance where you want to reference its 
properties in a JSP.

I could rework things to check for CGLIB in the classpath and revert to 
the traditional behaviour if it's unavailable, though the documentation 
would then need caveats about downloading/installing CGLIB to get the 
functionality.

L.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Hubert Rabago <hr...@gmail.com>.
> New Revision: 332894
>
>      <dependency>
> +      <groupId>cglib</groupId>
> +      <artifactId>cglib-nodep</artifactId>
> +      <version>2.1_3</version>
> +      <url>http://cglib.sourceforge.net/</url>
> +      <properties>
> +        <war.bundle>true</war.bundle>
> +      </properties>
> +    </dependency>

I apologize as well for only commenting now.  I didn't realize an
agreement had already been made to add a new dependency to support
this feature.

Given the size of the new dependency, can this be made configurable so
that only the users who want to use this feature be required to add
the 276k jar?  After all, the original reason for the enhancement was
to support "reusable components that must access form beans".  IMO,
this would really limit the audience that this feature targets.

Hubert

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Laurie Harper <la...@holoweb.net>.
Christian Meder wrote:
> Hmm. Should we handle a name of length "0" ? If we get an empty string
> I'd prefer an error instead of continuing.

I deliberately avoided making this an error because, whether it makes 
sense or not ;-), an empty string is currently allowed... Seemed better 
to retain compatibility.

> s/preven/prevent

Doh!

> Oh. It's one of that evil "Mix white space cleanup with doc updates
> making life harder for reviewers" ;-)

Ugg, sorry, thought I'd reverted all those.

L.


---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org


Re: svn commit: r332894 - in /struts: build/trunk/project.xml core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java core/trunk/xdocs/userGuide/building_controller.xml

Posted by Christian Meder <ch...@absolutegiganten.org>.
On Sun, 2005-11-13 at 05:30 +0000, laurieh@apache.org wrote:
> Author: laurieh
> Date: Sat Nov 12 21:30:23 2005
> New Revision: 332894
> 
> URL: http://svn.apache.org/viewcvs?rev=332894&view=rev
> Log:
> iEnhancing DyanActionForm and friends to remove the need for '.map' when
> accessing properties in JSPs, as described in #36794:
> 
> http://issues.apache.org/bugzilla/show_bug.cgi?id=36794
> 
> 
> Modified:
>     struts/build/trunk/project.xml
>     struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java
>     struts/core/trunk/xdocs/userGuide/building_controller.xml
> 
> Modified: struts/build/trunk/project.xml
> URL: http://svn.apache.org/viewcvs/struts/build/trunk/project.xml?rev=332894&r1=332893&r2=332894&view=diff
> ==============================================================================
> --- struts/build/trunk/project.xml (original)
> +++ struts/build/trunk/project.xml Sat Nov 12 21:30:23 2005
> @@ -361,6 +361,16 @@
>      </dependency>
>      
>      <dependency>
> +      <groupId>cglib</groupId>
> +      <artifactId>cglib-nodep</artifactId>
> +      <version>2.1_3</version>
> +      <url>http://cglib.sourceforge.net/</url>
> +      <properties>
> +        <war.bundle>true</war.bundle>
> +      </properties>
> +    </dependency>
> +
> +    <dependency>
>        <groupId>oro</groupId>
>        <artifactId>oro</artifactId>
>        <version>2.0.8</version>
> 
> Modified: struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java
> URL: http://svn.apache.org/viewcvs/struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java?rev=332894&r1=332893&r2=332894&view=diff
> ==============================================================================
> --- struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java (original)
> +++ struts/core/trunk/src/java/org/apache/struts/action/DynaActionFormClass.java Sat Nov 12 21:30:23 2005
> @@ -22,13 +22,24 @@
>  
>  import java.io.Serializable;
>  import java.util.HashMap;
> +import java.util.Map;
> +import java.lang.reflect.Method;
>  
>  import org.apache.commons.beanutils.DynaBean;
>  import org.apache.commons.beanutils.DynaClass;
>  import org.apache.commons.beanutils.DynaProperty;
> +import org.apache.commons.logging.Log;
> +import org.apache.commons.logging.LogFactory;
>  import org.apache.struts.config.FormBeanConfig;
>  import org.apache.struts.config.FormPropertyConfig;
>  import org.apache.struts.util.RequestUtils;
> +import net.sf.cglib.proxy.InterfaceMaker;
> +import net.sf.cglib.proxy.Enhancer;
> +import net.sf.cglib.proxy.MethodInterceptor;
> +import net.sf.cglib.proxy.MethodProxy;
> +import net.sf.cglib.asm.Type;
> +import net.sf.cglib.core.Signature;
> +import net.sf.cglib.core.Constants;
>  
> 
>  /**
> @@ -45,6 +56,7 @@
>  
>  public class DynaActionFormClass implements DynaClass, Serializable {
>  
> +    private Log log = LogFactory.getLog(DynaActionFormClass.class);
>  
>      // ----------------------------------------------------------- Constructors
>  
> @@ -181,10 +193,9 @@
>      public DynaBean newInstance()
>          throws IllegalAccessException, InstantiationException {
>  
> -        DynaActionForm dynaBean =
> -            (DynaActionForm) getBeanClass().newInstance();
> -        dynaBean.setDynaActionFormClass(this);
>          FormPropertyConfig[] props = config.findFormPropertyConfigs();
> +        DynaActionForm dynaBean = (DynaActionForm) doCreate(props);
> +        dynaBean.setDynaActionFormClass(this);
>          for (int i = 0; i < props.length; i++) {
>              dynaBean.set(props[i].getName(), props[i].initial());
>          }
> @@ -312,4 +323,112 @@
>      }
>  
> 
> +    // -------------------------------------------------------- Private Methods
> +
> +    private Object doCreate(FormPropertyConfig[] props) {
> +        // Build an interface to implement consisting of getter/setter
> +        // pairs for each property. Also create a lookup table so we
> +        // can map method names back to the corresponding dynamic
> +        // property on invocation. This allows us to correctly handle
> +        // property names that don't comply with JavaBeans naming
> +        // conventions.
> +        Map properties = new HashMap(props.length * 2);
> +        InterfaceMaker im = new InterfaceMaker();
> +        for (int i = 0; i < props.length; i++) {
> +            String name = props[i].getName();
> +            Class type = props[i].getTypeClass();
> +            Type ttype = Type.getType(type);
> +
> +            if (! name.matches("[\\w]+")) {
> +                // Note: this allows leading digits, which is not legal
> +                // for an identifier but is valid in a getter/setter
> +                // method name. Since you can define such getter/setter
> +                // directly, we support doing so dynamically too.
> +                if (log.isWarnEnabled()) {
> +                    log.warn(
> +                        "Dyna property name '" + name +
> +                        "' in form bean " + config.getName() +
> +                        " is not a legal Java identifier. " +
> +                        "No property access methods generated.");
> +                }
> +            } else {
> +                // Capitalize property name appropriately
> +                String property;
> +                if ((name.length() <= 1) ||

Hmm. Should we handle a name of length "0" ? If we get an empty string
I'd prefer an error instead of continuing.

> +                    (  Character.isLowerCase(name.charAt(0)) &&
> +                    (! Character.isLowerCase(name.charAt(1))))
> +                ) {
> +                    property = name;
> +                } else {
> +                    property =
> +                        Character.toUpperCase(name.charAt(0)) +
> +                        name.substring(1);
> +                }
> +
> +                // Create the getter/setter method pair
> +                Signature getter = new Signature("get"+property, ttype, Constants.TYPES_EMPTY);
> +                Signature setter = new Signature("set"+property, Type.VOID_TYPE, new Type[] { ttype });
> +                im.add(getter, Constants.TYPES_EMPTY);
> +                im.add(setter, Constants.TYPES_EMPTY);
> +                properties.put("get"+property, name);
> +                properties.put("set"+property, name);
> +            }
> +        }
> +        Class beanInterface = im.create();
> +
> +        // Now generate a proxy for the dyna bean that also implements
> +        // the getter/setter methods defined above. We turn off the
> +        // Factory interface to preven problems with BeanUtils.copyProperties

s/preven/prevent

> +        // when both source and target bean are enhanced (otherwise, the
> +        // target bean's callbacks get overwritten with the source beans,
> +        // leading to unexpected behaviour).
> +        Enhancer e = new Enhancer();
> +        e.setSuperclass(beanClass);
> +        e.setInterfaces(new Class[] { beanInterface });
> +        e.setCallback(new BeanInterceptor(properties));
> +        e.setUseFactory(false);
> +
> +        // Return the generated/enhanced bean
> +        return e.create();
> +    }
> +
> +    private static class BeanInterceptor implements MethodInterceptor, Serializable {
> +        private Map propertyLookup;
> +
> +        public BeanInterceptor(Map propertyLookup) {
> +            this.propertyLookup = propertyLookup;
> +        }
> +
> +        public Object intercept(Object obj, Method method, Object[] args,
> +                                MethodProxy proxy) throws Throwable {
> +
> +            String methodNm = method.getName();
> +            String propertyNm = (String) propertyLookup.get(methodNm);
> +            String targetNm = methodNm.substring(0, 3); // get/set
> +
> +            if (propertyNm == null) {
> +                // Not a dyna property access, just pass call along
> +                return proxy.invokeSuper(obj, args);
> +            }
> +
> +            // Handle the dyna property access:
> +            //  - map getFoo(...) to get("foo", ...)
> +            //  - map setFoo(bar, ...) to set("foo", bar, ...)
> +            Class[] targetArgTypes = new Class[args.length + 1];
> +            Object[] targetArgs = new Object[args.length + 1];
> +            targetArgTypes[0] = String.class; // for property name
> +            targetArgs[0] = propertyNm;
> +
> +            System.arraycopy(args, 0, targetArgs, 1, args.length);
> +
> +            for (int i = 0; i < args.length; i++) {
> +                targetArgTypes[i + 1] = args[i].getClass();
> +            }
> +
> +            Method target = obj.getClass().getMethod(targetNm, targetArgTypes);
> +
> +            // Return the result of mapped get/set
> +            return target.invoke(obj, targetArgs);
> +        }
> +    }
>  }
> 
> Modified: struts/core/trunk/xdocs/userGuide/building_controller.xml
> URL: http://svn.apache.org/viewcvs/struts/core/trunk/xdocs/userGuide/building_controller.xml?rev=332894&r1=332893&r2=332894&view=diff
> ==============================================================================
> --- struts/core/trunk/xdocs/userGuide/building_controller.xml (original)
> +++ struts/core/trunk/xdocs/userGuide/building_controller.xml Sat Nov 12 21:30:23 2005

Oh. It's one of that evil "Mix white space cleanup with doc updates
making life harder for reviewers" ;-)




					Christian

-- 
Christian Meder, email: chris@absolutegiganten.org

The Way-Seeking Mind of a tenzo is actualized 
by rolling up your sleeves.

                (Eihei Dogen Zenji)

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org