You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by hu...@apache.org on 2005/12/11 04:59:38 UTC

svn commit: r355892 - /struts/apps/trunk/mailreader/src/webapp/tour.html

Author: husted
Date: Sat Dec 10 19:59:35 2005
New Revision: 355892

URL: http://svn.apache.org/viewcvs?rev=355892&view=rev
Log:
MailReader tour 
* Additional interim changes
* The final pass will have to wait for the hackathon :)

Modified:
    struts/apps/trunk/mailreader/src/webapp/tour.html

Modified: struts/apps/trunk/mailreader/src/webapp/tour.html
URL: http://svn.apache.org/viewcvs/struts/apps/trunk/mailreader/src/webapp/tour.html?rev=355892&r1=355891&r2=355892&view=diff
==============================================================================
--- struts/apps/trunk/mailreader/src/webapp/tour.html (original)
+++ struts/apps/trunk/mailreader/src/webapp/tour.html Sat Dec 10 19:59:35 2005
@@ -1279,13 +1279,41 @@
 
     <p>
         The same thing will happen for the other markers.
-        The ActionForm name becomes "RegsiterForm".
-        The nsame of the Success server page becomes "Register.jsp".
+        The ActionForm name becomes "RegisterForm".
+        The name of the "Success" server page becomes "Register.jsp".
     </p>
 
     <p>
-        In any event, when either the Welcome page or MainMenu page link to "EditRegistration",
-        the framework invokes the Edit method of the RegistrationAction class.
+        The <strong>RegisterForm</strong> is a DynaActionForm, much like the LoginForm.
+    </p>
+
+    <hr />
+    <h5>The RegistrationForm form-bean element</h5>
+<pre><code>    &lt;!-- RegistrationAction form bean -->
+    &lt;form-bean
+            name="RegistrationForm"
+            extends="BaseForm">
+    &lt;form-property
+            name="fromAddress"
+            type="java.lang.String"/>
+    &lt;form-property
+            name="fullName"
+            type="java.lang.String"/>
+    &lt;form-property
+            name="password2"
+            type="java.lang.String"/>
+    &lt;form-property
+            name="replyToAddress"
+            type="java.lang.String"/>
+    &lt;/form-bean></code></pre>
+    <hr />
+
+    <p>
+        The <strong>RegistrationForm</strong> extends the BaseForm and adds properties peculiar to the Registration
+        action.
+        When either the Welcome page or MainMenu page link to "EditRegistration",
+        the framework creates the RegistrationForm, autopopulates the form-bean properties from any matching attributes
+        in the request, and invokes the Edit method of the RegistrationAction class.
     </p>
 
     <hr />
@@ -1438,223 +1466,407 @@
 
     <p>
         After the token has been saved,
-        Edit calls doFindSuccess and control flows to the Registration page.
+        Edit calls "doFindSuccess and" control flows to the Registration page.
     </p>
 
     <h3><a name="Registration.jsp" id="Registration.jsp">Registration page</a></h3>
 
     <p>
-        If you follow the "Edit your user registration profile" link from the mainMenu,
-        we will finally reach the heart of the Example application: the Registration, or "Profile", page.
-        This page displays everything the Example application knows about you (or at least your login),
-        while demonstrating several interesting techniques.
+        If you follow the "Edit your user registration profile" link from the Main Menu page,
+        we will finally reach the heart of the MailReader application: the Registration, or "Profile", page.
+        This page displays everything MailReader knows about you (or at least your login),
+        while utilizing several interesting techniques.
     </p>
 
     <p>
-        registation.jsp continues to use logic tags throughout the page so that a single JSP can be used to
-        perform more than one task.
-        For example, if you are editing the form (action == "Edit"),
+        To do double duty as the "Create" registration page and the "Edit" registration,
+        the Registation.jsp makes good use of logic tags throughout the page,
+        to make it appears as though there are two distinct pages.
+        For example, if you are editing the form (task == "Edit"),
         the page inserts your username from the RegistrationForm bean.
-        If you are new user (action == "Create"), the page creates an empty field, so you can pick your username.</p>
+        If you are new user (task == "Create"), the page creates an empty field, so you can pick your username.
+    </p>
 
+    <hr/>
+    <h5>Note:</h5>
     <blockquote>
-      <p><i>The Struts logic tags are a very convenient way to express application logic within your pages. This prevents user error and reduces the number of JSPs your application needs to maintain, among other benefits.</i></p>
+      <p><font class="hint">
+          <strong>Presention Logic</strong> -
+          The Struts logic tags are a convenient way to express presentation logic within your pages.
+          Customized pages help to prevent user error,
+          and dynamic customization reduces the number of JSPs your application needs to maintain,
+          among other benefits.
+      </font></p>
     </blockquote>
