UI logo
Agile Interaction Architecture
uidesign.net
 
     

David Anderson Headshot
Agile Management Cover Graphic
 
 

 
 
WhitePaper
Saturday, July 22, 2000
 

Web MVC - Browsers, Transactions and Exceptions

 

Introduction

This is the long awaited third part in the series of articles describing a generic server-side architecture for transactional eCommerce websites, using a Model View Controller architecture or Mediator Pattern [Gamma 95] configuration.

Part 1, outlined the architecture to be used to handle the event driven nature of an http request - html response, server to client system, and detailed how to deliver this in a 3 tiered model of Presentation Layer, Business Layer and Persistence Layer. The Presentation Layer is modeled using UML Statecharts [Horrocks 98] which lend themselves to easy implementation using Mediator Pattern which provides the View and Controller elements of the MVC model.

Part 2, explained how to implement the infrastructure to run the MVC model using UML Class Diagrams [Coad 99], Statecharts and Java code to process and respond to http request events.

In Part 3, we look at how all this hard work developing infrastructure can be made to pay off and deliver real advantages. We look at how to protect the system from unexpected browser (client-side) events or activities such as refresh, back, and bookmarks. We will see that this can be achieved with a single piece of code to manage all of this activity.

We will also see how Statecharts can be used to cleanly model the boundaries of logical transactions and how the MVC engine can be used to provide protection of these transaction boundaries (transaction isolation [Gray 93]) and scoping using a single piece of code.

Finally we will look at how exceptions to normal operation can be cleanly handled and implemented within the same system.

[To completely follow the discussion in this paper, you may find it helpful to refer to the diagrams from Part 2]

Protection from browser functionality

Current State And Session

The Current State and the Session ID are encoded into the URL with rewriting e.g. my.home.com/Connector?sessionID=12345;state=4.2.1 This guarantees that we will always see the Session ID and Current State at the server when we receive a request.

The Connector Class is arranged to hide the incoming request inside an IHTTPRequest interface implementor class which will turn the parameters into a nice set of name value pairs. The Connector class must then pass that IHTTPRequest off to the RequestManager class which is then responsible for identifying the appropriate State Object e.g. the State with name="4.2.1", and the appropriate LoginSession Object e.g. the LoginSession with ID="12345".

For security reasons it is advisable that Session IDs are randomly generated and contain some check digit. A Class method in LoginSession can be provided to validate the authenticity of the Session ID. If it fails then a suitable exception can be thrown by the code. An alternative scheme might involve encoding the Users name or ID along with the Session ID and validating one against the other. The idea is to make it sufficiently difficult for a hacker to guess the Session ID while a session is "live". It should also be near impossible for Session IDs to be modified in the URL and give instant access to someone else's session. Such goofs in security have been seen over the last year on several eCommerce sites, particularly those of online banks.

Refresh Button

On the event of a Refresh or Reload request from the client, we receive the Session ID and the Current State because they are encoded in the URL. Our HTTPRequest Class will translate this into a nice set of name value pairs. Our RequestManager Class will in turn see that we have no Event with this request. Therefore, its a request to deliver an existing State back to the client. That existing State will be held within the collection of StateVisits at the LoginSession. So, we ask to revisit the State by calling revisitState() on the LoginSession Class. This will identify the State object in its collection of StateVisits. If the State is not in the collection then it will throw an exception which we can catch back in RequestManager. Revisiting a State is a simple matter of rebuilding the html and streaming it to the client, based on the Persistent Parameter which we stored at the server attached to the appropriate StateVisit object. The result of this is that we can rebuild any given screen on a reload request and we don't require all the previous parameters to be encoded in the URL. The Session ID and State ID are enough. This shows that the MVC Engine architecture has a huge advantage. We have a single piece of code protecting the whole application environment from use of the Refresh key at the browser.

Back Button

Another big fear for stateful client systems is the Back Button. The client is able to change the client State without the server being aware. Well, it ought to be obvious that the mechanism for protecting against Refresh works equally well for the Back Button with a subtle difference, there is usually an event (unless Back followed by Refresh has been used). With the Back Button scenario, the client has pressed back several times. Let us suggest that the server believes the current state to be State 7. We receive a request and that contains StateID = State3, SessionID = 1234, Event = EventB. First of all we ask the LoginSession whether State3 is in its collection. If it is not then we throw an exception i.e. the Back Button hadn't been used, someone was bookmarking or editing the URL or hacking our system. Assuming that State3 is in the system, we now override the current state, setting it back to State3. Now working on the basis that we've been in State3 all along, we simply process the EventB in the normal fashion - see Web MVC Part2. So our Server-side MVC Engine has given us a single place, a single piece of code, which protects the whole application from use of the Back Button. What about an open Database Session or Transaction, you might ask? Well that is a good question which we will revisit later in this article.

Bookmarking

