JSP Controls Tag Library
 

Struts Integration

Overview

It is possible to integrate an action framework like Struts with JSP Controls, offloading the tasks of processing input, validating it, and converting strings to specific datatypes onto a framework.

Obviously you would like to have an action that is able to process events generated by the Library. Because input events will be processed by your Struts action class, you can change the format of event whatever you want, and create an action class that is able to parse these events. You may want to use either standard Action class or any of DispatchAction flavors.

The Library is shipped with EventAction class that extends standard DispatchAction class and is able to process events in the format native to the Library. This class is a great choice if you do not want to roll out your own event-handling scheme.

The class reads an event from the request and dispatches it to an appropriate handler method. All you need to do is to subclass EventAction class and to define handler methods. The names of a handler method must correspond to event it handles, and method signature must be the same as of Action.execute method.

public class LoginAction extends EventAction {

  public ActionForward login (ActionMapping mapping,
                              ActionForm form,
                              HttpServletRequest request,
                              HttpServletResponse response) 
    throws Exception {
      HttpSession session = request.getSession();
      LoginForm inputForm = (LoginForm) form;
      request.getSession().removeAttribute("USER");
      ActionMessages errors = inputForm.validate(mapping, request);

      if (errors != null) {
        session.setAttribute(Globals.ERROR_KEY, errors);
      } else {
        session.setAttribute("USER", inputForm.getName());
      }

      // Always return null, 
      // <jc:reload> tag will reload the page if needed
      return null;
  }

  public ActionForward logout (ActionMapping mapping,
                               ActionForm form,
                               HttpServletRequest request,
                               HttpServletResponse response) 
    throws Exception {
      LoginForm inputForm = (LoginForm) form;
      inputForm.setName(null);
      inputForm.setPassword(null);

      request.getSession().removeAttribute("USER");

      // Always return null, 
      // <jc:reload> tag will reload the page if needed
      return null;
  }
}

If you have used DispatchAction or a similar dispatching class before, you should be familiar with the concept. If your action class has to extend another class, you can use EventAction as utility class, instantiating it in your own action class:

public class LoginAction extends Action implements IEventAction {

    // Declare a separate instance of dispatching action.
    private DialogAction dialogAction = new DialogAction();

    // This is your regular execute() method.
    public ActionForward execute(ActionMapping mapping,
                                 ActionForm form,
                                 HttpServletRequest request,
                                 HttpServletResponse response)
            throws Exception {

        // ...
        // Do your own stuff here
        // ...

        // Dispatch to handler, do not forget to pass reference to self
        // in the first argument.
        return dialogAction.execute(this, mapping, form, request, response);
    }

    ...

}

A handler method from Struts action must properly set application state (or choose a view) and then return null. Never return a Forward object. The Struts action replaces content of Hanlder tags. The view portion of JSP component remains unchanged. Here is how the body of Accept tag looks when all event handling is offloaded to Struts action:

<jc:accept>
  <jsp:include page="/logincomponent.do" />
  <jc:reload/>
</jc:accept>

Action class does not need to forward to a view, this job is being done within JSP file. Such separation is necessary because current JSP/servlet specification does not allow to make server-side forwards from within an included resorce. Therefore, JSP page serves as a view helper and as a simple page controller.

Because Struts handles only accept phase of request/response cycle, the definition of LoginAction in the struts-config.xml file is very simple:

<form-beans>
  <form-bean name = "loginform" 
             type="net.jspcontrols.samples.component.LoginForm"/>
</form-beans>
<action-mappings>
  <!-- Login component -->
  <action path = "/logincomponent"
          type = "net.jspcontrols.samples.component.LoginComponent"
          name = "loginform" scope = "session" validate  = "false"/>
</action-mappings>

You need to set form scope to "session" to preserve form data between requests. You need to set "validate" to "false" to ensure that your handler methods are always called. Then you can validate input data from the handler method explicitly.

Here is the simple LoginForm that is used by LoginAction:

public class LoginForm extends ActionForm {

    private String name;
    public String getName() {return name;}
    public void setName(String name) {this.name = name;}

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

    // Do not forget to turn "validate" property of the action mapping off.
    public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
        if (!"user".equalsIgnoreCase(name) ||
            !"pass".equalsIgnoreCase(password)) {
            ActionErrors errors = new ActionErrors();
            errors.add("ERROR", new ActionMessage("dialog.badpassword"));
            return errors;
        } else {
            return null;
        }
    }
}

So now you have it, a JSP/Struts component with Ajax support, yeah baby!