Primeira tela:
Register:
Depois de salvar cadastro:
Edit:
Add:
Depois de salvar:
Se fizer login com user/pass:
O que é a aplicação?
"This example is the beginnings of a portal application that would allow users to register themselves, and maintain a set of subscriptions they own to mail servers elsewhere on the Internet. When completed, this application will provide the ability to read mail from various mail servers, through the application."
Como está, a aplicação só mantém o cadastro dos servidores
Ela não lê mail
<%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <html:html locale="true"> <head> <title><bean:message key="index.title"/></title> <html:base/> </head> <body bgcolor="white"> <logic:notPresent name="database" scope="application"> <font color="red"> ERROR: User database not loaded -- check servlet container logs for error messages. </font> <hr> </logic:notPresent> <logic:notPresent name="org.apache.struts.action.MESSAGE" scope="application"> <font color="red"> ERROR: Application resources not loaded -- check servlet container logs for error messages. </font> </logic:notPresent> <h3><bean:message key="index.heading"/></h3> <ul> <li><html:link page="/editRegistration.do?action=Create"><bean:message key="index.registration"/></html:link></li> <li><html:link page="/logon.jsp"><bean:message key="index.logon"/></html:link></li> </ul> <p> </p> <html:link page="/tour.do"> <font size="-1"><bean:message key="index.tour"/></font> </html:link> <p> </p> <html:img page="/struts-power.gif" alt="Powered by Struts"/> </body> </html:html>
As 3 linhas taglib dão informação sobre onde estão as "taglib descriptors" e como as tags serão prefixadas (bean, html, logic)
Struts tem mais uma taglib (template) que não está sendo usada aqui
A linha com tag <html:html> é usada para gerar um elemento <HTML> com indicação de locale
O HTML gerado pelo Struts e mandando na reposta seria algo como:
<html lang="pt">
A seguinte linha é usada para mostrar mensagens internacionalizadas
O servlet de controle já vai ter instanciado dois objetos antes de chegar a index.jsp
Um database Servlet
Um Message Resource (usado com <bean:message ...>)
<title><bean:message key="index.title"/></title>
Na página acima, os erros estão "hardcoded" para que apareçam caso o Message Resource não seja localizado
Voltaremos a index.jsp, já, já ...
... Para saber como esses dois objetos são carregados, veja o arquivo web.xml:
Dois servlets são carregados no startup
Um deles ("action", nosso servlet de controle) tem como parâmetro "application" uma referência a ApplicationResource
Desta forma, o arquivo "ApplicationResources.properties" será analisado e disponibilizado como recurso
Veja este arquivo aqui
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd"> <web-app> <!-- Database Initialization Servlet Configuration --> <servlet> <servlet-name>database</servlet-name> <servlet-class>org.apache.struts.webapp.example.DatabaseServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- Action Servlet Configuration --> <servlet> <servlet-name>action</servlet-name> <servlet-class>org.apache.struts.action.ActionServlet</servlet-class> <init-param> <param-name>application</param-name> <param-value>org.apache.struts.webapp.example.ApplicationResources</param-value> </init-param> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-value> </init-param> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>detail</param-name> <param-value>2</param-value> </init-param> <init-param> <param-name>validate</param-name> <param-value>true</param-value> </init-param> <load-on-startup>2</load-on-startup> </servlet> <!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- The Welcome File List --> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> <!-- Application Tag Library Descriptor --> <taglib> <taglib-uri>/WEB-INF/app.tld</taglib-uri> <taglib-location>/WEB-INF/app.tld</taglib-location> </taglib> <!-- Struts Tag Library Descriptors --> <taglib> <taglib-uri>/WEB-INF/struts-bean.tld</taglib-uri> <taglib-location>/WEB-INF/struts-bean.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-html.tld</taglib-uri> <taglib-location>/WEB-INF/struts-html.tld</taglib-location> </taglib> <taglib> <taglib-uri>/WEB-INF/struts-logic.tld</taglib-uri> <taglib-location>/WEB-INF/struts-logic.tld</taglib-location> </taglib> </web-app>
Vimos acima que o database servlet tem seu próprio bloco de inicialização:
<!-- Database Initialization Servlet Configuration --> <servlet> <servlet-name>database</servlet-name> <servlet-class>org.apache.struts.webapp.example.DatabaseServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>2</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet>
O código do servlet está aqui, mas não será examinado em detalhes
O conteúdo do banco de dados é mantido em XML e analisado pelo "digester" do struts
Na memória, o banco de dados consiste de tabelas hash aninhadas
A tabela externa mantém o cadastro de usuários e é armazenada no escopo de aplicação (no servlet context)
Cada usuário (classe User) tem uma tabela hash de assinaturas
Quando você se cadastra, um objeto User é adicionado à tabela hash externa
Veremos o código para isso depois
Quando você faz login, o objeto User é localizado e guardado na sessão
Veremos o código para isso depois
O banco de dados inicial está em database.xml
Eis seu conteúdo:
<database> <user username="user" password="pass" fullName="John Q. User" fromAddress="John.User@somewhere.com"> <subscription host="mail.yahoo.com" type="imap" username="jquser" password="foo"/> <subscription host="mail.hotmail.com" type="pop3" username="user1234" password="bar"/> </user> </database>
<html:html locale="true"> <head> <title><bean:message key="index.title"/></title> <html:base/> </head>
<html:link page="/editRegistration.do?action=Create">
A página logon.jsp é mostrada a seguir
<%@ page language="java" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <html:html locale="true"> <head> <title><bean:message key="logon.title"/></title> <html:base/> </head> <body bgcolor="white"> <html:errors/> <html:form action="/logon" focus="username"> <table border="0" width="100%"> <tr> <th align="right"> <bean:message key="prompt.username"/> </th> <td align="left"> <html:text property="username" size="16" maxlength="16"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.password"/> </th> <td align="left"> <html:password property="password" size="16" maxlength="16" redisplay="false"/> </td> </tr> <tr> <td align="right"> <html:submit property="submit" value="Submit"/> </td> <td align="left"> <html:reset/> </td> </tr> </table> </html:form> </body> </html:html>
A página logon.jsp usa o tag "html:form":
<html:form action="/logon" focus="username">
<form name="logonForm" method="POST" action="/struts-example/logon.do">
<?xml version="1.0" encoding="ISO-8859-1" ?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.0//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_0.dtd"> <struts-config> <!-- ========== Form Bean Definitions =================================== --> <form-beans> <!-- Logon form bean --> <form-bean name="logonForm" type="org.apache.struts.webapp.example.LogonForm"/> <!-- Registration form bean --> <form-bean name="registrationForm" type="org.apache.struts.webapp.example.RegistrationForm"/> <!-- Subscription form bean --> <form-bean name="subscriptionForm" type="org.apache.struts.webapp.example.SubscriptionForm"/> </form-beans> <!-- ========== Global Forward Definitions ============================== --> <global-forwards> <forward name="logoff" path="/logoff.do"/> <forward name="logon" path="/logon.jsp"/> <forward name="success" path="/mainMenu.jsp"/> </global-forwards> <!-- ========== Action Mapping Definitions ============================== --> <action-mappings> <!-- Edit user registration --> <action path="/editRegistration" type="org.apache.struts.webapp.example.EditRegistrationAction" name="registrationForm" scope="request" validate="false"> <forward name="success" path="/registration.jsp"/> </action> <!-- Edit mail subscription --> <action path="/editSubscription" type="org.apache.struts.webapp.example.EditSubscriptionAction" name="subscriptionForm" scope="request" validate="false"> <forward name="failure" path="/mainMenu.jsp"/> <forward name="success" path="/subscription.jsp"/> </action> <!-- Process a user logoff --> <action path="/logoff" type="org.apache.struts.webapp.example.LogoffAction"> <forward name="success" path="/index.jsp"/> </action> <!-- Process a user logon --> <action path="/logon" type="org.apache.struts.webapp.example.LogonAction" name="logonForm" scope="request" input="/logon.jsp"> </action> <!-- Save user registration --> <action path="/saveRegistration" type="org.apache.struts.webapp.example.SaveRegistrationAction" name="registrationForm" scope="request" input="/registration.jsp"/> <!-- Save mail subscription --> <action path="/saveSubscription" type="org.apache.struts.webapp.example.SaveSubscriptionAction" name="subscriptionForm" scope="request" input="/subscription.jsp"> <forward name="success" path="/editRegistration.do?action=Edit"/> </action> <!-- Display the "walking tour" documentation --> <action path="/tour" forward="/tour.htm"> </action> </action-mappings> </struts-config>
public final class LogonForm extends ActionForm { // ----------------------- Instance Variables /** * The password. */ private String password = null; /** * The username. */ private String username = null; // ----------------------- Properties /** * Return the password. */ public String getPassword() { return (this.password); } /** * Set the password. * * @param password The new password */ public void setPassword(String password) { this.password = password; } /** * Return the username. */ public String getUsername() { return (this.username); } /** * Set the username. * * @param username The new username */ public void setUsername(String username) { this.username = username; } // ----------------------- Public Methods /** * Reset all properties to their default values. * * @param mapping The mapping used to select this instance * @param request The servlet request we are processing */ public void reset(ActionMapping mapping, HttpServletRequest request) { this.password = null; this.username = null; } /** * Validate the properties that have been set from this HTTP request, * and return an <code>ActionErrors</code> object that encapsulates any * validation errors that have been found. If no errors are found, return * <code>null</code> or an <code>ActionErrors</code> object with no * recorded error messages. * * @param mapping The mapping used to select this instance * @param request The servlet request we are processing */ public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((username == null) || (username.length() < 1)) errors.add("username", new ActionError("error.username.required")); if ((password == null) || (password.length() < 1)) errors.add("password", new ActionError("error.password.required")); return errors; } }
<!-- Action Servlet Mapping --> <servlet-mapping> <servlet-name>action</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping>
<!-- Logon form bean --> <form-bean name="logonForm" type="org.apache.struts.webapp.example.LogonForm"/> ... <!-- Process a user logon --> <action path="/logon" type="org.apache.struts.webapp.example.LogonAction" name="logonForm" scope="request" input="/logon.jsp"> </action>
public final class LogonAction extends Action { // ------------------------- Public Methods /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. * * @param mapping The ActionMapping used to select this instance * @param actionForm The optional ActionForm bean for this request (if any) * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Extract attributes we will need Locale locale = getLocale(request); MessageResources messages = getResources(); User user = null; // Validate the request parameters specified by the user ActionErrors errors = new ActionErrors(); String username = ((LogonForm) form).getUsername(); String password = ((LogonForm) form).getPassword(); Hashtable database = (Hashtable) servlet.getServletContext().getAttribute(Constants.DATABASE_KEY); if (database == null) errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.database.missing")); else { user = (User) database.get(username); if ((user != null) && !user.getPassword().equals(password)) user = null; if (user == null) errors.add(ActionErrors.GLOBAL_ERROR, new ActionError("error.password.mismatch")); } // Report any errors we have discovered back to the original form if (!errors.empty()) { saveErrors(request, errors); return (new ActionForward(mapping.getInput())); } // Save our logged-in user in the session HttpSession session = request.getSession(); session.setAttribute(Constants.USER_KEY, user); if (servlet.getDebug() >= 1) servlet.log("LogonAction: User '" + user.getUsername() + "' logged on in session " + session.getId()); // Remove the obsolete form bean if (mapping.getAttribute() != null) { request.removeAttribute(mapping.getAttribute()); } // Forward control to the specified success URI return (mapping.findForward("success")); } }
return (new ActionForward(mapping.getInput()));
return (mapping.findForward("success"));
<global-forwards> <forward name="logoff" path="/logoff.do"/> <forward name="logon" path="/logon.jsp"/> <forward name="success" path="/mainMenu.jsp"/> </global-forwards>
Aqui está a página mainMenu.jsp
<%@ page language="java" %> <%@ taglib uri="/WEB-INF/app.tld" prefix="app" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <app:checkLogon/> <jsp:useBean id="user" scope="session" type="org.apache.struts.webapp.example.User"/> <html:html> <head> <title><bean:message key="mainMenu.title"/></title> <html:base/> </head> <body bgcolor="white"> <h3><bean:message key="mainMenu.heading"/> <jsp:getProperty name="user" property="username"/></h3> <ul> <li><html:link page="/editRegistration.do?action=Edit"><bean:message key="mainMenu.registration"/></html:link></li> <li><html:link forward="logoff"><bean:message key="mainMenu.logoff"/></html:link></li> </ul> </body> </html:html>
A primeira coisa interessante é:
<app:checkLogon/>
Isso é um custom tag só desta aplicação
Não veremos como é implementado agora porque não sabemos como criar custom tags (ainda :-))
Porém, é um excelente exemplo do poder de custom tags para encapsular lógica de aplicação
Este tag verifica se um usuário está logado
Se não tiver, o controle vai para "/logon.jsp"
Se quiser ter certeza que uma página é acessada apenas quando há um logon, use "<app:checkLogon/>" no início da página JSP
A segunda coisa interessante é que a mensagem inicial da página contém o nome do usuário
Isso foi feito com um jsp:useBean normal (não é Struts)
<jsp:useBean id="user" scope="session" type="org.apache.struts.webapp.example.User"/> ... <jsp:getProperty name="user" property="username"/>
Finalmente, tem dois tags html:link sem novidades
Vejamos o que struts-config.xml diz sobre editRegistration:
<!-- Registration form bean --> <form-bean name="registrationForm" type="org.apache.struts.webapp.example.RegistrationForm"/> ... <!-- Edit user registration --> <action path="/editRegistration" type="org.apache.struts.webapp.example.EditRegistrationAction" name="registrationForm" scope="request" validate="false"> <forward name="success" path="/registration.jsp"/> </action>
Nada de novo: a ação /editRegistration:
Está mapeada para EditRegistrationAction
Usa um bean de validação de formulário RegistrationForm
usa registration.jsp como input
Observe que nem toda página precisa usar um Form bean.
É interessante para validar dados digitados pelo usuário
É interessante também para juntar dados de um formulário, como é o caso aqui
Observe a linha validate="false", acima
public final class EditRegistrationAction extends Action { // --------------------------------------------------------- Public Methods /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. * * @param mapping The ActionMapping used to select this instance * @param actionForm The optional ActionForm bean for this request (if any) * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Extract attributes we will need Locale locale = getLocale(request); MessageResources messages = getResources(); HttpSession session = request.getSession(); String action = request.getParameter("action"); if (action == null) action = "Create"; if (servlet.getDebug() >= 1) servlet.log("EditRegistrationAction: Processing " + action + " action"); // Is there a currently logged on user? User user = null; if (!"Create".equals(action)) { user = (User) session.getAttribute(Constants.USER_KEY); if (user == null) { if (servlet.getDebug() >= 1) servlet.log(" User is not logged on in session " + session.getId()); return (servlet.findForward("logon")); } } // Populate the user registration form if (form == null) { if (servlet.getDebug() >= 1) servlet.log(" Creating new RegistrationForm bean under key " + mapping.getAttribute()); form = new RegistrationForm(); if ("request".equals(mapping.getScope())) request.setAttribute(mapping.getAttribute(), form); else session.setAttribute(mapping.getAttribute(), form); } RegistrationForm regform = (RegistrationForm) form; if (user != null) { if (servlet.getDebug() >= 1) servlet.log(" Populating form from " + user); try { PropertyUtils.copyProperties(regform, user); regform.setAction(action); regform.setPassword(null); regform.setPassword2(null); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t == null) t = e; servlet.log("RegistrationForm.populate", t); throw new ServletException("RegistrationForm.populate", t); } catch (Throwable t) { servlet.log("RegistrationForm.populate", t); throw new ServletException("RegistrationForm.populate", t); } } // Set a transactional control token to prevent double posting if (servlet.getDebug() >= 1) servlet.log(" Setting transactional control token"); saveToken(request); // Forward control to the edit user registration page if (servlet.getDebug() >= 1) servlet.log(" Forwarding to 'success' page"); return (mapping.findForward("success")); } }
<%@ page language="java" %> <%@ taglib uri="/WEB-INF/app.tld" prefix="app" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <logic:equal name="registrationForm" property="action" scope="request" value="Edit"> <app:checkLogon/> </logic:equal> <html:html> <head> <logic:equal name="registrationForm" property="action" scope="request" value="Create"> <title><bean:message key="registration.title.create"/></title> </logic:equal> <logic:equal name="registrationForm" property="action" scope="request" value="Edit"> <title><bean:message key="registration.title.edit"/></title> </logic:equal> <html:base/> </head> <body bgcolor="white"> <html:errors/> <html:form action="/saveRegistration"> <html:hidden property="action"/> <table border="0" width="100%"> <tr> <th align="right"> <bean:message key="prompt.username"/> </th> <td align="left"> <logic:equal name="registrationForm" property="action" scope="request" value="Create"> <html:text property="username" size="16" maxlength="16"/> </logic:equal> <logic:equal name="registrationForm" property="action" scope="request" value="Edit"> <bean:write name="registrationForm" property="username" scope="request" filter="true"/> <html:hidden property="username"/> </logic:equal> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.password"/> </th> <td align="left"> <html:password property="password" size="16" maxlength="16"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.password2"/> </th> <td align="left"> <html:password property="password2" size="16" maxlength="16"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.fullName"/> </th> <td align="left"> <html:text property="fullName" size="50"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.fromAddress"/> </th> <td align="left"> <html:text property="fromAddress" size="50"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.replyToAddress"/> </th> <td align="left"> <html:text property="replyToAddress" size="50"/> </td> </tr> <tr> <td align="right"> <html:submit> <bean:message key="button.save"/> </html:submit> </td> <td align="left"> <html:reset> <bean:message key="button.reset"/> </html:reset> <html:cancel> <bean:message key="button.cancel"/> </html:cancel> </td> </tr> </table> </html:form> <logic:equal name="registrationForm" property="action" scope="request" value="Edit"> <div align="center"> <h3><bean:message key="heading.subscriptions"/></h3> </div> <table border="1" width="100%"> <tr> <th align="center" width="30%"> <bean:message key="heading.host"/> </th> <th align="center" width="25%"> <bean:message key="heading.user"/> </th> <th align="center" width="10%"> <bean:message key="heading.type"/> </th> <th align="center" width="10%"> <bean:message key="heading.autoConnect"/> </th> <th align="center" width="15%"> <bean:message key="heading.action"/> </th> </tr> <logic:iterate id="subscription" name="user" property="subscriptions"> <tr> <td align="left"> <bean:write name="subscription" property="host" filter="true"/> </td> <td align="left"> <bean:write name="subscription" property="username" filter="true"/> </td> <td align="center"> <bean:write name="subscription" property="type" filter="true"/> </td> <td align="center"> <bean:write name="subscription" property="autoConnect"/> </td> <td align="center"> <app:linkSubscription page="/editSubscription.do?action=Delete"> <bean:message key="registration.deleteSubscription"/> </app:linkSubscription> <app:linkSubscription page="/editSubscription.do?action=Edit"> <bean:message key="registration.editSubscription"/> </app:linkSubscription> </td> </tr> </logic:iterate> </table> <html:link page="/editSubscription.do?action=Create" paramId="username" paramName="registrationForm" paramProperty="username"> <bean:message key="registration.addSubscription"/> </html:link> </logic:equal> </body> </html:html>
<logic:equal name="registrationForm" property="action" scope="request" value="Edit"> <app:checkLogon/> </logic:equal>
<html:text property="username" size="16" maxlength="16"/> ... <html:password property="password" size="16" maxlength="16"/> ... <html:submit> ... <html:reset> ... <html:cancel>
<bean:write name="registrationForm" property="username" scope="request" filter="true"/>
Mais uma coisa interessante em registration.jsp é a forma de fazer um laço para exibir as assinaturas
Usa-se o tag "<logic:iterate>
<logic:iterate id="subscription" name="user" property="subscriptions">
Este tag diz: itera na coleção "subscriptions" que é propriedade do bean "user" e chame cada objeto da coleção "subscription" para ser referenciado no laço
Podemos agora fazer referência ao objeto da coleção:
<bean:write name="subscription" property="host" filter="true"/>
filter="true" significa transformar caracteres especiais tais como "<" para "<"
A coleção de um tag iterate pode ser:
Um array de objetos
Um Iterator
Uma Collection (incluindo Lists, Sets e Vectors)
Um Map (incluindo tabelas hash)
Em registration.jsp, há dois custom tags, <app:linkSubscription> e <app:linkUser>, que não estudaremos
São usados para encapsular a forma de gerar um link meio complicado
Agora, vamos adicionar uma assinatura
struts-config.xml trata editSubscription assim:
Nada de novo aqui
<!-- Subscription form bean --> <form-bean name="subscriptionForm" type="org.apache.struts.webapp.example.SubscriptionForm"/> ... <!-- Edit mail subscription --> <action path="/editSubscription" type="org.apache.struts.webapp.example.EditSubscriptionAction" name="subscriptionForm" scope="request" validate="false"> <forward name="failure" path="/mainMenu.jsp"/> <forward name="success" path="/subscription.jsp"/> </action>
O arquivo EditSubscriptionAction.java é mostrado abaixo
/** * Implementation of <strong>Action</strong> that populates an instance of * <code>SubscriptionForm</code> from the currently specified subscription. * * @author Craig R. McClanahan * @version $Revision: 1.2 $ $Date: 2001/04/14 12:53:07 $ */ public final class EditSubscriptionAction extends Action { // --------------------------------------------------------- Public Methods /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. * * @param mapping The ActionMapping used to select this instance * @param actionForm The optional ActionForm bean for this request (if any) * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Extract attributes we will need Locale locale = getLocale(request); MessageResources messages = getResources(); HttpSession session = request.getSession(); String action = request.getParameter("action"); if (action == null) action = "Create"; String host = request.getParameter("host"); if (servlet.getDebug() >= 1) servlet.log("EditSubscriptionAction: Processing " + action + " action"); // Is there a currently logged on user? User user = (User) session.getAttribute(Constants.USER_KEY); if (user == null) { if (servlet.getDebug() >= 1) servlet.log(" User is not logged on in session " + session.getId()); return (servlet.findForward("logon")); } // Identify the relevant subscription Subscription subscription = null; if (action.equals("Create")) { subscription = new Subscription(); subscription.setUser(user); } else { subscription = user.findSubscription(host); } if (subscription == null) { if (servlet.getDebug() >= 1) servlet.log(" No subscription for user " + user.getUsername() + " and host " + host); return (mapping.findForward("failure")); } session.setAttribute(Constants.SUBSCRIPTION_KEY, subscription); // Populate the subscription form if (form == null) { if (servlet.getDebug() >= 1) servlet.log(" Creating new SubscriptionForm bean under key " + mapping.getAttribute()); form = new SubscriptionForm(); if ("request".equals(mapping.getScope())) request.setAttribute(mapping.getAttribute(), form); else session.setAttribute(mapping.getAttribute(), form); } SubscriptionForm subform = (SubscriptionForm) form; subform.setAction(action); if (servlet.getDebug() >= 1) servlet.log(" Populating form from " + subscription); try { PropertyUtils.copyProperties(subform, subscription); subform.setAction(action); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t == null) t = e; servlet.log("SubscriptionForm.populate", t); throw new ServletException("SubscriptionForm.populate", t); } catch (Throwable t) { servlet.log("SubscriptionForm.populate", t); throw new ServletException("SubscriptionForm.populate", t); } // Forward control to the edit subscription page if (servlet.getDebug() >= 1) servlet.log(" Forwarding to 'success' page"); return (mapping.findForward("success")); } }
return (servlet.findForward("logon"));
Eis a página subscription.jsp
<%@ page language="java" %> <%@ taglib uri="/WEB-INF/app.tld" prefix="app" %> <%@ taglib uri="/WEB-INF/struts-bean.tld" prefix="bean" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <%@ taglib uri="/WEB-INF/struts-logic.tld" prefix="logic" %> <app:checkLogon/> <%-- In real life, these would be loaded from a database --%> <% java.util.ArrayList list = new java.util.ArrayList(); list.add(new org.apache.struts.webapp.example.LabelValueBean("IMAP Protocol", "imap")); list.add(new org.apache.struts.webapp.example.LabelValueBean("POP3 Protocol", "pop3")); pageContext.setAttribute("serverTypes", list); %> <html:html> <head> <logic:equal name="subscriptionForm" property="action" scope="request" value="Create"> <title><bean:message key="subscription.title.create"/></title> </logic:equal> <logic:equal name="subscriptionForm" property="action" scope="request" value="Delete"> <title><bean:message key="subscription.title.delete"/></title> </logic:equal> <logic:equal name="subscriptionForm" property="action" scope="request" value="Edit"> <title><bean:message key="subscription.title.edit"/></title> </logic:equal> <html:base/> </head> <body bgcolor="white"> <html:errors/> <html:form action="/saveSubscription" focus="host"> <html:hidden property="action"/> <table border="0" width="100%"> <tr> <th align="right"> <bean:message key="prompt.username"/> </th> <td align="left"> <bean:write name="user" property="username" filter="true"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.mailHostname"/> </th> <td align="left"> <html:textarea property="host" cols="50" rows="1"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.mailUsername"/> </th> <td align="left"> <html:text property="username" size="50"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.mailPassword"/> </th> <td align="left"> <html:password property="password" size="50"/> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.mailServerType"/> </th> <td align="left"> <html:select property="type"> <html:options collection="serverTypes" property="value" labelProperty="label"/> </html:select> </td> </tr> <tr> <th align="right"> <bean:message key="prompt.autoConnect"/> </th> <td align="left"> <html:checkbox property="autoConnect"/> </td> </tr> <tr> <td align="right"> <logic:equal name="subscriptionForm" property="action" scope="request" value="Create"> <html:submit> <bean:message key="button.save"/> </html:submit> </logic:equal> <logic:equal name="subscriptionForm" property="action" scope="request" value="Delete"> <html:submit> <bean:message key="button.confirm"/> </html:submit> </logic:equal> <logic:equal name="subscriptionForm" property="action" scope="request" value="Edit"> <html:submit> <bean:message key="button.save"/> </html:submit> </logic:equal> </td> <td align="left"> <logic:notEqual name="subscriptionForm" property="action" scope="request" value="Delete"> <html:reset> <bean:message key="button.reset"/> </html:reset> </logic:notEqual> <html:cancel> <bean:message key="button.cancel"/> </html:cancel> </td> </tr> </table> </html:form> </body> </html:html>
Podemos ver alguns detalhes adicionais sobre validação de formulários em SubscriptionForm.java
public final class SubscriptionForm extends ActionForm { // --------------------------------------------------- Instance Variables /** * The maintenance action we are performing (Create or Edit). */ private String action = "Create"; /** * Should we auto-connect at startup time? */ private boolean autoConnect = false; /** * The host name. */ private String host = null; /** * The password. */ private String password = null; /** * The subscription type. */ private String type = null; /** * The username. */ private String username = null; // ----------------------------------------------------------- Properties /** * Return the maintenance action. */ public String getAction() { return (this.action); } /** * Set the maintenance action. * * @param action The new maintenance action. */ public void setAction(String action) { this.action = action; } /** * Return the auto-connect flag. */ public boolean getAutoConnect() { return (this.autoConnect); } /** * Set the auto-connect flag. * * @param autoConnect The new auto-connect flag */ public void setAutoConnect(boolean autoConnect) { this.autoConnect = autoConnect; } /** * Return the host name. */ public String getHost() { return (this.host); } /** * Set the host name. * * @param host The host name */ public void setHost(String host) { this.host = host; } /** * Return the password. */ public String getPassword() { return (this.password); } /** * Set the password. * * @param password The new password */ public void setPassword(String password) { this.password = password; } /** * Return the subscription type. */ public String getType() { return (this.type); } /** * Set the subscription type. * * @param type The subscription type */ public void setType(String type) { this.type = type; } /** * Return the username. */ public String getUsername() { return (this.username); } /** * Set the username. * * @param username The new username */ public void setUsername(String username) { this.username = username; } // --------------------------------------------------------- Public Methods /** * Reset all properties to their default values. * * @param mapping The mapping used to select this instance * @param request The servlet request we are processing */ public void reset(ActionMapping mapping, HttpServletRequest request) { this.action = "Create"; this.autoConnect = false; this.host = null; this.password = null; this.type = null; this.username = null; } /** * Validate the properties that have been set from this HTTP request, * and return an <code>ActionErrors</code> object that encapsulates any * validation errors that have been found. If no errors are found, return * <code>null</code> or an <code>ActionErrors</code> object with no * recorded error messages. * * @param mapping The mapping used to select this instance * @param request The servlet request we are processing */ public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) { ActionErrors errors = new ActionErrors(); if ((host == null) || (host.length() < 1)) errors.add("host", new ActionError("error.host.required")); if ((username == null) || (username.length() < 1)) errors.add("username", new ActionError("error.username.required")); if ((password == null) || (password.length() < 1)) errors.add("password", new ActionError("error.password.required")); if ((type == null) || (type.length() < 1)) errors.add("type", new ActionError("error.type.required")); else if (!"imap".equals(type) && !"pop3".equals(type)) errors.add("type", new ActionError("error.type.invalid", type)); return (errors); } }
O fim do trabalho é feito em SaveSubscriptionAction.java
Examine o código:
isCancelled() é um método da classe Action que permite saber se usuário clicou no botão Cancel gerado pelo CancelTag
Quando retorna true, a validação feita pelo método validate() do ActionForm terá sido pulada pelo controlador
/** * Implementation of <strong>Action</strong> that validates and creates or * updates the mail subscription entered by the user. * * @author Craig R. McClanahan * @version $Revision: 1.2 $ $Date: 2001/04/14 12:53:08 $ */ public final class SaveSubscriptionAction extends Action { // --------------------------------------------------------- Public Methods /** * Process the specified HTTP request, and create the corresponding HTTP * response (or forward to another web component that will create it). * Return an <code>ActionForward</code> instance describing where and how * control should be forwarded, or <code>null</code> if the response has * already been completed. * * @param mapping The ActionMapping used to select this instance * @param actionForm The optional ActionForm bean for this request (if any) * @param request The HTTP request we are processing * @param response The HTTP response we are creating * * @exception IOException if an input/output error occurs * @exception ServletException if a servlet exception occurs */ public ActionForward perform(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { // Extract attributes and parameters we will need Locale locale = getLocale(request); MessageResources messages = getResources(); HttpSession session = request.getSession(); SubscriptionForm subform = (SubscriptionForm) form; String action = request.getParameter("action"); if (action == null) action = "?"; if (servlet.getDebug() >= 1) servlet.log("SaveSubscriptionAction: Processing " + action + " action"); // Is there a currently logged on user? User user = (User) session.getAttribute(Constants.USER_KEY); if (user == null) { if (servlet.getDebug() >= 1) servlet.log(" User is not logged on in session " + session.getId()); return (servlet.findForward("logon")); } // Is there a related Subscription object? Subscription subscription = (Subscription) session.getAttribute(Constants.SUBSCRIPTION_KEY); if (subscription == null) { servlet.log(" Missing subscription for user '" + user.getUsername() + "'"); response.sendError(HttpServletResponse.SC_BAD_REQUEST, messages.getMessage("error.noSubscription")); return (null); } // Was this transaction cancelled? if (isCancelled(request)) { if (servlet.getDebug() >= 1) servlet.log(" Transaction '" + action + "' was cancelled"); if (mapping.getAttribute() != null) session.removeAttribute(mapping.getAttribute()); session.removeAttribute(Constants.SUBSCRIPTION_KEY); return (mapping.findForward("success")); } // Was this transaction a Delete? if (action.equals("Delete")) { if (servlet.getDebug() >= 1) servlet.log(" Deleting mail server '" + subscription.getHost() + "' for user '" + user.getUsername() + "'"); subscription.setHost(null); subscription.setUser(null); if (mapping.getAttribute() != null) session.removeAttribute(mapping.getAttribute()); session.removeAttribute(Constants.SUBSCRIPTION_KEY); return (mapping.findForward("success")); } // All required validations were done by the form itself // Update the persistent subscription information if (servlet.getDebug() >= 1) servlet.log(" Populating database from form bean"); try { PropertyUtils.copyProperties(subscription, subform); } catch (InvocationTargetException e) { Throwable t = e.getTargetException(); if (t == null) t = e; servlet.log("Subscription.populate", t); throw new ServletException("Subscription.populate", t); } catch (Throwable t) { servlet.log("Subscription.populate", t); throw new ServletException("Subscription.populate", t); } // Remove the obsolete form bean and current subscription if (mapping.getAttribute() != null) { if ("request".equals(mapping.getScope())) request.removeAttribute(mapping.getAttribute()); else session.removeAttribute(mapping.getAttribute()); } session.removeAttribute(Constants.SUBSCRIPTION_KEY); // Forward control to the specified success URI if (servlet.getDebug() >= 1) servlet.log(" Forwarding to success page"); return (mapping.findForward("success")); } }
strutsmvc programa