struts-sandbox/struts2/apps/mailreader-bang/src/main/webapp/pages/tour.html [1246:1576]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <result name="input">/pages/Logon.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>
In 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.
The second result element is named cancel. If someone presses the cancel button on the Logon 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 Logon 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.
<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.
<%@ 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 Logon 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, Logon, and Register actions which are available to everyone. To authenticate clients, the MailReader uses a custom Interceptor and a custom Interceptor stack.
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 Logon action.
The MailReader defines four custom Interceptor stacks: "user", "user-submit", "guest", and "guest-submit".
<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>
<interceptor-stack name="guest-submit" >
<interceptor-ref name="token-session" />
<interceptor-ref name="guest"/>
</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.
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, Logon, 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.
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". Below this legend should be two links:
Let's review the source for the "MainMenu" action mapping, and the "MainMenu.jsp".
<action name="MainMenu" class="mailreader2.MailreaderSupport">
<result>/pages/MainMenu.jsp</result>
</action>
<%@ 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="Logoff" />">
<s:text name="mainMenu.logoff"/>
</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 "Logoff the MailReader Demonstration Application".
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. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - struts-sandbox/struts2/apps/mailreader-wildone/src/main/webapp/pages/tour.jsp [1246:1576]: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - <result name="input">/pages/Logon.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>
In 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.
The second result element is named cancel. If someone presses the cancel button on the Logon 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 Logon 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.
<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.
<%@ 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 Logon 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, Logon, and Register actions which are available to everyone. To authenticate clients, the MailReader uses a custom Interceptor and a custom Interceptor stack.
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 Logon action.
The MailReader defines four custom Interceptor stacks: "user", "user-submit", "guest", and "guest-submit".
<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>
<interceptor-stack name="guest-submit" >
<interceptor-ref name="token-session" />
<interceptor-ref name="guest"/>
</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.
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, Logon, 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.
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". Below this legend should be two links:
Let's review the source for the "MainMenu" action mapping, and the "MainMenu.jsp".
<action name="MainMenu" class="mailreader2.MailreaderSupport">
<result>/pages/MainMenu.jsp</result>
</action>
<%@ 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="Logoff" />">
<s:text name="mainMenu.logoff"/>
</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 "Logoff the MailReader Demonstration Application".
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. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -