Blue Elephant Referee Scheduler

 

Transactions

 

Intro:

One of the challenges in writing a web application is correctly handling transaction, in order to keep the application in a consistent state.  When several actions need to performed, only two scenarios are possible: all will succeed or all will fail. Our approach uses the following concepts, each of which is explained below:

 

·        The Transactional Context and Threadlocal patterns

·        Hibernate and JDBC transactions

 

Motivation:

The motivation here is primarily correctness. We began with a desire to avoid needing to use a full-blown J2EE/EJB application server, and chose the above tools and patterns after some investigation of the following books:

 

Usage:

The transactional context pattern is based on the following observation:  transactional API’s generally provide support for ‘database level’ transactions. That is, each time you go to the database, your actions can be treated atomically.  However, what you usually want is Use Case Level  transactions, which are higher level, so that all of the actions that comprise a use case scenario succeed or fail atomically.  Our implementation is taken from chapter 10 of J2EE Design Patterns, and is pretty slick.  This chapter should be considered a must-read before diving into the model area of our application. 

Some key points, along with a running example, are shown below :

  • Use case scenarios correspond 1-1 with methods in our Business Delegate classes, which present a coarse grained application interface to the front end components.  For example, we have a business delegate with a resetPassword() method, which corresponds to the reset password use case.
  • Business Delegates are created using a factory object. This factory actually returns a proxy object that stands in for the delegate. In order to enforce that delegates are created using the factory, their constructors have package visbility, which prevents front end components from instantiating them directly.  In our example, the resetPassword() method is a member of the UserManagementDelegate class.  A front-end client obtains a reference to such a delegate as follows:

UserManagementDelegate del = BusinessDelegateFactory.getUserManagementDelegate();

           
What is returned is actually a proxy, but this is mostly transparent to the client.

  • This proxy object transparently wraps each method invocation on the business delegate in a transactional context – that is, a transaction is begun by the proxy before the method call is passed through to the underlying delegate. If the delegate method succeeds, the proxy commits the transaction. If the delegate method throws an exception on the other hand, the proxy rolls the transaction back. In our example, a front-end client would make the call:

 

del.resetPassword(user, newPassword);

 

  • This method has two responsibilities: To update the user’s password in the datbase, and then to send an e-mailnotifying the user of the change.  Either both actions need to succeed, or neither should succeed.  This is where this pattern shines.  The business delegate contains no code to handle the transaction, because it is taken care of by the transactional context proxy.
  •  Because proxies are built using Java reflection, which requires interfaces, all business delegate objects must have corresponding interface classes that declare all public methods provided by the business delegate. If you add a public method to a business delegate object, but not to its corresponding interface, it will not work. In our running example, the front-end client programs against the UserManagementDelegate interface, which is implemented by a class called UserManagementDelegateImpl.
  • Business delegate methods generally call through to DataAccessObjects, which encapsulate the data access methods (generally implemented using Hibernate in our case).  These methods need a way to join an in-progress transaction.  We accomplsh this by using a Singleton utility class called HibernateUtil, which implements the Threadlocal pattern. DataAccessObjects obtain a Hibernate session object from HibernateUtil.  HibernateUtil manages one session object per thread using the Java threadlocal type, so that concurrent requests each use a separate transaction.  Our example is slightly broken here, because the action of sending an e-mail does not participate in the transaction directly.  We accomplish the same effect, however, by making it the second of the two actions.  In the first action, we reset the password in the database. If this fails for any reason, the e-mail is not sent.  If it succeed, then we try to send the e-mail. If this fails for any reason, an exception is thrown, and the proxy rolls back the transaction, which nullifies the change of password.

 

Links:

The primary reference here is Chapter 10 of J2EE Design Patterns.  Chapter 9 is also quite useful as an explanation of the Business Delegate pattern.  This book is available online via Safari, which is free for OSU students.