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

5. Introducing UIX Controller

This chapter introduces the UIX Controller, which provides the means to group web pages together and control page flow. You'll learn to use UIX Controller to handle events and send pages back to a browser. In short, you can use UIX Controller to tie all of the pieces of UIX together into an application.

This chapter contains the following sections:

Introduction

What is UIX Controller? UIX Controller is a web application framework based on the Model-View-Controller (MVC) design pattern. You've already been introduced to our preferred View layer - UIX. You've also seen the generic abstraction UIX provides for a Model layer - data binding - which can glue any model code into UIX. UIX Controller is the Controller layer, where events from the View modify the Model, and data from the Model controls the flow from one View to another. UIX Controller groups individual web pages together to form an application, and exports interfaces that allow the programmer to control the page flow.

Figure 5.1: A Web Application

Diagram of a simple application

Figure 5.1: A Web Application gives a schematic of a simple application. Here, 5 pages are grouped together to form the web application. The arrows represent links from one page to another. This can easily be done using HTML hyperlinks; why do we need to use UIX Controller? Often, when a user clicks on a link, the next page to display depends on data entered by the user or state on the server. For example, if the user has entered invalid data, the user should be redirected to an error page. It might also be necessary to process the data entered by the user before going to the next step. For example, in a shopping cart application the data the user has entered must be saved on the server before continuing. This is where UIX Controller comes in (See Figure 5.2: UIX Controller Page Flow). UIX Controller sits on the server examining each request. Depending on the data the user has entered, server-side state (such as a database), and the results returned from developer-provided handlers, UIX Controller can determine which page to display next (performing operations on the data in the process).

Figure 5.2: UIX Controller Page Flow

Diagram of UIX Controller page flow

UIX Controller is written in Java and is designed to work with Java Servlets. It may be used with its own standalone UIXServlet, or with some other servlet (including JSPs).

The technologies used to create each HTML page are independent of UIX Controller. UIX Controller can use JSPs to create HTML, or serve raw HTML files. UIX Controller can even serve pages that aren't HTML - GIF images, for example. But UIX Controller is especially designed to support and integrate with UIX Components and uiXML. It can automatically parse and cache UIX files, and make the UIX Components calls to turn UIX Components and uiXMLdocuments into HTML. In this chapter, UIX Controller will be used in conjunction with UIX Components to render UIX pages.

Simple UIX Controller and uiXML Pages

The following is a simple UIX Controller and uiXML page. All UIX Controller pages must start with a page element (note that this element is in the UIX Controller namespace). This page element has a content child element. UIX Components and uiXML elements are added as children to this UIX Controller content element (note the change in the default namespace from UIX Controller to UIX Components).

<page xmlns="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          text="UIX Components Header Bean">
  <contents>
   <stackLayout>
    <contents>
      UIX Components Stuff
     <link text="This is a link" destination="www.cnn.com"/>
    </contents>
   </stackLayout>
  </contents>
  </header>
</content>
</page>

And just for completeness here is an example with data binding:-

<page xmlns="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          xmlns:data="http://xmlns.oracle.com/uix/ui" 
          text="UIX Components Header Bean">
  <contents>
   <dataScope>
    <contents>
     <link data:text="text1@dat1" data:destination="dest1@dat1"/>
     <link data:text="text2@dat1" data:destination="dest2@dat1"/>
    </contents>
    <provider>
     <data name="dat1">
      <inline text1="Oracle" dest1="http://www.oracle.com"
              text2="Cnn" dest2="http://cnn.com" />
     </data>
    </provider>
   </dataScope>
  </contents>
  </header>
</content>
</page>

Strictly speaking, UIX Controller pages do not have to begin with the page element; they may begin with UIX Components elements. However, in order to make full use of the UIX Controller page flow system the page elements need to be used.

Running the Examples

If you would like to run these examples in your own installation of UIX, simply create some "filename.uix" file, add the contents to it, and place the file in your servlet engine's web applications directory (e.g., "demos") under the desired subfolder. For instance, if the servlet engine was OC4J and your file was placed in: /servletEngine/demos/somepath/filename.uix, you can then view the file by pointing your browser to http://myHostName:8888/somepath/filename.uix.

Introducing UIX Controller Events

Conventionally, static HTML pages are linked together with hardcoded HTML links. This means that the page flow is static - clicking on a link always sends you to the same HTML page. However, in most web applications it is necessary to dynamically choose a page to go to when a link is activated or a form is submitted. This decision may be made depending on the data submitted by the user or the state of the the server. While making this decision, state on the server might change.

UIX Controller makes it easy to indicate which links (or form submissions) require special processing (as described above). All client requests are received by UIX Controller, and by default UIX Controller serves up the page identified by the client request. However, UIX Controller recognizes a special signal known as an "event." Any link (or form submission) can be encoded to trigger an event. When a user clicks on such a link, UIX Controller executes application specific code to handle the event (maybe modifying server-side state in the process), and uses the results to determine which page to render in response to the event.

Events are the means by which applications interact with UIX Controller to modify state depending on user input. All server-side state modifications in the UIX Controller framework are performed by event handlers. Note that UIX Controller events occur on the server-side and should not be confused with client-side Javascript events.

A UIX Controller event has a name and zero or more parameters. An event name is a string and a parameter is a simple name and value pair (just like a regular form parameter), which associates the string name with the string value. The Java interface that encapsulates a UIX Controller event is PageEvent. The event name is returned by the getName() method (in this class) and parameter values can be obtained by calling the getParameter(String key) method (where key is the parameter name). The following section describes how events can be generated.

Generating Events