+    <hr/>
 
-    <p>The page also uses logic tags to display a list of subscriptions for the given user. If the user enters this page with an edit action in the request context, the lower part of the page listing the subscriptions is exposed by this logic tag:</p>
+    <p>
+        The page also uses logic tags to display a list of subscriptions for the given user.
+        If the RegistrationForm has task set to "Edit",
+        the lower part of the page lists the subscriptions is exposed.
+    </p>
 
-    <blockquote>
-      <p><code>&lt;logic:equal<br />
-      name="RegistrationForm"<br />
-      property="action"<br />
-      scope="request"<br />
-      value="Edit"<br />
-      &gt;</code></p>
-    </blockquote>
+    <hr />
+    <h5></h5>
+<pre><code>&lt;logic:equal name="RegistrationForm" property="task"
+            scope="request" value="Edit">
+    &lt;h3>&lt;bean:message key="heading.subscriptions"/>&lt;/h3>
+    &lt;!-- ... -->
+    &lt;html:link action="/EditSubscription">
+      &lt;bean:message key="registration.addSubscription"/>
+    &lt;/html:link>
+&lt;/logic:equal></code></pre>
+    <hr />
 
-    <p>Otherwise, the page just contains the top portion -- a blank data-entry form for creating the user's registration.</p>
+    <p>
+        Otherwise, the page just contains the top portion --
+        a blank data-entry form for creating the user's registration.
+    </p>
 
     <h4><a name="logic:iterate" id="logic:iterate">logic:iterate</a></h4>
 
-    <p>Beside making the usual conditional tests, you can also use logic tags to forward control to other actions, to redirect control to another path, and to iterate over collections. The registration page includes a good example of using the logic:iterate tag to display the user's subscriptions.</p>
+    <p>
+        Beside making the usual conditional tests,
+        you can also use logic tags to forward control to other actions,
+        to redirect control to another path, and to iterate over collections.
+        The registration page includes a good example of using the logic:iterate tag to display the user's subscriptions.
+    </p>
 
-    <p>The subscriptions are stored in a hashtable object, which is in turn stored in the user object. So to display each subscription, we have to reach into the user object, and loop through the members of the subscription collection. Using the iterate tag, this couldn't be easier.</p>
+    <p>
+        The subscriptions are stored in a hashtable object, which is in turn stored in the user object.
+        So to display each subscription, we have to reach into the user object,
+        and loop through the members of the subscription collection.
+        Using the iterate tag, you can code it the way it sounds.
+    </p>
 
-    <blockquote>
-      <p>&lt;logic:iterate name="user" property="subscriptions" id="subscription"&gt;<br />
-      &lt;!-- block to repeat --&gt;<br />
-      &lt;/logic:iterate&gt;</p>
-    </blockquote>
+    <hr />
+    <h5>Using logic:iterate to list the Subscriptions</h5>
+<pre><code>    &lt;logic:iterate <strong>name</strong>="user" <strong>property</strong>="subscriptions" <strong>id</strong>="subscription">
+      &lt;tr>
+        &lt;td align="left">
+          &lt;bean:write name="subscription" property="host"/>
+        &lt;/td>
+        &lt;td align="left">
+          &lt;bean:write name="subscription" property="username"/>
+        &lt;/td>
+        &lt;td align="center">
+          &lt;bean:write name="subscription" property="type"/>
+        &lt;/td>
+        &lt;td align="center">
+          &lt;bean:write name="subscription" property="autoConnect"/>
+        &lt;/td>
+        &lt;td align="center">
+          &lt;html:link action="/DeleteSubscription"
+           <strong>paramName</strong>="subscription" <strong>paramProperty</strong>="host" <strong>paramId</strong>="host">
+            &lt;bean:message key="registration.deleteSubscription"/>
+          &lt;/html:link>
+            &nbsp;
+          &lt;html:link action="/EditSubscription"
+           paramName="subscription" paramProperty="host" paramId="host">
+            &lt;bean:message key="registration.editSubscription"/>
+          &lt;/html:link>
+        &lt;/td>
+      &lt;/tr>
+    &lt;/logic:iterate></code></pre>
+    <hr />
 
-    <p>The three parameters to the iterate tag ( name, property, and id) tell it to</p>
+    <p>
+        The three parameters to the iterate tag (name, property, and id) tell it to
+    </p>
 
     <ol>
-      <li>Check this context for an attribute (e.g. object) named "user",</li>
-
-      <li>Snag the property of user named "subscriptions",</li>
-
-      <li>In the block to iterate, use "subscription" (singular) as the name for each member of the collection.</li>
+      <li>
+          <em>name</em> - Check this context for an attribute (e.g. object) named <strong>user</strong>,
+      </li>
+
+      <li>
+          <em>property</em> - Snag the property of user named <strong>subscriptions</strong>,
+      </li>
+
+      <li>
+          <em>id</em> - In the block to iterate,
+          use <strong>subscription</strong> (singular) as the name for each member of the collection.
+      </li>
     </ol>
 
