Preston: a Service-to-Worker Framework


Jed Reynolds
Bellingham WA
May 2003
jed_reynolds at hotmail com

Summary

The Service to Worker pattern published in Sun Microsystem's J2EE Blueprints Catalog (see resources) is a pattern that quickly lends integrity to the design of a web application. It is not uncommon for JSP and Servlet websites to fall into chaos as the number of servlets and JSPs that try to work together multiply pel-mel into a maintenance nightmare. The design covered here partitions application components readily into workers, view helpers and business delegates. Furthermore, view components can become arbituarily placed multiple times on the same page, with different states. You end up with a flexible design for page components and decoupled business logic.

Motivations

To begin with, view-centric JSP programming is trivial and a downright misleading way to begin coding a web application. This is why the Front Controller pattern, and it's other related patterns (Service to Worker, Front Controller plus View Dispatcher, Command-and-Controller) evolved. Namely, for the purpose of encapsulating the control logic in role-appropriate classes, not in the view. Consider this: submitting a request to a JSP makes your view-layer technology responsible for processing application logic. What if your application logic determines you're in the wrong view, yet you've already rendered half of this incorrect page? That's the biggest motivation for using the Front Controller pattern that I can think of.

Furthermore, I find that one of the shortcommings of the existing JSP/Servlet framework is that it's semantics are far too vague. I discovered these antipatterns in two projects I worked on:
  1. Random Session attributes effect. You see this when just about all the values you want to display are tossed into either the HttpSession or the HttpServletRequest attributes, but you can't tell which, when. Often to gaurd, they get placed in both. Counter this with proper modeling and encapsulation. Consider a model class per form.
  2. JSP template decay. You see this when you make a change in what appears to be "the page template" and when you submit the form, the form response is rendered in what looks like the old template. You've got multiple templates swimming around back there and I'll bet you each has a separate case-statement with some funky logic that you're going to have to figure out. Counter this with a proper templating design that allows you to compose page components with polymorphic ease.
  3. The Lynchpin Template. You'll know when you find the Lynchpin Template when you find a SQL statement in a JSP template that, when removed, busts the user session entirely. Something so central about this template, when skipped, cripples your application. Counter this with an encapsulated login and session management mechanism, in objects.
Having seen a pile of programmers (including myself) repeatedly trapped in such antipatterns, I concluded that there needed to be a stricter roles for components in a web application framework. I started with these guidelines:
  1. Don't use the session for anything but the user id and to place a type-safe, serializable container for session data.
  2. No logic in those JSPs! Or as little logic as possible. Operation of iterators or conditional includes is fine. Data formatting is permissible but actually belongs encapuslated in a formatting strategy.
  3. No direct database access in the workers and certainly not from any JSPs! Ever. Database access to be thoroughly abstracted with a persistence layer. I know you see lots of taglibs offering JDBC access...but you can do better.
  4. Put application logic in worker classes and business delegates.
  5. All application requests go to the Front Controller. This eliminates JSPs submitting to themselves and the one-servlet-per-form antipattern.
This helped so much it allowed us to get on with actually thinking about our applications in an N-tier vocabulary.

Essential Design Concepts

The Preston Framework is based on the Service to Worker pattern. It's worth distinguishing the Front Controller pattern from the Service to Worker pattern. The Front Controller pattern that simply converts a request into a parameter object and passes it to a worker and then to a view dispatcher. Often the Front Controller is the View Dispatcher itself. The Service to Worker incorporates the Front Controller pattern by putting the emphasis on the Worker role. In Service to Worker, application logic is encapsulated in workers that are invoked before any page dispatching occurs. The Command-and-Controller and View Dispatcher variations of the Front Controller pattern emphasize view dispatching and application logic invoked by view-layer callbacks during rendering. If you have page rendering and application navigation that can entirely turn application on it's ear--you need to do your application processing up front before you render the view because your view cannot then predict how it will be invoked.


Front Controller Pattern


Basic Front Controller sequence

 
Likewise It's worth distinguishing the Preston Worker from the Command pattern. The Command Pattern distinguishes a Command object which is intantiated upon client request and encapsulates a distinct action. An example would be an InsertUserCommand class. If the FrontController were using the Command pattern, it would instantiate a new InsertUser command object with stateful information passed into it, as from the request, and there would be a one-to-one correspondence between command object and client instantiation of command.