UIX Controller event URLs look like a form submission with an "event" form parameter. But for the XML interface, we've provided a convenient "ctrl:event" attribute that gives you a consistent syntax for encoding events onto UIX elements. Therefore, an event can be constructed as follows:

<!-- This is pageName.uix ---->
<page xmlns="http://xmlns.oracle.com/uix/controller"
     xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          text="Enter Your Name">
  <contents>
   <form name="form1">
    <contents>
     <messageTextInput name="txt1" prompt="Enter Name" text="YourName" />
     <submitButton ctrl:event="StoreName" text="Submit" />
    </contents>
   </form>
  </contents>
  </header>
</content>
</page>

The above UIX creates a page with a form with two form elements. The first is a messageTextInput element, and the other is a submitButton. Notice that the form element has no destination attribute. UIX Controller automatically uses the current page as the destination if none is set. When the submit button is pushed by the user the following URL is generated by the browser:

http://hostname:port/pageName?event=StoreName&txt1=YourName

UIX Controller will interpret this request as a triggered event. The event has name "StoreName" and has one parameter with name "txt1" and value "YourName" (or whatever the user typed into the text field). A later section will talk about how to process this generated event on the server, but before that let's see a few more ways to generate events. You do not need to use a SubmitButtonBean to generate events. You can do the following:

<form name="form1">
<contents>
 <messageTextInput name="txt1" prompt="Enter Name" text="YourName" />
 <formValue ctrl:event="StoreName" />
</contents>
</form>

The above generates a hidden form element which encodes an event named "StoreName". When this form is submitted an event will still be generated. The advantage of this method of firing events is that no matter what process is used to submit the form, the event will still be generated. This means that the form can be submitted by different submit buttons, by JavaScript or by other UIX Components beans, and still have the desired behaviour of triggering an event.

You don't have to use form elements to trigger an event. Any UIX element that supports an URL "destination" attribute also supports UIX Controller events. For example:

<link text="Trigger Event Foo"
      ctrl:event="Foo"
      destination="page?param1=value1&amp;param2=value2" />

Clicking on the above link triggers an event with the name "Foo" and with two event parameters; the first has name "param1" and value "value1", and the second has name "param2" with value "value2". Also note that in XML certain special characters need to be encoded; in the above example the '&' character had to be encoded as '&amp;'.

Incidentally, you might try to encode events directly, as in the following example. This is not recommended; it breaks the abstraction of page events, and will bypass some encoding mechanisms.

<!-- ******* Do not try to fire events like this!!  ********* -->
<formValue name="event" value="StoreName" />

Instead, use the following form:

<formValue ctrl:event="StoreName" />

Note that certain UIX Components elements generate events; for example, NavigationBarBean generates events with name "goto", TotalRowBean generates "total" events, SortableHeaderBean generates "sort" events, and AddTableRowBean generates "addRows" events. Of course, these can be handled without using UIX Controller, but they all integrate well with the UIX Controller framework.

Now that we know how to generate events let's learn how to handle them inside UIX Controller. First we'll learn about registering event handlers from UIX; then, we'll learn about writing event handlers in Java.

Registering Event Handlers

An event handler is a piece of code that processes events. UIX Controller needs to know which event handler to call to process a specific event. This is done by registering each event handler with UIX Controller. The following UIX file gives an example of how this is done in UIX.

<!-- ********* This is FirstPage.uix ********-->
<page xmlns="http://xmlns.oracle.com/uix/controller"
     xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          text="Enter Your Name">
  <contents>
   <form name="form1">
    <contents>
     <messageTextInput name="txt1" prompt="Enter Name" text="YourName" />
     <submitButton ctrl:event="StoreName" text="Submit" />
    </contents>
   </form>
  </contents>
 </header>
</content>
<handlers>
 <event name="StoreName">
  <method class="MyClass" method="handleStoreNameEvent" />
 </event>
</handlers>
</page>

The above code introduces a new child element of the UIX Controller page element, the handlers element. All handlers for the page are listed as children of this element. Currently, in UIX Controller there is only one type of handler and that is event handler.

Event handlers are defined by using the UIX Controller event element. This takes one attribute "name", which specifies which UIX Controller event the handler is capable of handling. In the above example the handler is registering to handle all "StoreName" events occuring on this page.

Inside the event element, we specify the handler code by adding an event handler element. One such element is the UIX Controller method element. This element takes the fully qualified Java class name and a method name and calls this method (using the Java Reflection API) to handle the respective event. In the above example the method MyClass.handleStoreNameEvent(...) will be called to handle any "StoreName" events occuring on this page.

Another way to reference event handler code is to use the UIX Controller instance element. In the following example the method MyClass.getStoreNameEventHandler is used to produce an event handler (instead of handling the event directly). The event handler that is returned by this method is used to actually handle the event (this will be discussed in more detail in the next section). Incidentally, the method attribute may be left out; UIX Controller will use the default method name sharedInstance, or call a default (no-argument) constructor as a last resort.

<handlers>
<event name="StoreName">
 <instance class="MyClass" method="getStoreNameEventHandler" />
</event>
</handlers>

The null UIX Controller element is useful for demonstration purposes where events are generated, but the event handling code hasn't been written yet. If UIX Controller cannot find an event handler for an event it receives, UIX Controller throws an UnhandledEventException to warn the developer of the mistake. The null event handler can be used in this case to route the user back to the same page without any server-side processing. Use it as in the following example:

<handlers>
<event name="someEvent">
 <null/>
</event>
</handlers>

UIX is fully extensible and can be enhanced with new elements to the uiXML to support registering event handlers other than these. See Extending UIX for more information about extending UIX.

At this point let's see other ways to register event handlers. The following event handler is registered under "*". This event handler will be called whenever an event is triggered on the page for an event name that has no explicit handler. This type of event handler is also known as the default event handler. In the following example the UIX Controller null handler is registered as the default handler. Any events that do not have corresponding handlers will be handled (and consequently ignored) by null (this hides UnhandledEventExceptions, but masks the coding error).

<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
 <event name="*">
  <null/>
 </event>
</handlers>
</page>

Event handlers can also be registered under the name "null". This keyword is special and causes the event handler to be registered as a null event handler (not to be confused with the UIX Controller null element). Null event handlers are called when no events are triggered on a page (ie: no event signal is encoded on the URL of the page).

<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
 <event name="null">
  ...
 </event>
</handlers>
</page>

When an event is generated, the component that triggered the event is typically identified by the "source" event parameter. Different components set the "source" parameter in different ways; for example, the table's name, and the hideShow's id attributes (we apologize for the inconsistency) become the "source" parameters for all events generated by those components. Please see the documentation for each component to determine how it sets the "source" parameter.

It is possible to register event handlers for events generated by specific components by using this "source" parameter. In the following example, a handler is registered to handle the "goto" event, generated by some "table1" component:

<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
 <event name="goto" source="table1">
   <!-- handle the "goto" event generated by "table1" -->
   ...
 </event>
 <event name="goto">
   <!-- handle all other "goto" events -->
   ...
 </event>
</handlers>
</page>

As the above example illustrates, an event handler registered by both name and source takes precedence over one which is registered by name alone.

Note that you can register multiple event handlers under different event names and register the same event handler to handle different events, as in the following example:

<page xmlns="http://xmlns.oracle.com/uix/controller" >
<handlers>
 <event name="goto" source="searchResults">
  <!-- This handler performs the record navigation for the 'searchResults'
        table -->
  <method class="MyClass" method="navigateResults" />
 </event>
 <event name="goto" source="navBar">
  <!-- This handler responds to the event generated by a navigationBar -->
  <method class="MyClass" method="navigatePageFlow" />
 </event>
 <event name="total">
  <method class="MyClass" method="submitHandler" />
 </event>
 <event name="submit">
  <method class="MyClass" method="submitHandler" />
 </event>
</handlers>
</page>

It is possible to register a single event handler for multiple events from multiple sources by using space-separated lists for the event name and source. In the following example, The 'handleHGridEvent' method handler is registered to handle four events ('expand', 'focus', 'expandAll' and 'collapseAll') for two components - 'hgrid1' and 'hgrid2':

<handlers>
<event name="expand focus expandAll collapseAll" 
        source="hgrid1 hgrid2">
 <!-- Handle the HGrid events for 'hgrid1' and 'hgrid2'
            components  -->
 <method class="MyClass" method="handleHGridEvent" />
</event>
</handlers>

You can also register a "global" EventHandler - one that will process an event with a given name, no matter which page it is fired from. Since these don't belong to any one page, they can't be registered from inside a page's UIX. Instead, they're registered with Java code (see oracle.cabo.servlet.AbstractPageBroker). In fact, all these registration mechanisms are available from outside of UIX with UIX Controller's Java API.

Now that we have seen the different ways event handlers can be registered let's see how a typical event is handled. This is discussed in the next section.

Handling Events

In the previous section the method MyClass.handleStoreNameEvent(...) was introduced as an example of an event handler for FirstPage.uix. The following is the Java source code for the class:

import javax.servlet.http.HttpSession;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;

public class MyClass
{
  /** This is handleStoreNameEvent version 1 */
  public static EventResult handleStoreNameEvent(BajaContext context, 
                                                 Page page,
                                                 PageEvent event)
  {
    String userName = event.getParameter("txt1");
    HttpSession session = context.getServletRequest().getSession(true);
    session.putValue("User Name", userName);
    Page nextPage = new Page("NextPage");
    return new EventResult(nextPage);
  }
}

The first thing to note is the method signature. The UIX Controller method element requires the following signature:

  public static EventResult methodName(BajaContext context, 
                                       Page page,
                                       PageEvent event) throws Throwable;

context is the current BajaContext (BajaContext objects encapsulate all the request-specific pieces of application state, and provide access to global services needed to write a Servlet-based application) . page is the page in which the event occurred (Page objects are explained in a later section), and event encapsulates the event that was fired (the example demonstrates how to access the event name and properties by calling getName() and getParameter(String) methods on PageEvent).

The method must return an EventResult which is used by the current PageFlowEngine (which will be discussed in a latter section) to determine which page to display next. The method may throw a java.lang.Throwable; UIX Controller will catch the Throwable and redirect to an error page.

Back to MyClass.handleStoreNameEvent; this method starts by obtaining the name entered by the user (recall that this was in a text input form element with name "txt1"), and saves this user name on the HttpSession. It then wraps up the next Page (in the page flow) in an EventResult object and returns it. The current PageFlowEngine will examine this result object and render whichever Page is contained in it (page flow engines will be discussed later).

Note that the event handler uses the getParameter method in PageEvent to access the parameters instead of using the same method in HttpServletRequest. (The HttpServletRequest can be obtained from the current BajaContext with the getServletRequest() method). When accessing parameters the PageEvent abstraction must be respected to make sure that file uploads work correctly, and that parameters are properly decoded into the correct character set. The PageEvent abstraction also lets you transform parameters - like adding, removing, or merging key/value pairs - in a central location, all without touching your event handling code.

Recall the example with the UIX Controller instance element. In that example the method MyClass.getStoreNameEventHandler() is called to produce an EventHandler. This static method (which is called just once when the page is parsed) must take no arguments and has a signature as in the following code fragment:

import oracle.cabo.servlet.event.EventHandler;

