~ubuntu-branches/ubuntu/trusty/libstruts1.2-java/trusty-proposed

« back to all changes in this revision

Viewing changes to web/example/tour.html

  • Committer: Bazaar Package Importer
  • Author(s): Arnaud Vandyck
  • Date: 2004-11-19 15:35:25 UTC
  • Revision ID: james.westby@ubuntu.com-20041119153525-mdu08a76z4zo67xt
Tags: upstream-1.2.4
ImportĀ upstreamĀ versionĀ 1.2.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 
2
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 
3
 
 
4
<html xmlns="http://www.w3.org/1999/xhtml">
 
5
<head>
 
6
  <meta name="generator" content="HTML Tidy for Windows (vers 1st July 2003), see www.w3.org" />
 
7
  <meta http-equiv="Content-Type" content="text/html; charset=us-ascii" />
 
8
  <link rel="stylesheet" type="text/css" href="base.css" />
 
9
 
 
10
  <title>A Walking Tour of the Struts MailReader Demonstration Application</title>
 
11
</head>
 
12
 
 
13
<body>
 
14
  <blockquote>
 
15
    <h2>A Walking Tour of the Struts MailReader Demonstration Application</h2>
 
16
 
 
17
    <p><i>This article is meant to introduce a new user to Struts by "walking through" an application. See the <a href="http://jakarta.apache.org/struts/">Struts Users Guide and Strut's API</a> for more documentation.</i></p>
 
18
 
 
19
    <p><i>The MailReader application is based on the 1.2.0 build of Struts. To follow along, you should install the MailReader application on your own development workstation (e.g. localhost).</i></p>
 
20
 
 
21
    <p><i>The article assumes the reader has a basic understanding of the Java language, JavaBeans, web applications, and JavaServer Pages. For background on these technologies, see the <a href="http://jakarta.apache.org/struts/userGuide/preface.html">Preface to the Struts User Guide</a>.</i></p>
 
22
    <hr />
 
23
 
 
24
    <ul>
 
25
      <li>
 
26
        <a href="#index.jsp">index.jsp</a>
 
27
 
 
28
        <ul>
 
29
          <li><a href="#web.xml">web.xml and ApplicationResources.properties</a></li>
 
30
 
 
31
          <li><a href="#DatabaseServlet.java">DatabaseServlet.java</a></li>
 
32
        </ul>
 
33
      </li>
 
34
 
 
35
      <li>
 
36
        <a href="#logon.jsp">logon.jsp</a>
 
37
 
 
38
        <ul>
 
39
          <li><a href="#struts-config.xml">struts-config.xml</a></li>
 
40
 
 
41
          <li><a href="#struts-config.xml">LogonForm.java</a></li>
 
42
 
 
43
          <li><a href="#LogonAction.java">LogonAction.java</a></li>
 
44
 
 
45
          <li><a href="#struts-config.xml/2">struts-config.xml 2</a></li>
 
46
        </ul>
 
47
      </li>
 
48
 
 
49
      <li>
 
50
        <a href="#mainMenu.jsp">mainMenu.jsp</a>
 
51
 
 
52
        <ul>
 
53
          <li><a href="#CheckLoginTag.java">CheckLoginTag.java</a></li>
 
54
 
 
55
          <li><a href="#editRegistrationAction.java">editRegistrationAction.java</a></li>
 
56
        </ul>
 
57
      </li>
 
58
 
 
59
      <li>
 
60
        <a href="#subscription.jsp">registration.jsp</a>
 
61
 
 
62
        <ul>
 
63
          <li><a href="#RegistrationForm.java">RegistrationForm.java</a></li>
 
64
 
 
65
          <li><a href="#EditRegistrationAction.java">EditRegistrationAction.java</a></li>
 
66
 
 
67
          <li><a href="#LinkSubscriptionTag.java">LinkSubscriptionTag.java</a></li>
 
68
 
 
69
          <li><a href="#LinkUserTag.java">LinkUserTag.java</a></li>
 
70
 
 
71
          <li><a href="#EditSubscriptionAction.java">EditSubscriptionAction.java</a></li>
 
72
        </ul>
 
73
      </li>
 
74
 
 
75
      <li>
 
76
        <a href="#subcription.jsp">subscription.jsp</a>
 
77
 
 
78
        <ul>
 
79
          <li><a href="#SubscriptionForm.java">SubscriptionForm.java</a></li>
 
80
 
 
81
          <li><a href="#SaveSubscriptionAction.java">SaveSubscriptionAction.java</a></li>
 
82
        </ul>
 
83
      </li>
 
84
 
 
85
      <li><a href="#Summary">Summary</a></li>
 
86
    </ul>
 
87
    <hr />
 
88
 
 
89
    <p>The Struts distribution bundles four applications: struts-documentation, tiles-documentation, struts-example, and struts-examples. This document walks through the struts-example, also known as the "MailReader Demonstration Application".</p>
 
90
 
 
91
    <p>The premise of the MailReader is that it is the first iteration of a portal application. This version allows users to register themselves and maintain a set of accounts with various mail servers. When completed, the application would let users read mail from their accounts.</p>
 
92
 
 
93
    <p>The MailReader application demonstrates registering with an application, logging into an application, maintaining a master record, and maintaining child records. This document walks through the JSP pages, Struts Java classes, and Struts configuration elements needed to do these things.</p>
 
94
 
 
95
    <blockquote>
 
96
      <p>Note that for compatability and ease of deployment, the MailReader uses "application-based" authorization. However, use of the standard Java Authentication and Authorization Service (JAAS) is recommended for most applications. (See the <a href="http://jakarta.apache.org/struts/userGuide/preface.html">Preface to the Struts User Guide</a> for more about authentification technologies.)</p>
 
97
    </blockquote>
 
98
 
 
99
    <p>The walkthrough starts with how the initial welcome page is displayed, and then steps through logging in, adding and editing subscriptions, and creating a new registration.</p>
 
100
 
 
101
    <h3><a name="index.jsp" id="index.jsp">index.jsp</a></h3>
 
102
 
 
103
    <p>A web application, like any other web site, can specify a list of welcome pages. When you open a web application without specifying a particular page, a welcome page is used by default.</p>
 
104
 
 
105
    <p>Struts allows developers to manage an application through "virtual pages" called <i>actions</i>. An accepted practice in Struts is to never link directly to server pages, but only to these actions. The actions are listed in a configuration file. By linking to actions, developers can "rewire" an application without editing the server pages.</p>
 
106
 
 
107
    <blockquote>
 
108
      <p><font class="hint">"Link actions not pages."</font></p>
 
109
    </blockquote>
 
110
 
 
111
    <p>Unfortunately, actions cannot be specified as a welcome page. Since there can be a list of pages, the web server looks for each page on the list before selecting one. The web server doesn't see actions as pages and will never select one as a welcome page. So, in the case of a welcome page, how do we follow the Struts best practice of navigating through actions rather than pages?</p>
 
112
 
 
113
    <p>One solution is to use a server page to "bootstrap" a Struts action. A Java web application recognizes the idea of "forwarding" from one page to another page (or action). We can register the usual "index.jsp" as the welcome page and have it forward to a "Welcome" action. Here's the MailReader's index.jsp:</p>
 