Command and Controller styled Front Controller


Command and Controller Sequence


The Preston Worker, however, is a single instance (but not a Singleton: singletons and static methods are difficult to use in a unit testing environment) looked up from a Map of workers. It is passed a stateful WorkerRequest object that encapsulates the user request. The only state that the Worker contains is that pertaining to it's runtime context, not that of the client's session. Presuming no locking behavior is necessary for the Worker (because it has no effective state and no static members to be locked), an arbituary number of threads can be invoking Worker.handle( WorkerRequest w ).


Sequence diagram of dispatch to worker and to view.

Advanced Practices

Like many applications, the Preston Framework will probably have the most success if  database resources are abstracted, even to the level of entity management. Your workers will probably want to bind to SessionBeans or EntityBeans, or JDO controllers. You'll probably want to develop with a unit testing framework. Designing your applicaiton with Component Orientation and Inversion of Control patterns will certainly help, too.

The Preston framework begins with the concepts of Component Oriented Programming, and Inversion of Control. (Jakarta Avalon is a ready example.) Summarily, component oriented programming encourages configurable components. Configuration is from outside the component. Preston Workers are componentized by binding to abstract references to database and session resources. In an ideal Preston framework, a worker could be deployed at runtime, and likewise, undelpoyed. Inversion of Control allows this kind of quality in components: instead of a component instantiating resources it needs using the "new" keyword, it expects them precreated in it's operating context. Failure to find a resource would result in an exception and component deployment failure.

Preston Framework Overview

The following are the basic components in the Preston Framework. Some of the names are not quite the same as what is listed in Sun's J2EE Patterns Catalog, so they are explained a bit.

The Front Controller and the RequestInitializer

In the Preston Framework, the FrontController the submission point for HTTP requests. The FrontController passes these requests to a RequestInitializer. The RequestInitializer is responsible for normalizing the session. If the session is new, the browser is assigned an anonymous identity that might be upgraded to a user's identity upon login. New sessions are populated with a ViewModelManager. There is a ViewModelManager for each user session. The ViewModelManager maps ViewModel instances to component locations in the view, and can recycle the ViewModels upon logout.


The Front Controller, Request Initializer and ViewManager

