Skip navigation header
Oracle ADF UIX Developer's Guide Go to Table of Contents
Contents
Go to previous page
Previous
Go to next page
Next

4. Using a Controller in ADF UIX

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

Note: Earlier releases of Oracle ADF UIX included and promoted the UIX servlet technology, originally called "the UIX Controller," for building J2EE web applications. It was recommended as the page flow engine for building full-featured UIX applications based on the Model-View-Controller design pattern. In JDeveloper 10g, the UIX servlet's role as a page flow engine is de-emphasized in deference to Struts. Other pieces of the ADF UIX servlet will continue to be recommended, as they have no equivalent in Struts: event handling, partial page utilities, etc. This Guide continues to refer to the UIX servlet's role as the controller in a web application. Future versions will include updated documentation about features from the UIX Servlet that remain as core features in the UIX technology.

This chapter contains the following sections:

Introduction

What is the UIX servlet? It is web application technology based on the Model-View-Controller (MVC) design pattern. You've already been introduced to the UIX view layer - the UIX components technology. You've also seen the generic abstraction UIX provides for a Model layer - data binding - which can glue any model code into UIX. The UIX servlet 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. The UIX servlet groups individual web pages together to form an application, and exports interfaces that allow the programmer to control the page flow.

Figure 4-1: A Web Application

Diagram of a simple application

Figure 4-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 the UIX servlet? 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 the UIX servlet comes in (See Figure 4-2: UIX Servlet Page Flow). The UIX servlet 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, the UIX servlet can determine which page to display next (performing operations on the data in the process).

Figure 4-2: UIX Servlet Page Flow

Diagram of UIX Servlet page flow

UIX Servlet 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 the UIX servlet. The servlet can use JSPs to create HTML, or serve raw HTML files. The UIX servlet can even serve pages that aren't HTML - GIF images, for example. But the UIX servlet is especially designed to support and integrate with UIX components and UIX XML. It can automatically parse and cache UIX files, and make the UIX components calls to turn UIX components and UIX XML documents into HTML. In this chapter, the UIX servlet will be used in conjunction with UIX components to render UIX pages.

Simple UIX Servlet and UIX XML Pages

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

<page xmlns="http://xmlns.oracle.com/uix/controller"
      expressionLanguage="el">
<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"
          text="UIX Components Header Bean">
  <contents>
   <dataScope>
    <contents>
     <link text="${uix.data.dat1.text1}" destination="${uix.data.dat1.dest1}"/>
     <link text="${uix.data.dat1.text2}" destination="${uix.data.dat1.dest2}"/>
    </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 servlet 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 servlet 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 Servlet 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 Servlet makes it easy to indicate which links (or form submissions) require special processing (as described above). All client requests are received by the UIX servlet, and by default the UIX servlet serves up the page identified by the client request. However, the UIX servlet 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, the UIX servlet 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 the UIX servlet to modify state depending on user input. All server-side state modifications in the UIX servlet framework are performed by event handlers. Note that UIX servlet events occur on the server-side and should not be confused with client-side Javascript events.

A UIX servlet 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 servlet 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 Servlet event URLs look like a form submission with an event form parameter. Typically, you would use submitButtons to construct events as in the following example:

<!-- 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 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. The UIX servlet 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 Servlet 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 don't have to use submitButton elements to trigger an event. Any UIX element that supports an URL "destination" attribute also supports UIX servlet events. For example:

<link text="Trigger Event Foo"
      destination="${ctrl:eventUrl(uix,'Foo')}" />

Clicking on the above link triggers an event with the name Foo. The eventUrl function is described in detail later.

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 (the encodeParameter function is described in a later section):

<formValue name="${ui:encodeParameter(uix,'event')}"
           value="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 the UIX servlet, but they all integrate well with the UIX servlet framework.

Now that we know how to generate events let's learn how to handle them inside the UIX servlet. 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. The UIX servlet needs to know which event handler to call to process a specific event. This is done by registering each event handler with the UIX servlet. 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 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 servlet page element, the handlers element. All handlers for the page are listed as children of this element. Currently, in the UIX servlet there is only one type of handler and that is event handler.

