> ## A Walking Tour of the Struts 2 MailReader Application

> This article is meant to introduce a new user to Apache Struts 2 by "walking through" a simple, but functional, application. The article includes code snippets, but for the best result, you might want to install the MailReader application on your own development workstation and follow along. Of course, the full source code to the MailReader is included in the distribution.

> The tour assumes the reader has a basic understanding of the Java language, JavaBeans, web applications, and JavaServer Pages. For background on these technologies, see the Key Technologies Primer.

> - Welcome
>   - web.xml and resources.properties
>   - Welcome.do
>   - Welcome Action
>   - Global Results
>   - ApplicationListener.java
>   - Message Resources
>   - Welcome Page
> - Login
>   - Login Page
>   - Login-validation.xml
>   - Login.java
>   - MailreaderSupport.java
>   - Login Configuration
> - MainMenu
> - Registration page
>   - iterator
> - Subscription
>   - Subscription.java

> The premise of the MailReader is that it is the first iteration of a portal application. This version allows users to register and maintain a set of accounts with various mail servers. If completed, the application would let users read mail from their accounts.

> The MailReader application demonstrates registering with an application, logging into an application, maintaining a master record, and maintaining child records. This article overviews the constructs needed to do these things, including the server pages, Java classes, and configuration elements.

> For more about the MailReader, including alternate implementations and a set of formal Use Cases, please visit the Struts University MailReader site.

> > JAAS - Note that for compatibility 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 Key Technologies Primer for more about authentication technologies.)

> The tour starts with how the initial welcome page is displayed, and then steps through logging into the application and editing a subscription. Please note that this not a quick peek at a "Hello World" application. The tour is a rich trek into a realistic, best practices application. You may need to adjust your chair and get a fresh cup of coffee. Printed, the article is 29 pages long (US).

> ### Welcome Page

> 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 default "welcome page" is served as the response.

> #### web.xml

> When a web application loads, the container reads and parses the "Web Application Deployment Descriptor", or "web.xml" file. The framework plugs into a web application via a servlet filter. Like any filter, the "struts2" filter is deployed via the "web.xml".

> ##### web.xml - The Web Application Deployment Descriptor

>     <?xml version="1.0" encoding="ISO-8859-1"?>
>     <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
>       "http://java.sun.com/dtd/web-app_2_3.dtd">
>     <web-app>

>       <display-name>Struts 2 MailReader</display-name>

>       <filter>
>         <filter-name>struts2</filter-name>
>         <filter-class>
>           org.apache.struts2.dispatcher.FilterDispatcher
>         </filter-class>
>        </filter>

