Create an application-wide user session for J2EE

Span components from Web applications and J2EE clients to EJB components

1 2 Page 2
Page 2 of 2

Passing the session onto the EJB layer

As of J2EE 1.3, EJB components no longer must be exposed to the outside via a remote interface; they can be purely local to components in the same VM. Applications that merely have a Web tier with EJB components and Web applications collocated in the same server instance typically use the same thread for the entire request. This means that no extra work is required at the EJB tier as the RequestContext is already attached to the current thread through the RequestContextFactory.

This statement doesn't hold true, however, for architectures where a remote client needs to connect to EJB components or where asynchronous messaging is involved. In these scenarios, the RequestContext must be passed along with the method invocation. Our framework deals with the remote case, but does not address passing the RequestContext via JMS.

Figure 3 uses stars to mark the locations where a context must be propagated over RMI (remote method invocation), created, or revived at the client and EJB tiers:

Figure 3. RequestContext creation and revival

There are a number of ways the RequestContext can be passed along with an RMI call. A possible approach simply requires the RequestContext to be the first or last parameter for all business methods. Through AOP, the RequestContext is then caught in bean implementations, attached to the thread, which has been allocated for the EJB request, and control is handed to the business logic. However, this approach forces business methods to be aware of the RequestContext instead of it being transparent.

To hide the RequestContext, we must pass it at a level lower than the business logic; the transport layer seems the most logical place. RMI supports both CORBA IIOP and the Java native JRMP (Java Remote Method Protocol) for transport.

IIOP allows the addition of context information using so-called request interceptors, but unfortunately, EJB containers are not required to expose these interceptors to applications. Depending on your choice of application server, then, this approach may or may not be fruitful. JRMP adds no such support, and as RMI calls might as well take place over this protocol, the issue is best addressed at the RMI level. An excellent discussion on this problem is available in the JavaWorld article "Service-Context Propagation over RMI (January 2005), from which this article's solution is inspired. I encourage you to read Wenbo Zhu's original article.

Dynamic dispatch

The magic of transparent remote context propagation lies in the use of a dynamic dispatch mechanism, which routes all EJB calls to a single, dedicated method, exec(). This method takes as arguments the actual EJB method to call, any parameters, and the current RequestContext object. Having installed the context into the current thread, exec() uses the Java Reflection API to delegate to the actual business method, as shown in Figure 4. From there, things proceed in a normal fashion.

Figure 4. Remote RequestContext propagation with dynamic dispatch

A client that does not know of RequestContext objects may still call business methods directly using the EJB component's remote interface, provided those methods are able to handle such requests. Unfortunately, clients who do wish to pass the RequestContext must now call exec() rather than the actual business method. This is where the Business Delegate design pattern comes into play.

The Business Delegate pattern

Business Delegate is a design pattern in the Sun Blueprints Core J2EE Patterns Catalog and serves to hide the implementation details of a service layer from a calling client. In our framework, the business delegates allow clients to choose from a remote or local version of the underlying EJB component without any changes to actual method invocations. This functionality is achieved by having two different private implementations of the same business interface: one for local calls and the other for remote calls. The class diagram in Figure 5 shows this relationship.

Figure 5. Business Delegate classes and interfaces. Click on thumbnail to view full-sized image.

When the client and EJB live in the same VM, the local interface is used, and, hence, the RequestContext is already attached to the current thread. For the non-local case, the business delegate wraps the remote interface in a dynamic proxy. Instead of calling the associated business remote method directly, the proxy invokes exec(). During this operation, the method name and parameters of the original method are passed along with the current RequestContext and ApplicationSession.

The dynamic proxy is supplied via the RequestContextPropagationInterceptor class, whose main method, invoke(), is shown in the code below. The interceptor variable represents the actual remote interface returned by a previous JNDI (Java Naming and Directory Interface) lookup:

 

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result;

RequestContextFactory requestContextFactory = ConfigHelper.getRequestContextFactory(); RequestContext requestContext = requestContextFactory.getRequestContext();

if (args == null || args.length == 0) { result = interceptor.exec(method.getName(), args, new String[]{}, requestContext); } else { String[] argTypes = RequestContextUtil.getNames(method.getParameterTypes()); result = interceptor.exec(method.getName(), args, argTypes, requestContext); } return result; }

EJB components that wish to participate in request context propagation must implement the RequestContextInterceptor interface shown below, which has the single method, exec(), needed for the dynamic dispatch:

 public interface RequestContextInterceptor {
   Object exec(String methodName, Object[] arguments, String[] argumentTypes,
      RequestContext requestContext)
      throws RemoteException, InvocationTargetException;
}