-    <p>So, to list the host for each subscription in a HTML unordered list, we could write:</p>
-
-    <blockquote>
-      <p><code>&lt;ul&gt;<br />
-      &lt;logic:iterate name="user" property="subscriptions" id="subscription"&gt;<br />
-      &lt;li&gt;<br />
-      &lt;bean:write name="subscription" property="host" filter="true" /&gt;<br />
-      &lt;/li&gt;<br />
-      &lt;/logic:iterate&gt;<br />
-      &lt;/ul&gt;</code></p>
+    <p>
+        Next to each entry in the subscripotion list are links to Delete and Edit commands.
+        These links use the same name/property/id trinity as the interator,
+        except that the attributes are used to create a hyperlink with a single parameter.
+        (Multiple parameters are possible too, but if the code is well-factored,
+        most often one is sufficient.)
+    </p>
 
-      <p><i>This is another good example of how Struts works with the standard JSP tags, like bean. The filter option says to use convert HTML commands to their character entity. So a &lt; would be output in the HTML as &amp;lt;.</i></p>
-    </blockquote>
+    <p>
+        Given a subscription to "mail.yahoo.com",
+        the command links would translate to HTML links like these:
+    </p>
 
-    <p>In registration.jsp, iterate is used to create a menu of subscriptions, each linked with an edit and delete action.</p>
+    <hr />
+    <h5>The Delete and Edit links for mail.yahoo.com</h5>
+<pre><code>      &lt;a href="/struts-mailreader/DeleteSubscription.do?host=mail.yahoo.com">Delete&lt;/a>
+        &nbsp;
+      &lt;a href="/struts-mailreader/EditSubscription.do?host=mail.yahoo.com">Edit&lt;/a></code></pre>
+    <hr />
 
-    <blockquote>
-      <p><code>&lt;logic:iterate id="subscription" name="user" property="subscriptions"&gt;<br />
-      &lt;tr&gt;<br />
-      &lt;td align="left"&gt;<br />
-      &lt;bean:write name="subscription" property="host" filter="true"/&gt;<br />
-      &lt;/td&gt;<br />
-      &lt;td align="left"&gt;<br />
-      &lt;bean:write name="subscription" property="username" filter="true"/&gt;<br />
-      &lt;/td&gt;<br />
-      &lt;td align="center"&gt;<br />
-      &lt;bean:write name="subscription" property="type" filter="true"/&gt;<br />
-      &lt;/td&gt;<br />
-      &lt;td align="center"&gt;<br />
-      &lt;bean:write name="subscription" property="autoConnect"/&gt;<br />
-      &lt;/td&gt;<br />
-      &lt;td align="center"&gt;<br />
-      &lt;app:linkSubscription page="/EditSubscription.do?action=Delete"&gt;<br />
-      &lt;bean:message key="registration.deleteSubscription"/&gt;<br />
-      &lt;/app:linkSubscription&gt;<br />
-      &lt;app:linkSubscription page="/EditSubscription.do?action=Edit"&gt;<br />
-      &lt;bean:message key="registration.EditSubscription"/&gt;<br />
-      &lt;/app:linkSubscription&gt;<br />
-      &lt;/td&gt;<br />
-      &lt;/tr&gt;<br />
-      &lt;/logic:iterate&gt;</code></p>
+    <p>
+        At the foot of the Register page is a link for adding a subscription.
+        Let's wind up the tour by following the Add link and then logging off.
+        Like the link for creating a Registration, Add  points to an "Edit" action,
+        namely "EditSubscription".
+    </p>
 
-      <p><i>The collection in an iterate tag can be any of the following: an array of objects, an Iterator, a Collection (which includes Lists, Sets and Vectors), or a Map (which includes Hashtables) whose elements will be iterated over.</i></p>
-    </blockquote>
+    <h4>
+        <a name="EditSubscriptionAction.java" id="EditSubscriptionAction.java">EditSubscriptionAction.java</a>
+    </h4>
 
-    <p>You'll note that the hyperlinks to the edit and delete action for each subscription are written with another custom tag, app:linkSubscription. Writing a hyperlink to an action is not difficult, but it can be ugly, and makes an excellent case for encapsulation. If you trace through the app.tld, you will find that the source code for the linkSubscription tag lives in (<i>come on, take a guess</i>) LinkSubscriptionTag.java.</p>
+    <p>
+        The EditSubscription links reuses the Wildcard "/Edit*" link used by EditRegistration,
+        so there nothing new to show there.
+        As before, in the case of "Edit<em>Subscription</em>",
+        the "{1}Form" attribute maps to <strong>SubscriptionForm</strong>.
+    </p>
 