....and converts the request into a message to a worker, called a WorkRequest. The worker returns control to the Front Controller upon completion and the Front Controller forwards control to the top level view component. (This could be expanded to incorporate a View Dispatcher as noted in Sun's J2EE Front Controller pattern.)


Sequence with RequestInitializer

The Worker and the WorkRequest.

The HttpSession, HttpServletRequest and the HttpServletResponse are hidden behind the WorkRequest. This tightens the semantics between the Worker and the application, and decouples the Worker from the view. Workers are static logic and should be thread safe. A worker will recieve multiple requests to perform the same operation on separate WorkRequests. (Note that some variations of the Front controller patterns lable the Worker Request as a View Helper. This is appropriate if you combine the request with view-specific methods, something not emphasized in the Preston design.)


Class diagram of FrontController, Worker, and WorkRequest

The ViewHelper and the ViewModel.

The ViewHelper is a controller that provides ViewModels to the Worker. You can take a shortcut and merge the ViewHelper and the Worker into a single class per view component. The goal of theViewHelper is to provide static logic for manipulating the ViewModel given to it. Each user session has it's own collection of ViewModels, and application requests are a collaboration between Worker, ViewHelper and ViewModel. This is a fairly thread safe design even as their is only one ViewHelper per view, because the data being manipulated is encapsulated in model instances. Thread safety breaks down if you design interactions where the same ViewModel is manipulated simultaneously by two workers. Serial dispatch of workers provides simpler semantics anyhow. (There is not a design contingency yet for multithreaded operation per user session, but there is no reason why there's couldn't be.)


Class detail of view helper, view model, work request and model manager.

Nestable ViewTemplates.

A rendered page can present the same ViewTemplate multiple times with different data by differentiating the template invocation with a distinct NavigationPath. Consider ViewModels to be nodes in a tree of page components. The Front Controller will probably dispatch to one or two top-level ViewTemplates commonly, but the top-level template is specified in the WorkRequest. So in addition to being able to display the same page template repeatedly, it allows you to specify different top level templates. An example site might consist of MainTemplate and PopupTemplate: the MainTemplate typicallly just shows the standard header, content, navbar and footer. The PopupTemplate is told what specific child Template to display.


Sequence showing page template nesting.

Recieving, Handling and Dispatching

We'll consider some examples. The first will be a simple login example. The second will show the interaction of a popup dialog. The third will be a page switch, and the last will be a logout.

Login Example

  1. Form specifies action="/FrontController?worker=LoginWorker&activity=auth"
  2. http request reaches FrontControllerServlet, which passes HttpRequest to FrontControllerStrategy
  3. FrontController looks up worker "Login", passes WorkRequest to LoginWorker
  4. LoginWorker authenticates user, and then puts UserToken in session
  5. LoginWorker forwards WorkRequest to the DefaultHomepageWorker
    1. The HomepageWorker asks the HomepageViewHelper for a HomepageModel
    2. The HomepageWorker populates the HomepageModel
  6. The default login worker collaborates with the Various ViewHelpers that build the login page, possibly forwarding to other workers. There are many possible mechanisms to accomplish this:
    1. Asking the HomepagePageHelper for children to notify or forward control to.
    2. Hard-coding the HomepagePage children in the HomepageWorker.
    3. Delegating notification to the HomepagePageHelper.
    4. The children of LoginPage can be registered as HomepageWorker observers, and are duly notified.
  7. HomepageWorker returns control to the FrontController. If the FrontController is acting in the role of the Dispatcher, then we forward control to the DestinationPage. The destination page is responsible for including child views. Child view keys are found via the ViewModel of the current parent view. For example, the HomepageHelper specifies defaultpage.jsp as the destination page, it is up to defaultpage.jsp to look up the references to the child views to include.

Popup Dialog Example

A popup dialog is a good use for a distinctly different destination page. A link might be opened in a new window, as such:
    <script>
        window.new( "/Demo/FrontController?worker=load&workeract=refresh&destpage=graph.jsp" )
    </script>

The opening of the window invokes the FrontController, LoadWorker and ultimately forwards control to graph.jsp.

Page Switch Example

The concept of a navigation path is of much use to distinguish role of components in a user session. Consider four components, some of which are nested: a Homepage component that nests either a UserList or a MailList component, each composes the GenericList component. he GenericList composed by the UserList would be Homepage->UserList->GenericList, likewise MailList would compose GenericList with this navigation path: Homepage->MailList->GenericList.

What happens when we switch from viewing the UserList to the MailList components? Basically, we tell Homepage component to

Logout Example

Logging out of an application should either implicitly or explicitly deallocate application resources.
  1. The FrontController could invoke a LogoutWorker to manage resource deallocation. This could be the target for a logout link, like so: /Demo/FrontController?worker=logout. There's unlikely to be any reason to pass arguments to the logout worker.
  2. In the event of the timeout of a servlet container's Session, a timeout listener can trigger a LogoutWorker.

Message Passing Between Workers

There might be two basic designs to consider when wanting to pass messages between workers. First, there's the clever idea of a stack based queue. Next there's the more sophisticated Observer pattern.

A Stack Based Queue

The original design concept of the Preston Framework allowed for the Worker subclasses to communicate by pushing the key of the intended recipient of a message onto the WorkRequest queue attribute. This could be used creatively in a manner of ways. First, consider a LoginWorker: upon successful login, the LoginWorker looks up the various Workers that need to be invoked to setup the user's home page, and pushes their keys on the WorkRequest stack in reverse order. This presumes that the workers don't need to pass messages to their nested children. Next, consider the case when you have worker interaction crutial to nested children: the LoginWorker ceases to have knowledge (for sake of separation of concerns) of what workers will be called (except for the first worker). The responsibility is then placed in each of the Workers to find the children of the corresponding ViewHelpers and push those on the WorkRequest stack as necessary. Thus building the session of ViewModels recursively.


Sequence diagram of stack-based dispatching.

Why a stack? Because when the stack is empty, the Front Controller knows to forward control to the top level View Template. You don't risk the possible logic error of failing to remove the reference to your last processed worker key, decreasing the possiblility of accidentally looping. While this is rather clever, it should be pointed out that it could probably be more Object Oriented. Using a stack also forces the application designer to think in reverse, which is not very kind of the framework.

And how do Workers communicate? That becomes a very good question. The simplest manner is to over-write the form-submission properties (Worker-key, Worker-action-key, and form submission data) in the WorkRequest. However, this prohibits the ability for a Worker to queue multiple different messages to different workers--with only one worker-action-key, you can only send the same message to multiple different workers. Better is to create a WorkRequestEntry, having Worker-key, Worker-action-key and it's own request attributes. This encapsulates the request into an object you can push on the stack. However, this defeats the need for a WorkRequest set of attributes. Furthermore, the desire to create Worker-subclass sensitive WorkRequestEntry subclasses. Entry subclasses leads to subclass coupling between workers.

Example of WorkerRequestEntry:
public interface WorkRequestEntry
extends Serializable
{
    public RequestQueue getRequestQueue();
    public void setWorkerKey( String key );
    public void setWorkerAction( String action );
    public String getWorkerKey( );
    public String getWorkerAction( );
    // public void setAttribute( String key, Object a );
    // public Object getAttribute( String key );
}
Example of Dispatching a worker request entry:
WorkerSubclass implements Worker
{
    public void handle( WorkerRequestEntry r )
    {
        WorkerRequestEntry newRequest =
            r.getRequestQueue().createRequest();
       
newRequest.setWorkerKey( HumbleWorker.KEY );
       
newRequest.setWorkerAction( HumbleWorker.ACTION_FLIPOUT );
        r.getRequestQueue().push(
newRequest );
    }
}

The Observer Pattern

While the Stack oriented communication between workers sends messages between Workers, it passes along the ViewModelMap so workers can operate on some stateful information. You might expect that workers might observer one another. This would be erroneous, because the worker classes themselves should remain implicitly devoid of state. It is the ViewModel classes that should be observed.
 
Observing Workers or Sending WorkRequest directly?

We can further distinguish two basic types of message passing. We can couple the worker more closely by sending WorkRequests directly to the workers desired, without using the FrontController as a request dispatcher. If workers are truly anonymous with regaurd to one another, a work request might be sent to each associated ViewModel's child and it's parent. So, a worker might modify and send it's WorkRequest to each of it's children:

try
{
    Iterator workerKeys = viewModel.getChildNames().getKeys().iterator();j
    while( workerKeys.hasNext() )
    {
        workerMap.getWorkerByKey( workerKeys.next() ).handle( workRequest );
    }
    workerMap.getWorkerByKey( getviewModel.getParent().KEY ).handle( workRequest );
 }
catch( SecurityVetoException sve ) { }
catch( ApplicationException ae ) { }
catch( SystemErrorException see ) { }
Code example of  worker request dispatching.


This requires Worker classes be contextualized with the WorkerMap collection, and be capable of dealing with a host of exceptions that would be handled in the FrontController. This might be encapsulated in a RequestDispatcher which could be aggregated by the FrontController and the Workers. There is merit in keeping the Worker classes simple.

Observing View Models

Creating an Observer pattern over the ViewModels creates models that delegate to workers, which is a sigifigant change. It is possibly signifigant enough to design a different framework around. The signifigant aspects of doing this would be making the ViewModels aggregate references to their Workers, and registering ViewModels between themselves as ViewModelEventListeners.

Some Notes on Exception Handling

Crashing out of the application should not leave allocated resources. The Worker throws a SecurityVeto, ApplicationException or a SystemException (note here about masking J2EE RemoteExceptions, Runtime Exceptions, blah blah) from the handle( WorkerRequest r ) method. This has to be caught by the FrontController or you have a nasty crash. But what should the FrontController do? If we display an error page, we're assuming that the user's application session is recoverable. Is that smart? To be safe, it would be a good idea to log the user out by triggering the LogoutWorker. This deallocates all the application references kept by the ViewModels associated with the user's application session.

Conclusion

There are many web application frameworks. The Preston framework provides a Model View Controller style for web application design. By leveraging the strengths of the Service to Worker pattern, Preston applications can provide a very powerful way to build processing intensive web applications.

References and Resources