114
    <hr />
 
115
    <pre>
 
116
<code>&lt;%@ taglib uri="/tags/struts-logic" prefix="logic" %&gt;
 
117
&lt;logic:redirect action="/Welcome"/&gt;</code>
 
118
</pre>
 
119
    <hr />
 
120
 
 
121
    <p>At the top of the page, we import the "struts-logic" JSP tag library. (Again, see the <a href="http://jakarta.apache.org/struts/userGuide/preface.html">Preface to the Struts User Guide</a> for more about the technologies underlying Struts.) The page itself consists of a single tag that redirects to the "Welcome" action. The tag inserts the actual web address for the redirect when the page is rendered. But, where does the tag find the actual address to insert?</p>
 
122
 
 
123
    <p>The list of actions, along with other Struts components, are registered through one or more Struts configuration files. The configuration files are written as XML documents and processed when the application starts. If we just wanted to forward to the welcome page, we could use a configuration element like this:</p>
 
124
    <hr />
 
125
    <pre>
 
126
<code>&lt;!-- Display welcome page --&gt;
 
127
&lt;action path="/Welcome" forward="/welcome.jsp" /&gt;</code>
 
128
</pre>
 
129
    <hr />
 
130
 
 
131
    <p>If someone asked for the Welcome action ("/Welcome.do"), the welcome.jsp page would be displayed in return.</p>
 
132
 
 
133
    <h4><a name="WelcomeAction.java" id="WelcomeAction.java">WelcomeAction.java</a></h4>
 
134
 
 
135
    <p>But if we peek at the configuration file for the MailReader, we find a slightly more complicated XML element for the Welcome action:</p>
 
136
    <hr />
 
137
    <pre>
 
138
<code>&lt;!-- Display welcome page --&gt;
 
139
&lt;action path="/Welcome"
 
140
  type="org.apache.struts.webapp.example.WelcomeAction"&gt;
 
141
  &lt;forward name="failure" path="/Error.jsp" /&gt;
 
142
  &lt;forward name="success" path="/welcome.jsp" /&gt;
 
143
&lt;/action&gt;</code>
 
144
</pre>
 
145
    <hr />
 
146
 
 
147
    <p>Here, the "WelcomeAction" Java class executes whenever someone asks for the Welcome action. As it completes, the Action class can select which page is displayed. Two pages the class can select here are "Error.jsp" and "welcome.jsp". But the Action class doesn't need to know the path to the pages. The class can select them just using the names "success" or "failure".</p>
 
148
 
 
149
    <p>OK ... but why would a WelcomeAction want to choose between success and failure?</p>
 
150
 
 
151
    <p>The MailReader application retains a list of users along with their email accounts. The application stores this information in a database. If the application can't connect to the database, the application can't do its job. So before displaying the welcome page, the class checks to see if the database is available. The MailReader is also an internationalized application. So, the WelcomeAction checks to see if the message resources are available too. If both resources are available, the class forwards to the "success" path. Otherwise, it forwards to the "failure" path so that the appropriate error messages can be displayed.</p>
 
152
 
 
153
    <h4><a name="MemoryDatabasePlugIn.java" id="MemoryDatabasePlugIn.java">MemoryDatabasePlugIn.java</a></h4>
 
154
 
 
155
    <p>The database is exposed to the application as an object stored in application scope. The database object is based on an interface. Different implementations of the database could be loaded without changing the rest of the application. But how is the database object loaded in the first place?</p>
 
156
 
 
157
    <p>One section of the Struts configuration is devoted to "PlugIns". When a Struts application loads, it also loads whatever PlugIns are specified in its configuration. The PlugIn interface is quite simple, and you can use PlugIns to do anything that might need to be done when your application loads. The PlugIn is also notified when the application shuts down, so you can release any allocated resources.</p>
 
158
    <hr />
 
159
    <pre>
 
160
<code>&lt;plug-in className="org.apache.struts.webapp.example.memory.MemoryDatabasePlugIn"&gt;
 
161
  &lt;set-property property="pathname" value="/WEB-INF/database.xml"/&gt;
 
162
&lt;/plug-in&gt;</code>
 
163
</pre>
 
164
    <hr />
 
165
 
 
166
    <p>By default, the MailReader application loads a "MemoryDatabase" implementation of the UserDatabase. MemoryDatabase stores the database contents as a XML document, which is parsed by the Digester and loaded as a set of nested hashtables. The outer table is the list of user objects, each of which has its own inner hashtable of subscriptions. When you register, a user object is stored in this hashtable ... and when you login, the user object is stored within the session context.</p>
 
167
 
 
168
    <p>The database comes seeded with a sample user. If you check the database.xml file under WEB-INF, you'll see the sample user described as:</p>
 
169
    <hr />
 
170
    <pre>
 
171
<code>&lt;user username="user" fromAddress="John.User@somewhere.com" fullName="John Q. User" password="pass"&gt;
 
172
  &lt;subscription host="mail.hotmail.com" autoConnect="false" password="bar" type="pop3" username="user1234"&gt;
 
173
  &lt;/subscription&gt;
 
174
  &lt;subscription host="mail.yahoo.com" autoConnect="false" password="foo" type="imap" username="jquser"&gt;
 
175
  &lt;/subscription&gt;
 
176
&lt;/user&gt;</code>
 
177
</pre>
 
178
    <hr />
 
179
 
 
180
    <p>This creates a registration record for "John Q. User", with the detail for his hotmail account (or "subscription").</p>
 
181
 
 
182
    <h4><a name="MessageResources.properties" id="MessageResources.properties">MessageResources.properties</a></h4>
 
183
 
 
184
    <p>Another section of the Struts configuration loads the message resources for the application. If you change a message in the resource, and then reload the application, the change will appear throughout the application. If you provide message resources for additional locales, you can internationalize your application.</p>
 
185
    <hr />
 
186
    <pre>
 
187
<code>&lt;message-resources parameter="org.apache.struts.webapp.example.MessageResources" /&gt;</code>
 
188
</pre>
 
189
    <hr />
 
190
 
 
191
    <p>This is a standard properties text file. Here are the entries used by the welcome page:</p>
 
192
    <hr />
 
193
    <pre>
 
194
<code>index.heading=MailReader Demonstration Application Options
 
195
index.logon=Log on to the MailReader Demonstration Application
 
196
index.registration=Register with the MailReader Demonstration Application
 
197
index.title=MailReader Demonstration Application (Struts 1.2.1-dev)
 
198
index.tour=A Walking Tour of the MailReader Demonstration Application</code>
 
199
</pre>
 
200
    <hr />
 
201
 
 
202
    <p>The MailReader application uses a second set of message resources for non-text elements. The "key" element can be used to access this resource bundle rather than the default bundle.</p>
 
203
    <hr />
 
204
    <pre>
 
205
<code>&lt;message-resources parameter="org.apache.struts.webapp.example.AlternateMessageResources" key="alternate" /&gt;</code>
 
206
</pre>
 
207
    <hr />
 
208
 
 
209
    <h3><a name="welcome.jsp" id="welcome.jsp">welcome.jsp</a></h3>
 