-    <h4><a name="LinkSubscriptionTag.java" id="LinkSubscriptionTag.java">LinkSubscriptionTag.java</a></h4>
+    <hr />
+    <h5>The SubscriptionAction form-bean element</h5>
+<pre><code>        &lt;form-bean
+            name="SubscriptionForm"
+            extends="BaseForm">
+            &lt;form-property
+                    name="autoConnect"
+                    <strong>type="java.lang.Boolean"
+                    initial="FALSE"
+                    reset="true"</strong>/>
+            &lt;form-property
+                    name="host"
+                    type="java.lang.String" />
+            &lt;form-property
+                    name="type"
+                    type="java.lang.String" />
+        &lt;/form-bean></code></pre>
+    <hr />
 
-    <p>The Example application uses a subscription's host name (e.g. yahoo.com) as a primary key, which means you can only have one subscription for each host. It also means that to edit a subscription, all you need to know is the user and host. In fact, the EditSubscription action is designed to create, edit, or delete a subscription if provided a user and host names in the request. The goal of LinkSubscriptionTag is then to output a block like:</p>
+    <p>
+        The other DynaActionForms we've seen used only String properties.
+        SubscriptionForm is different in that it uses a Boolean type for the "autoConnect" property.
+        On the HTML form, the autoConnect field is represented by a checkbox,
+        and checkboxes need to be handled differently that other controls.
+    </p>
 
+    <hr/>
+    <h5>Note:</h5>
     <blockquote>
-      <p><code>&lt;A HREF=[path]EditSubscription.do?action=[action]&amp;username=[user]&amp;host=[host]"&gt;[action]<br />
-      &lt;/A&gt;</code></p>
-    </blockquote>
-
-    <p>based on input block like:</p>
+      <p class="hint">
+          <strong>Checkboxes</strong> -
+          The HTML checkbox is a tricky control.
+          The problem is that, according to the W3C specification, a value is only guarnateed to be sent
+          if the control is checked.
+          If the control is not checked, then the control may be omitted from the request, as if it was on on the page.
+          This can cause a problem with session-scope checkboxes.
+          Once you set the checkbox to true, the control can't set it to false again,
+          because if you uncheck the box, nothing is sent, and so the control stays false.
+      </p>
 
-    <blockquote>
-      <p><code>&lt;app:linkSubscription<br />
-      page="/EditSubscription.do?action=Delete"&gt;Delete<br />
-      &lt;/app:linkSubscription&gt;</code></p>
+      <p class="hint">
+          The simple solution is to set the value for a checkbox control to false before the form is populated.
+          If the checkbox is checked, it will return a value, and the checkbox will represent true.
+          If the checkbox is unchecked, it will not return a value, and the checkbox will remain false.
+      </p>
     </blockquote>
+    <hr/>
 
-    <p>To reduce overhead, LinkSubscriptionTag uses "subscription" as the default name (which the iterator refers to as "ID"), so that can be omitted from the tag properties. The "action" portion of the will differ, and so that is given as the page property to the tag</p>
+    <p>
+        To be sure the autoConnect checkbox is handled correctly,
+        the SubscriptionForm initilizes the property to FALSE, and sets "reset" to TRUE,
+        so that the property is set back to FALSE before autopopulation.
+    </p>
 
-    <p>Here are a few annotated highlights from LinkSubscriptionTag.java:</p>
+    <p>
+        The SubscriptionForm will also look familiar, but it also has a few twists of its own.
+    </p>
 
-    <ol>
-      <li><i>Create a string buffer, and ask the request for the relative path to the application</i><br />
-      <code>StringBuffer url = new StringBuffer(request.getContextPath());</code></li>
+    <hr/>
+    <h5>SubscriptionAction.Edit</h5>
+<pre><code>public ActionForward Edit(
+        ActionMapping mapping,
+        ActionForm form,
+        HttpServletRequest request,
+        HttpServletResponse response)
+        throws Exception {
 
-      <li><i>Snag a reference to the subscription bean (for this iteration)<br /></i> <code>subscription = (Subscription) pageContext.findAttribute(name);</code></li>
+        final String method = Constants.EDIT;
+        doLogProcess(mapping,method);
 
-      <li><i>Append the username and host from the bean to the path request.<br /></i> <code>url.append("&amp;username="); url.append(BeanUtils.filter(subscription.getUser().getUsername()));<br />
-      url.append("&amp;host=");<br />
-      url.append(BeanUtils.filter(subscription.getHost()));</code></li>
-    </ol>
+        HttpSession session = request.getSession();
+        User user = doGetUser(session);
+        <strong>if (user==null) return doFindLogon(mapping);</strong>
+
+        // Retrieve the subscription, if there is one
+        Subscription subscription;
+        <strong>String host = doGet(form,HOST);
+        boolean updating = (host!=null);</strong>
+        if (updating) {
+            subscription = <strong>doFindSubscription</strong>(user,host);
+            if (subscription==null) return <strong>doFindFailure</strong>(mapping);
+            session.setAttribute(Constants.SUBSCRIPTION_KEY, subscription);
+            <strong>doPopulate</strong>(form,subscription);
+            doSet(form,TASK,method);
+        }
 
-    <p>These are the essentials, but be sure to see the full source in LinkSubscriptionTag.java for the rest of the error and logic checking that a working application needs to succeed.</p>
+        return doFindSuccess(mapping);
+    }</code></pre>
+    <hr />
 