>       <filter-mapping>
>         <filter-name>struts2</filter-name>
>         <url-pattern>/*</url-pattern>
>       </filter-mapping>

>       <listener>
>         <listener-class>
>           org.springframework.web.context.ContextLoaderListener
>         </listener-class>
>       </listener>

>       <!-- Application Listener for MailReader database -->
>       <listener>
>         <listener-class>
>           mailreader2.ApplicationListener
>         </listener-class>
>       </listener>

>       <welcome-file-list>
>         <welcome-file>index.html</welcome-file>
>       </welcome-file-list>

>       </web-app>

> You might note that the web.xml configuration does not specify which file extension to use with actions. The default extension for Struts 2 is ".action", but the extension can be changed in the struts.properties file. For compatability with prior releases, the MailReader uses a .do extension for actions.

> ##### struts.properties

>     struts.action.extension = do

> The web.xml does specify a "Welcome File List" for the application. When a web address refers to a directory rather than an individual file, the container consults the Welcome File List for the name of a page to open by default.

> However, most Struts applications do not refer to physical pages, but to "virtual resources" called actions. Actions specify code that we want to be run before a page or other resource renders the response. An accepted practice is to never link directly to server pages, but only to logical action mappings. By linking to actions, developers can often "rewire" an application without editing the server pages.

> ##### Best Practice:

> > "Link actions not pages."

> The actions are listed in one or more XML configuration files, the default configuration file being named "struts.xml". When the application loads, the struts.xml, and any other files it includes, are parsed, and the framework creates a set of configuration objects. Among other things, the configuration maps a request for a certain page to a certain action mapping.

> Sites can list zero or more "Welcome" pages in the web.xml. Unless you are using Java 1.5, actions cannot be specified as a Welcome page. So, in the case of a Welcome page, how do we follow the best practice of navigating through actions rather than pages?

> One solution is to use a page to "bootstrap" one of our actions. We can register the usual "index.html" as the Welcome page and have it redirect to a "Welcome" action.

> ##### MailReader's index.html

>     <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
>     <html><head>
>       <META HTTP-EQUIV="Refresh" CONTENT="0;URL=Welcome.do">
>       </head>
>       <body>
>         <p>Loading ...</p>
>     </body></html>

> As an alternative, we could also have used a JSP page that issued the redirect with a Struts tag, but a plain HTML solution works as well.

> #### Welcome.do

> When the client requests "Welcome.do", the request is passed to the "struts2" FilterDispatcher (that we registered in the web.xml file). The FilterDispatcher retrieves the appropriate action mapping from the configuration. If we just wanted to forward to the Welcome page, we could use a simple configuration element.

> ##### A simple "forward thru" action element

>     <action name="Welcome">
>       <result>/pages/Welcome.jsp</result>
>     </action>

> If a client asks for the Welcome action ("Welcome.do"), the "/page/Welcome.jsp" page would be returned in response. The client does not know, or need to know, that the physical resource is located at "/pages/Welcome.jsp". All the client knows is that it requested the resource "Welcome.do".

> But if we peek at the configuration file for the MailReader, we find a slightly more complicated XML element for the Welcome action.

> ##### The Welcome action element

>     <action name="Welcome" class="mailreader2.Welcome">
>         <result>/pages/Welcome.jsp</result>
>         <interceptor-ref name="guest"/>
>         </action>

> Here, the Welcome Java class executes whenever someone asks for the Welcome action. As it completes, the Action class can select which "result" is displayed. The default result name is "success". Another available result, defined at a global scope, is "error".

> ##### Key concept:

> > The Action class doesn't need to know what result type is needed for "success" or "error". The Action can just return the logical name for a result, without knowing how the result is implemented.

> The net effect is that all of the result details, including the paths to server pages, all can be declared once in the configuration. Tightly coupled implementation details are not scattered all over the application.

> ##### Key concept:

> > The Struts configuration lets us separate concerns and "say it once". The configuration helps us "normalize" an application, in much the same way we normalize a database schema.

> OK ... but why would a Welcome Action want to choose between "success" and "error"?

> #### Welcome Action

> 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 Welcome class checks to see if the database is available.

> The MailReader is also an internationalized application. So, the Welcome Action class checks to see if the message resources are available too. If both resources are available, the class passes back the "success" token. Otherwise, the class passes back the "error" token, so that the appropriate messages can be displayed.

> ##### The Welcome Action class

>     package mailreader2;
>     public class Welcome extends MailreaderSupport {

>       public String execute() {

>         // Confirm message resources loaded
>         String message = getText(Constants.ERROR_DATABASE_MISSING);
>         if (Constants.ERROR_DATABASE_MISSING.equals(message)) {
>           addActionError(Constants.ERROR_MESSAGES_NOT_LOADED);
>         }

>         // Confirm database loaded
>         if (null==getDatabase()) {
>           addActionError(Constants.ERROR_DATABASE_NOT_LOADED);
>         }

>         if (hasErrors()) {
>           return ERROR;
>         }
>         else {
>           return SUCCESS;
>         }
>       }
>     }

> Several common result names are predefined, including ERROR, SUCCESS, LOGIN, NONE, and INPUT, so that these tokens can be used consistently across Struts 2 applications.

> #### Global Results

> As mentioned, "error" is defined in a global scope. Other actions may have trouble connecting to the database later, or other unexpected errors may occur. The MailReader defines the "error" result as a Global Result, so that any action can use it.

> ##### MailReader's global-result element

>      <global-results>
>       <result name="error">/pages/Error.jsp</result>
>       <result name="invalid.token">/pages/Error.jsp</result>
>       <result name="login" type="redirect-action">Login_input</result>
>     </global-results>

> Of course, if an individual action mapping defines its own "error" result type, the local result would be used instead.

> #### ApplicationListener.java

> The database is exposed 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?

> The database is created by a custom Listener that we configured in the "web.xml".

> ##### mailreader2.ApplicationListener

>      <listener>
>       <listener-class>
>         mailreader2.ApplicationListener
>       </listener-class>
>     </listener>

> By default, our ApplicationListener loads a MemoryDatabase implementation of the UserDatabase. MemoryDatabase stores the database content as a XML document, which is parsed 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. When you login, the user object is stored within the session context.

> The database comes seeded with a sample user. If you check the "database.xml" file under "/src/main/resources", you'll see the sample user described in XML.

> ##### The "seed" user element from the MailReader database.xml

>     <user username="user" fromAddress="John.User@somewhere.com"
>       fullName="John Q. User" password="pass">
>         <subscription host="mail.hotmail.com" autoConnect="false"
>           password="bar" type="pop3" username="user1234">
>         </subscription>
>         <subscription host="mail.yahoo.com" autoConnect="false" password="foo"
>           type="imap" username="jquser">
>         </subscription>
>     </user>

> The "seed" user element creates a registration record for "John Q. User", with the subscription detail for his hotmail and yahoo accounts.

> #### Message Resources

> As mentioned, MailReader is an internationalized application. In Struts 2, message resources are associated with the Action class being processed. If we check the source, we find a language resource bundle named MailreaderSupport. MailreaderSupport is our base class for all the MailReader Actions. Since all of our Actions extend MailreaderSupport, all of our Actions can use the same resource bundle.

> ##### Message Resource entries used by the Welcome page

>     index.heading=MailReader Application Options
>     index.login=Log on to the MailReader Application
>     index.registration=Register with the MailReader Application
>     index.title=MailReader Demonstration Application
>     index.tour=A Walking Tour of the MailReader Demonstration Application

> If you change a message in the resource, and then rebuild and reload the application, the change will appear throughout the application. If you provide message resources for additional locales, you can localize your application. The MailReader provides resources for English, Russian, and Japanese.

> #### Welcome Page

> After confirming that the necessary resources exist, the Welcome action forwards to the Welcome page.

> ##### Welcome.jsp

>     <%@ page contentType="text/html; charset=UTF-8" %>
>     <%@ taglib prefix="s" uri="http://struts.apache.org/tags" %>
>       <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
>         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>       <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>         <head>
>           <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
>           <title><s:text name="index.title"/></title>
>           <link href="<s:url value="/css/mailreader.css"/>" rel="stylesheet"
>           type="text/css"/>
>         </head>

>         <body>
>           <h3><s:text name="index.heading"/></h3>

>           <ul>
>             <li><a href="<s:url action="Registration_input"/>"><s:text
>               name="index.registration"/></a></li>
>             <li><a href="<s:url action="Login_input"/>"><s:text
>               name="index.login"/></a></li>
>           </ul>

>           <h3>Language Options</h3>
>           <ul>
>               <li>
>                   <s:url id="en" action="Welcome">
>                       <s:param name="request_locale">en</s:param>
>                   </s:url>
>                    <s:a href="%{en}">English</s:a>
>                </li>
>               <li>
>                   <s:url id="ja" action="Welcome">
>                     <s:param name="request_locale">ja</s:param>
>                   </s:url>
>                   <s:a href="%{ja}">Japanese</s:a>
>               </li>
>               <li>
>                   <s:url id="ru" action="Welcome">
>                   <s:param name="request_locale">ru</s:param>
>                   </s:url>
>                   <s:a href="%{ru}">Russian</s:a>
>               </li>
>           </ul>

>         <hr />

>         <p><s:i18n name="alternate">
>         <img src="<s:text name="struts.logo.path"/>"
>           alt="<s:text name="struts.logo.alt"/>"/>
>         </s:i18n></p>

>         <p><a href="<s:url action="Tour" />"><s:text name="index.tour"/></a></p>

>       </body>
>     </html>

> At the top of the Welcome page, there are several directives that load the Struts 2 tag libraries. These are just the usual red tape that goes with any JSP file. The rest of the page utilizes three Struts JSP tags: "text", "url", and "i18n".

> (We use the tag prefix "s:" in the Struts 2 MailReader application, but you can use whatever prefix you like in your applications.)

> The text tag inserts a message from an application's default resource bundle. If the framework's locale setting is changed for a user, the text tag will render messages from the new locale's resource bundle instead.

> The url tag can render a reference to an action or any other web resource, applying "URL encoding" to the hyperlinks as needed. Java's URL encoding feature lets your application maintain client state without requiring cookies.

> ##### Tip:

> > Cookies - 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.)

> The i18n tag provides access to multiple resource bundles. The MailReader application uses a second set of message resources for non-text elements. When these are needed, we use the "i18n" tag to specify a different bundle.

> The alternate bundle is stored in the {{/src/main/resources}} folder, so that it ends up under "classes", which is on the application's class path.

> In the span of a single request for the Welcome page, the framework has done quite a bit already:

> - Confirmed that required resources were loaded during initialization.
> - Written all the page headings and labels from internationalized message resources.
> - Automatically URL-encoded paths as needed.

> When rendered, the Welcome page lists two menu options: one to register with the application and one to log on (if you have already registered). Let's follow the Login link first.

> ### Login

> If you choose the Login link, and all goes well, the Login action forwards control to the Login page.

> #### Login Page

> The Login page displays a form that accepts a username and password. You can use the default username and password to login (user and pass), if you like. Try omitting or misspelling the username and password in various combinations to see how the application reacts. Note that both the username and password are case sensitive.

> ##### Login.jsp

>     <%@ page contentType="text/html; charset=UTF-8" %>
>       <%@ taglib prefix="s" uri="http://struts.apache.org/tags"  %>
>       <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
>         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>       <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>       <head>
>         <title><s:text name="login.title"/></title>
>           <link href="<s:url value="/css/mailreader.css"/>" rel="stylesheet"
>             type="text/css"/>
>       </head>
>       <body onLoad="self.focus();document.Login.username.focus()">
>         <s:actionerror/>
>         <s:form action="Login" validate="true">
>           <s:textfield key="username"/>
>           <s:password key="password"/>
>           <s:submit key="button.save"/>
>           <s:reset key="button.reset"/>
>           <s:submit action="Login_cancel" onclick="form.onsubmit=null"
>             key="button.cancel"/>
>         </s:form>
>         <jsp:include page="Footer.jsp"/>
>       </body>
>     </html>

> We already saw some of the tags used by the Login page on the Welcome page. Let's focus on the new tags.

> The first new tag on the Login page is actionerrors. Most of the possible validation errors are related to a single field. If you don't enter a username, the framework can place an error message near the tag prompting you to enter a username. But some messages are not related to a single field. For example, the database might be down. If the action returns an "Action Error", as opposed to a "Field Error", the messages are rendered in place of the "actionerror" tag. The text for the validation errors, whether they are Action Errors or Field Errors, can be specified in the resource bundle, making the messages easy to manage and localize.

> The second new tag is form. This tag renders a HTML form tag. The "validate=true" setting enables client-side validation, so that the form can be validated with JavaScript before being sent back to the server. The framework will still validate the form again, just to be sure, but the client-side validation can save a few round-trips to the server.

> Within the form tag, we see four more new tags: "textfield", "password", "submit", and "reset". We also see a second usage of "submit" that utilizes an "action" attribute.

> When we place a control on a form, we usually need to code a set of HTML tags to do everything we want to do. Most often, we do not just want a plain "input type=text" tag. We want the input field to have a label too, and maybe even a tooltip. And, of course, a place to print a message should invalid data be entered.

> The Struts Tags support templates and themes so that a set of HTML tags can be rendered from a single Struts Tag. For example, the single tag

>         <s:textfield key="username"/>

> generates a wad of HTML markup.

>     <tr>
>       <td class="tdLabel">
>         <label for="Login_username" class="label">Username:</label>
>       </td>
>       <td>
>         <input type="text" name="username" value="" id="Login_username"/>
>       </td>
>     </tr>

> If for some reason you don't like the markup generated by a Struts Tag, it's each to change. Each tag is driven by a template that can be updated on a tag-by-tag basis. For example, here is the default template that generates the markup for the ActionErrors tag:

>     <#if (actionErrors?exists && actionErrors?size > 0)>
>       <ul>
>         <#list actionErrors as error>
>           <li><span class="errorMessage">${error}</span></li>
>         </#list>
>       </ul>
>     </#if>

> If you wanted ActionErrors displayed in a table instead of a list, you could edit a copy of this file, save it as a file named "template/simple/actionerror.ftl", and place this one file at the base of your application's classpath.

>     <#if (actionErrors?exists && actionErrors?size > 0)>
>       <table>
>         <#list actionErrors as error>
>           <tr><td><span class="errorMessage">${error}</span></td></tr>
>         </#list>
>       </table>
>     </#if>

> Under the covers, the framework uses Freemarker for its standard templating language. FreeMarker is similar to Velocity, but it offers better error reporting and some additional features. If you prefer, Velocity and JSP templates can also be used to create your own tags.

> The password tag renders a "input type=password" tag, along with the usual template/theme markup. By default, the password tag will not retain input if the submit fails. If the username is wrong, the client will have to enter the password again too. (If you did want to retain the password when validation fails, you can set the tag's "showPassword" property to true.)

> Unsurprisingly, the submit and reset tags render buttons of the corresponding types.

> The second submit button is more interesting.

>       <s:submit action="Login_cancel" onclick="form.onsubmit=null"
>         key="button.cancel"/>

> Here we are creating the Cancel button for the form. The button's attribute action="Login_cancel" tells the framework to submit to the Login's "cancel" method instead of the usual "execute" method. The onclick="form.onsubmit=null" script defeats client-side validation. On the server side, "cancel" is on a special list of methods that bypass validation, so the request will go directly to the Action's cancel method. Another entry on the special-case list is the "input" method.

> ##### Tip:

> > The Struts Tags have options and capabilities beyond what we have shown here. For more see, the Struts Tag documentation.

> OK, but how do the tags know that both of these fields are required? How do they know what message to display when the fields are empty?

> For the answers, we need to look at another flavor of configuration file: the "validation" file.

> #### Login-validation.xml

> While it is not hard to code data-entry validation into an Action class, the framework provides an even easier way to validate input.

> The validation framework is configured through another XML document, the Login-validation.xml.

> ##### Validation file for Login Action

>     <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
>       "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
>     <validators>
>       <field name="username">
>         <field-validator type="requiredstring">
>         <message key="error.username.required"/>
>       </field-validator>
>       </field>
>       <field name="password">
>         <field-validator type="requiredstring">
>         <message key="error.password.required"/>
>         </field-validator>
>       </field>
>     </validators>

> You may note that the DTD refers to "XWork". Open Symphony XWork is a generic command-pattern framework that can be used outside of a web environment. Essentially, Struts 2 is a web-based extension of the XWork framework.

> The field elements correspond to the ActionForm properties. The username and password field elements say that each field depends on the "requiredstring" validator. If the username is blank or absent, validation will fail and an error message is generated. The messages would be based on the "error.username.required" or "error.password.required" message templates from the resource bundle.

> #### Login Action

> If validation passes, the framework invokes the "execute" method of the Login Action. The actual Login Action is brief, since most of the functionality derives from the base class, MailreaderSupport.

> ##### Login.java

>     package mailreader2;
>     import org.apache.struts.apps.mailreader.dao.User;
>     public final class Login extends MailreaderSupport {
>     public String execute() throws ExpiredPasswordException {
>       User user = findUser(getUsername(), getPassword());
>       if (user != null) {
>         setUser(user);
>       }
>       if (hasErrors()) {
>         return INPUT;
>       }
>         return SUCCESS;
>       }
>     }

> Login lays out what we do to authenticate a user. We try to find the user using the credentials provided. If the user is found, we cache a reference. If the user is not found, we return "input" so the client can try again. Otherwise, we return "success", so that the client can access the rest of the application.

> #### MailreaderSupport.java

> Let's look at the relevant properties and methods from MailreaderSupport and another base class, ActionSupport, namely "getUsername", "getPassword", "findUser", "setUser", and "hasErrors".

> The framework lets you define JavaBean properties directly on the Action. Any JavaBean property can be used, including rich objects. When a request comes in, any public properties on the Action class are matched with the request parameters. When the names match, the request parameter value is set to the JavaBean property. The framework will make its best effort to convert the data, and, if necessary, it will report any conversion errors.

> The Username and Password properties are nothing fancy, just standard JavaBean properties.

> ##### MailreaderSupport.getUsername() and getPassword()

>     private String username = null;
>     public String getUsername() {
>       return this.username;
>     }
>     public void setUsername(String username) {
>       this.username = username;
>     }

>     private String password = null;
>     public String getPassword() {
>       return this.password;
>     }
>     public void setPassword(String password) {
>       this.password = password;
>     }

> We use these properties to capture the client's credentials, and pass them to the more interesting findUser method.

> ##### MailreaderSupport.findUser

>     public User findUser(String username, String password)
>       throws ExpiredPasswordException {
>       User user = getDatabase().findUser(username);
>       if ((user != null) && !user.getPassword().equals(password)) {
>         user = null;
>       }
>       if (user == null) {
>         this.addFieldError("password", getText("error.password.mismatch"));
>       }
>       return user;
>     }

> The "findUser" method dips into the MailReader Data Access Object layer, which is represented by the Database property. The code for the DAO layer is maintained as a separate component. The MailReader application imports the DAO JAR, but it is not responsible for maintaining any of the DAO source. Keeping the data access layer at "arms-length" is a very good habit. It encourages a style of development where the data access layer can be tested and developed independently of a specific end-user application. In fact, there are several renditions of the MailReader application, all which share the same MailReader DAO JAR!

> ##### Best Practice:

> > "Strongly separate data access and business logic from the rest of the application."

> When "findUser" returns, the Login Action looks to see if a valid (non-null) User object is returned. A valid User is passed to the User property. Although it is still a JavaBean property, the User property is not implemented in quite the same way as Username and Password.

> ##### MailreaderSupport.setUser

>     public User getUser() {
>       return (User) getSession().get(Constants.USER_KEY);
>     }
>     public void setUser(User user) {
>       getSession().put(Constants.USER_KEY, user);
>     }

> Instead of using a field to store the property value, "setUser" passes it to a Session property.

> ##### MailreaderSupport.getSession() and setSession()

>     private Map session;
>     public Map getSession() {
>       return session;

>     public void setSession(Map value) {
>       session = value;
>     }

> To look at the MailreaderSupport class, you would think the Session property is a plain-old Map. In fact, the Session property is an adapter that is backed by the servlet session object at runtime. The MailreaderSupport class doesn't need to know that though. It can treat Session like any other Map. We can also test the MailreaderSupport class by passing it some other implementation of Map, running the test, and then looking to see what changes MailreaderSupport made to our "mock" Session object.

> But, when MailreaderSupport is running inside a web application, how does it acquire a reference to the servlet session?

> Good question. If you were to look at just the MailreaderSupport class, you would not see a single line of code that sets the session property. But, yet, when we run the class, the session property is not null. Hmmm.

> The magic that provides the Session property a runtime value is called "dependency injection". The MailreaderSupport class implements a interface called SessionAware. SessionAware is bundled with the framework, and it defines a setter for the Session property.

> `public void setSession(Map session);`

> Also bundled with the framework is an object called the ServletConfigInterceptor. If the ServletConfigInterceptor sees that an Action implements the SessionAware interface, it automatically set the session property.

>     if (action instanceof SessionAware) {
>       ((SessionAware) action).setSession(context.getSession());
>     }

> The framework uses these "Interceptor" classes to create a front controller for each action an application defines. Each Interceptor can peek at the request before an Action class is invoked, and then again after the Action class is invoked. (If you have worked with Servlet Filters, you will recognize this pattern. But, unlike Filters, Interceptors are not tied to HTTP. Interceptors can be tested and developed outside of a web application.)

> You can use the same set of Interceptors for all your actions, or define a special set of Interceptors for any given action, or define different sets of Interceptors to use with different types of actions. The framework comes with a default set of Interceptors, that it will use when another set is not specified, but you can designate your own default Interceptor set (or "stack") in the Struts configuration.

> Many Interceptors provide a utility or helper functions, like setting the session property. Others, like the ValidationInterceptor, can change the workflow of an action. Interceptors are key feature of the framework, and we will see a few more on the tour.

> If a valid User is not found, or the password doesn't match, the "findUser" method invokes the addFieldError method to note the problem. When "findUser" returns, the Login Action checks for errors, and then it returns either INPUT or SUCCESS.

> The "addFieldError" method is provided by the ActionSupport class, which is bundled with the framework. The constants for INPUT and SUCCESS are also provided by ActionSupport. While the ActionSupport class provides many useful utilities, you are not required to use it as a base class. Any Java class can be used as an Action, if you like.

> It is a good practice to provide a base class with utilities that can be shared by an application's Action classes. The framework does this with ActionSupport, and the MailReader application does the same with the MailreaderSupport class.

> ##### Best Practice:

> > "Use a base class to define common functionality."

> But, what happens if Login returns INPUT instead of SUCCESS. How does the framework know what to do next?

> To answer that question, we need to turn back to the Struts configuration and look at how Login is declared.

> #### Login Configuration

> The Login action element outlines how the Login workflow operates, including what to do when the Action returns "input", or the default result name "success".

> ##### mailreader-support.xml Login

>     <action name="Login_*" method="{1}" class="mailreader2.Login">
>       <result name="input">/pages/Login.jsp</result>
>       <result name="cancel" type="redirect-action">Welcome</result>
>       <result type="redirect-action">MainMenu</result>
>       <result name="expired" type="chain">ChangePassword</result>
>       <exception-mapping
>         exception="org.apache.struts.apps.mailreader.dao.ExpiredPasswordException"
>       result="expired"/>
>       <interceptor-ref name="guest"/>
>     </action>

> You might notice that the name of the Login action element is not "Login" but "Login_*". The asterisk is a special "wildcard" notation that tells the framework to match any series of character at this point. In the method attribute, the "{1}" notation indicates that framework should substitute whatever characters match the asterisk at runtime. When we cite actions like "Login_cancel" or "Login_input", the framework matches "cancel" or "input" with the wildcard and fills in the blanks.

> The "trailing bang" notation was hardwired into WebWork 2. To provide backward compatibility, the notation is supported by Struts 2.0. If you prefer to use wildcards to emulate the same notation, as the Mailreader does, you should disable the old notation in the Struts properties file.

> ##### struts.properties

>     struts.enable.DynamicMethodInvocation = false

> Using wildcards with a exclamation point (or "bang") is not the only way we can use wilcards to invoke methods. If we wanted to use actions like "inputLogin", we could move the asterisk and use an action name like "*Login".

> Within the Login action element, the first result element is named "input". If validation or authentification fail, the Action class will return "input" and the framework will transfer control to the "Login.jsp" page.

> The second result element is named cancel. If someone presses the cancel button on the Login page, the Action class will return "cancel", this result will be selected, and the framework will issue a redirect to the Welcome action.

> The third result has no name, so it will be called if the default success token is returned. So, if the Login succeeds, control will transfer to the MainMenu action.

> The MailReader DAO exposes a "ExpiredPasswordException". If the DAO throws this exception when the User logs in, the framework will process the exception-mapping and transfer control to the "ChangePassword" action.

> Just in case any other Exceptions are thrown, the MailReader application also defines a global handler.

> ##### mailreader-default.xml exception-mapping

>     <global-exception-mappings>
>       <exception-mapping
>         result="error"
>         exception="java.lang.Exception"/>
>     </global-exception-mappings>

> If an unexpected Exception is thrown, the exception-mapping will transfer control to the action's "error" result, or to a global "error" result. The MailReader defines a global "error" result which transfers control to an "Error.jsp" page that can display the error message.

> ##### Error.jsp

>     <%@ page contentType="text/html; charset=UTF-8" %>
>     <%@ taglib prefix="s" uri="http://struts.apache.org/tags" %>
>       <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
>         "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>       <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>       <head>
>         <title>Unexpected Error</title>
>       </head>
>       <body>
>         <h2>An unexpected error has occured</h2>
>         <p>
>           Please report this error to your system administrator
>           or appropriate technical support personnel.
>           Thank you for your cooperation.
>         </p>
>         <hr />
>         <h3>Error Message</h3>
>         <s:actionerror />
>         <p>
>           <s:property value="%{exception.message}"/>
>         </p>
>         <hr />
>         <h3>Technical Details</h3>
>         <p>
>           <s:property value="%{exceptionStack}"/>
>         </p>
>         <jsp:include page="Footer.jsp"/>
>       </body>
>     </html>

> The Error page uses property tags to expose the Exception message and the Exception stack.

> Finally, the Login action specifies an InterceptorStack named defaultStack. If you've worked with Struts 2 or WebWork 2 before, that might seem strange, since "defaultStack" is the factory default.

> In the MailReader application, most of the actions are only available to authenticated users. The exceptions are the Welcome, Login, and Register actions which are available to everyone. To authenticate clients, the MailReader uses a custom Interceptor and a custom Interceptor stack.

> ##### mailreader2.AuthenticationInterceptor

>     package mailreader2;
>     import com.opensymphony.xwork2.interceptor.Interceptor;
>     import com.opensymphony.xwork2.ActionInvocation;
>     import com.opensymphony.xwork2.Action;
>     import java.util.Map;
>     import org.apache.struts.apps.mailreader.dao.User;

>     public class AuthenticationInterceptor implements Interceptor {
>       public void destroy () {}
>       public void init() {}
>       public String intercept(ActionInvocation actionInvocation) throws Exception {
>         Map session = actionInvocation.getInvocationContext().getSession();
>         User user = (User) session.get(Constants.USER_KEY);
>         boolean isAuthenticated = (null!=user) && (null!=user.getDatabase());
>         if (isAuthenticated) {
>           return actionInvocation.invoke();
>         }
>         else {
>           return Action.LOGIN;
>         }
>       }
>     }

> The AuthenticationInterceptor looks to see if a User object has been stored in the client's session state. If so, it returns normally, and the next Interceptor in the set would be invoked. If the User object is missing, the Interceptors returns "login". The framework would match "login" to the global result, and transfer control to the Login action.

> The MailReader defines three custom Interceptor stacks: "user", "user-submit", and "guest".

> ##### mailreader-default.xml interceptors

>     <interceptors>
>       <interceptor name="authentication"
>                    class="mailreader2.AuthenticationInterceptor"/>
>       <interceptor-stack name="user" >
>           <interceptor-ref name="authentication" />
>           <interceptor-ref name="defaultStack"/>
>       </interceptor-stack>
>       <interceptor-stack name="user-submit" >
>           <interceptor-ref name="token-session" />
>           <interceptor-ref name="user"/>
>       </interceptor-stack>
>       <interceptor-stack name="guest" >
>           <interceptor-ref name="defaultStack"/>
>       </interceptor-stack>
>     </interceptors>
>     <default-interceptor-ref name="user"/>

> The user stacks require that the client be authenticated. In other words, that a User object is present in the session. The actions using a guest stack can be accessed by any client. The -submit versions of each can be used with actions with forms, to guard against double submits.

> ##### Double Submits

> A common problem with designing web applications is that users are impatient and response times can vary. Sometimes, people will press a submit button a second time. When this happens, the browser submits the request again, so that we now have two requests for the same thing. In the case of registering a user, if someone does press the submit button again, and their timing is bad, it could result in the system reporting that the username has already been used. (The first time the button was pressed.) In practice, this would probably never happen, but for a longer running process, like checking out a shopping cart, it's easier for a double submit to occur.

> To forestall double submits, and "back button" resubmits, the framework can generate a token that is embedded in the form and also kept in the session. If the value of the tokens do not compare, then we know that there has been a problem, and that a form has been submitted twice or out of sequence.

> The Token Session Interceptor will also attempt to provide intelligent fail-over in the event of multiple requests using the same session. That is, it will block subsequent requests until the first request is complete, and then instead of returning the "invalid.token" code, it will attempt to display the same response that the original, valid action invocation would have displayed

> Because the default interceptor stack will now authenticate the client, we need to specify the standard "defaultStack" for the three "guest actions", Welcome, Login, and Register. Requiring authentification by default is the better practice, since it means that we won't forget to enable it when creating new actions. Meanwhile, those pesky users will ensure that we don't forget to disable authentification for "guest" services.

> ### MainMenu

> On a successful login, the Main Menu page displays. If you logged in using the demo account, the page title should be "Main Menu Options for John Q. User". Below this legend should be two links:

> - Edit your user registration profile
> - Log off MailReader Demonstration Application

> Let's review the source for the "MainMenu" action mapping, and the "MainMenu.jsp".

> ##### Action mapping element for MainMenu

>     <action name="MainMenu" class="mailreader2.MailreaderSupport">
>         <result>/pages/MainMenu.jsp</result>
>         </action>

> ##### MainMenu.jsp

>     <%@ page contentType="text/html; charset=UTF-8" %>
>     <%@ taglib prefix="s" uri="http://struts.apache.org/tags"  %>
>     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
>       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>       <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>       <head>
>         <title><s:text name="mainMenu.title"/></title>
>           <link href="<s:url value="/css/mailreader.css"/>" rel="stylesheet"
>           type="text/css"/>
>       </head>

>       <body>
>       <h3><s:text name="mainMenu.heading"/> <s:property
>         value="user.fullName"/></h3>
>       <ul>
>         <li><a href="<s:url action="Registration_input" />">
>             <s:text name="mainMenu.registration"/>
>           </a>
>         </li>
>         <li><a href="<s:url action="Logout" />">
>           <s:text name="mainMenu.logout"/>
>           </a>
>         </ul>
>       </body>
>     </html>

> The source for "MainMenu.jsp" also contains a new tag, property, which we use to customize the page with the "fullName" property of the authenticated user.

> Displaying the user's full name is the reason the MainMenu action references the MailreaderSupport class. The MailreaderSupport class has a User property that the text tag can access. If we did not utilize MailreaderSupport, the property tag would not be able to find the User object to print the full name.

> The customized MainMenu page offers two standard links. One is to "Edit your user registration profile". The other is to "Logout the MailReader Demonstration Application".

> ### Registration page

> 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.

> To do double duty as the "Create" Registration page and the "Edit" Registration page, the "Registration.jsp" makes extensive use of the test tags, to make it appears as though there are two distinct pages.

> ##### Registration.jsp - head element

>     <head>
>       <s:if test="task=='Create'">
>         <title><s:text name="registration.title.create"/></title>
>       </s:if>
>       <s:if test="task=='Edit'">
>         <title><s:text name="registration.title.edit"/></title>
>       </s:if>
>       <link href="<s:url value="/css/mailreader.css"/>" rel="stylesheet"
>         type="text/css"/>
>     </head>

> For example, if client is editing the form (task == 'Edit'), the page inserts the username from the User object. For a new Registration (task == 'Create'), the page creates an empty data-entry field.

> ##### Note:

> > Presention Logic - The "test" tag is a convenient way to express presentation logic within your pages. Customized pages help to prevent user error, and dynamic customization reduces the number of server pages your application needs to maintain, among other benefits.

> 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 that lists the subscriptions is exposed.

> #####

>     <s:if test="task == 'Edit'">
>       <div align="center">
>         <h3><s:text name="heading.subscriptions"/></h3>
>       </div>
>         <!-- ... -->
>       </s:if>
>     <jsp:include page="Footer.jsp"/>
>     </body></html>

> Otherwise, the page contains just the top portion -- a data-entry form for managing the user's registration.

> #### iterator

> Besides "if" there are several other control tags that you can use to sort, filter, or iterate over data. The Registration page includes a good example of using the iterator tag to display the User's Subscriptions.

> 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 iterator tag, you can code it the way it sounds.

> ##### Using iterator to list the Subscriptions

>     <s:iterator value="user.subscriptions">
>       <tr>
>         <td align="left">
>           <s:property value="host"/>
>         </td>
>         <td align="left">
>            <s:property value="username"/>
>        </td>
>       <td align="center">
>           <s:property value="type"/>
>       </td>
>       <td align="center">
>          <s:property value="autoConnect"/>
>       </td>
>       <td align="center">
>         <a href="<s:url action="Subscription_delete"><s:param name="host" value="host"/></s:url>">
>           <s:text name="registration.deleteSubscription"/>
>         </a>
>         <a href="<s:url action="Subscription_edit"><s:param name="host" value="host"/></s:url>">
>           <s:text name="registration.editSubscription"/>
>          </a>
>        </td>
>      </tr>
>     </s:iterator>

> When the iterator renders, it generates a list of Subscriptions for the current User.

> ### Current Subscriptions

> Host Name | User Name | Server Type | Auto | Action
> --- | --- | --- | --- | ---
> mail.hotmail.com | user1234 | pop3 | false | Delete Edit
> mail.yahoo.com | jquser | imap | false | Delete Edit

> Add

> Now look back at the code used to generate this block.

> Notice anything nifty?

> How about that the markup between the iterator tag is actually simpler than the markup that we would use to render one row of the table?

> Instead of using a qualified reference like "value=user.subscription[0].host", we use the simplest possible reference: "value=host". We didn't have to define a local variable, and reference that local in the loop code. The reference to each item in the list is automatically resolved, no fuss, no muss.

> Nice trick!

> The secret to this magic is the value stack. Next to Interceptors, the value stack is probably the coolest thing there is about the framework. To explain the value stack, let's step back and start from the beginning.

> Merging dynamic data into static web pages is a primary reason we create web applications. The Java API has a mechanism that allows you to place objects in a servlet scope (page, request, session, or application), and then retrieve them using a JSP scriplet. If the object is placed directly in one of the scopes, a JSP tag or scriptlet can find that object by searching page scope and then request scope, and session scope, and finally application scope.

> The value stack works much the same way, only better. When you push an object on the value stack, the public properties of that object become first-class properties of the stack. The object's properties become the stack's properties. If another object on the stack has properties of the same name, the last object pushed onto the stack wins. (Last-In, First-Out.)

> When the iterator tag loops through a collection, it pushes each item in the collection onto the stack. The item's properties become the stack's property. In the case of the Subscriptions, if the Subscription has a public Host property, then during that iteration, the stack can access the same property.

> Of course, at the end of each iteration, the tag "pops" the item off the stack. If we were to try and access the Host property later in the page, it won't be there.

> When an Action is invoked, the Action class is pushed onto the value stack. Since the Action is on the value stack, our tags can access any property of the Action as if it were an implicit property of the page. The tags don't access the Action directly. If a textfield tag is told to render the "Username" property, the tag asks the value stack for the value of "Username", and the value stack returns the first property it finds by that name, on any object on the stack.

> The Validators also use the stack. When validation fails on a field, the value for the field is pushed onto the value stack. As a result, if the client enters text into an Integer field, the framework can still redisplay whatever was entered. An invalid input value is not stored in the field (even if it could be). The invalid input is pushed onto the stack for the scope of the request.

> The Subscription list uses another new tag: the param tag. As tags go, "param" takes very few parameters of its own: just "name" and "value", and neither is required. Although simple, "param" is one of the most powerful tags the framework provides. Not so much because of what it does, but because of what "param" allows the other tags to do.

> Essentially, the "param" tag provides parameters to other tags. A tag like "text" might be retrieving a message template with several replaceable parameters. No matter how many parameters are in the template, and no matter what they are named, you can use the "param" tag to pass in whatever you need.

>     pager.legend = Displaying {current} of {count} items matching {criteria}.
>     ...
>     <s:text name="pager.legend">
>         <s:param name="current" value="42" />
>         <s:param name="count" value="314" />
>         <s:param name="criteria" value="Life, the Universe, and Everything" />
>     </s:text>

> In the case of an "url" tag, we can use "param" to create the query string. A statement like this:

>       <s:url action="Subscription_edit"><s:param name="host" value="host"/></s:url>">

> can render a hyperlink like this:

>       <a href="/struts2-mailreader/Subscription_edit.do?host=mail.yahoo.com">Edit</a>

> If a hyperlink needs more parameters, you can use "param" to add as many parameters as needed.

> ### Subscription

> If we follow one of the "Edit" subscription links on the Registration page, we come to the Subscriptions page, which displays the details of our description in a data-entry form. Let's have a look at the Subscription configuration and follow the bouncing ball from page to action to page.

> ##### mailreader-support.xml Subscription element

>     <action name="Subscription_*" method="{1}" class="mailreader2.Subscription">
>       <result name="input">/pages/Subscription.jsp</result>
>       <result type="redirect-action">Registration_input</result>
>     </action>

> The Edit link specified the Subscription action, but also includes the qualifier _edit. The wildcard notation tells the framework to use any characters given after "Subscription_" as the name of a method to invoke on the Action class, instead of the default execute method. The "alternate" execute methods are called alias methods.

> ##### Subscription edit alias

>     public String edit() {
>       setTask(Constants.EDIT);>
>       return find();
>     }

>     public String find() {
>       org.apache.struts.apps.mailreader.dao.Subscription
>         sub = findSubscription();
>        if (sub == null) {
>            return ERROR;
>        }
>        setSubscription(sub);
>        return INPUT;
>     }

> The "edit" alias has two responsibilities. First, it must set the Task property to "Edit". The Subscription page will render itself differently depending on the value of the Task property. Second, "edit" must locate the relevant Subscription and set it to the Subscription property. If all goes well, "edit" returns the INPUT token, so that the "input" result will be invoked.

> In the normal course, the Subscription should always be found, since we selected the entry from a system-generated list. If the Subscription is not found, it would be because the database disappeared or the request is being spoofed. If the Subscription is not found, edit returns the token for the global "error" result, because this condition is unexpected.

> The business logic for the "edit" alias is a simple wrapper around the MailReader DAO classes.

> ##### MailreaderSupport findSubscription()

>     public Subscription findSubscription() {
>         return findSubscription(getHost());
>     }

>     public Subscription findSubscription(String host) {
>         Subscription subscription;
>         subscription = getUser().findSubscription(host);
>         return subscription;
>     }

> This code is very simple and doesn't seem to provide much in the way of error handling. But, that's OK. Since the page is suppose to be entered from a link that we created, we do expect everything to go right here. But, if it doesn't, the global exception handler we defined in the MailReader configuration will trap the exception for us.

> Likewise, the AuthentificationInterceptor will ensure that only clients with a valid User object can try to edit a Subscription. If the session expired, or someone bookmarked the page, the client will be redirected to the Login page automatically.

> As a final layer of defense, we also configured a validator for Subscription, to ensure that we are passed a Host parameter.

> ##### Subscription-validation.xml

>     <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
>     <validators>
>       <field name="host">
>         <field-validator type="requiredstring">
>             <message key="error.host.required"/>
>         </field-validator>
>       </field>
>     </validators>

> By keeping routine safety precautions out of the Action class, the all-important Action becomes smaller and easier to maintain.

> After setting the relevent Subscription object to the Subscription property, the framework transfers control to the (you guessed it) Subscription page.

> ##### Subscription.jsp

>     <%@ page contentType="text/html; charset=UTF-8" %>
>     <%@ taglib prefix="s" uri="http://struts.apache.org/tags" %>
>     <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
>     "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
>     <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
>       <head>
>         <s:if test="task=='Create'">
>             <title><s:text name="subscription.title.create"/></title>
>         </s:if>
>         <s:if test="task=='Edit'">
>             <title><s:text name="subscription.title.edit"/></title>
>         </s:if>
>         <s:if test="task=='Delete'">
>             <title><s:text name="subscription.title.delete"/></title>
>         </s:if>
>         <link href="<s:url value="/css/mailreader.css"/>" rel="stylesheet"
>               type="text/css"/>
>       </head>
>       <body onLoad="self.focus();document.Subscription.username.focus()">

>         <s:actionerror/>
>         <s:form action="Subscription_save" validate="true">
>           <s:token />
>           <s:hidden name="task"/>
>           <s:label key="username" name="user.username"/>

>           <s:if test="task == 'Create'">
>             <s:textfield key="mailHostname" name="host"/>
>           </s:if>
>           <s:else>
>             <s:label key="mailHostname" name="host"/>
>             <s:hidden name="host"/>
>           </s:else>

>           <s:if test="task == 'Delete'">
>             <s:label key="subscription.username"/>
>             <s:label key="subscription.password"/>
>             <s:label key="subscription.type"/>
>             <s:label key="subscription.autoConnect"/>
>             <s:submit key="button.confirm"/>
>           </s:if>
>           <s:else>
>             <s:textfield key="subscription.username"/>
>             <s:textfield key="subscription.password"/>
>             <s:select key="subscription.type" list="types"/>
>             <s:checkbox key="subscription.autoConnect"/>
>             <s:submit key="button.save"/>
>             <s:reset key="button.reset"/>
>           </s:else>

>           <s:submit action="Registration_input"
>                     key="button.cancel"
>                     onclick="form.onsubmit=null"/>
>       </s:form>

>       <jsp:include page="Footer.jsp"/>
>       </body>
>     </html>

> As before, we'll discuss the tags and attributes that are new to this page: "token", "hidden", "label", "select", and "checkbox".

> The token tag works with the Token Session Interceptor to foil double submits. The tag generates a key that is embedded in the form and cached in the session. Without this tag, the Interceptor can't work it's magic.

> The hidden tag embeds the Task property into the form. When the form is submitted, the Subscription_save action will use the Task property to decide whether to insert or update the form.

> The label renders a "read only" version of a property, suitable for placement in the form. In Edit or Delete mode, we want the Host property to be immutable, since it is used as a key. (As unwise as that might sound.) In Delete mode, all of the properties are immutable, since we are simply confirming the delete operation.

> Saving the best for last, the Subscription form utilizes two more interesting tags, "select" and "checkbox".

> Unsurprisingly, the select tag renders a select control, but the tag does so without requiring a lot of markup or redtape.

>     <s:select key="subscription.type" list="types" />

> The interesting attribute of the "select" tag is "list", which, in our case, specifies a value of "types". If we take another look at the Subscription action, we can see that it implements an interface named Preparable and populates a Types property in a method named "prepare".

> ##### Subscription-validation.xml

>     public class Subscription extends MailreaderSupport
>       implements Preparable {

>       private Map types = null;
>       public Map getTypes() {
>         return types;
>        }

>        public void prepare() {
>          Map m = new LinkedHashMap();
>            m.put("imap", "IMAP Protocol");
>            m.put("pop3", "POP3 Protocol");
>            types = m;
>            setHost(getSubscriptionHost());
>         }

>         // ...

> The default Interceptor stack includes the PrepareInterceptor, which observes the Preparable interface.

> ##### PrepareInterceptor

>     public class PrepareInterceptor extends AroundInterceptor {

>       protected void after(ActionInvocation dispatcher, String result) throws Exception {
>       }

>       protected void before(ActionInvocation invocation) throws Exception {
>         Object action = invocation.getAction();
>          if (action instanceof Preparable) {
>             ((Preparable) action).prepare();
>         }
>       }
>     }

> The PrepareInterceptor ensures that the "prepare" method will always be called before "execute" or an alias method is invoked. We use "prepare" to setup the list of items for the select list to display. We also transfer the Host property from our Subscription object to a local property, where it is easier to manage.

> #### Subscription.java

> Like many applications, the MailReader uses mainly String properties. One exception is the AutoConnect property of the Subscription object. On the HTML form, the AutoConnect property is represented by a checkbox.

> When writing web applications, the checkbox can be a tricky control. The Subscription object has a boolean AutoConnect property, and the checkbox simply has to represent its state. The problem is, if you clear a checkbox, the browser client will not submit anything. Nada. Zip. It is as if the checkbox control never existed. The HTTP protocol has no way to affirm "false". If the control is missing, we need to figure out it's been unclicked.

> In Struts 1, we use the `reset` method to work around checkbox issues. In Struts 2, checkbox state is handled automatically. The framework can detect when a checkbox tag has not been sent back, and when that happens, a default "false" value is used for the checkbox value. No worries, mate.

> If we press the SAVE button, the form will be submitted to the Subscription_save action. Since the save method needs some additional validation, we can add a validation file.

> ##### Subscription-Subscription_save-validation.xml

>     <!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN"
>         "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
>     <validators>
>       <field name="host">
>         <field-validator type="requiredstring">
>             <message key="error.host.required"/>
>         </field-validator>
>       </field>
>     </validators>

> The validators follow the same type of inheritance path as the classes. SubscriptionSave extends Subscription, so when Subscription_save is validated, the Host property specified by "Subscription-validation.xml" will also be required.

> If validation succeeds, the `save` method of Subscription will fire.

> ##### Subscription

>     public String save() throws Exception {

>       if (Constants.DELETE.equals(getTask())) {
>        removeSubscription();
>       }

>       if (Constants.CREATE.equals(getTask())) {
>         copySubscription(getHost());
>       }

>       saveUser();
>       return SUCCESS;
>     }

> The save method uses the Task property to handle the special cases of deleting and creating, and then updates the state of the User object.

> The removeSubscription method calls the DAO facade, and then updates the application state.

> ##### removeSubscription

>     public void removeSubscription() throws Exception {
>       getUser().removeSubscription(getSubscription());
>       getSession().remove(Constants.SUBSCRIPTION_KEY);
>     }

> The copySubscription method is a bit more interesting. The MailReader DAO layer API includes some immutable fields that can't be set once the object is created. Because key fields are immutable, we can't just create a Subscription, let the framework populate all the fields, and then save it when we are done -- because some fields can't be populated, except at construction.

> One workaround would be to declare properties on the Action for all the properties we need to pass to the Subscription or User objects. When we are ready to create the object, we could pass the new object values from the Action properties.

> Another workaround is to declare only the immutable properties on the Action, and then use what we can from the domain object.

> This implementation of the MailReader utilizes the second alternative. We define User and Subscription objects on our base Action, and add other properties only as needed.

> To add a new Subscription or User, we create a blank object to capture whatever fields we can. When this "input" object returns, we create a new object, setting the immutable fields to appropriate values, and copy over the rest of the properties.

> ##### copySubscription

>     public void copySubscription(String host) {
>       Subscription input = getSubscription();
>       Subscription sub = createSubscription(host);
>       if (null != sub) {
>         BeanUtils.setValues(sub, input, null);
>         setSubscription(sub);
>         setHost(sub.getHost());
>       }
>     }

> Of course, this is not a preferred solution, but merely a way to work around an issue in the MailReader DAO API that would not be easy for us change.

> #### Subscription Submit

> When we pressed the SAVE button, there was one step that we overlooked. The Mailreader application uses a "double submit" guard to keep people from clicking the SAVE button multiple times and submitting the form again.

> To add the double-submit guard, we can change the actions default processing stack to `user-submit`. But, we don't want to just copy and paste the other action settings from the main Subscription action. What we can do is put the subscription actions in their own package, so that they can share result types.

> ##### mailreader-support.xml

>     </package>

>     <package name="subscription" namespace="/" extends="mailreader-support">

>         <global-results>
>             <result name="input">/Subscription.jsp</result>
>             <result type="redirect-action">Registration_input</result>
>         </global-results>

>         <action name="Subscription_save" method="save" class="mailreader2.Subscription">
>             <interceptor-ref name="user-submit" />
>         </action>

>         <action name="Subscription_*" method="{1}" class="mailreader2.Subscription" />

>     </package>

>     <package name="wildcard" namespace="/" extends="mailreader-support">

>         <action name="*" class="mailreader2.MailreaderSupport">
>             <result>/{1}.jsp</result>
>         </action>

>     </package>
>     }

> Aftering a successful save, the Subscription Action will return "success", and the framework will redirect us back to Registration input.

> ### Summary

> At this point, we've booted the application, logged on, reviewed a Registration record, and edited a Subscription. Of course, there's more, but from here on, it is mostly more of the same. The full source code for MailReader is available online and in the distribution.

> Enjoy!