210
 
 
211
    <p>After confirming that the necessary resources exist, the WelcomeAction forwards to the welcome.jsp page.</p>
 
212
    <hr />
 
213
    <pre>
 
214
<code>&lt;%@ page contentType="text/html;charset=UTF-8" language="java" %&gt;
 
215
&lt;%@ taglib uri="/tags/struts-bean" prefix="bean" %&gt;
 
216
&lt;%@ taglib uri="/tags/struts-html" prefix="html" %&gt;
 
217
 
 
218
&lt;html&gt;
 
219
&lt;head&gt;
 
220
&lt;title&gt;&lt;bean:message key="index.title"/&gt;&lt;/title&gt;
 
221
&lt;link rel="stylesheet" type="text/css" href="base.css" /&gt;
 
222
&lt;/head&gt;
 
223
 
 
224
&lt;h3&gt;&lt;bean:message key="index.heading"/&gt;&lt;/h3&gt;
 
225
&lt;ul&gt;
 
226
&lt;li&gt;&lt;html:link action="/EditRegistration?action=Create"&gt;<br />
 
227
&lt;bean:message key="index.registration"/&gt;&lt;/html:link&gt;&lt;/li&gt;
 
228
&lt;li&gt;&lt;html:link action="/Logon"&gt;&lt;bean:message key="index.logon"/&gt;&lt;/html:link&gt;&lt;/li&gt;
 
229
&lt;/ul&gt;
 
230
 
 
231
&lt;h3&gt;Change Language&lt;/h3&gt;
 
232
&lt;ul&gt;
 
233
&lt;li&gt;&lt;html:link action="/Locale?language=en"&gt;English&lt;/html:link&gt;&lt;/li&gt;
 
234
&lt;li&gt;&lt;html:link action="/Locale?language=ja" useLocalEncoding="true"&gt;Japanese&lt;/html:link&gt;&lt;/li&gt;
 
235
&lt;li&gt;&lt;html:link action="/Locale?language=ru" useLocalEncoding="true"&gt;Russian&lt;/html:link&gt;&lt;/li&gt;
 
236
&lt;/ul&gt;
 
237
 
 
238
&lt;hr /&gt;
 
239
 
 
240
&lt;p&gt;&lt;html:img bundle="alternate" pageKey="struts.logo.path" altKey="struts.logo.alt"/&gt;&lt;/p&gt;
 
241
 
 
242
&lt;p&gt;&lt;html:link action="/Tour"&gt;&lt;bean:message key="index.tour"/&gt;&lt;/html:link&gt;&lt;/p&gt;
 
243
 
 
244
&lt;/body&gt;
 
245
&lt;/html&gt;</code>
 
246
</pre>
 
247
    <hr />
 
248
 
 
249
    <p>At the top of the welcome.jsp page, there are several directives that load the Struts tag libraries. These are just the usual red tape that goes with any JSP file. The rest of the page demonstrates three Struts JSP tags: "bean:message", "html:link", and "html:img".</p>
 
250
 
 
251
    <p>The bean:message tag inserts a message from the MessageResources file. The MailReader comes with support for three locales: English (the default), Russian, and Japanese. If the Struts locale setting is changed for a user, the bean:message tag will render messages from that locale's property bundle instead.</p>
 
252
 
 
253
    <p>The html:link tag does double duty. First, you can refer to an action or forward stored in the Struts configuration, and the tag will insert the corresponding path when the page is rendered. This makes it easy to "rewire" an application without touching all the pages. Second, the link tag will "URL encode" the hyperlink to maintain the client session. Your application can maintain client state without requiring cookies.</p>
 
254
 
 
255
    <blockquote>
 
256
      <p><font class="hint">If you turn cookies off in your browser, and then reload your browser and this page, you will see the links with the Java session id information attached. (If you are using Internet Explorer and try this, be sure you reset cookies for the appropriate security zone, and that you disallow "per-session" cookies.)</font></p>
 
257
    </blockquote>
 
258
 
 
259
    <p>The html:img tag renders an img tag. When necessary, the src URI is encoded as it is with the link tag. In this case, the tag inserts the src path from the "alternate" MessageResource bundle, along with the text for the alt element.</p>
 
260
 
 
261
    <p>In the span of a single request, Struts has done quite a bit already:</p>
 
262
 
 
263
    <ul>
 
264
      <li>Confirmed that required objects were created during initialization.</li>
 
265
 
 
266
      <li>Written all the page headings and labels from internationalized message resources.</li>
 
267
 
 
268
      <li>Automatically URL-encoded paths as needed.</li>
 
269
    </ul>
 
270
 
 
271
    <p>When rendered, the welcome page lists two menu options: one to register with the application and one to login in (if you have already registered). Let's follow the login link first.</p>
 
272
 
 
273
    <h3><a name="logon.jsp" id="logon.jsp">logon.jsp</a></h3>
 
274
 
 
275
    <p>If you choose the logon link, the Logon action forwards control to the logon.jsp page. The logon page displays a form that accepts a username and password. You can use the default username and password to logon (user and pass). Note that both the username and password are case sensitive. Better yet, try omitting or misspelling the username and password in various combinations and see how the application reacts.</p>
 
276
 
 
277
    <p>Here's how the login.jsp is coded:</p>
 
278
    <hr />
 
279
    <pre>
 
280
<code>&lt;%@ page contentType="text/html;charset=UTF-8" language="java" %&gt;
 
281
&lt;%@ taglib uri="/tags/struts-bean" prefix="bean" %&gt;
 
282
&lt;%@ taglib uri="/tags/struts-html" prefix="html" %&gt;
 
283
 
 
284
&lt;html:xhtml/&gt;
 
285
&lt;html&gt;
 
286
&lt;head&gt;
 
287
&lt;title&gt;&lt;bean:message key="logon.title"/&gt;&lt;/title&gt;
 
288
&lt;/head&gt;
 
289
 
 
290
&lt;html:errors/&gt;
 
291
 
 
292
&lt;html:form action="/SubmitLogon" focus="username"
 
293
         onsubmit="return validateLogonForm(this);"&gt;
 
294
&lt;table border="0" width="100%"&gt;
 
295
 
 
296
  &lt;tr&gt;
 
297
    &lt;th align="right"&gt;
 
298
      &lt;bean:message key="prompt.username"/&gt;:
 
299
    &lt;/th&gt;
 
300
    &lt;td align="left"&gt;
 
301
      &lt;html:text property="username" size="16" maxlength="18"/&gt;
 
302
    &lt;/td&gt;
 
303
  &lt;/tr&gt;
 
304
 
 
305
  &lt;tr&gt;
 
306
    &lt;th align="right"&gt;
 
307
      &lt;bean:message key="prompt.password" bundle="alternate"/&gt;:
 
308
    &lt;/th&gt;
 
309
    &lt;td align="left"&gt;
 
310
      &lt;html:password property="password" size="16" maxlength="18"
 
311
                    redisplay="false"/&gt;
 
312
    &lt;/td&gt;
 
313
  &lt;/tr&gt;
 
314
 
 
315
  &lt;tr&gt;
 
316
    &lt;td align="right"&gt;
 
317
      &lt;html:submit property="Submit" value="Submit"/&gt;
 