-    <p>Meanwhile, back on registration.jsp, there is one more link on the page. This uses yet another custom tag, the app:linkUser tag.</p>
+    <p>
+        In RegistrationAction, we looked for the user object to decide if we were updating or inserting.
+        In SubscriptionAction, the user object is required (and we trot off to the Login page if it is missing).
+        This could happen because a session expired, or because someone bookmarked a page.
+    </p>
 
-    <blockquote>
-      <p><code>&lt;app:linkUser page="/EditSubscription.do?action=Create"&gt;<br />
-      &lt;bean:message key="registration.addSubscription"/&gt;<br />
-      &lt;/app:linkUser&gt;</code></p>
-    </blockquote>
+    <p>
+        To decide if we are inserting or updating a subscription,
+        we look to see if the <strong>host</strong> is set to the ActionForm.
+        If it is an update, we fetch the subcription from the database.
+    </p>
 
-    <p>By this time, you must be ready to flip directly to LinkUserTag.java with nary a glance at the configuration file ...</p>
+    <hr />
+    <h5>SubscriptionAction.doFindSubscription</h5>
+<pre><code>    private Subscription doFindSubscription(User user, String host) {
 
-    <h4><a name="LinkUserTag.java" id="LinkUserTag.java">LinkUserTag.java</a></h4>
+        Subscription subscription;
 
-    <p>Since they solve the same general problem, LinkUserTag and LinkSubscriptionTag are quite a bit a like, except that LinkUserTag grabs the user bean from the session context, instead of a subscription bean from the iteration. Like the LinkSubscriptionTag, the name for the user bean (e.g. "user") is defaulted, and can be omitted from the tag; all that's needed is the page property -- the rest is automatic!</p>
+        try {
+            subscription = user.findSubscription(host);
+        }
+        catch (NullPointerException e) {
+            subscription = null;
+        }
 
-    <blockquote>
-      <p><code>&lt;app:linkUser page="/EditSubscription.do?action=Create"&gt;<br />
-      &lt;bean:message key="registration.addSubscription"/&gt;<br />
-      &lt;/app:linkUser&gt;</code></p>
-    </blockquote>
+        if ((subscription == null) && (log.isTraceEnabled())) {
+            log.trace(
+                " No subscription for user "
+                + user.getUsername()
+                + " and host "
+                + host);
+        }
 
-    <p>When rendered, this displays a HTML hypertext link like:</p>
+        return subscription;
+    }</code></pre>
+    <hr />
 
-    <blockquote>
-      <p><code>&lt;a href="/struts-example/EditSubscription.do?action=Create&amp;amp;username=user"&gt;<br />
-      Add<br />
-      &lt;/a&gt;</code></p>
+    <p>
+        If we can't find the subscription,
+        we use <strong>doFindFailure</strong> to forward to the Failure result (Error.jsp).
+    </p>
 