Event handlers are defined by using the UIX servlet event element. This takes one attribute name, which specifies which UIX servlet 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 servlet 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 servlet 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; the UIX servlet 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 servlet element is useful for demonstration purposes where events are generated, but the event handling code hasn't been written yet. If the UIX servlet cannot find an event handler for an event it receives, the UIX servlet 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 UIX XML to support registering event handlers other than these. See Extending ADF 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 servlet 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 servlet 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 servlet'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 servlet 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; the UIX servlet 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 Servlet 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 servlet method event handler signature).

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

Incidentally, UIX servlet 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 servlet Page object. This is done in the next section.

UIX Servlet Page Objects

In the UIX servlet, 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 servlet page within an application. Every request sent to the UIX servlet 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, the UIX servlet 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"
          text="Enter Your Data">
  <contents>
   <form name="form1" >
    <contents>
     <messageTextInput name="age" prompt="Enter your age" />
     <submitButton 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:pageUrl function, 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"
        destination="${ctrl:pageUrl(uix,'NextPage')}" />
 </ctrl:content>
</page>

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

<frame source="${ctrl:pageUrl(uix,'Tree')}" name="TreeFrame" />

If you want to set up some event with some combination of page properties and parameters, you need to use a <ctrl:pageURL> BoundValue element. For example:


<link text="Approve and go to next" >
 <boundAttribute name="destination">
  <ctrl:pageURL event="approve">
   <ctrl:properties>
    <ctrl:property key="uid" value="1234"/>
    <ctrl:property key="gid" value="643"/>
   </ctrl:properties>
   <ctrl:parameters>
    <ctrl:parameter key="nextStep=" value="shipping"/>
   </ctrl:parameters>
  </ctrl:pageURL>
 </boundAttribute>
</link>

You can also 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 pages built with UIX components are running in a UIX servlet 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">
<contents>
 <link text="Goto UIX Components product page"
       destination="${uix.data.productID.768}" />
 <link text="Goto JEWT product page"
       destination="${uix.data.productID.927" />
</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 servlet will take care of managing that State object and will encode a short ID string into the URL so that the UIX servlet 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 Servlet PageFlowEngine that supports more complicated flows such as the enforcing of a login.

Page Brokers

A PageBroker is the UIX servlet'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 servlet. 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 servlet, the PageDescription. While the UIX servlet 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 servlet, UIXPageBroker. This class (oracle.cabo.servlet.xml.UIXPageBroker in full) is the glue that ties your UIX XML files into the UIX servlet. 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 UIX XML with the UIX servlet, 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 the UIX servlet.

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 servlet event handler to a UIX components data provider. EventResults can also be used to directly exchange information between a UIX servlet event handler and a UIX file. A UIX file can access the current EventResult by using the implicit variable uix.eventResult. Each key/value in this Map 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"
          text="Update The Counter">
  <contents>
   <form name="form1">
    <contents>
     <messageStyledText prompt="Counter is at"
                        text="${uix.eventResult.counter}" />
     <submitButton 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 servlet 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 the UIX servlet 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 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 the UIX servlet

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 the UIX servlet automatically encodes servlet session IDs on URLs when the client browser does not support cookies. The UIX servlet 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 Servlet UIX XML Extensions

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

Implicit Variables

This section lists the implicit variables implemented by UIX servlet data providers. The following is an example of setting the text of a messageTextInput element to be the username property of the current UIX servlet Page.

<messageTextInput prompt="Your Name"
   text="uix.pageProp.username" />

Functions

The following is a list of UIX servlet functions that can be used in a UIX file.

Uploading Files Using the UIX Servlet

The UIX servlet also supports handling file uploads. If you're new to Servlets, it may come as a surprise that Servlets - which are so good at automatically handling GET and POST HTTP requests - don't do a thing with file uploads. Instead, the Servlet API forces this responsibility onto each developer. Thankfully, you don't have to deal with parsing the multipart/form-data request format; we've taken care of that for you.

