struts-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Apache Wiki <>
Subject [Struts Wiki] Update of "StrutsManualActionWebComponentAsync" by MichaelJouravlev
Date Sat, 24 Jun 2006 03:22:19 GMT
Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Struts Wiki" for change notification.

The following page has been changed by MichaelJouravlev:

New page:
== Struts Component Lifecycle In Asynchronous Mode ==

If the browser has !JavaScript turned on and the XMLHTTPRequest object is available, a web
component is updated asynchronously; steps 1, 2 and 3 do not differ from synchronous mode:

   1. The cycle begins with the initial load of a composite page, starting the render phase.
   2. When the JSP processor encounters a <c:import> action in a composite page, it
generates an HTTP request to obtain the content of the included component.
   3. The Action class forwards to a view relevant to component state. After all components
on the composite page have rendered themselves, the render phase finishes and a composite
page is presented to a user.
   4. The user initiates the input phase by submitting an HTML form or by activating a command
link. Javascript engine sends input data to the Action asynchronously using XMLHTTPRequest
object. The Action processes data and updates component state if needed. Component state can
be stored in a session-scoped form bean, in a database or in other location.
   5. After input data has been processed, the Action forwards to a view relevant to component
state. This view is returned to the browser in response to asynchronous request. The Javascript
engine finds the spot in the composite page where the component resides, and updates page
fragment in place. The user sees an updated page.


A Struts component incorporated into a page looks and behaves uniformly whether it runs in
Ajax mode or not. The dual-mode functionality of Struts web components is invaluable for environments
where !JavaScript is not allowed or in browsers that do not support the XMLHTTPRequest object,
like some mobile browsers. 

It is worth noting that both Ajax and non-Ajax modes utilize the same code and markup.

== Component Configuration ==

First let us define the Login Component in {{{struts-config.xml}}} file:


    <!-- Login form -->
    <form-bean name = "loginform" type="samples.login.LoginForm"/>


      <!-- Composite page containing login component -->
      <action path="/login-struts"

      <!-- Login component -->
      <action component = "Login"
              view = "/login-struts/loginComponent.jsp"
              path = "/logincomponent"
              type = "samples.login.LoginAction"
              form = "loginform"
              scope = "session"
              validate  = "false">
          <event name="loginEvent" handler="login"/>
          <event name="logoutEvent" handler="logout"/>


Notice action mapping attributes and properties that are new for Struts 1.4:

   * "component" attribute identifies an action as a component manager, such actions are processed
differently by Action class. This name is also used in generated HTML for in-place update
in Ajax mode.
   * "view" attribute identifies a default view for a component. Must be a JSP page. Often
consists from several subviews, in our case the Login Component has two subviews "Not Logged
In" and "Logged In", they will be defined in JSP file.
   * "form" is just another name for "name" property
   * "event" property allows to define request parameters as events, and corresponding method
handlers. This is made possible by supporting dispatching functionality directly in Action

== Component Action ==

The action class is deceptively simple. It handles only two events, the corresponding handlers
are called automatically by Action class. The location of component's view is defined in the
action mapping, so render method is not needed. On the other hand, most non-trivial components
need to process data before rendering themselves or to exchange data with other components.
For these cases you can use render" method. Its default implementation does nothing.

public class LoginAction extends Action {

    public ActionForward login (ActionMapping mapping,
                                ActionForm form,
                                HttpServletRequest request,
                                HttpServletResponse response) throws Exception {

        HttpSession session = request.getSession();
        LoginForm inputForm = (LoginForm) form;

        // Log out current user first

        // Validation is turned off in struts-config.xml,
        // so explicitly validate user input;
        ActionMessages errors = inputForm.validate(mapping, request);

        if (errors != null) {
            saveErrors(session, errors);
        } else {
            // Use this session attribute to hold user's name
            session.setAttribute("USER", inputForm.getUsername());

        // Always return null.
        return null;

    public ActionForward logout (ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response) throws Exception {

        LoginForm inputForm = (LoginForm) form;

        // Clean name and password in the input/output form bean

        // Remove dialog name from session, effectively logging out

        // Always return null.
        return null;

== Component Form Bean ==

Nothing exciting here, just a session-scoped form to hold user name and to validate credentials:

public class LoginForm extends ActionForm {

    private String username;
    public String getUsername() {return username;}
    public void setUsername(String username) {this.username = username;}

    private String password;
    public String getPassword() {return password;}
    public void setPassword(String password) {this.password = password;}

    // Generate the error messages in the same manner as usual,
    // but do not forget to turn "validate" property of the action mapping off.
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        if (!"guest".equalsIgnoreCase(username) ||
            !"pass".equalsIgnoreCase(password)) {
            ActionErrors errors = new ActionErrors();
            errors.add("ERROR", new ActionMessage("login.badpassword"));
            return errors;
        } else {
            return null;

== Login/Logout JSP page ==

The Login/Logout component has two subviews, both defined in one JSP page. Notice that content
type is set to "text/xml", this is important for Ajax mode.

<%@ page contentType="text/xml;charset=UTF-8" language="java" %>

<%@ page import="java.util.ArrayList, org.apache.struts.Globals"%>
<%@ taglib prefix="c" uri="" %>
<%@ taglib uri="" prefix="bean" %>
<%@ taglib uri="" prefix="html" %>
<%@ taglib uri="" prefix="logic" %>
<%@ taglib uri="" prefix="comp" %>


<%-- "Not Logged In" subview --%>

<c:if test='${empty USER}'>
  <h3>Please Log In</h3>

  <!-- Displaying Struts errors -->
    <html:messages id="error">
      <li><bean:write name="error"/></li>

  <%-- "strutsCommand" CSS class is a hook for Ajax-mode handler, it is set
       in runtime using Behaviour library. --%>

  <html:form method="get" styleClass="strutsCommand" action="/">
    <label for="username">Username:</label>
    <input type="text" name="username" value="${loginform.username}" class="datavalue"/><br/>

    <label for="password">Password:</label>
    <input type="text" name="password" value="" class="datavalue"/><br/>

    <input type="submit" name="loginEvent" value="Log In" class="strutsCommand"/>
  <p><em>Username is "guest", password is "pass".</em></p>

<%-- "Logged In" subview --%>

<c:if test='${not empty USER}'>
  <h3>User Information</h3>

  <html:form method="post" styleClass="strutsCommand" action="/">
    <label>Current user:</label>
    <div class="datavalue">${USER}</div><br/>
    <input type="submit" name="logoutEvent" value="Log Out" class="strutsCommand"/><br/>


== Composite Page ==

Now we need to include the Login Component into a larger page (composite page). This is done
with JSTL c:import tag. Do not use jsp:include, it may not work on some containers:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="" %>
<%@ taglib uri="" prefix="html" %>
<%@ taglib uri="" prefix="comp" %>

    <p>This paragraph is defined directly in the parent page
    and should precede the content of login control.</p>

    <%-- The login component, notice that DIV has the same ID as
         the component's name --%>
    <div id="Login">
      <c:import url="/" />

    <p>This paragraph is defined directly in the parent page
    and should follow the content of login control.</p>

== Done! ==

This is pretty much it. Now run the application and navigate to composite page. The included
component will evaluate user's state and will display a login form. Try to log in. The submitted
credentials are sent directly to a component, if they are not correct, the composite page
is redisplayed. How? Behind the scenes the improved Action class as well as JSP tags work
together to distinguish the address of a composite page during first render. This address
is saved automatically. Then after component finishes, it reloads the composite page using
saved address. Now you can develop independent Struts components!

View raw message