Oracle ADF UIX Developer's Guide | ![]() Contents |
![]() Previous |
![]() Next |
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:
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
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
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.
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.
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
.
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.
UIX Servlet event URLs look like a form submission with an event
form
parameter. Typically, you would use submitButton
s 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.
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.
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.
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.
Page
s 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
).
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>
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.
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 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 EventResult
s 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.
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 PageFlowEngine
s 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. EventResult
s 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.
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);
}
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;
}
}
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 HttpSession
s. 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:
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.
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 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).
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" />
uix.pageProp
Page
. Each key maps to a Page
property name and returns the corresponding value.
uix.pageState
State
associated with the current Page
. Each key maps to a State
select key and returns the corresponding value.
uix.eventResult
EventResult
. Each key maps to
an EventResult
property name and returns the
corresponding value. Examples of using this data object can be found in the Page Flow Engine section.
sessionScope
javax.servlet.http.HttpSession
object
associated with the current user. Each key maps to an attribute name and
returns the respective value. If the session has not been created, then every
key will return null. Please note the section Using HttpSession
in the UIX servlet.
requestScope
javax.servlet.ServletRequest
. Each key maps to an
attribute name and returns the respective value. This doesn't
provide access to a ServletRequest's
parameters - it only provides access to its attributes.
applicationScope
This is the current javax.servlet.ServletContext
, which is global to
an entire web application, and shared among all users. Each key maps
to an attribute name and returns the respective value.
The following is a list of UIX servlet functions that can be used in a UIX file.
ctrl:destination
(UIImplicitObject uix, String pageName, String eventName)
<!-- Home.uix?event=init -->
<link text="Cancel"
destination="${ctrl:destination(uix,'Home','init')}" />
ctrl:page
(UIImplicitObject uix, String pageName)
Page
with the given pageName:
<frame source="${ctrl:pageUrl(uix,'Home')}" /> <!-- Home.uix -->
ctrl:eventUrl
(UIImplicitObject uix, String eventName)
<link text="Add Item"
destination="${ctrl:eventUrl(uix,'addItem')}" />
<button text="Remove" destination="${ctrl:eventUrl(uix,'delItem')}" />
ctrl:parsePage
(UIImplicitObject uix, String pageName)
UINode
tree
created by parsing a UIX file. This can be used with a UIX components include
element to include a UIX file, as in the
following example:
<!-- includes TabBar.uix -->
<include node="${ctrl:parsePage(uix,'TabBar')}" />
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:
usesFileUpload
to true
on your <form>
<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:
AbstractPageBroker.setFileUploadManager()
oracle.cabo.servlet.io.FileUploadManager
servlet
initialization parameter:
<init-param>
<param-name>oracle.cabo.servlet.io.FileUploadManager</param-name>
<param-value>yourPackage.YourFileUploadManager</param-value>
</init-param>
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.
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.
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
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:
... 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.
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:
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.
Copyright © 2001, 2004, Oracle.
All rights reserved.