The use of bookmarks to "jump" into the middle of a website is another headache for a web based application. Again our Web MVC engine is well placed to cope with this problem. Remember that the SessionID and StateID will be encoded in the Bookmark. We have two choices when we receive a request from a bookmark. We can choose to ignore it. Simply we look at the SessionID which will be for an invalid Session more often than not, and we simply throw an exception. RequestManager will catch this and request output of a page saying something like, "Please Login".

However, it could be that the Session ID is valid. The User may still be in the same Session. In this case, we can treat the problem in a similar fashion to Back Button and Refresh. The bookmarked state may already be in the collection of StateVisits, in which case, simply call revisitState() on the LoginSession class. If the State is new then you will need to call gotoState(). There are possible complications if a transaction boundary or open database session is involved. However, this will be tested and caught by the same transaction scoping code already mentioned and discussed below.

Transactions

There is a second type of behavior which we need in a navigation mechanism, other than the Condition Evaluation we have just shown. This second type of behavior is known as an Action. An Action is something which must happen after an Event transition takes place but just before the new State is entered.

One key use for Actions is the scoping of Database Transactions. Typically, the Begin Transaction, the Commit and the Rollback of a transaction will happen as Actions. The ability to manage transactions is fundamental to eCommerce Websites and Application Service Providers and Extranet Sites etc.

Here is a basic example of how we can model the Transaction Boundaries onto a Statechart. Note the Transaction Boundary is not part of the currently adopted UML Statechart notation but is shown as an aid to understanding.

Diagram1. The Transaction Boundary for a Conversational Tx in an HTML Website

It is important that a Transaction only have a single starting point. A discussion of why this is so is out of the scope of this paper. The Starting point is given when the First Page is entered. An Action is set on the initialization of the FirstPage State, which will cause a Database Transaction to begin. The appropriate server-side script would be run to make this happen.

The other important Actions are the Commit and the Rollback. Commit or Rollback must happen when the navigation crosses outwith the Transaction Boundary. The Commit Action is set to happen when the Navigation moves to the Good Exit State. In other words, the Name, Address and Credit Card Number have all validated correctly. The Rollback Action will happen if any of the earlier parameters were not valid. [Note: again this is a simplified example]

Now consider how the Transaction Boundary will look on the WML Version

Diagram2. The Transaction Boundary for a Conversational Tx in a WML Website

Now let's consider how we might represent the transaction boundary in the data model and what we might do to enforce the transaction isolation. [We are making a big assumption here - single transaction per client at any one time and no nesting of transaction, though in principle nesting would be possible]. The Object model to support the data for the transaction boundary would look like this.

Diagram3. Updated Class model fragment with Unit of Work Class

The reference from the LoginSession is holding the "current" Unit of Work for the client. The relationship is either 0 or 1. The client with this Login Session is either in a conversational transaction or they are not. A method can be added to LoginSession called isInTransaction() which returns a Boolean answer. We will use this value in the RequestManager class for processing possible exceptions related to transactions.

So now let us consider the rules for ensuring the transaction isolation.

1. Staying out of a transaction

If the LoginSession is NOT in a transaction ( isInTransaction == false )
and the destination (Goto) State is NOT in a transaction then gotoState(). Proceed as usual.

2. Staying inside a transaction

If the LoginSession is IN a transaction and the destination State is IN the same transaction ( validate this with a getName on both UnitOfWork objects or equivalency test on the ObjectID of the UnitOfWork ) then gotoState().

If the UnitOfWork equivalency fails then you must check for a valid exit and a valid entry into the new transaction (see below).

3. Entering a Transaction

If the LoginSession is NOT in a transaction and the destination State in IN a transaction then validate that the destination state is the "startState" of the Unit of Work i.e. only enter by the allowed front door. If so then gotoState() and set the LoginSession to UnitOfWork reference.

If not entering by the UnitOfWork startState then throw new IllegalTxEntryException();

4. Exiting a transaction

This is slightly more tricky as their can be multiple legal exits.

If the LoginSession is IN a transaction and the destination State in NOT in a transaction and the transition between states is a legal one then gotoState(). Proceed as usual. The code to tidy up the transaction should be written explicitly as an Action on the StateTransistion.

If the transition is not a legal one, then by some means the client has exited the transaction, perhaps through use of the Back button. In this case, explicitly rollback() the transaction and proceed to the new State.

If the destination state is in a different Unit Of Work and the transition is a legal one then determine that the destination State is the "startState" for the new Unit of Work and proceed. Otherwise, throw new InvalidTxException();

If the destination state is in a different Unit of Work and the transition is not a legal one then first of all explicitly rollback() the current transaction and then determine whether the destination State is the "startState" for the new Unit of Work. If so proceed. Otherwise throw an exception.

All of these rules would be written in to the RequestManager class and executed just prior to it executing the gotoState() method on LoginSession.

Exceptions

Catching Exceptions

Generally speaking all exceptions in the MVC Engine should be propagated up to the RequestManager class which should have an extensive try{} catch{}; block.

As was shown above, a relatively fine grained approach to exception naming works best as it gives finer control. Groups of exceptions to be processed together can be grouped using inheritance if necessary. For example, all Transaction Isolation related exceptions might inherit from TxIsolationViolationException. This may or may not be useful depending on your design.