public class MyClass
{
  public static EventHandler getStoreNameEventHandler()
  {
   ...
  }
}

The EventHandler that is returned will be used to service the event. Not surprisingly, the EventHandler interface exports the following method (notice the similarity with the UIX Controller method event handler signature).

public interface EventHandler
{
  public EventResult handleEvent(
    BajaContext   context,
    Page          page,
    PageEvent     event) throws Throwable;
}

Incidentally, UIX Controller instance handlers execute slightly faster than corresponding method event handlers, because the instance handlers do not incur the introspection costs of the Java Reflection API. However, the difference is small enough that most applications shouldn't care.

Before we go into more complicated event handling it is necessary to describe the UIX Controller Page object. This is done in the next section.

UIX Controller Page Objects

In UIX Controller, Page objects are used to identify resources much like a URI. Each Page has a name (obtained by the getName() method) which uniquely identifies a UIX Controller page within an application. Every request sent to UIX Controller is automatically decoded to produce a Page object, which identifies the source of the request.

Pages can also have text properties set on them. These properties are simple string name-value pairs. Properties are set and retrieved with the setProperty(String key, String value) and getProperty(String key) methods. Property values don't always have to be strings; as long as a value can be encoded as a string it can be set as a property (see the Java API for Page).

Maintaining State with Page Properties

Page properties can be encoded directly onto an URL; the corresponding Page object produced by decoding that URL will have those properties already set on it. This means that property state can survive a trip to the client browser and back, and also be stored as a bookmarked link. Because of this feature page properties can be used to maintain user specific state without using server-side storage; the user state is stored on the URL as properties. The following example uses events and page properties to accumulate information about a user without using server-side state.

import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;

public class MyClass
{
  /** This is handleStoreNameEvent version 2 */
  public static EventResult handleStoreNameEvent(BajaContext context, 
                                                 Page page,
                                                 PageEvent event)
  {
    String userName = event.getParameter("txt1");
    Page nextPage = new Page("NextPage");
    nextPage.setProperty("UserName", userName);
    return new EventResult(nextPage);
  } 

  public static EventResult handleStoreAgeEvent(BajaContext context, 
                                                Page page,
                                                PageEvent event)
  {
    String userName = page.getProperty("UserName");
    String age = event.getParameter("age");
    Page nextPage = new Page("FinalPage");
    nextPage.setProperty("UserName", userName);
    nextPage.setProperty("Age", age);
    return new EventResult(nextPage);
  } 
}

Recall that the method MyClass.handleStoreNameEvent(...) was used as an event handler in FirstPage.uix. The above code is a bit different from the original handleStoreNameEvent(...) method. The first thing to note is that this new version doesn't use HttpSession. This version does not store state on the server-side. Instead, it encodes state on the URL using page properties.

This is achieved by creating a new Page to reference the next page in the page flow. The "UserName" property is set on this Page and the Page is returned as an EventResult

This new Page references NextPage.uix. The form element's destination attribute is not set (in this UIX file). Therefore, UIX Controller defaults it to reference the current Page which has the "UserName" property set. Hence, when this form is submitted the destination URL will have the "UserName" property encoded on it. Also, submitting the form triggers a "StoreAge" event which is handled by MyClass.handleStoreAgeEvent(...).

<!-- ********* This is NextPage.uix ******** -->
<page xmlns="http://xmlns.oracle.com/uix/controller"
     xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          xmlns:data="http://xmlns.oracle.com/uix/ui" 
          text="Enter Your Data">
  <contents>
   <form name="form1" >
    <contents>
     <messageTextInput name="age" prompt="Enter your age" />
     <submitButton ctrl:event="StoreAge"
              text="Submit" />
    </contents>
   </form>
  </contents>
  </header>
</content>
<handlers>
 <event name="StoreAge">
  <method class="MyClass" method="handleStoreAgeEvent" />
 </event>
</handlers>
</page>

Now consider the MyClass.handleStoreAgeEvent(...). The user name is accessed off the current page's properties. It is added (along with the "Age" property) to the next page in the flow and life continues. This is an example of using page properties to maintain client state without using memory on the server-side. One important drawback is that only state in text form can be maintained in this way. Another point to make is that since property names/values are not encrypted they shouldn't be used to store sensitive information (like passwords or credit card information). Also, URLs are limited in length - Internet Explorer 5 supports about 2000 bytes per URL, and others may support as few as 255 bytes per URL. That places a strong limit on how many page properties can be used at a time.

The above example uses an encoded Page URL as the destination of a form submission. It is sometimes necessary in UIX to use Page URLs as the destinations of other elements, for example, links. This can be done by using the ctrl:destination attribute, as in the following example:

<page xmlns="http://xmlns.oracle.com/uix/controller"
     xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<ctrl:content   xmlns="http://xmlns.oracle.com/uix/ui" >
 <link text="Go to the next page" ctrl:destination="NextPage" />
</ctrl:content>
</page>

Similarly, you can set the source attribute of an element to a Page URL by using ctrl:source as in the following code fragment:

<frame ctrl:source="Tree" name="TreeFrame" />

Unfortunately, at this time there is no way to encode page properties onto an URL inside of UIX alone. To obtain an URL with properties encoded in it, it is necessary to first data bind the attribute and use Java to encode the URL inside the data provider.

The encoding of a Page into an URL is obtained by using a PageEncoder. The current PageEncoder can be accessed by calling getPageEncoder() on the current BajaContext. Consider the following example:

import oracle.cabo.ui.RenderingContext;
import oracle.cabo.ui.data.DataObject;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.ui.BajaRenderingContext;

public class MyDataProvider
{
  public static DataObject getProductIDEncoder(RenderingContext context,
                                               String namespace,
                                               String name)
  {
    return _DATA_OBJECT;
  }