Triggering file uploads from your UI is easy. You only need to do two things:

  1. Set usesFileUpload to true on your <form>
  2. Add <fileUpload> or <messageFileUpload> elements inside the form.

For most developers, the most difficult issue with understanding file uploads is that the parameters of the file upload are intermixed with the files that are uploaded. The only way to extract the parameters is to read through the request - but that also forces you to handle the uploaded files right then and there. This has one very important implication: file uploads cannot be processed with an event handler. Once a PageEvent been created, all the parameters have been processed, and that means the uploaded files have already been passed over. So, you need a different approach for dealing with file uploads - the FileUploadManager.

A UIX PageBroker has one and only one FileUploadManager. You can register a manager using either:

While you can subclass FileUploadManager, almost all UIX developers will want to subclass the BaseFileUploadManager. Your subclass only needs to override a single method:


  protected abstract String doUploadFile(
    BajaContext       context,
    Page              page,
    MultipartFormItem item) throws IOException;

UIX calls doUploadFile() once for each file. The MultipartFormItem interface provides methods for reading the file and discovering the filename and MIME type. The return value of doUploadFile will be merged into the PageEvent that is created once all file uploads are complete. The name of the parameter will be the value of MultipartFormItem.getName() (not to be confused with MultipartFormItem.getFilename()), which is in turn derived from the name of the <fileUpload> element.

Example:

As a quick example, here's a FileUploadManager implementation that will always upload files into a common directory. The directory is itself configured using a servlet configuration parameter (if omitted, the files are stored in the temporary directory). After writing each file, the full path and filename of the file is returned, so any registered EventHandler can locate the just-written file.


    package yourPackage;

    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.OutputStream;

    import javax.servlet.ServletConfig;

    import oracle.cabo.servlet.BajaContext;
    import oracle.cabo.servlet.Page;

    import oracle.cabo.servlet.io.BaseFileUploadManager;

    import oracle.cabo.share.util.MultipartFormItem;

    public class YourFileUploadManager extends BaseFileUploadManager
    {
      public void init(
  ServletConfig config)
      {
  // Get the directory for an initialization parameter
  _directory = config.getInitParameter("UploadDirectory");
      }

      protected String doUploadFile(
  BajaContext       context,
  Page              page,
  MultipartFormItem item) throws IOException
      {
  File         file;
  if ((_directory != null) && !"".equals(_directory))
  {
    file = new File(_directory, item.getFilename());
  }
  else
  {
    // If we haven't been told where to save the file,
    // save it as a temporary file.
    file = File.createTempFile(item.getFilename(), null, null);
  }

  // Write out the file
  OutputStream stream = new BufferedOutputStream(
         new FileOutputStream(file));
  item.writeFile(stream);
  stream.close();

  // Return the file name, so that the file can be found after
  //
  return file.getName();
      }

      private String _directory;
    }

This is just an example, so it's missing some features. For example, it never cleans up after itself or places any limits on the size of the uploaded files.

If you aren't using the UIX servlet, or you want to handle file uploads on your own, you'll want to use the oracle.cabo.share.util.MultipartFormHandler class. You'll lose the convenient hooks into the UIX servlet architecture, but keep all of the parsing code and the MultipartFormItem API.

If you're using BC4J, UIX includes a FileUploadManager that supports interMedia; see the Working with Media in ADF UIX chapter of the developer's guide for more information.

UIX Servlet: How the Parts Fit Together

By now, we hope you've gotten a good idea of how the pieces of the UIX servlet 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 the UIX servlet 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 Servlet.

Figure 4-3: UIX Servlet Overview

Diagram overview of UIX Servlet

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 servlet request, encapsulates the request in a BajaContext, and forwards it to a small UIX servlet class called PageBrokerHandler.

This handler converts the request URI into a UIX servlet 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 servlet applications have one PageBroker. It's the first point of entry into UIX Servlet for each request, and all persistent UIX servlet 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, the UIX servlet is done, and processing is complete until the next browser request comes along.

How a UIX Servlet UIX XML Application is Built

By now, you've been introduced to most of the pieces of a UIX servlet 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 servlet UIX XML 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 servlet 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 the UIX servlet 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.