318
    &lt;/td&gt;
 
319
    &lt;td align="left"&gt;
 
320
      &lt;html:reset/&gt;
 
321
    &lt;/td&gt;
 
322
  &lt;/tr&gt;
 
323
 
 
324
&lt;/table&gt;
 
325
 
 
326
&lt;/html:form&gt;
 
327
 
 
328
&lt;html:javascript formName="LogonForm"
 
329
        dynamicJavascript="true"
 
330
         staticJavascript="false"/&gt;
 
331
&lt;script language="Javascript1.1" src="staticJavascript.jsp"&gt;&lt;/script&gt;
 
332
 
 
333
&lt;jsp:include page="footer.jsp" /&gt;
 
334
&lt;/body&gt;
 
335
&lt;/html&gt;</code>
 
336
</pre>
 
337
    <hr />
 
338
 
 
339
    <p>We saw some of these tags on the welcome page. Let's focus on the new tags.</p>
 
340
 
 
341
    <p>The first new tag on the logon page is "html:errors". The credentials you entered are processed by a "LogonAction" class. If the credentials are incorrect, the LogonAction posts an appropriate error message and forwards back to the input page. If the html:errors tag sees that one or more messages were posted, the tag ouputs the messages to the page. The text of the messages can be specified in the MessageResource bundle, making them easy to internationalize.</p>
 
342
 
 
343
    <p>The second new tag is "html:form". This tag renders a html form tag. The "action" element tells the tag to use "SubmitLogon.do" for the form's action. The "focus" attribute tells the tag to generate a little Javascript after the form that sets its focus to the "username" field. The "onsubmit" attribute tells the form to run a Javascript when the form is submitted. (Just like the corresponding attribute on the standard form tag.)</p>
 
344
 
 
345
    <p>Within the html:form tag, we see four other new tags: "html:text", "html:password", "html:submit", and "html:reset".</p>
 
346
 
 
347
    <p>The html:text tag renders a "input type=text" tag. The "property" attribute becomes the input tag's "name" attribute.</p>
 
348
 
 
349
    <p>The html:password tag renders a "input type=password" tag. The "redisplay" attribute tell the tag not to render the password back into the file, if the submit fails. The html:submit and html:reset tags render buttons of the corresponding types.</p>
 
350
 
 
351
    <p>Following the form is a "html:javascript" tag. This tag works with the Struts Validator component to generate a JavaScript that can validate input before it is submitted to the LogonAction.</p>
 
352
 
 
353
    <blockquote>
 
354
      <p><font class="hint">Most of these tags have many more options than the ones we use in this application. For the complete documentation for each tag, see the Tag Developers Guides in the Struts documentation bundle.</font></p>
 
355
    </blockquote>
 
356
 
 
357
    <p>But, how do these tags know so much? How does the Javascript tag know what scripts to write? How do the text and password tags know what values to redisplay?</p>
 
358
 
 
359
    <p>For the answers, we need to turn to the Struts configuration files, "struts-config.xml" and "validation.xml".</p>
 
360
 
 
361
    <h4><a name="struts-config.xml" id="struts-config.xml">struts-config.xml</a></h4>
 
362
 
 
363
    <p>In the struts-config.xml file, we find an element for the "/SubmitLogon" action</p>
 
364
    <hr />
 
365
    <pre>
 
366
<code>&lt;!-- Process a user logon --&gt;
 
367
&lt;action    path="/SubmitLogon"
 
368
                  type="org.apache.struts.webapp.example.LogonAction"
 
369
                  name="LogonForm"
 
370
                 scope="request"
 
371
                 input="logon"&gt;
 
372
 &lt;exception
 
373
                   key="expired.password"
 
374
                  type="org.apache.struts.webapp.example.ExpiredPasswordException"
 
375
                  path="/ExpiredPassword.do"/&gt;
 
376
       &lt;/action&gt;</code>
 
377
</pre>
 
378
    <hr />
 
379
 
 
380
    <p>We saw the path and type attributes in the Welcome action. Let's look at the new attributes.</p>
 
381
 
 
382
    <p>The "name" attribute specifies something Struts calls an "ActionForm". The ActionForm buffers input from a form and delivers it to an Action class as an object. The ActionForm can also validate the input. If validation fails, the tags can rewrite the input values from the ActionForm.</p>
 
383
 
 
384
    <p>The ActionForms are defined in the "formbeans" section of the configuration file. Here's the formbean element for the "LogonForm".</p>
 
385
    <hr />
 
386
    <pre>
 
387
<code>&lt;form-bean       name="LogonForm"
 
388
                 type="org.apache.struts.validator.DynaValidatorForm"&gt;
 
389
                 &lt;form-property name="username" type="java.lang.String"/&gt;
 
390
                 &lt;form-property name="password" type="java.lang.String"/&gt;
 
391
&lt;/form-bean&gt;</code>
 
392
</pre>
 
393
    <hr />
 
394
 
 
395
    <p>ActionForms can be "conventional" or "dynamic". Here, we are using a dynamic ActionForm. Rather than cobble up an actual JavaBean class, we specify the properties the ActionForm can accept in the configuration file. If the property is not specified here, it is not captured, validated, or passed up to the Action class.</p>
 
396
 
 
397
    <p>Struts creates the ActionForms automatically. The "scope" attribute in the action element tells the controller wether to store the ActionForm in the request or in the user's session.</p>
 
398
 
 
399
    <blockquote>
 
400
      <p><font class="hint">The Struts best practice is to use request scope for single-page forms that contain all the properties needed by the Action. There is usually no need to maintain form data across requests.</font></p>
 
401
    </blockquote>
 
402
 
 
403
    <p>Struts can also validate the ActionForm automatically. If validation fails, Struts looks for the forward specified by the "input" attribute. In this case, the "logon" forward sends control back to the input.jsp page.</p>
 
404
    <hr />
 
405
    <pre>
 
406
<code>&lt;forward name="logon" path="/Logon.do"/&gt;</code>
 
407
</pre>
 
408
    <hr />
 
409
 
 
410
    <p>Within the logon action element is another new element, "exception". When a user logons on, it's possible that an "ExpiredPasswordException" will be thrown. Should this happen, Struts will capture the exception and send control to the "ExpiredPassword" action.</p>
 
411
 
 
412
    <h4><a name="validations.xml" id="validations.xml">validations.xml</a></h4>
 
413
 
 
414
    <p>In the logon.jsp, we mentioned that the html:javascript tag confers with the Struts Validator components. The Validator is configured through another XML document, the "validation.xml". Here's the element for our LogonForm:</p>
 
415
    <hr />
 
416
    <pre>
 
417
<code>&lt;form name="LogonForm"&gt;
 
418
 
 
419
        &lt;field property="username"
 
420
                        depends="required"&gt;
 
421
                &lt;arg0   key="prompt.username"/&gt;
 
422
        &lt;/field&gt;
 
423
 
 
424
        &lt;field property="password"
 
425
                        depends="required, minlength,maxlength"&gt;
 
426
                &lt;arg0   key="prompt.password"/&gt;
 
427
                &lt;arg1   key="${var:minlength}" name="minlength"
 
428
                   resource="false"/&gt;
 