  private static final DataObject _DATA_OBJECT = new DataObject()
  {
    public Object selectValue(RenderingContext context, Object key)
    {
      BajaContext bajaContext = BajaRenderingContext.getBajaContext(context);
      Page page = new Page("Products"); // refers to Products.uix
      if (key != null)
        page.setProperty("productID", key.toString());
      String encoding = bajaContext.getPageEncoder().encodePageURL(page);
      return encoding;
    }
  };
}

The above code implements a UIX Components data provider. Its basic function is to encode the given key as a certain property on a Page and return that Page's encoding. Notice the method call BajaRenderingContext.getBajaContext(...). If UIX Components is running in a UIX Controller environment this method can be used to get at the current BajaContext (Similarly BajaRenderingContext.getPage(...) can be used to get at the current Page).

The following UIX code demonstrates how the above data provider is used to compute destinations for links. The first link is encoded with the Oracle bug database product code for UIX Components, while the second is encoded with the code for JEWT. The destination page in this example ("Products.uix") will be able to access the correct product ID from the Page object and render the appropriate data.

<dataScope xmlns="http://xmlns.oracle.com/uix/ui"
          xmlns:data="http://xmlns.oracle.com/uix/ui" >
<contents>
 <link text="Goto UIX Components product page"
     data:destination="768@productID" />
 <link text="Goto JEWT product page"
     data:destination="927@productID" />
</contents>
<provider>
 <data name="productID">
  <method class="MyDataProvider" method="getProductIDEncoder" />
 </data>
</provider>
</dataScope>

Maintaining State with Page State

Page properties have some excellent advantages for storing state. Because they're placed directly inside of URLs, you don't have to maintain any state on the server. You also don't have to worry about users hitting the Back and Next buttons, because the URL changes as the user hits those buttons.

However, the same reasons for these benefits are also the reasons for limitations of page properties. As discussed earlier, Page properties have to be Strings, should not contain sensitive information, and can run into limits on the length of URLs. You can solve all of these problems simply by putting values into an HttpSession. However, you then run into the classic Back/Next problem: when the user hits the Back button in a web browser, the server's state doesn't unwind at all. The user may think he's undone some actions, but the server doesn't behave accordingly. The Page State API addresses all of these issues.

Instead of storing values directly on the Page object or inside an HttpSession, you'll instead store these values inside a State object, then attach that State object to the Page. The UIX Controller will take care of managing that State object and will encode a short ID string into the URL so that the UIX Controller can relocate the State object.

State objects are created and managed by a StateManager. To get the current StateManager, call BajaContext.getStateManager(). Then, use getNewState() to get the State object:

  BajaContext context = ...;
  // Pass "true" to force the StateManager to be created
  StateManager stateManager = context.getStateManager(true);
  
  // Create a mutable State object.
  MutableState state = stateManager.getNewState();
 
  // Store some state - any Object, not just Strings
  state.putValue(someKey, someValue);

  // And attach the State
  page.setState(state);

State objects come in two forms: mutable and immutable. When State objects are initially created off the state manager, they are returned as MutableState objects, which let you set values before hooking it up to a Page. Then, on a subsequent request, you can get the State object from the page and retrieve values - but not change values. If you do need to change values in an immutable State object, you'll want to use the StateUtils.cloneState() function:

 
  // We want to modify a pre-existing State object;  clone it
  // into a MutableState object
  State oldState = page.getState();
  MutableState newState = StateUtils.cloneState(stateManager, oldState);  
  state.putValue(someKey, someValue);
  page.setState(newState);

Let's put the State API to use by changing our handleStoreNameEvent() code:

  /** This is handleStoreNameEvent version 3 */
  public static EventResult handleStoreNameEvent(BajaContext context, 
                                                 Page page,
                                                 PageEvent event)
  {
    String userName = event.getParameter("txt1");
    Page nextPage = new Page("NextPage");

    // Store the user name inside a State object instead of
    // as a page property
    StateManager stateManager = context.getStateManager(true);
    MutableState state = stateManager.getNewState();
    state.putValue("UserName", userName);
    nextPage.setState(state);

    return new EventResult(nextPage);
  } 

Applications should always gracefully handle the absence of a State object. For reasons to be explained shortly, a State object may become unavailable if the user hits the "Back" button too many times.

It may be useful to understand the implementation of this architecture. If you're not interested in how State objects are implemented, feel free to skip ahead. StateManagers are, by default, stored in an HttpSession. This means that they do require stateful behavior on the server. It also means that State objects cannot be shared between users, because each user has a separate StateManager and separate IDs. This also implies that, from a security standpoint, ensuring that HttpSession IDs cannot be spoofed is sufficient to ensure that State IDs cannot be spoofed.

The default StateManager implementation does not maintain all State objects ever created for a user. If it did, the longer a user was logged in, the more memory that user would require on the server. Instead, it maintains a fixed length, first-in-first-out queue of State objects. By default, this queue is 20 states long: this means that when the twenty-first State object is created for a given user, the first will be discarded. In practice, this means that a user can hit the Back button up to 19 times before running into problems. States are only placed on the queue once their ID has been requested, so State objects that are created and then immediately dropped don't actually count against this limit. The length of the queue can be adjusted by overriding BaseBajaContext.createStateManager().

At this point we have learned how to use events and page properties and state to perform operations and maintain state while moving between the pages of an application. The next section describes the UIX Controller PageFlowEngine that supports more complicated flows such as the enforcing of a login.

Page Brokers

A PageBroker is the UIX Controller's top-level interface. Each UIX application will have one instance of an object that implements the PageBroker interface, and it will act as the major point of entry into your applications logic. A PageBroker is responsible for the following operations:

Since it provides a first point of entry into your application, it also provides hooks for initialization and shutdown of the application, as well as hooks for the start and end of each request.

The default servlet (UIXServlet) provided by UIX can pick a PageBroker using a servlet configuration parameter: oracle.cabo.servlet.pageBroker. Here's a sample configuration if you're using a "WEB-INF/web.xml" file:

<servlet>
 <servlet-name>uix</servlet-name>
  <!-- We'll use the default UIX servlet -->
 <servlet-class>oracle.cabo.servlet.UIXServlet</servlet-class>
  <!-- And we'll use a custom page broker -->
 <init-param>
    <param-name>oracle.cabo.servlet.pageBroker</param-name>
    <param-value>yourPackage.YourPageBroker</param-value>
 </init-param>
</servlet>

While you're always free to implement PageBroker directly, UIX provides some useful basic implementations.

The oracle.cabo.servlet.AbstractPageBroker class is a useful abstract base class for anyone working with the UIX Controller. It provides a lot of default implementation for all pieces of a PageBroker. For example, it makes it trivial to support file upload; you need only override the doUploadFile() method, which will be called once for each file sent to your servlet.

In addition to providing a good basis for support of all the basic responsibilities of a PageBroker, the AbstractPageBroker adds one important abstraction to the UIX Controller, the PageDescription. While the UIX Controller allows complete separation of event handling and user interface as a pure Model-View-Controller architecture, many developers find it much more convenient to associate the event handling of a page with its user interface, and a PageDescription is just this coupling.

We could say a lot more about PageBrokers, but we'll wrap up this section by briefly describing perhaps the most useful PageBroker implementation provided with the UIX Controller, UIXPageBroker. This class (oracle.cabo.servlet.xml.UIXPageBroker in full) is the glue that ties your uiXML files into the UIX Controller. It supports finding your XML files, parsing them into PageDescriptions then caching the results, and provides all the hooks you'll need to customize the processing of UIX. If you're using uiXML with the UIX Controller, your application will almost certainly either use UIXPageBroker or subclass it.

Page Flow Engines

Page flow engines control page access and and page flow. They are responsible for determining which page must be rendered in response to a client browser request. The flow engines are also responsible for interpreting the EventResults produced by event handlers. Page flow engines are pluggable; a user may plug in his/her engine by implementing the oracle.cabo.servlet.event.PageFlowEngine interface. This section will introduce TrivialPageFlowEngine which is the default page flow engine included with UIX Controller.

The EventResult object

The TrivialPageFlowEngine interprets the object stored in an EventResult as a destination Page. This will be the Page to render in response to the event. Examples of using EventResult in this way were presented in earlier sections (eg: See MyClass.handleStoreNameEvent(...)).

The superclass (BasePageFlowEngine) also stores the EventResult as a property on the current BajaContext (all PageFlowEngines should do this). Use EventResult.getEventResult(BajaContext) to get at this result. For example, an EventResult can be accessed from a UIX Components data provider using the following code:

public static DataObject getDataObject(RenderingContext context,
                                       String namespace,
                                       String name)
{
  BajaContext bajaContext = BajaRenderingContext.getBajaContext(context);
  EventResult result = EventResult.getEventResult(bajaContext);
  ...
}

The above example illustrates a way to send data from a UIX Controller event handler to a UIX Components data provider. EventResults can also be used to directly exchange information between a UIX Controller event handler and a UIX file. A UIX file can access the current EventResult by using the DataObject with name "eventResult" in the "http://xmlns.oracle.com/cabo/controller" namespace. Each key/value in this DataObject maps to the corresponding EventResult property name/value. For example:

<page xmlns="http://xmlns.oracle.com/uix/controller"
     xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          xmlns:data="http://xmlns.oracle.com/uix/ui"
          text="Update The Counter">
  <contents>
   <form name="form1">
    <contents>
     <messageStyledText prompt="Counter is at" 
             data:text="counter@ctrl:eventResult" />
     <submitButton ctrl:event="inc"
              text="Increment Counter" />
    </contents>
   </form>
  </contents>
  </header>
</content>
<handlers>
 <event name="inc">
  <method class="MyClass" method="doIncCounter" />
 </event>
</handlers>
</page>

In the above example the "text" attribute of messageStyledText is data bound to the "counter" property on the current UIX Controller EventResult. When the user clicks on the submit button, an "inc" event is triggered and the following event handler is called:

import javax.servlet.http.HttpSession;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;

public class MyClass
{
  private static int _counter=0;

  // Multiple threads may call this method and mutate _counter.
  // Therefore, this method must be synchronized.
  public static synchronized EventResult doIncCounter(BajaContext context, 
                                                      Page page,
                                                      PageEvent event)
  {
    _counter++;
    EventResult result = new EventResult(page);
    result.setProperty("counter", new Integer(_counter));
    return result;
  }
}

This event handler increments the internal _counter variable and sets it as a property on the EventResult. This result points to the current Page. This Page identifies the same page the event was triggered on and so the same page is displayed again; however, this time the counter has incremented.

It's also worth noting that EventResult objects can have generic Java Object name/value pairs as properties (compare with Page objects that can only have string name/value pairs). But the properties of an EventResult are only available until the page finishes rendering, while the properties of a Page object will still be available to the next event handler.

Redirection

The Page returned in an EventResult may be different to the Page that triggered the event. However, it is important to know that the client-side browser has no way of knowing that the pages have been switched. Here is an example:

public static EventResult handleEvent(BajaContext context, 
                                      Page sourcePage,
                                      PageEvent event)
{
  // Render NextPage.uix in response to this event
  Page nextPage = new Page("NextPage");
  return new EventResult(nextPage);
}

