From 44742b72421583a0a6abdd3420ce99df069d913b Mon Sep 17 00:00:00 2001
From: Ted Nathan Husted Logging In
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.
+
+ 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?
@@ -288,8 +287,8 @@
- When the client requests "Welcome.do", the request is passed to the "struts2" FilterDispatcher
- (that we registered in the web.xml file).
+ 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
@@ -303,7 +302,7 @@
- If a client asks for the Welcome action ("Welcome.do), the "/page/Welcome.jsp"
+ 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".
@@ -485,7 +484,7 @@ public class Welcome extends MailreaderSupport {
The database comes seeded with a sample user.
- If you check the "database.xml" file under "/src/main",
+ If you check the "database.xml" file under "/src/main/resources",
you'll see the sample user described in XML.
As mentioned, MailReader is an internationalized application.
- The message resources for the application are loaded through a reference in the
- "struts.properties" file.
- Like the database contents, the "struts.properties" file is kept under
- "/src/main/" in the source tree.
-
- When we specify "resources" in the properties file,
- we are telling the framework to scan the classpath
- for a Resource Bundle named "resources.properties".
- The bundle might be embedded in a JAR, or found in the "WEB-INF/classes"
- folder, or anywhere else on the runtime classpath.
- In the MailReader, we keep the original bundle in the
- source tree under "src/main/". When the application is built, the
- properties files are copied to "WEB-INF/classes", so
- that they are on the Java classpath.
+ 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.
-
Welcome.do
-struts.properties
-
-struts.custom.i18n.resources = resources
-struts.action.extension = do
-
-
@@ -585,9 +568,24 @@ struts.action.extension = do
<h3>Language Options</h3>
<ul>
- <li><a href="<s:url action="Welcome?request_locale=en"/>">English</a></li>
- <li><a href="<s:url action="Welcome?request_locale=ja"/>">Japanese</a></li>
- <li><a href="<s:url action="Welcome?request_locale=ru"/>">Russian</a></li>
+ <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 />
@@ -655,7 +653,7 @@ struts.action.extension = do
- The alternate bundle is stored next to the default 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.
@@ -718,7 +716,7 @@ struts.action.extension = do </head> <body onLoad="self.focus();document.Logon.username.focus()"> <s:actionerror/> - <s:form method="POST" validate="true"> + <s:form action="Logon" validate="true"> <s:textfield label="%{getText('username')}" name="username"/> <s:password label="%{getText('password')}" name="password"/> <s:submit value="%{getText('button.save')}"/> @@ -754,14 +752,11 @@ struts.action.extension = doThe second new tag is form. This tag renders a HTML form tag. - By default, the form will submit back to whatever action invoked the page. 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. - You can use the method attribute to designate "GET" or "POST", - just like the HTML form tag.
@@ -781,8 +776,8 @@ struts.action.extension = do
- The UI Tags support templates and themes so that a set of HTML tags can be - rendered from a single UI Tag. For example, the single tag + 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
@@ -805,7 +800,7 @@ struts.action.extension = do
- If for some reason you don't like the markup generated by a UI Tag, + 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, @@ -824,8 +819,9 @@ struts.action.extension = do
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 "actionerror.ftl", - and place this one file somewhere on your classpath. + 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.
@@ -879,15 +875,16 @@ struts.action.extension = do 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. - (Other special aliases on the bypass list include "input" and "back".) + Another entry on the special-case list is the "input" method.
- The UI tags have options and capabilities beyond what we have shown here. - For more see, the UI Tag documentation. + The Struts Tags have options and capabilities beyond what we have shown here. + For more see, the + Struts Tag documentation.
-"Strongly separate data access and business logic from the rest of the application." +
+ "Strongly separate data access and business logic from the rest of + the application."
- 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. + 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, + 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.
@@ -1143,9 +1144,11 @@ public void setSession(Map value) {- The magic that provides the Session property a runtime value is called "dependency injection". + 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. + SessionAware is bundled with the framework, + and it defines a setter for the Session property.
@@ -1153,7 +1156,8 @@ public void setSession(Map value) {
- Also bundled with the framework is an object called the ServletConfigInterceptor. + 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.
@@ -1181,19 +1185,22 @@ public void setSession(Map value) { 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.xml configuration file. + 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. + 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. + the "findUser" method invokes the addFieldError method to note the + problem. When "findUser" returns, the Logon Action checks for errors, and then it returns either INPUT or SUCCESS.
@@ -1228,7 +1235,8 @@ public void setSession(Map value) {To answer that question, - we need to turn back to the "struts.xml" file and look at how Logon is configured. + we need to turn back to the Struts configuration + and look at how Logon is declared.
@@ -1241,8 +1249,8 @@ public void setSession(Map value) {<action name="Logon" class="mailreader2.Logon">
+mailreader-support.xml Logon
+<action name="Logon!*" class="mailreader2.Logon" method="{1}">
<result name="input">/pages/Logon.jsp</result>
<result name="cancel" type="redirect-action">Welcome</result>
<result type="redirect-action">MainMenu</result>
@@ -1255,7 +1263,27 @@ public void setSession(Map value) {
- In the Logon action element, the first result element is named "input".
+ You might notice that the name of the Logon action element is not "Logon" but "Logon!*".
+ 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 "Logon!cancel" or "Logon!input",
+ the framework matches "cancel" or "input" with the wildcard and fills in the blanks.
+
+
+
+ Using wildcards with a exclamation point (or "bang") is not the only way we can use
+ wilcards.
+ If we wanted to use actions like "inputLogin",
+ we could move the asterisk and use an action name like "*Logon".
+ (Albeit, the trailing bang is a common convention,
+ adopted from on a similar WebWork 2 feature.)
+
+
+
+ Within the Logon 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
"Logon.jsp" page.
@@ -1288,7 +1316,7 @@ public void setSession(Map value) {
-struts.xml exception-mapping
+mailreader-default.xml exception-mapping
<global-exception-mappings>
<exception-mapping
result="error"
@@ -1400,7 +1428,7 @@ public class AuthenticationInterceptor implements Interceptor {
-struts.xml interceptors
+mailreader-default.xml interceptors
<interceptors>
<interceptor name="authentication"
class="mailreader2.AuthenticationInterceptor"/>
@@ -1769,7 +1797,8 @@ public class AuthenticationInterceptor implements Interceptor {
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?
+ 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",
@@ -1783,7 +1812,8 @@ public class AuthenticationInterceptor implements Interceptor {
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.
+ 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.
@@ -1826,11 +1856,13 @@ public class AuthenticationInterceptor implements Interceptor {
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.
+ 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.
+ and the value stack returns the first property it finds by that name,
+ on any object on the stack.
@@ -1845,14 +1877,17 @@ public class AuthenticationInterceptor implements Interceptor {
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.
+ 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.
+ 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.
+ 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.
@@ -1883,7 +1918,6 @@ public class AuthenticationInterceptor implements Interceptor {
<a href="/struts2-mailreader/Subscription!edit.do?host=mail.yahoo.com">Edit</a>
-