429
                &lt;arg2   key="${var:maxlength}" name="maxlength"
 
430
                   resource="false"/&gt;
 
431
                &lt;var&gt;
 
432
                        &lt;var-name&gt;maxlength&lt;/var-name&gt;
 
433
                        &lt;var-value&gt;16&lt;/var-value&gt;
 
434
                &lt;/var&gt;
 
435
                &lt;var&gt;
 
436
                        &lt;var-name&gt;minlength&lt;/var-name&gt;
 
437
                        &lt;var-value&gt;3&lt;/var-value&gt;
 
438
                &lt;/var&gt;
 
439
        &lt;/field&gt;
 
440
 
 
441
&lt;/form&gt;</code>
 
442
</pre>
 
443
    <hr />
 
444
 
 
445
    <p>The field elements correspond to the ActionForm properties. The "username" field element says it depends on the "required" validator. If the username is blank or absent, validation will fail and an error message is automatically generated.</p>
 
446
 
 
447
    <p>The "password" field (or property) is also required. In addition, it must also pass the "maxlength" and "minlength" validations. Here, the minimum length is three characters and the maximum length is sixteen. If the length of the password doesn't meet these criteria, a corresponding error message is generated. Of course, the messages are generated from the MessageResource bundles and are easy to localize.</p>
 
448
 
 
449
    <h4><a name="LogonAction.java" id="LogonAction.java">LogonAction.java</a></h4>
 
450
 
 
451
    <p>If validation passes, the LogonForm object is forwarded to the LogonAction. The LogonAction interacts with the database to see if the credentials are valid. If so, the user is logged on, and control passes to the "success" forward. Otherwise, control is forwarded to the input page and the list of error messages displayed.</p>
 
452
 
 
453
    <p>Here's the LogonAction (sans comments):</p>
 
454
    <hr />
 
455
    <pre>
 
456
<code>package org.apache.struts.webapp.example;
 
457
import ...
 