-      <p><i>Note that anchor links with ampersands should use the character entity &amp;amp; as the LinkUserTag has done here (<a href="http://www.w3.org/TR/html401/appendix/notes.html#h-B.2.2">http://www.w3.org/TR/html401/appendix/notes.html#h-B.2.2</a>).</i></p>
-    </blockquote>
+    <hr />
+    <h5>BaseAction.doFindFailure</h5>
+<pre><code>    protected ActionForward doFindFailure(ActionMapping mapping) {
+        if (log.isTraceEnabled()) {
+            log.trace(Constants.LOG_FAILURE);
+        }
+        return (mapping.findForward(Constants.FAILURE));
+    }
+</code></pre>
+    <hr />
 
-    <p>Let's follow that "Add" link now and see what's up with the EditSubcription action anyway.</p>
+    <p>
+        In the normal course, the subscription should alwawys be found,
+        since we selected the host from a system-generated list.
+        If the subscription is not found,
+        it would because the database disappeared or the request is being spoofed.
+    </p>
 
-    <h4><a name="EditSubscriptionAction.java" id="EditSubscriptionAction.java">EditSubscriptionAction.java</a></h4>
+    <p>
+        Like the RegisterAction, the <strong>doPopulate</strong> method transfers data from the form to the domain
+        object. In this case, a Subscription object.
+    </p>
 
-    <p>Predictably, we find a some now-familiar mappings in struts-config.xml</p>
+    <hr />
+    <h5>SubscriptionAction.doPopulate</h5>
+<pre><code>    private void doPopulate(ActionForm form, Subscription subscription) throws ServletException {
 
-    <blockquote>
-      <p><code>&lt;!-- Subscription form bean --&gt;<br />
-      &lt;form-bean<br />
-      name="SubscriptionForm"<br />
-      type="org.apache.struts.webapp.example.SubscriptionForm"<br />
-      /&gt;</code></p>
+        final String title = Constants.EDIT;
 
-      <p><code>&lt;!-- Edit mail subscription --&gt;<br />
-      &lt;action path="/EditSubscription"<br />
-      type="org.apache.struts.webapp.example.EditSubscriptionAction"<br />
-      name="SubscriptionForm"<br />
-      scope="request"<br />
-      validate="false"<br />
-      &gt;<br />
-      &lt;forward name="failure" path="/mainMenu.jsp"/&gt;<br />
-      &lt;forward name="success" path="/subscription.jsp"/&gt;<br />
-      &lt;/action&gt;</code></p>
+        if (log.isTraceEnabled()) {
+            log.trace(Constants.LOG_POPULATE_FORM + subscription.getHost());
+        }
 
-      <p><i>When we've introduced these type of mappings before, and mentioned that the struts-config.xml was parsed when the ActionServlet was initialized. But we should make it clear that when the Struts digester parsed this file, it actually created standard Java objects, linked as properties to the controller. This means you don't have to edit Java source files just to add a bunch of "new" statements. (How cool is that?)</i></p>
-    </blockquote>
+        try {
+            <strong>PropertyUtils.copyProperties(form, subscription);</strong>
+            doSet(form,TASK,title);
+        } catch (InvocationTargetException e) {
+            Throwable t = e.getTargetException();
+            if (t == null) t = e;
+            log.error(LOG_SUBSCRIPTION_POPULATE, t);
+            throw new ServletException(LOG_SUBSCRIPTION_POPULATE, t);
+        } catch (Throwable t) {
+            log.error(LOG_SUBSCRIPTION_POPULATE, t);
+            throw new ServletException(LOG_SUBSCRIPTION_POPULATE, t);
+        }
+    }</code></pre>
+    <hr />
 
-    <p>Following what was specified by struts-config.xml, the controller makes sure that a SubscriptionForm bean exists, along with the SubscriptionAction object, and then calls the action object's perform method. The perform method first checks to see that the user is logged-in. If not, control is forwarded to the Login action. EditSubscriptionAction.perform then either creates a new subscription object (if the task is Create), or searches the user's subscription hashtable for a matching hostname (if the task is Edit).</p>
+    <p>
+        Most of the code in "doPopulate" is window dressing for the call to
+        <strong>PropertyUtils.copyProperties</strong>, which does the heavy lifting.
+    </p>
 
-    <p>Finally, EditSubscriptionAction conforms the ActionForm bean with the database bean. There may be several subscriptions in the database, but in EditSubscriptionAction we expose the one selected (or just created) for this request to use. Once the Action form (called "subform" in the code) is created and populated from the database, the bean's action is set to either Create or Edit, and control is forwarded to our "success" form, subscription.jsp .</p>
+    <p>
+        But before turning to our final JSP, a word about our database model ...
+    </p>
 
-    <blockquote>
-      <p><i>Note that the servlet only creates one object for each action. Each request is handled as a separate thread, and passed to the single action object instance. This means your action objects must be multi-thread safe.</i></p>
-    </blockquote>
+    <h4>
+        <a name="User.java" id="User.java">User.java</a> and <a name="Subscription.java" id="Subscription.java">Subscription.java</a></h4>
 
-    <p>But before turning to our final JSP, a word about our database model ...</p>
+    <p>
+        If you're used to working with relational databases,
+        the links between the user and subscription objects may be confusing.
+        A conventional relational database would create two distinct tables,
+        one for the users and another for the subscriptions,
+        and link them together with a user ID.
+        The MailReader application implements a different model, a hierarchical database.
+        Here a "table" of subscriptions is stored within each user object,
+        something like the way a filing system stores documents within folders.
+    </p>
 
-    <h4><a name="User.java" id="User.java">User.java</a> and <a name="Subscription.java" id="Subscription.java">Subscription.java</a></h4>
+    <p>
+        Development teams often use frameworks like Hibernate to map a relational database to a hierarchy of objects,
+        like the one used by MailReader.
+        For simplicity, the MailReader doesn't use a conventional database, but saves its data as an XML file.
+        While the MailReader is running, the database is kept in main memory, and written to back to disk when changed.
+    </p>
 
-    <p>If you're used to working with relational databases, the links between the user and subscription objects may be confusing. A conventional relational database would create two distinct tables, one for the users and another for the subscriptions, and link them together with a user ID. The Example application implements a different model, a hierarchical database. Here a "table" of subscriptions is stored within each user object, something like the way a filing system stores documents within folders.</p>
+    <p>
+        In addition to the usual getters and setters,
+        the user object also has two methods for working with subscription objects.
+        The <strong>findSubscription</strong> method takes a hostname and returns the subscription object for that host.
+        The <strong>getSubscriptions</strong> method returns an array of all the subscriptions for the user
+        (ready-made for the iterate tag!).
+        Besides the fields needed to manage the SubscriptionForm data,
+        the object also maintains a runtime link to its own user object.
+    </p>
 
-    <p>In addition to the usual getters and setters, the user object also has two methods for working with subscription objects. findSubscription takes a hostname and returns the subscription object for that host. getSubscriptions returns an array of all the subscriptions for the user (ready-made for the iterate tag!). Besides the fields needed to manage the SubscriptionForm data, the object also maintains a runtime link to its user object.</p>
+    <p>
+        To create a new subscription,
+        EditSubscriptionAction.java simply creates a new subscription object,
+        and sets its user to the object found in the request,
+        and then forwards control to its input form, Subscription.jsp.
+    </p>
 
-    <p>To create a new subscription, EditSubscriptionAction.java simply creates a new subscription object, and sets its user to the object found in the request, and then forwards control to its input form, subscription.jsp.</p>
+    <h3><a name="subcription.jsp" id="subcription.jsp">Subscription.jsp</a></h3>
 
-    <h3><a name="subcription.jsp" id="subcription.jsp">subscription.jsp</a></h3>Saving the best for last, subscription.jsp demonstrates use of some interesting Struts custom form tags, html:options and html:checkbox.
+    <p>
+        Saving the best for last, Subscription.jsp utilizes two interesting Struts custom form tags,
+        "html:options" and "html:checkbox".
 
-    <p>In registration.jsp, the Struts iteration tag was used to write a list of subscriptions. Another place where iterations and collections are handy is the option list for a HTML select tag. Since this is such a common situation, Struts offers a html:options (plural) tag can take an array of objects as a parameter. The tag then iterates over the members of the array (beans) to place each one inside an standard option tag. So given a block like</p>
+    <p>
+        In Registration.jsp, the Struts iteration tag was used to write a list of subscriptions.
+        Another place where iterations and collections are handy is the option list for a HTML select tag.
+        Since this is such a common situation, Struts offers a html:options (plural) tag
+        that can take an array of objects as a parameter.
+        The tag then iterates over the members of the array (beans) to place each one inside an standard option tag.
+        So given a block like</p>
 
     <blockquote>
       <p><code>&lt;html:select property="type"&gt;<br />
@@ -1666,7 +1878,7 @@
       &lt;/html:select&gt;</code></p>
     </blockquote>
 
-    <p>Struts outputs a block like</p>
+    <p>The tag outputs a block like</p>
 
     <blockquote>
       <p><code>&lt;select name="type"&gt;<br />
@@ -1675,32 +1887,54 @@
       &lt;/select&gt;</code></p>
     </blockquote>
 
-    <p>Here, one collection contained both the labels and the values, from properties of the same name. Options can also use a second array for the labels, if they do not match the values. Options can use a Collection, Iterator, or Map for the source of the list.</p>
-
-    <p>For demonstrations purposes, the serverTypes array is created at the top of this page. Usually, the html:options tag would be used to list valid items from a database maintained elsewhere. For example, if the application needed you to select a default subscription, a form might list the subscriptions in an options tag.</p>
+    <p>
+        Here, one collection contained both the labels and the values, from properties of the same name.
+        Options can also use a second array for the labels, if they do not match the values.
+        Options can use a Collection, Iterator, or Map for the source of the list.
+    </p>
 
-    <blockquote>
-      <p><i>The LabelValueBean used to create the demonstration array is also a good example of simple but useful bean object.</i></p>
-    </blockquote>
+    <p>
+        Unlike other data, the serverTypes array is not fetched from the database.
+        Instead, it is loaded by a Struts plugin.
+        The <strong>DigestingPlugin</strong> parses an XML document using a given set of Digester rules.
+        The MailReader uses a set of rules for "LabelValueBeans" to create a list of server types.
+    </p>
+
+        <hr/>
+        <h5>Note:</h5>
+        <blockquote>
+          <p><font class="hint">
+              <strong>LabelValueBeans</strong> -
+              Many developers find the LabelValueBeans useful,
+              so the class is available in the Struts Action distribution as
+              [org.apache.struts.util.LabelValueBean].
+          </font></p>
+        </blockquote>
+        <hr/>
+
+    <p>
+        The plugin stores the list is stored in application scope.
+        Since the Struts custom tags, like standard JSP tags, search the scopes in succession,
+        the tag finds the list in application scope and uses it to write out the options.
+    </p>
 
-    <p>A particularly tricky HTML control is the checkbox. A problem with a checkbox is that it is only sent in the request if it is checked. If it is not checked, it is not sent (i.e. null). This can be problematic when trying to validate the form's data after it has been translated to a bean. The autoconnect property for a subscription demonstrates how to handle validation of a checkbox.</p>
 
     <h4><a name="SubscriptionForm.java" id="SubscriptionForm.java">SubscriptionForm.java</a></h4>
 
-    <p>Struts validation is handled by the reset and validate methods of the ActionForm bean. When creating your own form beans, you should subclass ActionForm, add your own fields and their getters/setters, and implement the reset and validate methods.</p>
-
-    <p>Struts calls reset before populating the form, and calls validate after populating it but before the perform method of the action. Reset should assign default values to each of your form fields, usually null. But in the case of checkboxes, the default value should usually be false instead of null.</p>
-
-    <blockquote>
-      <p><i>For more examples of validating forms, take another look at LoginForm.java and RegistrationForm.java.</i></p>
-    </blockquote>
-
-    <p>Back in subscription.jsp, we have one more block to cover. Although the same basic form can be used to created, edit, or delete a subscription, people might expect the buttons to be labeled differently in each case. subscription.jsp accommodates by using a logic tag to output a different set of buttons for each case. This doesn't really change the way subscription.jsp works, but it does make things less confusing for the user.</p>
+    <p>
+        Back in Subscription.jsp, we have one more block to cover.
+        Although the same basic form can be used to created, edit, or delete a subscription,
+        people might expect the buttons to be labeled differently in each case.
+        Like the Registration page, the Subscription page handles customization
+        by using a logic tag to output a different set of buttons for each case.
+        Changing buttons doesn't really change the way the Subscription page works,
+        but customizing the buttons does make things less confusing for the user.
+    </p>
 
     <blockquote>
       <p><code>&lt;logic:equal<br />
       name="SubscriptionForm"<br />
-      property="action"<br />
+      property="task"<br />
       scope="request"<br />
       value="Create"&gt;<br />
       &lt;html:submit&gt;<br />
@@ -1708,15 +1942,34 @@
       &lt;/logic:equal&gt;</code></p>
     </blockquote>
 
-    <p>In the case of a request to delete a subscription, the submit button is labeled "Confirm", since this view is meant to give the user a last chance to cancel, before sending that task along to SaveSubscriptionAction.java.</p>
+    <p>
+        In the case of a request to delete a subscription,
+        the submit button is labeled "Confirm", since this view is meant to give the user a last chance to cancel,
+        before sending that task along to SaveSubscriptionAction.java.
+    </p>
 
-    <p>The actual action property is placed into the form as a hidden field, and SaveSubscriptionAction checks that property to execute the appropriate task.</p>
+    <p>
+        The actual task property is placed into the form as a hidden field,
+        and SaveSubscriptionAction checks that property to execute the appropriate task.
+    </p>
 
     <h4><a name="SaveSubscriptionAction.java" id="SaveSubscriptionAction.java">SaveSubscriptionAction.java</a></h4>
 
-    <p>Our final stop has the job of finishing what EditSubscriptionAction.java and subscription.jsp started. After the usual logic and error checking, SaveSubscriptionAction either deletes or updates the subscription object being handled by this request, and cleans up the bean, just to be tidy. By now, you should be very comfortable reading through the source on your own, to pickup the finer points.</p>
+    <p>
+        Our final stop has the job of finishing what EditSubscriptionAction.java and subscription.jsp started.
+        After the usual logic and error checking,
+        SaveSubscriptionAction either deletes or updates the subscription object being handled by this request,
+        and cleans up the bean, just to be tidy.
+        By now, you should be very comfortable reading through the source on your own, to pickup the finer points.
+    </p>
 
-    <p>This concludes our tour. To review, you may wish to trace the path a new user takes when they register with the application for the first time. You should also read over each of the .java and JSP files carefully, since we only covered the high points here.</p>
+    <p>
+        This concludes our tour.
+        To review, you may wish to trace the path a new user takes
+        when they register with the application for the first time.
+        You should also read over each of the .java and JSP files carefully,
+        since we only covered the high points here.
+    </p>
 
     <h3><a name="Summary" id="Summary">Summary</a></h3>
 



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