Oracle® Application Development Framework Developer's Guide For Forms/4GL Developers 10g (10.1.3.1.0) Part Number B25947-01 |
|
|
View PDF |
Most real-world business applications need to support multi-step user tasks. Modern sites tend to use a "step-by-step" style user interface to guide the end user through a logical sequence of pages to complete these tasks. When the task is done, the user can save or cancel everything as a unit.
In a typical search-then-edit scenario, the end user searches to find an appropriate row to update, then may open several different pages of related master/detail information to make edits before deciding to save or cancel his work. Consider another scenario where the end user wants to book a vacation online. The process may involve the end user's entering details about:
One or more flight segments that comprise the journey
One or more passengers taking the trip
Seat selections and meal preferences
One or more hotel rooms in different cities
Car they will rent
Along the way, the user might decide to complete the transaction, save the reservation for finishing later, or abandoning the whole thing.
It's clear these scenarios involve a logical unit of work that spans multiple web pages. You've seen in previous chapters how to use JDeveloper's JSF page navigation diagram to design the page flow for these use cases, but that is only part of the puzzle. The pending changes the end user makes to business domain objects along the way — Trip
, Flight
, Passenger
, Seat
, HotelRoom
, Auto
, etc. — represent the in-progress state of the application for each end user. Along with this, other types of "bookkeeping" information about selections made in previous steps comprise the complete picture of the application state.
While it may be easy to imagine these multi-step scenarios, implementing them in web applications is complicated by the stateless nature of HTTP, the hypertext transfer protocol. Figure 28-1 illustrates how an end user's "visit" to a site comprises a series of HTTP request/response pairs. However, HTTP affords a web server no way to distinguish one user's request from another user's, or to differentiate between a single user's first request and any subsequent requests he makes while interacting with the site. The server gets each request from any user always as if it were the first (and only!) one they make.
But even if you've never implemented your own web applications before, since you've undoubtedly used a web application to buy a book, plan a holiday, or even just read your email, it's clear that a solution must exist to distinguish one user from another.
As shown in Figure 28-2, the technique to recognize an ongoing sequence of requests from the same end user over the stateless HTTP protocol involves a unique identifier called a "cookie.." A cookie is a name/value pair that is sent in the header information of each HTTP request the user makes to a site. On the initial request made by a user, the cookie is not part of the request. The server uses the absence of the cookie to detect the start of a user's session of interactions with the site, and it returns a unique identifier to the browser that represents this session for this user. In practice, the cookie value is a long string of letters and numbers, but for simplicity's sake, assume that the unique identifier is a letter like "A" or "Z" that corresponds to different users using the site.
Web browsers support a standard way of recognizing the cookie returned by the server, along with a way of identifying what site sent the cookie and how long it should remember the cookie value. On each subsequent request made by that user, until the cookie "expires" the browser sends the cookie along in the header of the request.
The server uses the value of the cookie to distinguish the requests made by different users. A cookie that expires when you close your browser is known as a "session cookie," while other cookies set to live beyond a single browser session might expire in a week, a month, or a year from when they were first created.
J2EE-compliant web servers provide a standard server-side facility called the HttpSession
that allows a web developer to store Java objects related to a particular user's session as named attribute/value pairs. An object placed in this session Map
on one request can be retrieved by the developer while handling a subsequent request during the same session. The session stays "active" while the user continues to send new requests within the timeframe configured by the session-timeout element in the web.xml
file. The default session length is 35 minutes.
The HttpSession
facility is an ingredient in most application state management strategies, but it can present performance and reliability problems if not used judiciously. First, since the session-scope Java objects you create are held in the memory of the J2EE web server, the objects in the HTTP session are lost if the server should fail.
As shown in Figure 28-3, one way to improve the reliability is to configure multiple J2EE servers in a cluster. By doing this, the J2EE application server replicates the objects in the HTTP session for each user across multiple servers in the cluster so that if one server goes down, the objects exist in the memory of the other servers in the cluster that can continue to handle the users requests. Since the cluster comprises separate servers, replicating the HTTP session contents among them involves broadcasting the changes made to HTTP session objects over the network.
You can begin to see some of the performance implications of overusing the HTTP session:
The more active users, the more HTTP sessions will be created on the server.
The more objects stored in each HTTP session, the more memory you will need.
In a cluster, the more objects in each HTTP session that change, the more network traffic will be generated to replicate the changed objects to other servers in the cluster.
At the outset, it would seem that keeping the number of objects stored in the session to a minimum addresses the problem. However, this implies leveraging an alternative mechanism for temporary storage for each user's pending application state. The most popular alternatives involve saving the application state to the database between requests or to a file of some kind on a shared file system.
Of course, this is easier said than done. A possible approach involves eagerly saving the pending changes to your underlying database tables and committing the transaction at the end of each HTTP request. But this idea has two key drawbacks:
Your database constraints might fail.
At any given step of the multi-step process, the information may only be partially complete, and this could cause errors at the database level when trying to save the changes.
You complicate rolling back the changes.
Cancelling the logical of unit of work would involves carefully deleting all of the eagerly-committed rows in possible multiple tables.
These limitations have lead developers in the past to invent solutions involving a "shadow" set of database tables with no constraints and with all of the column types defined as character-based. Using such a solution becomes very complex very quickly. Ultimately, you will conclude that you need some kind of generic application state management facility to address these issues in a more generic and workable way. The solution comes in the form of ADF Business Components, which implements this for you out of the box.