458
 
 
459
public final class LogonAction extends BaseAction {
 
460
 
 
461
    private static String USERNAME = "username";
 
462
    private static String PASSWORD = "password";
 
463
 
 
464
    User getUser(UserDatabase database, String username,
 
465
                 String password, ActionMessages errors)
 
466
                 throws ExpiredPasswordException {
 
467
 
 
468
        User user = null;
 
469
        if (database == null){
 
470
            errors.add(
 
471
                ActionMessages.GLOBAL_MESSAGE,
 
472
                new ActionMessage("error.database.missing"));
 
473
        }
 
474
        else {
 
475
            user = database.findUser(username);
 
476
            if ((user != null) &amp;&amp; !user.getPassword().equals(password)) {
 
477
                user = null;
 
478
            }
 
479
            if (user == null) {
 
480
                errors.add(
 
481
                    ActionMessages.GLOBAL_MESSAGE,
 
482
                    new ActionMessage("error.password.mismatch"));
 
483
            }
 
484
        }
 
485
 
 
486
        return user;
 
487
 
 
488
    }
 
489
 
 
490
    void SaveUser(HttpServletRequest request, User user) {
 
491
 
 
492
        HttpSession session = request.getSession();
 
493
 
 
494
        session.setAttribute(Constants.USER_KEY, user);
 
495
        if (log.isDebugEnabled()) {
 
496
            log.debug(
 
497
                "LogonAction: User '"
 
498
                    + user.getUsername()
 
499
                    + "' logged on in session "
 
500
                    + session.getId());
 
501
        }
 
502
 
 
503
    }
 
504
 
 
505
    public ActionForward execute(
 
506
        ActionMapping mapping,
 
507
        ActionForm form,
 
508
        HttpServletRequest request,
 
509
        HttpServletResponse response)
 
510
        throws Exception {
 
511
 
 
512
        UserDatabase database = getUserDatabase(request);
 
513
        String username = (String) PropertyUtils.getSimpleProperty(form,
 
514
                USERNAME);
 
515
        String password = (String) PropertyUtils.getSimpleProperty(form,
 
516
                PASSWORD);
 
517
        ActionMessages errors = new ActionMessages();
 
518
 
 
519
        User user = getUser(database,username,password,errors);
 
520
 
 
521
        SaveUser(request,user);
 
522
 
 
523
        if (!errors.isEmpty()) {
 
524
            this.saveErrors(request, errors);
 
525
            return (mapping.getInputForward());
 
526
        }
 
527
 
 
528
        return (findSuccess(mapping));
 
529
 
 
530
    }</code>
 
531
</pre>
 
532
    <hr />
 
533
 
 
534
    <h4><a name="MainMenu.do" id="MainMenu.do">MainMenu.do and mainMenu.jsp</a></h4>
 
535
 
 
536
    <p>On a successful logon, the Main Menu page displays. If you logged in using the default account, the page title should be "Main Menu Options for John Q. User". Below this legend should be two links:
 
537
 
 
538
    <ul>
 
539
      <li>Edit your user registration profile</li>
 
540
 
 
541
      <li>Log off MailReader Demonstration Application</li>
 
542
    </ul>
 
543
 
 
544
    <p>If you check the address shown by your browser, you will see that it shows "/SubmitLogon.do" not "/MainMenu.do". The Java servlet platform supports the idea of server-side forwards. When control passed from the SubmitLogon action to the MainMenu action, everything occured server-side. All the browser knows is that we are looking at the result of submitting a form to "/LogonSubmit.do", so that's the address that shows. It doesn't know control passed from one action to another. The difference between server-side forwards and client-side redirects is subtle and often confuses new developers. </p>
 
545
 
 
546
    <p>If you change the address to "/MainMenu.do" and press enter, the same page will display again.</p>
 
547
 
 
548
    <p>Here's the JSP source for the "MainMenu" action and the "mainMenu.jsp".</p>
 
549
 
 
550
    <hr />
 
551
 
 
552
    <blockquote><pre><code>&lt;action path="/MainMenu" forward="/mainMenu.jsp"/></blockquote></code></pre>
 
553
 
 
554
    <hr />
 
555
 
 
556
    <blockquote><pre><code>&lt;%@ page contentType="text/html;charset=UTF-8" language="java" %>
 
557
&lt;%@ taglib uri="/tags/app" prefix="app" %>
 
558
&lt;%@ taglib uri="/tags/struts-bean" prefix="bean" %>
 
559
&lt;%@ taglib uri="/tags/struts-html" prefix="html" %>
 
560
&lt;app:checkLogon/>
 
561
&lt;html>
 
562
&lt;head>
 
563
&lt;title>&lt;bean:message key="mainMenu.title"/>&lt;/title>
 
564
&lt;link rel="stylesheet" type="text/css" href="base.css" />
 
565
&lt;/head>
 
566
&lt;h3>&lt;bean:message key="mainMenu.heading"/> &lt;bean:write name="user" property="fullName" />&lt;/h3>
 
567
&lt;ul>
 
568
&lt;li>&lt;html:link action="/EditRegistration?action=Edit">&lt;bean:message key="mainMenu.registration"/>&lt;/html:link>&lt;/li>
 
569
&lt;li>&lt;html:link forward="logoff">&lt;bean:message key="mainMenu.logoff"/>&lt;/html:link>&lt;/li>
 
570
&lt;/ul>
 
571
&lt;/body>
 
572
&lt;/html></blockquote></code></pre>
 
573
 
 
574
    <hr />
 
575
 
 
576
    <p>If you check the source for mainMenu.jsp, you will find two new tags: "app:checkLogon" and "bean:write". Let's look at the bean tag first.</p>
 
577
 
 
578
 
 
579
    <p>When control passed through the LogonAction, it retrieved a "User" object from the database and stored a reference in "session" scope. This object is a JavaBean with several properties. One property is "fullName". The bean:write tag can find the User bean and print the fullName property for us.</p>
 
580
 
 
581
    <p>But how can the page be sure that we are logged in and that the User object exists?</p>
 
582
 
 
583
        <p>At the top of the page, above the "html" tag, is the other new tag, checkLogon. This tag, true to its name, checks to see of the user is logged on. If not, the tag forwards to the LogonAction. Let's have a look:
 
584
 
 
585
    <hr />
 
586
 
 
587
    <blockquote><pre><code>package org.apache.struts.webapp.example;
 
588
import ...
 
589
 
 
590
public final class CheckLogonTag extends TagSupport {
 
591
 
 
592
    private String name = Constants.USER_KEY;
 
593
    private static String LOGIN_PATH = "/Logon.do";
 
594
    private String page = LOGIN_PATH;
 
595
 
 
596
    public int doStartTag() throws JspException {
 
597
           return (SKIP_BODY);
 
598
    }
 
599
 
 
600
    public int doEndTag() throws JspException {
 
601
        boolean valid = false;
 
602
        HttpSession session = pageContext.getSession();
 
603
        if ((session != null) && (session.getAttribute(name) != null)) {
 
604
            valid = true;
 
605
        }
 
606
        if (valid) {
 
607
            return (EVAL_PAGE);
 
608
        } else {
 
609
            ModuleConfig config =
 
610
                (ModuleConfig) pageContext.getServletContext().getAttribute(
 
611
                    org.apache.struts.Globals.MODULE_KEY);
 
612
 
 
613
                try {
 
614
                    pageContext.forward(config.getPrefix() + page);
 
615
                } catch (ServletException e) {
 
616
                    throw new JspException(e.toString());
 
617
                } catch (IOException e) {
 
618
                    throw new JspException(e.toString());
 
619
                }
 
620
 
 
621
            return (SKIP_PAGE);
 
622
        }
 
623
    }
 
624
 
 
625
    public void release() {
 
626
        super.release();
 
627
        this.name = Constants.USER_KEY;
 
628
        this.page = LOGIN_PATH;
 
629
    }
 
630
}</blockquote></code></pre>
 
631
 
 
632
        <hr />
 
633
 
 
634
                <p>As tags go, CheckLogon is quite simple. It takes no attributes. It's just hardwired to check for a User object in session scope and forward to the LogonAction if the object is missing. </p>
 
635
 
 
636
                <p>Simple but effective. Try following the logout link and then putting in the "/MainMenu.do" address by hand. If you do, the tag will automatically forward you to the logon page. For any page that requires a login, we simply need to add a checkLogon tag at the top (before any of the page is written).</p>
 
637
 
 
638
                <blockquote>
 
639
                <p><font class="hint">Many Struts developers would frown on the CheckLogon tag solution. They might argue that a page should not need to know about logons. We shouldn't rely on people remembering to put a CheckLogon tag on every protected page. <em>And "they" would be right!</em> We do recommend that most applications use standard JAAS. Perhaps in the next iteration, the MailReader developers will do just that. But for now, the CheckLogon tag is a simple solution to a simple problem.</font></p>
 
640
                </blockquote>
 
641
 
 
642
    <p>The other links we've seen have either gone directly to a JSP file, or to a Struts action path, like Login.do. The "Edit your user registration profile" link is a little different, since it also uses a parameter, as in EditRegistration.do?action=Edit. When the Struts ActionServlet processes this link, it will ignore the parameter for the purpose of matching the request, but still pass the parameter along to action's object.</p>
 
643
 
 
644
    <blockquote>
 
645
      <p><i>This means that in Struts, an action object must be able to handle every valid parameter for it's base path. (In the Example, EditRegistration <b>must</b> handle both Edit and Create.)</i></p>
 
646
 
 
647
      <p><i>You may want to check for invalid parameters too. (And be careful of differences in case if your comparisons are not case insensitive!)</i></p>
 
648
    </blockquote>
 
649
 
 
650
    <p>If you check the struts-config.xml, you'll see that the EditRegistration action is mapped to the (surprise again!), the EditRegistrationAction; it uses a RegistrationForm bean, and registration.jsp for input.</p>
 
651
 
 
652
    <blockquote>
 
653
      <p><code>&lt;!-- Registration form bean --&gt;<br />
 
654
      &lt;form-bean name="RegistrationForm"<br />
 
655
      type="org.apache.struts.webapp.example.RegistrationForm"/&gt;</code></p>
 
656
 
 
657
      <p><code>&lt;!-- Edit user registration --&gt;<br />
 
658
      &lt;action path="/EditRegistration"<br />
 
659
      type="org.apache.struts.webapp.example.EditRegistrationAction"<br />
 
660
      name="RegistrationForm"<br />
 
661
      scope="request"<br />
 
662
      validate="false"<br />
 
663
      input="/registration.jsp"&gt;<br />
 
664
      &lt;forward name="success" path="/registration.jsp"/&gt;<br />
 
665
      &lt;/action&gt;</code></p>
 
666
 
 
667
      <p><i>Hint: Consistent naming conventions, like the ones used throughout the Example, make applications much easier to write and understand. Save your creativity for the things that matter, and follow an established standard for source code formatting, like the <a href="www.amazon.com/exec/obidos/ISBN=0521777682/">Elements of Java Style</a>.</i></p>
 
668
    </blockquote>
 
669
 
 
670
    <h4><a name="EditRegistrationAction.java" id="EditRegistrationAction.java">EditRegistrationAction.java</a></h4>
 
671
 
 
672
    <p>Many objects in an application may do double-duty. For example, EditRegistrationAction not only lets you update a registration, but is also used to create a new one. Which task the object performs is determined by the action passed to it. In the case of EditRegistrationAction, it can either edit or create a registration, the default being create if a task is not specified. To select between tasks, simply add ?create or ?edit to the hyperlink or form action.</p>
 
673
 
 
674
    <blockquote>
 
675
      <p><i>Like most classes in the example application, editRegistration makes good use of the log to track it's progress. Note that ActionServlet has had a new log method added since the Example was written. You can now specify both the message and a minimum logging (or debug) level. For more, see the Javadoc in your struts-documentation application.</i></p>
 
676
    </blockquote>
 
677
 
 
678
    <h3><a name="registration.jsp" id="registration.jsp">registration.jsp</a> and <a name="RegistrationForm.java" id="RegistrationForm.java">RegistrationForm.java</a></h3>
 
679
 
 
680
    <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 page. This page displays everything the Example application knows about you (or at least your login), while demonstrating several interesting techniques.</p>
 
681
 
 
682
    <p>You'll remember that mainMenu.jsp wanted to be sure that everyone was logged in, and used the CheckLogin tag to enforce that. The registration.jsp is a little different. First it uses a Struts logic tag to see if the task at hand is to register a new User. If not (e.g. action != "Create"), the logic tag exposes a CheckLoginTag to be sure we have a user (and therefore a registration) to edit.</p>
 
683
 
 
684
    <blockquote>
 
685
      <p><code>&lt;logic:equal<br />
 
686
      name="RegistrationForm"<br />
 
687
      property="action"<br />
 
688
      scope="request"<br />
 
689
      value="Edit"<br />
 
690
      &gt;<br />
 
691
      &lt;app:checkLogon/&gt;<br />
 
692
      &lt;/logic:equal&gt;</code></p>
 
693
 
 
694
      <p><i>Note that the Struts html:form tag will refer to properties set by struts-config.xml and automatically create a RegistrationForm bean if one is not present. However, that does not happen until the form tag is processed within the page. Since this block appears before the html:form tag, a runtime error is exposed if you try to access registration.jsp directly (rather then going through the EditRegistration.do action).</i></p>
 
695
    </blockquote>
 
696
 
 
697
    <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"), 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>
 
698
 
 
699
    <blockquote>
 
700
      <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>
 
701
    </blockquote>
 
702
 
 
703
    <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>
 
704
 
 
705
    <blockquote>
 
706
      <p><code>&lt;logic:equal<br />
 
707
      name="RegistrationForm"<br />
 
708
      property="action"<br />
 
709
      scope="request"<br />
 
710
      value="Edit"<br />
 
711
      &gt;</code></p>
 
712
    </blockquote>
 
713
 
 
714
    <p>Otherwise, the page just contains the top portion -- a blank data-entry form for creating the user's registration.</p>
 
715
 
 
716
    <h4><a name="logic:iterate" id="logic:iterate">logic:iterate</a></h4>
 
717
 
 
718
    <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>
 
719
 
 
720
    <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>
 
721
 
 
722
    <blockquote>
 
723
      <p>&lt;logic:iterate name="user" property="subscriptions" id="subscription"&gt;<br />
 
724
      &lt;!-- block to repeat --&gt;<br />
 
725
      &lt;/logic:iterate&gt;</p>
 
726
    </blockquote>
 
727
 
 
728
    <p>The three parameters to the iterate tag ( name, property, and id) tell it to</p>
 
729
 
 
730
    <ol>
 
731
      <li>Check this context for an attribute (e.g. object) named "user",</li>
 
732
 
 
733
      <li>Snag the property of user named "subscriptions",</li>
 
734
 
 
735
      <li>In the block to iterate, use "subscription" (singular) as the name for each member of the collection.</li>
 
736
    </ol>
 
737
 
 
738
    <p>So, to list the host for each subscription in a HTML unordered list, we could write:</p>
 
739
 
 
740
    <blockquote>
 
741
      <p><code>&lt;ul&gt;<br />
 
742
      &lt;logic:iterate name="user" property="subscriptions" id="subscription"&gt;<br />
 
743
      &lt;li&gt;<br />
 
744
      &lt;bean:write name="subscription" property="host" filter="true" /&gt;<br />
 
745
      &lt;/li&gt;<br />
 
746
      &lt;/logic:iterate&gt;<br />
 
747
      &lt;/ul&gt;</code></p>
 
748
 
 
749
      <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>
 
750
    </blockquote>
 
751
 
 
752
    <p>In registration.jsp, iterate is used to create a menu of subscriptions, each linked with an edit and delete action.</p>
 
753
 
 
754
    <blockquote>
 
755
      <p><code>&lt;logic:iterate id="subscription" name="user" property="subscriptions"&gt;<br />
 
756
      &lt;tr&gt;<br />
 
757
      &lt;td align="left"&gt;<br />
 
758
      &lt;bean:write name="subscription" property="host" filter="true"/&gt;<br />
 
759
      &lt;/td&gt;<br />
 
760
      &lt;td align="left"&gt;<br />
 
761
      &lt;bean:write name="subscription" property="username" filter="true"/&gt;<br />
 
762
      &lt;/td&gt;<br />
 
763
      &lt;td align="center"&gt;<br />
 
764
      &lt;bean:write name="subscription" property="type" filter="true"/&gt;<br />
 
765
      &lt;/td&gt;<br />
 
766
      &lt;td align="center"&gt;<br />
 
767
      &lt;bean:write name="subscription" property="autoConnect"/&gt;<br />
 
768
      &lt;/td&gt;<br />
 
769
      &lt;td align="center"&gt;<br />
 
770
      &lt;app:linkSubscription page="/EditSubscription.do?action=Delete"&gt;<br />
 
771
      &lt;bean:message key="registration.deleteSubscription"/&gt;<br />
 
772
      &lt;/app:linkSubscription&gt;<br />
 
773
      &lt;app:linkSubscription page="/EditSubscription.do?action=Edit"&gt;<br />
 
774
      &lt;bean:message key="registration.EditSubscription"/&gt;<br />
 
775
      &lt;/app:linkSubscription&gt;<br />
 
776
      &lt;/td&gt;<br />
 
777
      &lt;/tr&gt;<br />
 
778
      &lt;/logic:iterate&gt;</code></p>
 
779
 
 
780
      <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>
 
781
    </blockquote>
 
782
 
 
783
    <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>
 
784
 
 
785
    <h4><a name="LinkSubscriptionTag.java" id="LinkSubscriptionTag.java">LinkSubscriptionTag.java</a></h4>
 
786
 
 
787
    <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>
 
788
 
 
789
    <blockquote>
 
790
      <p><code>&lt;A HREF=[path]EditSubscription.do?action=[action]&amp;username=[user]&amp;host=[host]"&gt;[action]<br />
 
791
      &lt;/A&gt;</code></p>
 
792
    </blockquote>
 
793
 
 
794
    <p>based on input block like:</p>
 
795
 
 
796
    <blockquote>
 
797
      <p><code>&lt;app:linkSubscription<br />
 
798
      page="/EditSubscription.do?action=Delete"&gt;Delete<br />
 
799
      &lt;/app:linkSubscription&gt;</code></p>
 
800
    </blockquote>
 
801
 
 
802
    <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>
 
803
 
 
804
    <p>Here are a few annotated highlights from LinkSubscriptionTag.java:</p>
 
805
 
 
806
    <ol>
 
807
      <li><i>Create a string buffer, and ask the request for the relative path to the application</i><br />
 
808
      <code>StringBuffer url = new StringBuffer(request.getContextPath());</code></li>
 
809
 
 
810
      <li><i>Snag a reference to the subscription bean (for this iteration)<br /></i> <code>subscription = (Subscription) pageContext.findAttribute(name);</code></li>
 
811
 
 
812
      <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 />
 
813
      url.append("&amp;host=");<br />
 
814
      url.append(BeanUtils.filter(subscription.getHost()));</code></li>
 
815
    </ol>
 
816
 
 
817
    <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>
 
818
 
 
819
    <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>
 
820
 
 
821
    <blockquote>
 
822
      <p><code>&lt;app:linkUser page="/EditSubscription.do?action=Create"&gt;<br />
 
823
      &lt;bean:message key="registration.addSubscription"/&gt;<br />
 
824
      &lt;/app:linkUser&gt;</code></p>
 
825
    </blockquote>
 
826
 
 
827
    <p>By this time, you must be ready to flip directly to LinkUserTag.java with nary a glance at the configuration file ...</p>
 
828
 
 
829
    <h4><a name="LinkUserTag.java" id="LinkUserTag.java">LinkUserTag.java</a></h4>
 
830
 
 
831
    <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>
 
832
 
 
833
    <blockquote>
 
834
      <p><code>&lt;app:linkUser page="/EditSubscription.do?action=Create"&gt;<br />
 
835
      &lt;bean:message key="registration.addSubscription"/&gt;<br />
 
836
      &lt;/app:linkUser&gt;</code></p>
 
837
    </blockquote>
 
838
 
 
839
    <p>When rendered, this displays a HTML hypertext link like:</p>
 
840
 
 
841
    <blockquote>
 
842
      <p><code>&lt;a href="/struts-example/EditSubscription.do?action=Create&amp;amp;username=user"&gt;<br />
 
843
      Add<br />
 
844
      &lt;/a&gt;</code></p>
 
845
 
 
846
      <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>
 
847
    </blockquote>
 
848
 
 
849
    <p>Let's follow that "Add" link now and see what's up with the EditSubcription action anyway.</p>
 
850
 
 
851
    <h4><a name="EditSubscriptionAction.java" id="EditSubscriptionAction.java">EditSubscriptionAction.java</a></h4>
 
852
 
 
853
    <p>Predictably, we find a some now-familiar mappings in struts-config.xml</p>
 
854
 
 
855
    <blockquote>
 
856
      <p><code>&lt;!-- Subscription form bean --&gt;<br />
 
857
      &lt;form-bean<br />
 
858
      name="SubscriptionForm"<br />
 
859
      type="org.apache.struts.webapp.example.SubscriptionForm"<br />
 
860
      /&gt;</code></p>
 
861
 
 
862
      <p><code>&lt;!-- Edit mail subscription --&gt;<br />
 
863
      &lt;action path="/EditSubscription"<br />
 
864
      type="org.apache.struts.webapp.example.EditSubscriptionAction"<br />
 
865
      name="SubscriptionForm"<br />
 
866
      scope="request"<br />
 
867
      validate="false"<br />
 
868
      &gt;<br />
 
869
      &lt;forward name="failure" path="/mainMenu.jsp"/&gt;<br />
 
870
      &lt;forward name="success" path="/subscription.jsp"/&gt;<br />
 
871
      &lt;/action&gt;</code></p>
 
872
 
 
873
      <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>
 
874
    </blockquote>
 
875
 
 
876
    <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>
 
877
 
 
878
    <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>
 
879
 
 
880
    <blockquote>
 
881
      <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>
 
882
    </blockquote>
 
883
 
 
884
    <p>But before turning to our final JSP, a word about our database model ...</p>
 
885
 
 
886
    <h4><a name="User.java" id="User.java">User.java</a> and <a name="Subscription.java" id="Subscription.java">Subscription.java</a></h4>
 
887
 
 
888
    <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>
 
889
 
 
890
    <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>
 
891
 
 
892
    <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>
 
893
 
 
894
    <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.
 
895
 
 
896
    <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>
 
897
 
 
898
    <blockquote>
 
899
      <p><code>&lt;html:select property="type"&gt;<br />
 
900
      &lt;html:options<br />
 
901
      collection="serverTypes"<br />
 
902
      property="value"<br />
 
903
      labelProperty="label"<br />
 
904
      /&gt;<br />
 
905
      &lt;/html:select&gt;</code></p>
 
906
    </blockquote>
 
907
 
 
908
    <p>Struts outputs a block like</p>
 
909
 
 
910
    <blockquote>
 
911
      <p><code>&lt;select name="type"&gt;<br />
 
912
      &lt;option value="imap" selected&gt;IMAP Protocol&lt;/option&gt;<br />
 
913
      &lt;option value="pop3"&gt;POP3 Protocol&lt;/option&gt;<br />
 
914
      &lt;/select&gt;</code></p>
 
915
    </blockquote>
 
916
 
 
917
    <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>
 
918
 
 
919
    <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>
 
920
 
 
921
    <blockquote>
 
922
      <p><i>The LabelValueBean used to create the demonstration array is also a good example of simple but useful bean object.</i></p>
 
923
    </blockquote>
 
924
 
 
925
    <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>
 
926
 
 
927
    <h4><a name="SubscriptionForm.java" id="SubscriptionForm.java">SubscriptionForm.java</a></h4>
 
928
 
 
929
    <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>
 
930
 
 
931
    <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>
 
932
 
 
933
    <blockquote>
 
934
      <p><i>For more examples of validating forms, take another look at LoginForm.java and RegistrationForm.java.</i></p>
 
935
    </blockquote>
 
936
 
 
937
    <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>
 
938
 
 
939
    <blockquote>
 
940
      <p><code>&lt;logic:equal<br />
 
941
      name="SubscriptionForm"<br />
 
942
      property="action"<br />
 
943
      scope="request"<br />
 
944
      value="Create"&gt;<br />
 
945
      &lt;html:submit&gt;<br />
 
946
      <b>&lt;bean:message key="button.save"/&gt;<br /></b> &lt;/html:submit&gt;<br />
 
947
      &lt;/logic:equal&gt;</code></p>
 
948
    </blockquote>
 
949
 
 
950
    <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>
 
951
 
 
952
    <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>
 
953
 
 
954
    <h4><a name="SaveSubscriptionAction.java" id="SaveSubscriptionAction.java">SaveSubscriptionAction.java</a></h4>
 
955
 
 
956
    <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>
 
957
 
 
958
    <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>
 
959
 
 
960
    <h3><a name="Summary" id="Summary">Summary</a></h3>
 
961
 
 
962
    <ul>
 
963
      <li>Struts uses a single controller servlet to route HTTP requests.</li>
 
964
 
 
965
      <li>The requests are routed to action objects according to path (or URI).</li>
 
966
 
 
967
      <li>Each request is handled as a separate thread</li>
 
968
 
 
969
      <li>There is only one object for each action (URI), so your action objects must be multi-thread safe.</li>
 
970
 
 
971
      <li>The configuration of action objects are loaded from a XML resource file, rather than hardcoded.</li>
 
972
 
 
973
      <li>Action objects can respond to the request, or ask the controller to forward the request to another object or to another page, such as an input form.</li>
 
974
 
 
975
      <li>A library of custom tags works with the rest of the framework to enhance use of JavaServer Pages.</li>
 
976
 
 
977
      <li>The Struts form tag can work closely with an action objects via a Struts ActionFormBean to retain the state of a data-entry form, and validate the data entered.</li>
 
978
 
 
979
      <li>ActionForm beans can be automatically created by the JSP form or controller servlet.</li>
 
980
 
 
981
      <li>Struts supports a message resource for loading constants strings. Alternate message resources can be provided to internationalize an application.</li>
 
982
    </ul>
 
983
    <hr />
 
984
  </blockquote>
 
985
</body>
 
986
</html>