Blue Elephant Referee Scheduler
High-Level
Architecture
Intro:
Model-View-Controller
is an extremely popular buzz-word for web applications. The problem is that if
you ask ten different people what it means, you will get 10 different answers.
Still, having some idea of the advatages and importance of this pattern is
recommended.
Motivation:
:
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.