UIX Developer's Guide
Go to Table of Contents
Contents
Go to previous page
Previous
Go to next page
Next

15. Using uiXML With Struts

The Struts framework (see http://jakarta.apache.org/struts/) has become one of the most popular frameworks for building J2EE web applications. While Struts developers often use JSPs for their pages, Struts supports the use of alternative view technologies. UIX provides a set of extensions that lets you use uiXML for part or all of your Struts applications, and lets you use completely standard Struts Actions, ActionForm beans and configuration files to control your uiXML-based application.

This chapter assumes you're familiar with the basics of Struts development. If not, you may want to visit the Struts site to learn more about this technology.

This chapter contains the following sections:

Installing the Struts Extensions

We'll assume that you've installed both UIX and Struts already (though UIX requires at least Struts 1.1 beta 1). Now, you'll need to register UIX's Struts extensions. Here's a minimal WEB-INF/uix-config.xml file:


<?xml version="1.0" encoding="ISO-8859-1"?>
<configurations xmlns="http://xmlns.oracle.com/uix/config">

  <application-configuration>
    <ui-extensions>
      <extension-class>oracle.cabo.servlet.struts.StrutsUIExtension</extension-class>
    </ui-extensions>
  </application-configuration>

</configurations>

If you haven't seen this configuration file before, you might want to read the Configuration chapter.

That's all you need. With the setup out of the way, let's start building a basic two-page login application. First, we'll write the Struts code. As you'll see, this is completely generic Struts code without any reference to UIX - a good thing, since your controller code shouldn't contain any dependencies on your view code.

Example: Struts login code

We'll use four files: a Struts configuration file, a LogonBean for storing the login information, a LogonAction for processing the login, and a small .properties file for storing our messages. We won't talk much about this code: if you're familiar with Struts, it should all be straightforward.

struts-config.xml


<?xml version="1.0"?>
<!DOCTYPE struts-config SYSTEM "struts-config_1_1.dtd">
<struts-config>
  <form-beans>
    <form-bean
      name="logonForm"
      type="LogonBean" />
  </form-beans>      
  <global-forwards
      type="org.apache.struts.action.ActionForward">
    <forward name="welcome" path="/welcome.uix"
         redirect="false" /> 
  </global-forwards>      
  <action-mappings>     
    <action
        path="/logon" 
        type="LogonAction"
        name="logonForm"
        scope="request"
        input="/logon.uix"
        unknown="false"
        validate="true"/>
  </action-mappings>
  
  <message-resources parameter="LogonText"/>
</struts-config>

LogonBean:


import javax.servlet.http.HttpServletRequest;
import org.apache.struts.action.ActionError;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionMapping;

public class LogonBean extends ActionForm
{
  public String getUser()
  {
    return _user;
  }

  public void setUser(String user)
  {
    _user = user;
  }

  public String getPassword()
  {
    return _password;
  }

  public void setPassword(String password)
  {
    _password = password;
  }

  public ActionErrors validate(
    ActionMapping mapping, HttpServletRequest request)
  {
    // For the purposes of this demo, just call
    // "123" a good password!
    if (!"123".equals(_password))
    {
      ActionErrors errors = new ActionErrors();
      errors.add("pwd", new ActionError("password"));
      return errors;
    }

    return null;
  }

  private String _user;
  private String _password;
}

LogonAction:


import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;

public class LogonAction extends Action
{
  public ActionForward perform(
    ActionMapping mapping,
    ActionForm    form,
    HttpServletRequest request,
    HttpServletResponse response)
  {
    LogonBean bean = (LogonBean) form;
    // Tell the user the login was successful
    ActionMessages messages = new ActionMessages();
    messages.add(ActionMessages.GLOBAL_MESSAGE,
                 new ActionMessage("loggedIn", bean.getUser()));
    saveMessages(request, messages);

    // And go to the generic "welcome" page
    return mapping.findForward("welcome");
  }
}

LogonText.properties:


password=Bogus password! (Try 123)
loggedIn=Hello {0}, you logged in successfully!

As you can see, this is all generic Struts code. If the user logs in successfully - for the purposes of this demo, by entering the magic "123" password - validation succeeds, and the LogonAction will redirect the user to the welcome page. The welcome page is defined in the Struts configuration file as handled by "welcome.uix".

Writing the UIX Pages

If we were to write our "login" and "welcome" UIX pages without Struts, they'd probably look like:

logon.uix:


<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
 <content>
 <dataScope xmlns="http://xmlns.oracle.com/uix/ui"
            xmlns:data="http://xmlns.oracle.com/uix/ui">
   <contents>
    <pageLayout>
      <contents>
        <form name="theForm" method="post">
          <contents>
            <labeledFieldLayout>
              <contents>
                <messageTextInput prompt="User" name="user"/>
                <messageTextInput secret="true" prompt="Password"
                                  name="password"/>
                <submitButton ctrl:event="login" text="Log On"/>
              </contents>
            </labeledFieldLayout>
          </contents>
        </form>
      </contents>
    </pageLayout>
   </contents>
  </dataScope>
 </content>
 <handlers>
   <event name="login">
     <!-- Here, we handle the event... somehow -->
   </event>
 </handlers>
</page>

welcome.uix:


<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
 <content>
 <dataScope xmlns="http://xmlns.oracle.com/uix/ui"
            xmlns:data="http://xmlns.oracle.com/uix/ui">
   <contents>
    <pageLayout title="Hi there!"/>
   </contents>
  </dataScope>
 </content>
</page>

The only really interesting thing about these two pages is the name of the two <messageTextInput> elements: "user" and "password". These correspond to the names of the two properties on LogonBean. Struts will rely on this when we hook it up to this page.

Adding Struts Actions

These are two pretty simple pages, but they don't yet do anything. First, we'll hook in our LogonAction class.

logon.uix with a Struts action handler:


<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
      xmlns:struts="http://xmlns.oracle.com/uix/struts">
 <content>
 <dataScope xmlns="http://xmlns.oracle.com/uix/ui"
            xmlns:data="http://xmlns.oracle.com/uix/ui">
   <contents>
    <pageLayout>
      <contents>
        <form name="theForm" method="post">
          <contents>
            <labeledFieldLayout>
              <contents>
                <messageTextInput prompt="User" name="user"/>
                <messageTextInput secret="true" prompt="Password"
                                  name="password"/>
                <submitButton ctrl:event="login" text="Log On"/>
              </contents>
            </labeledFieldLayout>
          </contents>
        </form>
      </contents>
    </pageLayout>
   </contents>
  </dataScope>
 </content>
 <handlers>
   <event name="login">
     <struts:action path="/do/logon"/>
   </event>
 </handlers>
</page>

We've added two pieces to this page. First, we assigned the XML namespace http://xmlns.oracle.com/uix/struts to the "struts" prefix. Next, we used the <struts:action> event to handle our "login" event.

Now, we have two functional pages. Entering the wrong password takes you back to try to login again, and getting the right password sends you to the welcome page. But we've got three things to fix up:

Both are easy to solve with the other UIX Struts XML elements.

Using <struts:form>

To preserve values entered by the user, we'll change from the standard uiXML <form> element to the special uiXML <struts:form> element. We'll also change our <messageTextInput> elements to <struts:messageTextInput>.

logon.uix with <struts:form>:


<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
      xmlns:struts="http://xmlns.oracle.com/uix/struts">
 <content>
 <dataScope xmlns="http://xmlns.oracle.com/uix/ui"
            xmlns:data="http://xmlns.oracle.com/uix/ui">
   <contents>
    <pageLayout>
      <contents>
        <struts:form name="theForm" method="post"
                      beanName="logonForm">
          <contents>
            <labeledFieldLayout>
              <contents>
                <struts:messageTextInput prompt="User" name="user"/>
                <struts:messageTextInput secret="true" prompt="Password"
                                  name="password"/>
                <submitButton ctrl:event="login" text="Log On"/>
              </contents>
            </labeledFieldLayout>
          </contents>
        </struts:form>
      </contents>
    </pageLayout>
   </contents>
  </dataScope>
 </content>
 <handlers>
   <event name="login">
     <struts:action path="/do/logon"/>
   </event>
 </handlers>
</page>

In addition to changing <form> and <messageTextInput> to the Struts versions, we've also set a "beanName" attribute on <struts:form>. This attribute is required, and must match the <form-bean> name defined in your struts-config.xml (the "name", not the "type").

If you try this, you'll notice that the "user" is automatically restored to what the user entered in case of a password error. The "password" isn't, though, and this is intentional - passwords get sent in plain text in the HTML, even though they look like a series of asterisks in your browser.

In addition to <struts;form> and <struts:messageTextInput>, uiXML includes Struts elements for:

Showing Struts messages

If you've read the Handling Errors chapter, you're already familiar with the <messageBox> element and inline messaging. UIX doesn't include a special Struts <struts:messageBox> element, but it doesn't need to. Instead, a special <struts:dataScope> element fills this need.

The <struts:dataScope> element has all the features of a normal <dataScope>, so if you're using a <dataScope> element around your page already, just substitute the Struts version. In addition to the usual data-scope features, this will automatically find any ActionMessage or ActionError objects and add them to the list of UIX messages.

logon.uix with Struts messaging:


<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
      xmlns:struts="http://xmlns.oracle.com/uix/struts">
 <content>
 <struts:dataScope xmlns="http://xmlns.oracle.com/uix/ui"
            xmlns:data="http://xmlns.oracle.com/uix/ui">
   <contents>
    <pageLayout>
      <messages>
        <messageBox automatic="true"/>
      </messages>
  
      <contents>
        <struts:form name="theForm" method="post"
                      beanName="logonForm">
          <contents>
            <labeledFieldLayout>
              <contents>
                <struts:messageTextInput prompt="User" name="user"/>
                <struts:messageTextInput secret="true" prompt="Password"
                                  name="password">
                  <boundMessage select="pwd"/>
                </struts:messageTextInput>
                <submitButton ctrl:event="login" text="Log On"/>
              </contents>
            </labeledFieldLayout>
          </contents>
        </struts:form>
      </contents>
    </pageLayout>
   </contents>
  </struts:dataScope>
 </content>
 <handlers>
   <event name="login">
     <struts:action path="/do/logon"/>
   </event>
 </handlers>
</page>

welcome.uix with Struts messaging:


<page xmlns="http://xmlns.oracle.com/uix/controller"
      xmlns:ctrl="http://xmlns.oracle.com/uix/controller"
      xmlns:struts="http://xmlns.oracle.com/uix/struts">
 <content>
 <struts:dataScope xmlns="http://xmlns.oracle.com/uix/ui"
            xmlns:data="http://xmlns.oracle.com/uix/ui">
   <contents>
    <pageLayout title="Hi there!">
      <messages>
        <messageBox automatic="true"/>
      </messages>
    </pageLayout>
   </contents>
  </struts:dataScope>
 </content>
</page>

We've changed our <dataScope> to <struts:dataScope>, and added a standard <messageBox>. In the login page, we also added a <boundMessage> element inside of our password field; this will automatically add inline error messages. The "pwd" string here matches the string used in the validate() method of our LogonBean.