EJBs are the de facto standard when it comes to server-side components developed in J2EE. Session beans are used for business logic components and are primarily of two types: Stateful and Stateless. The type of the bean, stateful or stateless, is decided at deployment time through some deployment parameters. There are many scenarios where the decision of needing a stateful bean can be deferred until runtime. In this article, I will explain a pattern that can be used to dynamically choose a stateful bean at runtime.
The session bean performs work for its client, shielding the client from complexity by executing business tasks inside of the server. The stateless session bean does not hold any conversational state with the client, and every request made to the bean needs to be accompanied with the data required for the processing the request. Stateful session beans, on the other hand, can hold state and support state across multiple conversations with the client.
The added convenience of stateful beans supporting conversational state comes at the cost of performance. Stateless beans give much better performance, as they do not have client affinity. EJB containers can pool stateless beans and can serve client requests from any bean in the pool, in the case of stateless beans.
The choice of having a stateful or stateless bean is a design-time decision, and the deployment descriptors of the EJB indicate the nature of the session bean to the EJB container. There is no paradigm in which the nature of a bean can change dynamically based on the result of processing.
Consider an example scenario: a search component needs to be developed that will allow the client to search the data store for various parameters. A customer search will be a part of the various searches offered by the component. If the client logs in as a particular customer, then any subsequent searches by the component should apply the customer's profile for searches. The associated searches could include a search for the list of products that the customer is eligible for. In the absence of a specific customer, this search would return results for a default user of the system. For the sake of keeping the example generic, I will not outline the specific searches but this scenario is common to many business applications.
I will explain a pattern to provide statefulness to stateless beans dynamically and will apply that pattern to the sample business scenario described above. The forces that necessitate the need for such a pattern are:
I will detail a pattern that will solve the issues we have with the current scenario. Create a focus class that is a POJO (Plain Old Java Object). The focus class will have the implementation logic for the component that needs to be developed. Model the component as both stateful and stateless session beans. The session bean will instantiate and use the focus class for fulfilling client requests. The client will, by default, access the services of the stateless bean through a delegate. If processing the request deems that the client needs a stateful service, a stateful service is created and the stateful service starts serving subsequent client requests. The stateful service also uses the focus class and passes on the client's state when invoking focus class methods.
The class diagram shown in Figure 1 has the main classes that are involved in the pattern and also shows the collaboration between the various classes.
Figure 1. Class diagram of stateless/stateful session bean pattern
The client is any Java object that needs to use the services offered by the EJB component. The client uses the delegate in order to interact with the service. The client is oblivious of the existence of both stateful and stateless services and consumes the services offered by the EJB.
The delegate here is a combination of a service locator as well as a business delegate pattern. The delegate is responsible for looking up the stateless service and making calls onto the service. The delegate is also responsible for parsing the returned dataset, storing the stateful bean's handle, and calling on the stateful service if the handle is valid. An alternative approach is for the delegate to parse the dataset when it's given as an input to a method, and call the stateful service if the handle points to a valid stateful bean.
The stateless service is a stateless session EJB that offers the component services to its clients. The stateless service offers no conversational state with the client and is the bean that's looked up by the delegate class. The stateless bean makes use of the focus class to provide functionality.
The stateful service is created on the fly by the stateless service when it determines that the request cannot be served by its stateless nature. This can be determined based on the requesting data or the processing logic in the stateless service. The stateful service is capable of storing state on behalf of the calling client and applies the state to any calls made on the focus class.
The dataset is a generic return type from the server to the client. The dataset has a placeholder for the actual data that's returned from the service as a result of the method call, and can store metadata about the returned data to the client. The dataset will also hold the handle to the stateful service and is used by the delegate to request data that's required of the stateful service.
The focus class has the main processing logic for the component. The focus class contains the base functionality and does not hold any state. It works on the data passed into it and holds no state between method calls. The focus class is used by both the stateful and the stateless service. As a rule of thumb, it's a good practice to have those methods in the focus class that are common to both the stateful and the stateless service.
The client instantiates and uses the delegate in order to make any calls to the component. The delegate has the logic to look up the stateless bean and makes the call to the corresponding method on the stateless service. The stateless service processes the request by calling on the focus class and obtains the results. The results of the processing are returned back to the client in the form of the dataset object.
If during the processing of the request, the stateless service determines that the service needs to store the state of the request for future processing, the stateless service creates a stateful service. The handle to the stateful bean is stored in the dataset object that's returned back to the client. The returned dataset also contains the resultant data for the client. The delegate could parse the dataset object before returning it back to the client and store the handle to the stateful bean as its state. The next time a call is made to the delegate by the client, the delegate would make a request to the stateful bean whose handle is being stored as a part of the delegate's state.
The stateful bean can use the state that it stores on behalf of the client and pass it on as an additional attribute to the focus class. The focus class need not be concerned about whether the request was made by the stateful service or the stateless service. The request made to the focus class from the stateful bean will have additional state information, and hence will allow the result to reflect any conversational state on behalf of the client. Figure 2 shows this relationship as a UML diagram.7
Figure 2. Interaction sequence between pattern participants
I will use the above pattern to solve the example scenario. The client will use a
delegate to use the features of the search component. The delegate will have the lookup code and will
also expose the same set of methods exposed by the service. The client invokes the
method on the
delegate and the delegate calls the
searchParam1 method of the stateless service.
If the client calls the
login method, the delegate calls on the corresponding method of the
stateless service. If the search result is a single customer, the stateless service creates an instance of
the stateful service and passes the customer details to the
ejbCreate method of the stateful service.
The stateless service also creates an instance of the
DataSet and sets the customer data, as well as the stateful handle, to the
DataSet object. The delegate stores the handle and passes on the
DataSet to the client.
The client now needs to obtain the list of products associated with the customer. The client calls
retrieveProducts method. The delegate retrieves the stateful handle that
it has and calls the associated method of the stateful bean.
The associated class stubs used for this example scenario are given in the Resources section below.
The pattern is generic and can be used in a variety of applications. The pattern described above can be used where a value list handler needs to be used. The generic searches can be done using a stateless bean and when the result size exceeds a maximum size, we can create a stateful bean and pass back the results in the form of the dataset object. The client can approach the delegate for the next set of records and the delegate can go to the stateful bean that stores the details, such as last record processed, maximum number of objects, etc.
Service Locator Pattern: The Service Locator pattern reduces the client complexity that results from the client's dependency on, and need to perform, lookup and creation processes, which are resource-intensive. To eliminate these problems, this pattern provides a mechanism to abstract all dependencies and network details into the service locator.
Business Delegate Pattern: The Business Delegate acts as a client-side business abstraction; it provides an abstraction for, and thus hides, the implementation of the business services. Using a Business Delegate reduces the coupling between presentation-tier clients and the system's business services. Depending on the implementation strategy, the Business Delegate may shield clients from possible volatility in the implementation of the business service API. Potentially, this reduces the number of changes that must be made to the presentation-tier client code when the business service API or its underlying implementation changes.
Value List Handler: The Value List Handler pattern creates a Value List Handler to control query execution functionality and results caching. The Value List Handler directly accesses a DAO that can execute the required query. The Value List Handler stores the results obtained from the DAO as a collection of Transfer Objects. The client requests the Value List Handler to provide the query results as needed. The Value List Handler implements an Iterator pattern (GoF) to provide the solution.
The Dynamic EJB Statefulness pattern helps model components that exhibit both stateful and stateless behavior. A component using this pattern delivers the performance of stateless session beans and serves the stateful needs of clients. Clients of a component developed using the pattern have a simple unified interface and are hidden from the internal swap between the stateful and stateless beans. The pattern is a natural fit with other J2EE patterns, such as Business Delegate and Service Locator.
Swaminathan Radhakrishnan works as a senior technical architect for Infosys Technologies, Ltd.
Return to ONJava.com.
Copyright © 2009 O'Reilly Media, Inc.