The class RequestContextService implements this interface and provides a superclass for EJB components that wish to receive contexts. This implementation pulls RequestContexts off the wire, links them to the current thread, and calls the method specified by the methodName parameter using reflection.

Generated infrastructure

At this point, we have established the basic framework for passing the application-wide session within a unique RequestContext. We have used AOP, servlet filters, and business delegates to hide the underlying plumbing from application developers, but we have also introduced more classes that need to be written. Moreover, developing an EJB component implies writing deployment descriptors, interfaces, and implementation classes, and at this point, you may be asking yourself how you would ever be productive with this framework. Fear not, for there is a way out!

Figure 6. Artifacts that can be generated

Figure 6 shows the artifacts involved in writing our extended EJB component. The white boxes indicate what you must write, and the green boxes show derived artifacts that can be generated. While Java Specification Request 175, the metadata specification, threatens to challenge XDoclet's position, it is the de facto tool for code generation in Java today.

Most of the green artifacts come out of the box with XDoclet, but the business delegate still must be tailored. Our framework contains a module for generating business delegates automatically from EJB bean implementation classes. When run, the XDoclet task will scan through the set of source files, filter out stateless session bean implementation classes, and generate business delegates from them.

The code below shows the demo EJB bean implementation class with content stripped and XDoclet tags highlighted:

 /**
 * @ejb.bean type="Stateless" view-type="both" name="DemoService"
 * @ejb.interface extends="javax.ejb.EJBObject,
   dk.rhos.fw.rampart.common.request.RequestContextInterceptor"
 * @generate.businessdelegate
 */ 
public abstract class DemoServiceBean extends RequestContextService 
   implements SessionBean 
{
   /**
    * @ejb.interface-method
    */
   public RequestContext getRequestContext() {
      ...
   }
   ...
}

The XDoclet tag @ejb.bean is used to generate home, remote, local, and localhome interfaces, while ejb.interface tells XDoclet that the remote interface must extend our RequestContextInterceptor. Finally @generate.businessdelegate is our custom tag used in generating the business delegate, and @ejb.interface-method tells XDoclet to include the getRequestContext() method in remote and local interfaces.

The code

This article is accompanied by a complete framework, which can be used as is, along with a sample application. To build the system, you must first download and install Apache Maven, then change directories into the root folder, and type maven. This will download all dependent libraries and build both the framework and demo application. Artifacts will be copied to your local Maven repository under "rampart" and "rampart-demo."

The framework is composed of the following modules:

  • rampart\modules\rampart-common: Contains common framework code
  • rampart\modules\rampart-util: Optional utilities for Web applications
  • rampart\modules\rampart-doclet: XDoclet extension for generating business delegates

The sample application contains a single EJB component, a single Web application, and a single standalone J2EE client. The application has the following structure:

  • rampart\demoapp\application: Files needed for the EAR
  • rampart\demoapp\client: The standalone J2EE client
  • rampart\demoapp\service: Files needed for the EJB component
  • rampart\demoapp\webclient: The Web application client

Wrap up

J2EE lacks an EAR-wide session facility, but, in this article, we have built one using a wide variety of utilities from the architect's toolbox: design patterns, custom code generators, aspect-oriented programming, and dynamic proxies. The codebase is fairly small, but quite a bit of infrastructure is still involved in hiding the details from application developers and in making development efficient.

An application-wide session is just one of many facilities you may want to add to your application infrastructure before building the actual application logic. Chances are, your system will need a custom security mechanism, personalization, auditing, performance metrics, robust exception handling...the list goes on and on.

These aspects can be handled using the same tools on mechanisms similar to those described in this article. So download the code, try it out, and start hammering out the infrastructure for the extra facilities you need!

Kåre Kjelstrøm is the cofounder of Silverbullets, a Danish consulting company that helps design and architect J2EE applications. He worked at Trifork on the EAS J2EE Application Server and as a J2EE consultant for several customers in the areas of shipping and healthcare. For a couple of years, Kjelstrøm lived in Silicon Valley, where he worked as architect and developer in the areas of e-business process automation, Web services management, and J2EE application server appliances. He holds a B.S. and an M.S. in computer science from the University of Aarhus, Denmark.

Learn more about this topic

This story, "Create an application-wide user session for J2EE" was originally published by JavaWorld.

Copyright © 2005 IDG Communications, Inc.

1 2 Page 2
Page 2 of 2