Author: husted Date: Fri Mar 31 17:22:23 2006 New Revision: 390561 URL: http://svn.apache.org/viewcvs?rev=390561&view=rev Log: Action2 Apps * Mailreader Tour ** Up to "MainMenu"
Modified: struts/sandbox/trunk/action2/README.txt struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Welcome.java struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/tour.html Modified: struts/sandbox/trunk/action2/README.txt URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/README.txt?rev=390561&r1=390560&r2=390561&view=diff ============================================================================== --- struts/sandbox/trunk/action2/README.txt (original) +++ struts/sandbox/trunk/action2/README.txt Fri Mar 31 17:22:23 2006 @@ -60,6 +60,13 @@ ---- +Hmmmm ... + +* Are the validator objects singletons? + + +---- + Example idea jar * See http://www.niallp.pwp.blueyonder.co.uk/strutsvalidatorextends.html @@ -93,8 +100,8 @@ * Setting form type to "POST" ** (Should POST be the default?) -* DRY UI Tags -** http://forums.opensymphony.com/thread.jspa?threadID=24140&tstart=0 +* Proxy Result +** http://forums.opensymphony.com/thread.jspa?threadID=23621&tstart=0 ---- @@ -112,7 +119,12 @@ * Wizard ** http://forums.opensymphony.com/thread.jspa?threadID=23778&tstart=15 +* DRY UI Tags +** http://forums.opensymphony.com/thread.jspa?threadID=24140&tstart=0 +* Default package names +** Allow a default package name to be set for Action classes + so that .MyAction could resolve to org.mycorp.myapp.mypackage.MyAction ---------------------------------------------------------------------------- Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java?rev=390561&r1=390560&r2=390561&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Logon.java Fri Mar 31 17:22:23 2006 @@ -26,7 +26,7 @@ */ public final class Logon extends MailreaderSupport { - public String execute() throws ExpiredPasswordException { + public String execute() throws ExpiredPasswordException { User user = findUser(getUsername(), getPassword()); @@ -38,7 +38,7 @@ return INPUT; } - return SUCCESS; + return SUCCESS; } Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java?rev=390561&r1=390560&r2=390561&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/MailreaderSupport.java Fri Mar 31 17:22:23 2006 @@ -137,10 +137,10 @@ /** * <p>Store new workflow task.</p> * - * @param task The task to set. + * @param value The task to set. */ - public void setTask(String task) { - task = task; + public void setTask(String value) { + task = value; } // ---- Host property ---- Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Welcome.java URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Welcome.java?rev=390561&r1=390560&r2=390561&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Welcome.java (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/mailreader2/Welcome.java Fri Mar 31 17:22:23 2006 @@ -5,7 +5,7 @@ */ public class Welcome extends MailreaderSupport { - public String execute() throws Exception { + public String execute() { // Confirm message resources loaded String message = getText(Constants.ERROR_DATABASE_MISSING); Modified: struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml?rev=390561&r1=390560&r2=390561&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/java/xwork.xml Fri Mar 31 17:22:23 2006 @@ -32,11 +32,9 @@ </global-results> <global-exception-mappings> - <exception-mapping result="error" - exception="java.lang.Exception"/> - + exception="java.lang.Throwable"/> </global-exception-mappings> <action name="Welcome" class="mailreader2.Welcome"> @@ -59,10 +57,8 @@ <result>/pages/ChangePassword.jsp</result> </action> - <!-- See note for Welcome --> <action name="MainMenu" class="mailreader2.MailreaderSupport"> <result>/pages/MainMenu.jsp</result> - <result name="input">/pages/MainMenu.jsp</result> </action> <action name="Registration" class="mailreader2.Registration"> Modified: struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/tour.html URL: http://svn.apache.org/viewcvs/struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/tour.html?rev=390561&r1=390560&r2=390561&view=diff ============================================================================== --- struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/tour.html (original) +++ struts/sandbox/trunk/action2/apps/mailreader/src/webapp/pages/tour.html Fri Mar 31 17:22:23 2006 @@ -39,7 +39,7 @@ </i> </p> -<hr/> +<hr /> <p>Logging In</p> @@ -89,7 +89,7 @@ <ul> <li> - <a href="#MainMenu.jsp">MainMenu.jsp</a> + <a href="#MainMenu">MainMenu</a> <ul> <li><a href=""></a></li> @@ -99,7 +99,7 @@ <ul> <li> - <a href="#MainMenu.jsp">Registration.do</a> + <a href="#Registration.do">Registration</a> <ul> <li><a href="#Registration.java">Registration.java</a></li> </ul> @@ -133,7 +133,7 @@ <li><a href="#Summary">Summary</a></li> </ul> -<hr/> +<hr /> <p> The premise of the MailReader is that it is the first iteration of a @@ -159,7 +159,7 @@ Struts University MailReader site</a>. </p> -<hr/> +<hr /> <blockquote> <p><font class="hint"> <strong>JAAS</strong> - @@ -173,7 +173,7 @@ authentification technologies.) </font></p> </blockquote> -<hr/> +<hr /> <p> The walkthrough starts with how the initial welcome page is displayed, and @@ -199,7 +199,7 @@ Like any filter, the Action servlet is deployed via the web.xml. </p> -<hr/> +<hr /> <h5>web.xml - The Web Application Deployment Descriptor</h5> <pre><code><?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" @@ -238,7 +238,7 @@ </welcome-file-list> </web-app></code></pre> -<hr/> +<hr /> <p> Among other things, @@ -259,12 +259,12 @@ without editing the server pages. </p> -<hr/> +<hr /> <h5>Best Practice:</h5> <blockquote> <p><font class="hint">"Link actions not pages."</font></p> </blockquote> -<hr/> +<hr /> <p> The actions are listed in one or more XML configuration files, @@ -292,7 +292,7 @@ forward to a "Welcome" action. </p> -<hr/> +<hr /> <h5>MailReader's index.html</h5> <pre><code><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html><head> @@ -301,7 +301,7 @@ <body> <p>Loading ...</p> </body></html></code></pre> -<hr/> +<hr /> <p> As an alternative, we could also have used a JSP page that issued the redirect with Action2 tags, @@ -318,12 +318,12 @@ If we just wanted to forward to the Welcome page, we could use a simple configuration element. </p> -<hr/> +<hr /> <h5>A simple "forward thru" action element</h5> <pre><code><action name="<strong>>Welcome</strong>"> <result><strong>/pages/Welcome.jsp</strong></result> </action></code></pre> -<hr/> +<hr /> <p> If a client asks for the Welcome action ("/Welcome.do"), the /page/Welcome.jsp @@ -338,13 +338,13 @@ we find a slightly more complicated XML element for the Welcome action. </p> -<hr/> +<hr /> <h5>The Welcome action element</h5> <pre><code><action name="Welcome" <b>class="mailreader2.Welcome"</b>> <strong><interceptor-ref name="defaultStack"/></strong> <result>/pages/Welcome.jsp</result> </action></code></pre> -<hr/> +<hr /> <p> Here, the <strong>Welcome</strong> Java class executes whenever @@ -354,7 +354,7 @@ Another available result, defined at a global scope, is "error". </p> -<hr/> +<hr /> <h5>Key concept:</h5> <blockquote> <p> @@ -364,7 +364,7 @@ without knowing how the result is implemented. </p> </blockquote> -<hr/> +<hr /> <p> The net effect is that all of the result details, @@ -400,13 +400,13 @@ so that the appropriate messages can be displayed. </p> -<hr/> +<hr /> <h5>The Welcome Action class</h5> <pre><code>package mailreader2; public class Welcome extends MailreaderSupport { - public String execute() throws Exception { + public String execute() { // Confirm message resources loaded String message = getText(Constants.ERROR_DATABASE_MISSING); @@ -427,7 +427,7 @@ } } }</code></pre> -<hr/> +<hr /> <p> Several common result name are predefined @@ -446,14 +446,14 @@ so that any action can use it. </p> -<hr/> +<hr /> <h5>MailReader's global-result element</h5> <pre><code> <global-results> <result name=<strong>"error"</strong>><strong>/pages/Error.jsp</strong></result> <result name="invalid.token">/pages/Error.jsp</result> <result name="login" type="redirect-action">Logon!input</result> </global-results></code></pre> -<hr/> +<hr /> <p> Of course, if an individual action mapping defines its own "error" result type, @@ -475,14 +475,14 @@ The database is created by a custom Listener that we configured in the web.xml. </p> -<hr/> +<hr /> <h5>mailreader2.ApplicationListener</h5> <pre><code> <listener> <listener-class> <strong>mailreader2.ApplicationListener</strong> </listener-class> </listener></code></pre> -<hr/> +<hr /> <p> By default, our ApplicationListener loads a "MemoryDatabase" @@ -501,7 +501,7 @@ user described in XML. </p> -<hr/> +<hr /> <h5>The "seed" user element from the MailReader database.xml</h5> <pre><code><user username="<strong>user</strong>" fromAddress="[EMAIL PROTECTED]" fullName="<strong>John Q. User</strong>" password="pass"> @@ -512,7 +512,7 @@ type="imap" username="jquser"> </subscription> </user></code></pre> -<hr/> +<hr /> <p> The "seed" user element creates a registration record for "John Q. User", @@ -530,11 +530,11 @@ /src/java/ in the source tree. </p> -<hr/> +<hr /> <h5>webwork.properties</h5> <pre><code>webwork.custom.i18n.resources = <strong>resources</strong> webwork.action.extension = <strong>do</strong></code></pre> -<hr/> +<hr /> <p> When we specify "resources" here, we are telling the framework to scan the classpath @@ -553,14 +553,14 @@ The MailReader provides resources for English, Russian, and Japanese. </p> -<hr/> +<hr /> <h5>Message Resource entries used by the Welcome page</h5> <pre><code><strong>index.heading=</strong>MailReader Application Options <strong>index.logon=</strong>Log on to the MailReader Application <strong>index.registration=</strong>Register with the MailReader Application <strong>index.title=</strong>MailReader Demonstration Application <strong>index.tour=</strong>A Walking Tour of the MailReader Demonstration Application</code></pre> -<hr/> +<hr /> <h4><a name="Welcome.jsp" id="Welcome.jsp">Welcome Page</a></h4> @@ -568,7 +568,7 @@ After confirming that the necessary resources exist, the Welcome action forwards to the Welcome page. </p> -<hr/> +<hr /> <h5>Welcome.jsp</h5> <pre><code><%@ page contentType="text/html; charset=UTF-8" %> <strong><%@ taglib uri="/webwork" prefix="saf" %></strong> @@ -599,7 +599,7 @@ <li><a href="<saf:url action="Welcome?request_locale=ru"/>">Russian</a></li> </ul> - <hr/> + <hr /> <p><strong><saf:i18n name="alternate"></strong> <img src="<saf:text name="struts.logo.path"/>" @@ -610,7 +610,7 @@ </body> </html></code></pre> -<hr/> +<hr /> <p> At the top of the Welcome page, there are several directives that load the @@ -641,7 +641,7 @@ without requiring cookies. </p> -<hr/> +<hr /> <h5>Tip:</h5> <blockquote> <p><font class="hint"> @@ -654,7 +654,7 @@ and that you disallow "per-session" cookies.) </font></p> </blockquote> -<hr/> +<hr /> <p> The <strong>i18n</strong> tag provides access to multiple resource bundles. @@ -716,7 +716,7 @@ various combinations to see how the application reacts. </p> -<hr/> +<hr /> <h5>Login.jsp</h5> <pre><code><%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib uri="/webwork" prefix="saf" %> @@ -746,7 +746,7 @@ <jsp:include page="Footer.jsp"/> </body> </html></code></pre> -<hr/> +<hr /> <p> We already saw some of the tags used by the Logon page on the Welcome page. @@ -810,14 +810,14 @@ generates a wad of HTML markup. </p> -<hr/> +<hr /> <pre><code><tr> <td class="tdLabel"><label for="Logon_username" class="label">Username:</label></td> <td> <input type="text" name="username" value="" id="Logon_username"/> </td> </tr></code></pre> -<hr/> +<hr /> <p> If for some reason you don't like the markup generated by a UI Tag, @@ -826,7 +826,7 @@ For example, here is the default template that generates the markup for the ActionErrors tag: </p> -<hr/> +<hr /> <pre><code><#if (actionErrors?exists && actionErrors?size > 0)> <ul> <#list actionErrors as error> @@ -841,7 +841,7 @@ and place this one file somewhere on your classpath. </p> -<hr/> +<hr /> <pre><code><#if (actionErrors?exists && actionErrors?size > 0)> <table> <#list actionErrors as error> @@ -849,7 +849,7 @@ </#list> lt;/table> </#if></code></pre> -<hr/> +<hr /> <p> Under the covers, the framework uses @@ -894,7 +894,7 @@ (Other special aliases on the bypass list include "input" and "back".) </p> -<hr/> +<hr /> <h5>Tip:</h5> <blockquote> <p><font class="hint"> @@ -902,7 +902,7 @@ For more see, the <a href="http://wiki.opensymphony.com/display/WW/Tags">UI Tag documentation.</a> </font></p> </blockquote> -<hr/> +<hr /> <p> OK, but how do the UI Tags know that both of these fields are required? @@ -927,7 +927,7 @@ Logon-validation.xml</strong>. </p> -<hr/> +<hr /> <h5>Validation file for Logon Action</h5> <pre><code><!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd"> <validators> @@ -943,7 +943,7 @@ </field> </validators> </code></pre> -<hr/> +<hr /> <p> The field elements correspond to the ActionForm properties. @@ -977,7 +977,7 @@ from the base class, MailreaderSupport. </p> -<hr/> +<hr /> <h5>Logon.java</h5> <pre><code>package mailreader2; import org.apache.struts.apps.mailreader.dao.User; @@ -999,7 +999,7 @@ return SUCCESS; } }</code></pre> -<hr/> +<hr /> <p> Logon lays out what we do to authenticate a user. @@ -1033,7 +1033,7 @@ just standard JavaBean properties. </p> -<hr/> +<hr /> <h5>MailreaderSupport.getUsername() and getPassword()</h5> <pre><code>private String username = null; public String <strong>getUsername()</strong> { @@ -1050,17 +1050,17 @@ public void setPassword(String password) { this.password = password; }</code></pre> -<hr/> +<hr /> <p> We use these properties to capture the client's credentials, and pass them to the more interesting <strong>findUser</strong> method. </p> -<hr/> +<hr /> <h5>MailreaderSupport.findUser</h5> <pre><code>public User <strong>findUser</strong>(String username, String password) - throws ExpiredPasswordException { + throws <strong>ExpiredPasswordException</strong> { User user = <strong>getDatabase().findUser(username)</strong>; if ((user != null) && !user.getPassword().equals(password)) { @@ -1071,7 +1071,7 @@ } return user; }</code></pre> -<hr/> +<hr /> <p> The findUser method dips into the MailReader Data Access Object layer, @@ -1086,13 +1086,13 @@ all which share the same MailReader DAO JAR! </p> -<hr/> +<hr /> <h5>Best Practice:</h5> <blockquote> <p><font class="hint">"Strongly separate data access and business logic from the rest of the application."</font> </p> </blockquote> -<hr/> +<hr /> <p> When findUser returns, the Logon Action looks to see if a valid (non-null) User object is returned. @@ -1100,7 +1100,7 @@ The User property is not implemented in quite the same way as Username and Password. </p> -<hr/> +<hr /> <h5>MailreaderSupport.setUser</h5> <pre><code>public User getUser() { return (User) <strong>getSession().get(Constants.USER_KEY)</strong>; @@ -1108,7 +1108,7 @@ public void setUser(User user) { getSession().put(Constants.USER_KEY, user); }</code></pre> -<hr/> +<hr /> <p> Instead of using a field to store the property value, @@ -1220,12 +1220,12 @@ and the MailReader application does the same with the MailreaderSupport class. </p> -<hr/> +<hr /> <h5>Best Practice:</h5> <blockquote> <p><font class="hint">"Use a base class to define common functionality."</font></p> </blockquote> -<hr/> +<hr /> <p> But, what happens if Logon returns INPUT instead of SUCCESS. How does the framework know what to do next? @@ -1245,7 +1245,7 @@ or the default result name "success". </p> -<hr/> +<hr /> <h5>xwork.xml Logon</h5> <pre><code><action name="<strong>Logon</strong>" class="mailreader2.Logon"> <result name="<strong>input</strong>">/pages/Logon.jsp</result> @@ -1257,7 +1257,7 @@ result="<strong>expired</strong>"/> <interceptor-ref name="<strong>defaultStack</strong>"/> </action></code></pre> -<hr/> +<hr /> <p> In the Logon action element, the first result element is named "input". @@ -1285,18 +1285,82 @@ </p> <p> + Just in case any other Exceptions are thrown, + the MailReader application also defines a global handler. +</p> + +<hr /> +<h5>xwork.xml exception-mapping</h5> +<pre><code><global-exception-mappings> + <exception-mapping + result="error" + exception="java.lang.Exception"/> +</global-exception-mappings></code></pre> +<hr /> + +<p> + 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. +</p> + +<hr /> +<h5>Error.jsp</h5> +<pre><code><%@ page contentType="text/html; charset=UTF-8" %> +<%@ taglib uri="/webwork" prefix="saf" %> +<!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> +<strong><saf:actionerror /></strong> +<p> + <strong><saf:property value="%{exception.message}"/></strong> +</p> +<hr /> +<h3>Technical Details</h3> +<p> + <strong><saf:property value="%{exceptionStack}"/></strong> +</p> +<jsp:include page="Footer.jsp"/> +</body> +</html></code></pre> +<hr /> + +<p> + The Error page uses <strong>property</strong> tags to expose + the Exception message and the Exception stack. +</p> + +<p> Finally, the Logon action specifies an InterceptorStack named "defaultStack". - If you've worked with Struts Action 2 or WebWork 2 before, that might seem strange, + If you've worked with Action 2 or WebWork 2 before, that might seem strange, since "defaultStack" is the factory default. </p> <p> - In the MailReader application, most of the actions are only available to authenticated users. - The exceptions are the "Welcome" action and the "Logon" action, which are available to everyone. - To authenticate clients, the MailReader uses a custom Interceptor and a custom Interceptor stack. + In the MailReader application, most of the actions are only available + to authenticated users. + The exceptions are the "Welcome" action and the "Logon" action, + which are available to everyone. + To authenticate clients, + the MailReader uses a custom Interceptor and a custom Interceptor stack. </p> -<hr/> +<hr /> <h5>mailreader2.AuthenticationInterceptor</h5> <pre><code>package mailreader2; import com.opensymphony.xwork.interceptor.Interceptor; @@ -1320,7 +1384,7 @@ } } }</code></pre> -<hr/> +<hr /> <p> The "AuthenticationInterceptor" looks to see if a User object @@ -1336,7 +1400,7 @@ and <strong>submit</strong>, and sets the default-interceptor to "access". </p> -<hr/> +<hr /> <h5>xwork.xml interceptors</h5> <pre><code><interceptors> <interceptor name="<strong>authenticate</strong>" class="mailreader2.AuthenticationInterceptor"/> @@ -1351,7 +1415,7 @@ </interceptors> <<strong>default-interceptor-ref</strong> name="access"/></code></pre> -<hr/> +<hr /> <p> The "submit" stack is to be used with actions that post forms. @@ -1367,15 +1431,12 @@ Welcome and Logon. </p> -<!-- TODO ... --> - -<h4><a name="MainMenu.do" id="MainMenu.do">MainMenu.do and MainMenu.jsp</a> -</h4> +<h3><a name="MainMenu" id="MainMenu">MainMenu</a></h3> <p> On a successful logon, 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". + 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: </p> @@ -1389,441 +1450,75 @@ </ul> <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 "/SubmitLogon.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> - -<p> - If you change the address to "/MainMenu.do" and press enter, the same page - will display again, - but only because you are logged on. - If you bookmark MainMenu.do, log off, and try to return later, the system - will force you to logon first. -</p> - -<p> Let's review the source for the "MainMenu" action mapping, - "MainMenuAction.java" class, and the "MainMenu.jsp". + and the "MainMenu.jsp". </p> -<hr/> +<hr /> <h5>Action mapping element for MainMenu</h5> -<pre><code> <!-- Display MainMenu --> - <action - <strong>path="/MainMenu"</strong> - type="org.apache.struts.apps.mailreader.actions.MainMenuAction"> - <forward - name="Success" - path="/MainMenu.jsp"/> - </action></code></pre> - -<h5>MainMenuAction.java</h5> -<pre><code>public final class MainMenuAction extends BaseAction { - - public ActionForward <strong>execute</strong> ( - ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) - throws Exception { - - User user = doGetUser(request); - if (user==null) return <strong>doFindLogon</strong>(mapping); - return <strong>doFindSuccess</strong>(mapping); - } - - }</code></pre> +<pre><code><action name="MainMenu" class="mailreader2.MailreaderSupport"> + <result>/pages/MainMenu.jsp</result> +</action></code></pre> <h5>MainMenu.jsp</h5> -<pre><code><%@ page contentType="text/html;charset=UTF-8" language="java" - %> - <%@ taglib uri="/tags/app" prefix="app" %> - <%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %> - <%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %> - <html> - <head> - <title><bean:message key="mainMenu.title"/></title> - <link rel="stylesheet" type="text/css" href="base.css" /> - </head> - <h3><bean:message key="mainMenu.heading"/> <<strong> - bean:write</strong> name="user" property="fullName" /></h3> - <ul> - <li><html:link action="/EditRegistration"><bean:message - key="mainMenu.registration"/></html:link></li> - <li><html:link action="/Logoff"><bean:message - key="mainMenu.logoff"/></html:link></li> - </ul> - </body> - </html></code></pre> -<hr/> - -<p> - In the MainMenuAction, we find two new helper methods: "doFindLogon" and - "doFindSuccess". -</p> - -<p> - The <strong>doFindLogon</strong> helper locates the "logon" result, - and logs the event if trace logging is enabled. - If someone has bookmarked "MainMenu.do" and tries to return without - logging in, - the MainMenuAction sends the request to Logon.do instead. -</p> - -<hr/> -<h5>BaseAction.doFindLogon</h5> -<pre><code> protected ActionForward <strong>doFindLogon</strong>(ActionMapping - mapping) { - if (log.isTraceEnabled()) { - log.trace(Constants.LOG_LOGON); - } - <strong>return mapping.findForward(Constants.LOGON);</strong> - } -</code></pre> - -<p> - Likewise, <strong>doFindSuccess</strong> locates the "success" result, - and logs the event if trace logging is enabled. -</p> +<pre><code><%@ page contentType="text/html; charset=UTF-8" %> +<%@ taglib uri="/webwork" prefix="saf" %> +<!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><saf:text name="mainMenu.title"/></title> + <link href="<saf:url value="/css/mailreader.css"/>" rel="stylesheet" + type="text/css"/> +</head> -<hr/> -<h5>BaseAction.doFindSuccess</h5> -<pre><code> protected ActionForward <strong>doFindSuccess</strong> - (ActionMapping mapping) { - if (log.isTraceEnabled()) { - log.trace(Constants.LOG_SUCCESS); - } - <strong>return mapping.findForward(Constants.SUCCESS);</strong> - }</code></pre> -<hr/> +<body> +<h3><saf:text name="mainMenu.heading"/> <strong><saf:property + value="user.fullName"/></strong></h3> +<ul> + <li><a href="<saf:url action="Registration!input" />"> + <saf:text name="mainMenu.registration"/> + </a> + </li> + <li><a href="<saf:url action="Logoff"/>"> + <saf:text name="mainMenu.logoff"/> + </a> +</ul> +</body> +</html></code></pre> +<hr /> <p> The source for MainMenu.jsp also contains a new tag, <strong> - bean:write</strong>. - 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. + property</strong>, which we use to customize the page with the + full name property of the authenticated user. + The property tag checks the value stack for a property + or an object's property. </p> <p> - Since the MainMenuAction verifies that the User object exists, - the MainMenu page can refer to the object without ado. - The application does not reveal the location of any server page on the - browser address bar, - but if you did happen to know the address for the server page, and you did - enter it directly, - the tag would throw an exception, and the framework would transfer to the - standard error page. + 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. </p> -<hr/> -<h5>Error.jsp</h5> -<pre><code> An unexpected error has occured - - Cannot find bean: "user" in any scope - - <u>MailReader Demonstration Application</u></code></pre> -<hr/> - <p> - The MainMenu action doesn't have an exception handler of its own, - so it falls back to the standard handler in the MailReader web.xml. -</p> - -<hr/> -<h5>error-page element in web.xml</h5> -<pre><code> <!-- The default error page --> - <error-page> - <exception-type>java.lang.Exception</exception-type> - <location>/Error.jsp</location> - </error-page></code></pre> -<hr/> - -<p> - Meanwhile, The MainMenu page offers two links. + The customized MainMenu page offers two standard links. One link is to the "Logoff" action. The other is to "EditRegistration". - Hmmmm, back on the Welcome page, we also linked to the EditRegistration - action to create an account. - Which is it? Insert or Update? -</p> - -<h4><a name="EditRegistration.do" id="MainMenu.do">EditRegistration.do</a> -</h4> - -<p> - If you scan the struts-config for an EditRegisration action, you won't - find one. - Instead, you will find a "/Edit*" action. - The asterisk is a wildcard character that lets you apply a common action - configuration - to any request that beings with "Edit". -</p> - -<hr/> -<h5>The Wilcard Edit* action and the BaseAction element it extends</h5> -<pre><code> <action - path="/Edit<strong>*</strong>" - extends="//BaseAction" - <strong>parameter</strong>="Edit" - validate="false"/> - - <!-- "Abstract" mapping to use as base --> - <action path="//BaseAction" - input="Input" - type="org.apache.struts.apps.mailreader.actions.<strong>{1}</strong> - Action" - name="<strong>{1}</strong>Form" - scope="request"> - <forward - name="Success" - path="/"<strong>{1}</strong>.jsp"/> - <forward - name="Input" - path="/"<strong>{1}</strong>.jsp"/> - </action></code></pre> -<hr/> - -<p> - The <strong>Edit*</strong> action extends the "BaseAction" element (as do - other Wilcard actions), - and provides two attibutes of its own: "validate" and "parameter". - Validate we've seen before, but what about "parameter"? -</p> - -<p> - The Action class underlying the EditRegistration action extends a standard - Struts class called - "MappingDispatchAction". - Usually, an Action has one entry point, the "execute" method. - Dispatch Actions let you define multiple entry points to an Action. - People find Dispatch Actions handy for related actions, like editing or - saving the same entity. - (In our case, a Registration.) -</p> - -<p> - A <strong>MappingDispatchAction</strong> uses the parameter attribute to - determine - which method to use as the entry point. - Each dispatch entry point is a separate method, but each method must same - signature as execute. - In this case, we're telling the framework to call the "Edit" method when - this action is invoked. </p> -<p> - OK, but how do we know which Action class to call? The "Edit*" action - mapping doesn't specify a type attribute. -</p> -<p> - If you look at the BaseAction mapping, - you'll see that it does specify a type attribute, but the class name isn't - valid. - It contains a "{1}" token instead of the fully qualified class name. -</p> - -<p> - Since the type attribute will be used with a Wildcard action ("/Edit*"), - we can use the {1} to represent the first Wildcard replaced in the path. - In the case of EditRegistration, the "wild" text would be "Registration", - so that's what is plugged into the type statement. - Then the EditRegistration action is called, - the type becomes "org.apache.struts.apps.mailreader.actions.<strong> - Registration</strong>Action". -</p> - -<p> - The same thing will happen for the other markers. - The ActionForm name becomes "RegistrationForm". - The name of the "Success" server page becomes "Registration.jsp". -</p> - -<p> - The <strong>RegistrationForm</strong> is a DynaActionForm, much like the - LoginForm. -</p> - -<hr/> -<h5>The RegistrationForm form-bean element</h5> -<pre><code> <!-- RegistrationAction form bean --> - <form-bean - name="RegistrationForm" - <strong>extends="BaseForm"</strong>> - <form-property - name="fromAddress" - type="java.lang.String"/> - <form-property - name="fullName" - type="java.lang.String"/> - <form-property - name="password2" - type="java.lang.String"/> - <form-property - name="replyToAddress" - type="java.lang.String"/> - </form-bean></code></pre> -<hr/> - -<p> - The <strong>RegistrationForm</strong> extends the BaseForm and adds - properties peculiar to the Registration - action. - When either the Welcome page or MainMenu page link to "<strong> - Edit</strong>Registration", - the framework creates the RegistrationForm, autopopulates the form-bean - properties from any matching attributes - in the request, and invokes the <strong>Edit</strong> method of the - RegistrationAction class. -</p> - -<hr/> -<h5>RegistrationAction.Edit</h5> -<pre><code> public ActionForward <strong>Edit</strong>( - ActionMapping mapping, - ActionForm form, - HttpServletRequest request, - HttpServletResponse response) - throws Exception { - - final String method = Constants.EDIT; - <strong>doLogProcess</strong>(mapping,method); - HttpSession session = request.getSession(); - User user = <strong>doGetUser</strong>(session); - boolean updating = (user!=null); - if (updating) { - <strong>doPopulate</strong>(form,user); - } - - <strong>doSaveToken</strong>(request); - return doFindSuccess(mapping); - } -</code></pre> -<hr/> - -<p> - The <strong>RegistrationAction.Edit</strong> method does most of its work - by calling various helper methods, - including "doLogProcess", "doGetUser", "doPopulate", and "doSaveToken". -</p> - -<hr/> -<h5>BaseAction.doLogProcess</h5> -<pre><code> protected void <strong>doLogProcess</strong>(ActionMapping - mapping, String method) { - if (log.isDebugEnabled()) { - StringBuffer sb = new StringBuffer(128); - sb.append(" "); - sb.append(mapping.getPath()); - sb.append(":"); - sb.append(Constants.LOG_PROCESSING); - sb.append(method); - log.debug(sb.toString()); - } - }</code></pre> -<hr/> - -<p> - MailReader uses <a href="http://jakarta.apache.org/commons/logging/">Commons - Logging</a> from Apache Jakarta. - Commons Logging is an API utility that can be used with several different - logging systems. - The <strong>doLogProcess</strong> helper is just a wrapper around a - logging statement - that MailReader uses in several places. - All of the MailReader's Dispatch methods call this helper to log when - control reaches the method. -</p> - -<hr/> -<h5>BaseAction.doGetUser</h5> -<pre><code> protected User <strong>doGetUser</strong>(HttpSession session) { - return (User) session.getAttribute(Constants.USER_KEY); - } -</code></pre> -<hr/> - -<p> - Most of the MailReader actions utilize the User object, - and so BaseAction encapsulates obtaining the User into the very simple - <strong>doGetUser</strong> helper. -</p> - -<hr/> -<h5>RegistrationAction.doPopulate</h5> -<pre><code> private void <strong>doPopulate</strong>(ActionForm form, User - user) throws ServletException { - - final String title = Constants.EDIT; - - if (log.isTraceEnabled()) { - log.trace(Constants.LOG_POPULATE_FORM + user); - } - - try { - PropertyUtils.copyProperties(form, user); - DynaActionForm dyna = (DynaActionForm) form; - <strong>dyna.set(TASK,title);</strong> - dyna.set(PASSWORD,null); - dyna.set(PASSWORD2,null); - } catch (InvocationTargetException e) { - Throwable t = e.getTargetException(); - if (t == null) - t = e; - log.error(LOG_REGISTRATION_POPULATE, t); - throw new ServletException(LOG_REGISTRATION_POPULATE, t); - } catch (Throwable t) { - log.error(LOG_REGISTRATION_POPULATE, t); - throw new ServletException(LOG_REGISTRATION_POPULATE, t); - } - }</code></pre> -<hr/> +<!-- TODO ... --> -<p> - The <strong>doPopulate</strong> helper lives in RegistrationAction rather - than BaseAction, - since it involves the RegistrationForm, which only the RegistrationAction - uses. - The main job of doPopulate is to transfer the input values from the - ActionForm to the User object. - The constants, like "PASSWORD", are defined on RegisrationAction as public - constants. - These constants document the property names specified in the Struts - configuration for the dynamic - RegistrationForm. -</p> -<p> - An interesting point is the line that sets the "TASK" to "Edit". - This value is used by the server page to adjust the page title and form - layout. - When we defined the RegistrationForm, we set the default value for TASK to - be "Create". - When EditRegistration is called from the Welcome page, we don't have a - User object yet, - and so the TASK remains at "Create". - When EditRegistration is called from the MainMenu page, we do have a User - object, - and so the TASK is set to "Edit". -</p> -<hr/> +<hr /> <h5>BaseAction.doSaveToken</h5> <pre><code> protected void doSaveToken(HttpServletRequest request) { if (log.isTraceEnabled()) { @@ -1831,7 +1526,7 @@ } saveToken(request); }</code></pre> -<hr/> +<hr /> <p> A common problem with designing web applications is that response times @@ -1894,7 +1589,7 @@ so you can pick your username. </p> -<hr/> +<hr /> <h5>Note:</h5> <blockquote> <p><font class="hint"> @@ -1907,7 +1602,7 @@ among other benefits. </font></p> </blockquote> -<hr/> +<hr /> <p> The page also uses logic tags to display a list of subscriptions for the @@ -1916,7 +1611,7 @@ the lower part of the page that lists the subscriptions is exposed. </p> -<hr/> +<hr /> <h5></h5> <pre><code><logic:equal name="RegistrationForm" <strong> property="task"</strong> @@ -1927,7 +1622,7 @@ <bean:message key="registration.addSubscription"/> </html:link> </logic:equal></code></pre> -<hr/> +<hr /> <p> Otherwise, the page contains just the top portion -- @@ -1952,7 +1647,7 @@ Using the iterate tag, you can code it the way it sounds. </p> -<hr/> +<hr /> <h5>Using logic:iterate to list the Subscriptions</h5> <pre><code> <logic:iterate <strong>name</strong>="user" <strong> property</strong>="subscriptions" <strong>id</strong>="subscription"> @@ -1983,7 +1678,7 @@ </td> </tr> </logic:iterate></code></pre> -<hr/> +<hr /> <p> The three parameters to the iterate tag (name, property, and id) tell it @@ -2023,7 +1718,7 @@ the command links would translate to HTML links like these: </p> -<hr/> +<hr /> <h5>The Delete and Edit links for mail.yahoo.com</h5> <pre><code> <a href="/struts-mailreader/DeleteSubscription.do?host=mail.yahoo.com">Delete</a> @@ -2031,7 +1726,7 @@ <a href="/struts-mailreader/EditSubscription.do?host=mail.yahoo.com">Edit</a></code> </pre> -<hr/> +<hr /> <p> At the foot of the Register page is a link for adding a subscription. @@ -2051,7 +1746,7 @@ the "{1}Form" attribute maps to <strong>SubscriptionForm</strong>. </p> -<hr/> +<hr /> <h5>The SubscriptionAction form-bean element</h5> <pre><code> <form-bean name="SubscriptionForm" @@ -2068,7 +1763,7 @@ name="type" type="java.lang.String" /> </form-bean></code></pre> -<hr/> +<hr /> <p> The other DynaActionForms we've seen used only String properties. @@ -2078,7 +1773,7 @@ and checkboxes need to be handled differently that other controls. </p> -<hr/> +<hr /> <h5>Tip:</h5> <blockquote> <p class="hint"> @@ -2105,7 +1800,7 @@ checkbox will remain unchecked ("false"). </p> </blockquote> -<hr/> +<hr /> <p> To be sure the autoConnect checkbox is handled correctly, @@ -2119,7 +1814,7 @@ few twists of its own. </p> -<hr/> +<hr /> <h5>SubscriptionAction.Edit</h5> <pre><code>public ActionForward Edit( ActionMapping mapping, @@ -2149,7 +1844,7 @@ return doFindSuccess(mapping); }</code></pre> -<hr/> +<hr /> <p> In RegistrationAction.Edit, we looked for the user object to decide if we @@ -2166,7 +1861,7 @@ If it is an update, we fetch the Subscription from the database. </p> -<hr/> +<hr /> <h5>SubscriptionAction.doFindSubscription</h5> <pre><code> private Subscription <strong>doFindSubscription</strong>(User user, String host) { @@ -2190,7 +1885,7 @@ return subscription; }</code></pre> -<hr/> +<hr /> <p> If we can't find the subscription, @@ -2198,7 +1893,7 @@ (Error.jsp). </p> -<hr/> +<hr /> <h5>BaseAction.doFindFailure</h5> <pre><code> protected ActionForward <strong>doFindFailure</strong> (ActionMapping mapping) { @@ -2208,7 +1903,7 @@ return (mapping.findForward(Constants.FAILURE)); } </code></pre> -<hr/> +<hr /> <p> In the normal course, the subscription should always be found, @@ -2224,7 +1919,7 @@ object. In this case, a Subscription object. </p> -<hr/> +<hr /> <h5>SubscriptionAction.doPopulate</h5> <pre><code> private void <strong>doPopulate</strong>(ActionForm form, Subscription subscription) throws ServletException { @@ -2248,7 +1943,7 @@ throw new ServletException(LOG_SUBSCRIPTION_POPULATE, t); } }</code></pre> -<hr/> +<hr /> <p> Most of the code in "doPopulate" is window dressing for the call to @@ -2358,7 +2053,7 @@ of server types. </p> -<hr/> +<hr /> <h5>Tip:</h5> <blockquote> <p><font class="hint"> @@ -2368,7 +2063,7 @@ [org.apache.struts.util.LabelValueBean]. </font></p> </blockquote> -<hr/> +<hr /> <p> The plugin stores the list is stored in application scope. @@ -2430,7 +2125,7 @@ own, to pickup the finer points. </p> -<hr/> +<hr /> <h5>SubscriptionAction.Save</h5> <pre><code> public ActionForward <strong>Save</strong>( ActionMapping mapping, @@ -2472,7 +2167,7 @@ return doFindSuccess(mapping); }</code></pre> -<hr/> +<hr /> <p> This concludes our tour. @@ -2516,7 +2211,7 @@ Alternate message resources can be provided to internationalize an application.</li> </ul> -<hr/> +<hr /> </blockquote> </body> </html> --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]