In the above example, the URL displayed by the browser will be the URL for sourcePage and not the URL for nextPage. If it is important that the URL displayed by the browser agrees with the page rendered by the server, then use the class oracle.cabo.servlet.util.RedirectUtils, to force the browser to redirect to the correct page (note that this will incur the cost of a second round-trip request to the server). The following is an example:

public static EventResult handleEvent(BajaContext context, 
                                      Page sourcePage,
                                      PageEvent event)
{
  Page nextPage = new Page("NextPage"); 
  // Force the browser to redirect to NextPage.uix in response to this
  // event
  Page redirectPage = RedirectUtils.getRedirectPage(context, nextPage);
  return new EventResult(redirectPage);
}

Login pages with TrivialPageFlowEngine

A very useful feature of a page flow engine is to control access to certain pages. For example, a client may not be allowed to view certain pages until he/she has authenticated him/herself to the server. All client requests, with or without events, flow through the PageFlowEngine. So all access control can be centralized here, which is much better than distributing this responsibility among many pages. An implementation of this interface can integrate with any login or access control technology, like Oracle's single-sign-on server.

TrivialPageFlowEngine provides a very basic implementation of a login page flow. Calling the method setLoginPage(Page loginPage) on TrivialPageFlowEngine (or setting the servlet configuration parameter oracle.cabo.servlet.loginPage) indicates to this engine that access control is enabled; any request from a client which has not been authorized will cause the loginPage to be rendered regardless of the URL that was originally requested.

After the loginPage has been rendered and the client authenticates him/herself TrivialPageFlowEngine will redirect to the page that corresponds to the original request URL.

How does TrivialPageFlowEngine know if a client has been authenticated? It checks for an HttpSession value at a certain key. If the value exists, the corresponding client is deemed authenticated. The key that is used to get at this value is set using the method setLoggedInKey(String) in TrivialPageFlowEngine (or by setting the servlet configuration parameter oracle.cabo.servlet.loggedInKey).

A simple example is presented here. First the appropriate servlet configuration parameters must be set. The example presented here uses a web.xml file, supported by any 2.2 Servlet API engine (e.g. Tomcat). So, the servlet configuration parameters are set in the following manner:

<init-param>
  <param-name>oracle.cabo.servlet.loginPage</param-name>
  <param-value>Login</param-value>  <!-- corresponds to Login.uix -->
</init-param>

<init-param>
  <param-name>oracle.cabo.servlet.loggedInKey</param-name>
  <param-value>MyClass.isLoggedIn</param-value>
</init-param>

The above configuration sets Login.uix as the login page on TrivialPageFlowEngine and sets the key to be the string "MyClass.isLoggedIn". Now UIX Controller will check for this key in every HttpSession for every client request. If the value associated with this key is null, or no HttpSession has been created, then the client will be temporarily redirected to the following login page:

<!-- This is Login.uix ---->
<page xmlns="http://xmlns.oracle.com/uix/controller"
        xmlns:ctrl="http://xmlns.oracle.com/uix/controller">
<content>
 <header xmlns="http://xmlns.oracle.com/uix/ui" 
          text="Login">
  <contents>
   <!-- method=post is used here so that the password
             is not displayed in the URL -->
   <form name="form1" method="post">
    <contents>
     <messageTextInput name="username" prompt="Enter Name" />
     <messageTextInput name="password" prompt="Enter Password" 
                   secret="true" />
     <submitButton ctrl:event="login" text="Login" />
    </contents>
   </form>
  </contents>
  </header>
</content>
</page>

After entering his/her username and password the user clicks on the submit button which promptly triggers a "login" event which is handled by the following event handler. Notice that if the password is correct some object is associated with the login key "MyClass.isLoggedIn" on the HttpSession. This is an indication to the TrivialPageFlowEngine that this client has been authenticated, and will cause the engine to render the original page requested by the user.

import javax.servlet.http.HttpSession;
import oracle.cabo.servlet.Page;
import oracle.cabo.servlet.BajaContext;
import oracle.cabo.servlet.event.PageEvent;
import oracle.cabo.servlet.event.EventResult;

public class MyClass
{
  public static EventResult handleLogin(BajaContext context, 
                                        Page page,
                                        PageEvent event)
  {
    String userName = event.getParameter("username");
    String password = event.getParameter("password");
    if (password.equals(getPasswordForUser(userName)))
    {
      HttpSession session = context.getServletRequest().getSession(true);
      session.putValue("MyClass.isLoggedIn",
                  new Object());  /** this will be some object that holds
                                      all of this user's server-side state */
    }
    return null;
  } 
}

Using HttpSession in UIX Controller

In order to maintain client state on the server, many applications make use of the HttpSession object. Servers need to know which HttpSession is associated with which client and the usual way this is done is by setting a cookie on the client browser to identify the client to the server.

URL encoding is an alternate technique of identifying a client by placing an ID on every URL on a page, so that when the user clicks on a link the URL sent back to the server has the ID in it helping the server identify the client and the associated HttpSession. This alternate technique is used with browsers that do not support cookies (or have cookie support disabled).

The URLEncoder used in UIX Controller automatically encodes servlet session IDs on URLs when the client browser does not support cookies. UIX Controller performs a test to make sure that URL encoding is necessary; URL encoding would be necessary only if the client browser has no cookie support and the user application requires HttpSessions. Therefore, in order for URL encoding to work, the user must call getSession(true) on javax.servlet.http.HttpServletRequest before any rendering takes place; ie: before the start of a render cycle (if HttpSession is created in the middle of a render cycle this may result in URLs rendered at the beginning of the cycle to not be encoded).