I have also found it useful to have two basic categories of exception: recoverable; and non-recoverable. Recoverable implies that the current session can continue but something undesirable may have happened. For example, the Credit Card verification mainframe is off-line. This is a temporary fault and nothing that should end the client session. In theory there could be different levels of these. The new page may have pieces missing, the client might be pushed back to a previous page, or moved out a level within the site hierarchy, their current Unit Of Work may have to end, and so forth.

A non-recoverable exception implies that the client session must die. You may also find it helpful to identify two categories of non-recoverable exception: non-critical; and critical or catastrophic. Non-critical generally implies that the current user session has to disappear possibly because that user is doing stuff that just isn't allowed or possible. It could be a security breech, a hacker attack or similar. The catastrophic type of exception implies that the whole web server may be unstable. Perhaps the processor has run out of swap space or working memory. In which case, the whole website may need to go off-line.

The design of a complete exception strategy is really outwith the scope of this paper.

Processing Exceptions

With a fine grained strategy it should be fairly easy to identify the condition the system was in when the exception was thrown. Other clues such as the LoginSession, the current State and so forth can be used. All necessary clean up for the current state should be done in the catch{} block. This may include rolling back a transaction, freeing temporary memory, etc.

The final act in the catch{} block should be to try and recover the situation. The recovery point is really determined by what exception was thrown and where it happened i.e. what state the system was in when it happened.

BrĂ­an O'Byrne and I have proposed an extension to the Statechart notation to allow for the modeling of recovery of exception states.

Diagram4. A Statechart Diagram with Exception State Extension

In this diagram we have used a notation similar to the existing History State notation, where an X is encased in a circle to denote the Exception State. The condition evaluation on the transition to a recovery state contains the name of the Exception which has been caught. It could also include Actions such as a rollback() on a transaction.

These resumption states can be hard coded into the catch{} block or soft coded through a resource file. Note that soft coding assumes that the system is sufficiently "alive" to be able to load the appropriate parameter from a file. Hard coding can have its advantages when you are dealing with exceptional conditions.

In the example above where we catch CCNumInvalidException, the last instruction inside the catch block would be a LoginSession.gotoState("State3"). This would have the effect of recovering the client session to the View associated with State 3 after it had done any necessary clean up.

Summary

This series of articles has intended to demonstrate that a rigorous approach to eCommerce web site design is possible. It has set out to show that appropriate modeling of the Presentation Layer can improve the robustness of the code and will improve the shared understanding of how the code works.

Modeling a Presentation Layer using UML Statecharts significantly aids the effort of designing a robust system which can handle conversational transactions, withstand abuse and attack from the client end, and handle the processing of exceptional conditions appropriately.

There is always an argument, particularly from programmers, that developing and maintaining such detailed designs is a waste of time and costly. I would advocate that when done with the use of a tool such as Together, the maintenance of the diagrams is a relatively low overhead activity. The cost in time upfront to develop and maintain the Statecharts is easily outweighed by the cost at the back end on debugging and re-working faults. Naturally there is a caveat. This approach becomes appropriate as the scale of the project grows. On a small site, you may never see pay back. On a medium sized site, you certainly will and on a large site your project may be in danger of complete failure if you do not follow an approach like the one described in these articles.

The design described in these 3 pages was loosely intended for use with Oracle Application Server 4.x with cookies turned off. Your choice of application server and environment will affect how much or how little of the MVC engine infrastructure that you have to build. Many environments are now supporting conversational transactions and nested transactions. Support for exception handling may not be so good.

When implementing a Presentation Layer, you must determine how much of the infrastructure you need to build and how much is provided by your choice of application server. Nevertheless, modeling the Presentation Layer with Statecharts and implementing that Presentation Layer using a Mediator pattern to process State Transitions through Events is a strong, robust and controllable approach which will lead to reduced bug count and easier debugging. It also lends itself to better isolation of problems.

This third part has shown where the real strong benefit lies in developing a rigorous UML Statechart modeling approach to the Presentation Layer. The browser exceptions such as Back Button, the Transaction scoping and isolation and finally the server-side exception handling are all neatly contained in one place, the RequestManager class. You only have to write that code once and you only have to maintain and debug it in one place. It can then be used for the whole implementation. That's a big win.

References

[Horrocks 98] Constructing the User Interface with Statecharts, Addison Wesley, Ian Horrocks, 1998
[Gray 93] Transaction Processing : Concepts and Techniques, Jim Gray and Andreas Reuter, Morgan Kaufmann, 1993
[Gamma 95] Design Patterns : Elements of reusable Object-Oriented Software, Gamma, Helm, Johnson, Vlissides, Addison Wesley, 1995
[Coad 99] Java Modeling in Color with UML, Coad, De Luca, Le Febvre, PTR-PH, 1999

Advertisement

For more information about the commercial Web MVC framework ViewControl visit Statesoft.

     
 
           
hosted by likk.net
Weblog Commenting by HaloScan.com