Struts e a Arquitetura MVC:
Servlets e JSP juntos

Introdução

Struts e Custom Tags

Struts e a Arquitetura Model-View-Controller (MVC)

Alguns links úteis

Struts by Example

Primeira tela:

Register:

Depois de salvar cadastro:

Edit:

Add:

Depois de salvar:

Se fizer login com user/pass:

Uma tournê do exemplo

A página index.jsp

<%@ 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>&nbsp;</p>
<html:link page="/tour.do">
<font size="-1"><bean:message key="index.tour"/></font>
</html:link>
<p>&nbsp;</p>

<html:img page="/struts-power.gif" alt="Powered by Struts"/>

</body>
</html:html>
<html lang="pt">
<title><bean:message key="index.title"/></title>

web.xml e ApplicationResource.properties

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

DatabaseServlet.java

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

index.jsp, novamente

<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

struts-config.xml e LogonForm.java

<%@ 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>
<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;
    }
}

LogonAction.java

  <!-- 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"));

struts-config.xml, novamente

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>
<app:checkLogon/>
<jsp:useBean id="user" scope="session" type="org.apache.struts.webapp.example.User"/>
...
<jsp:getProperty name="user" property="username"/>
<!-- 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>

EditRegistrationAction.java

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"));
    }
}

A página registration.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" %>
<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>
      &nbsp;
      <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"/>

logic:iterate

<logic:iterate id="subscription" 
             name="user" 
         property="subscriptions">
      <bean:write name="subscription" property="host" filter="true"/>

LinkSubscriptionTag.java e LinkUserTag.java

EditSubscriptionAction.java

<!-- 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>
/**
 * 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"));

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>
      &nbsp;
      <html:cancel>
        <bean:message key="button.cancel"/>
      </html:cancel>
    </td>
  </tr>

</table>

</html:form>

</body>
</html:html>

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);
    }
}

SaveSubscriptionAction.java

/**
 * 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"));
    }
}

Finalmente ...

strutsmvc programa