To ensure that URL encoding works as intended the following two rules must be adhered to:

  1. Data providers must never create sessions. Data provider code is called during a render cycle; therefore, do not call getSession(true) on HttpServletRequest from within data provider code. If a session is needed call getSession(false) and use default values if the HttpSession is null.
  2. Event handler code may create sessions (if needed) by calling getSession(true). This is safe because event handlers are called at the very beginning of a render cycle.

If you absolutely need an HttpSession on all pages, you can explicitly code your application to force one to be created. The simplest technique is subclassing your PageBroker. Their requestStarted() method is a perfect place for this:

  public class YourPageBroker extends SomeBuiltInPageBroker
  {
    public void requestStarted(BajaContext context)
    {
      // Force the creation of an HttpSession
      context.getServletRequest().getSession(true);
    }
  }

Because State objects rely on an HttpSession, it's also illegal to create State objects while rendering. Consequently, DataProviders should never create State objects. They must be created either during event handling or immediately prior to rendering.

UIX Controller uiXML Extensions

UIX Controller provides some convenient extensions to UIX, so that important objects pertinent to the UIX Controller framework may be accessed from within a UIX file. These extensions are grouped into two categories, Data Providers and Attributes. Note that in all of the examples in this chapter, the XML namespace prefix "ctrl" maps to the UIX Controller namespace (http://xmlns.oracle.com/uix/controller) and the prefix "data" maps to the UIX Components namespace (http://xmlns.oracle.com/uix/ui).

Data Providers

This section lists the data objects implemented by UIX Controller data providers. All these data objects are in the UIX Controller namespace, and can be accessed from UIX like this data:attribute="key@ctrl:bajaDataObject". The following is an example of setting the text of a messageTextInput element to be the username property of the current UIX Controller Page.

<messageTextInput prompt="Your Name"
   data:text="username@ctrl:page" />

Attributes

The following is a list of UIX Controller attributes that can be used in a UIX file.

UIX Controller: How the Parts Fit Together

By now, we hope you've gotten a good idea of how the pieces of UIX Controller work, but you're probably wondering how all of these pieces fit together - who calls the page flow engine? How are these UIX pages actually getting turned into HTML, and how would UIX Controller use a JSP or Java code to create HTML? This section will help you to answer those questions, and introduce you to some of the Java APIs that make up UIX Controller.

Figure 5.3: UIX Controller Overview

Diagram overview of UIX Controller

Any request begins with the client browser issuing a request to the web server (for example, Apache), which passes it to the servlet engine (for example, Tomcat or JServ), which finally passes it to a servlet (or JSP). The servlet recognizes that this request is a UIX Controller request, encapsulates the request in a BajaContext, and forwards it to a small UIX Controller class called PageBrokerHandler.

This handler converts the request URI into a UIX Controller Page object and PageEvent and forwards it to a PageBroker. It calls the page broker twice: once for event processing, and a second time for page rendering. (This explicit division forces developers to separate their controller and view code.) A subtle but important point - the event processing phase happens even if there is no event. This makes access control (like forwarding to a login page) consistent across all requests.

All UIX Controller applications have one PageBroker. It's the first point of entry into UIX Controller for each request, and all persistent UIX Controller objects hang off of the PageBroker. It's the PageBroker's responsibility to find EventHandlers and PageRenderers for each page (more on that latter item below). How it find these handlers and renderers is up to the page broker - our UIXPageBroker class looks for UIX files.

For the event processing phase, the PageBroker sends the the Page, PageEvent, and the correct EventHandler to its PageFlowEngine to determine which Page to render in response to the client request. This response Page is returned to the PageBrokerHandler. The PageFlowEngine is free to ignore the event and event handler - if, for instance, the end user shouldn't have access to a page - or can use the handler to get an EventResult. But it always has the final word on what page will be rendered.

For, the rendering phase, the PageBrokerHandler passes the Page to be rendered to the PageBroker. The PageBroker find a PageRenderer, and asks it to render the page. How it renders is up to the specific implementation of PageRenderer. Our implementations include renderers that:

  1. Render UIX into HTML
  2. Render a UIX Components UINode into HTML
  3. Forward to a local JSP or Servlet
  4. Redirect to any URL
  5. Upload a static file

... but an implementation could dynamically create a GIF image, run the results of an XSLT transform, or anything else you can imagine. Once the PageRenderer finishes, UIX Controller is done, and processing is complete until the next browser request comes along.

How a UIX Controller uiXML Application is Built

By now, you've been introduced to most of the pieces of a UIX Controller application, but you may still be missing the "big picture". How can a team use these pieces to put together an entire application? What roles will individual team members play, and how is the application architected? It's time to give you that big picture, the view from 30,000 feet.

A UIX Controller uiXML application can be built by three types of developers:

  1. User interface developers
  2. Model developers
  3. Controller developers

The user interface developers are responsible for creating the View of an application. Their output is the UIX pages that define the user interface.

The model developers are responsible for defining the Model of an application. Their output is Java classes that expose the back-end. Those classes should be free of any trace of UIX, so they can be reused for other applications.

Finally, the controller developers. These are the developers that glue the user interface and model together. They'll code UIX Components data providers that convert the model to DataObjects, and code UIX Controller event handlers that update the model and handle page flow. And they'll code some custom data providers for the parts of pages that don't belong in the model layer, like error handling. This is the layer where UIX Controller lives.

Of course, this three-way division is a simplification of reality. Teams will need internationalization developers and translators. Some developers will work on more than one layer - for instance, most controller developers will need to modify the UIX to support data binding. But this is a good overview of how to divide UIX development among the members of a team.