Using Stateful Session Beans

Stateful Session Beans (SFSBs) have their uses, but are far from being the staple of EJB that their stateless counterparts are. It's questionable whether they deserve the prominence they are accorded in the EJB specification, which implies that stateful session beans are the norm, and stateless session beans a special case.

Unlike stateless session beans, stateful session beans add significant memory and performance overhead. Stateful session beans are not shared between clients, meaning that the server must manage one SFSB instance for each client. In contrast, a few SLSB instances may service the requests of many clients.

Applications can often be designed to accomplish their work using stateless methods. Where this is possible, it will usually produce superior scalability. Even when server-side state must be maintained, stateful session beans are not the only, and may not be the best, option.

Why Not to Use Stateful Session Beans

There are strong arguments for avoiding the use of stateful session beans. Let's look at the most important.

Performance and Scalability Issues

The number of SFSB instances maintained on a server is proportional to the number of distinct active clients at any time. As SFSBs are relatively heavyweight objects, this limits the number of clients an application can serve concurrently in a given deployment configuration. It also places an onus on clients to free server-side resources when they are finished with them that does not apply to clients of stateless session or entity beans.

Important 

If using SFSBs, ensure that your client code removes them when they are no longer required, by invoking the remove() method on the bean's component or home interface.

To help manage the overhead imposed by maintaining stateful session bean instances, the EJB specification allows containers to passivate and re-activate stateful session bean instances using a policy of their choosing. This means that the least recently used instances may be swapped out of memory into a persistent store. (The choice of store is left to the container.) Passivation and re-activation are usually implemented using Java language serialization to a database Binary Large Object (BLOB) or disk file, although the EJB specification allows EJB containers to use other strategies.

The subset of the stateful session bean's conversation state that survives passivation and re-activation is described in section 7.4 of the EJB 2.0 specification. It follows the rules for Java language serialization, with the addition that enterprise resources such as EJB local and remote home interfaces, EJB local and remote interfaces, SessionContext references, JNDI contexts, and resource manager connection factory references are preserved. It is the bean provider's responsibility to implement the ejbPassivate() method (invoked by the container to notify an instance of impending passivation) to ensure that instance methods are left with legal values.

Important 

When coding stateful session beans, ensure that your beans can be passivated according to the rules outlined in section 7.4 of the EJB specification, and will have the correct behavior when re-activated after passivation. This means that you should use the ejbPassivate() method to ensure that instance variables have legal values before bean passivation.

For example, any JDBC connections must be closed, with the referencing instance variables set to null. The ejbActivate() method must ensure that any necessary resources that couldn't be preserved are reset to valid values.

The following code example shows a correct partial implementation of a hypothetical stateful session bean that uses a helper instance variable, MyHelper, which is not serializable, and thus is not a legal value for an instance variable at passivation:

    public class MySessionBean implements javax.ejb.SessionBean { 

      private MyHelper myHelper; 

The ejbCreate() method initializes this instance variable when the SFSB is first used. Note that the ejbActivate() method is called only when a bean is re-activated after being passivated; it is not part of the bean's initialization by the container (see the lifecycle diagram in section 7.6 of the EJB specification):

    public void ejbCreate() {      this.myHelper = new MyHelper();    } 

The implementation of the ejbPassivate() method sets the instance variable to null, which is an acceptable value:

    public void ejbPassivate() {      this.myHelper = null;    } 

The implementation of the ejbActivate() method re-initializes the helper if the bean instance is re-activated:

    public void ejbActivate() throws Create Exception {      this.myHelper = new MyHelper();    } 

Note that the myHelper instance variable could be marked as transient, in which case it would be unnecessary to set it to null in ejbPassivate(), but it would still be necessary to re-initialize it in the ejbActivate() method.

Note 

Remember the discussion of Java 1.4 assertions in Chapter 4? A check that the ejbPassivate() method leaves instance data in a legal state would be a good use of an assertion.

Also remember that the container can time out passivated stateful session instances. This means that they can no longer be used by clients, who will receive a java.remote.NoSuchObjectException (a subclass of java.remote.RemoteException) when trying to access them.

On the other hand, using stateful session beans may improve performance in a distributed environment (in comparison with stateless session beans) by reducing the amount of context that needs to be passed up to the server with each method call: a stateful session bean holds state on behalf of its client, and therefore usually only needs to be told what to do, not what to do on what data.

Reliability Issues

Given that EJB is intended to help build robust, scalable applications, it may come as a surprise that stateful session beans are little help on either count. The central problem is that stateful session beans pose difficulties for efficient replication of state between clustered servers - an essential requirement if they are to prove reliable as the application deployment scales up.

The EJB specification makes no guarantees about the failover of stateful session beans. This means, for example, that implementing an online store with a stateful session bean to hold each user's shopping cart (as suggested in the EJB specification) is risky. The chances are that if the server hosting any particular shopping cart instance goes down, that user (along with many others) will lose their shopping cart. For truly reliable EJB state management, there is no alternative to using database persistence, managed through either entity beans or session beans using another persistence strategy.

To understand the limitations of stateful session failover, let's consider how stateful session beans behave in a cluster in WebLogic 7.0, probably the current market leading server. WebLogic has only supported replication of the conversational state of stateful session beans since version 6.0, and that replication is limited, because of its potential effect on performance. Each stateful session bean has a primary and a secondary WebLogic server instance in the cluster.

Note 

Note that failover support and the necessary replication is not the same thing as load balancing of the creation of stateful session instances across a cluster, which WebLogic has long supported.

Normally, the EJBObject stub (that is, the implementation of the bean's remote interface supplied by WebLogic) routes all calls to the instance of a given bean on the primary server. Whenever a transaction is committed on the stateful session bean, WebLogic replicates the bean's state to the secondary server using in-memory replication (the alternative is no replication at all, WebLogic's default). Should the primary server instance fail, subsequent client method calls will be routed to the EJB instance on the secondary server, which will become the new primary server. A new secondary server will be assigned to restore failover capabilities. This approach has the following consequences:

  • Stateful session bean instances are bound to one server in normal operation, resulting in server affinity.

  • We cannot guarantee that state will be preserved in the event of failure, because replication occurs in memory, without a backing persistent store. As the replication involves only two servers, both may fail, wiping out the bean's state altogether.

  • We cannot guarantee that clients will always see the committed state of persistent resources updated by the stateful session bean. Although the scenario is unlikely, it is possible that a transaction is successfully committed against the stateful session bean instance on the primary WebLogic server, but that server fails before the stateful session bean's state is replicated to the secondary server. When the client next invokes a method against the stateful session bean, the call will go to the old secondary server (now the primary), which will have out-of-date conversational state, which may conflict with the results of any committed persistent updates.

  • It's impossible to recover gracefully from a failure on the primary server while a client is waiting for a return from a call on a stateful session bean. Such recovery may be possible with stateless session beans.

  • Even in-memory replication will have an impact on performance. By contrast, there is no need to replicate state between stateless session bean instances in a cluster.

  • Regardless of the replication strategy adopted by a container, correctly reverting the conversational state on a stateful session bean in the event of a transaction rollback is entirely the responsibility of the bean provider. Even if the stateful session bean implements the SessionSynchronization interface (discussed below), causing it to receive notifications on transaction boundaries, potentially complex code must be written to handle rollbacks.

In contrast, the handling of data set in a Servlet API HttpSession is typically more robust. WebLogic and other products offer the option to back HttpSession state persistence with a database, rather than merely offering in-memory replication (of course, database-backed persistence is a severe performance hit).

Perhaps the biggest problem for both reliability and scalability is that an SFSB's state is likely to be replicated as a whole, in its serialized representation, regardless of the extent of changes since it was last replicated. The EJB specification provides no way of identifying which part of a SFSB's state has changed after each method invocation, and it is very difficult for a container to work this out at run time.

The need to replicate each SFSB's entire state typically imposes greater overhead than is necessary for HttpSession objects, to which multiple fine-grained attributes can be bound and replicated only when rebound following changes. Oracle 9iAS, which offers highly configurable state management, provides a non-portable way of using a proprietary StatefulSessionContext to mark which parts of an SFSB's state have changed and need to be replicated, imitating HttpSession behavior (see http://otn.oracle.com/tech/java/oc4j/doc_library/902/ejb/cluster.htm#1005738 for details). The use of a proprietary class indicates how difficult it is to implement fine-grained SFSB replication within the EJB specification.

Often the use of stateful session beans leads to state being held in two locations. Client code can only benefit from the use of a stateful session bean if it holds a reference or handle to it. For example, in a web application, an HttpSession object might hold a reference to the user's stateless session bean. This means that we must manage not one, but two, session state objects for the one client. If we'll need an HttpSession object anyway, we may be able to avoid this by holding all the required session state in the HttpSession object, rather than having an additional stateful session bean in the EJB tier.

One potential way around this is to hold a representation of the session object's handle in a cookie or hidden form field. However, this is more complex than using an HttpSession object and not guaranteed to work in all servers (we don't know how large a handle might be).

Proponents of stateful session beans will argue that this problem of duplication doesn't apply to clients without a web front end. However, such clients are likely to be more sophisticated and interactive than web clients and are more likely to want to hold their own state - a Swing standalone application, for example, has no need for its state to be managed on the server.

Important 

Advocacy of using stateful session beans as the norm for session beans indicates an unrealistic view of J2EE: the view that sees J2EE as it should work. The fact is that SFSBs do not work as developers might expect, even in the most sophisticated application server on the market. This severely reduces their value.

When to Use Stateful Session Beans

I feel that the circumstances under which SFSBs should be used are relatively rare. For example:

  • When a stateless interface would be unduly complex and would require the transport of excessive amounts of user data across the network
    Sometimes attempting to model the EJB tier in terms of stateless interfaces results in excessive complexity: for example, SLSB methods that require an unacceptable amount of session state to be passed with each invocation.

  • When multiple client types require state to be handled in the same location, or even shared
    For example, a SFSB might validly hold state on behalf of an applet and a servlet application. (However, such sharing normally occurs in persistent storage.)

  • To handle clients that frequently connect and disconnect
    In this, a typical scenario, if reliable preservation of state is vital, it must be held in the database. However, if an SFSB offers adequate reliability, the client may be able to store an SFSB handle and efficiently reconnect to the server at will.

  • When scalability is not a major priority
    Sometimes we don't need to consider the performance implications of clustering with stateful session beans - for example, if we're sure that an application will never run on more than one server. In this case, there's no need to complicate design by considering the impact of state replication (however, an HttpSession object is likely to be a simpler alternative in a web application).

The first three of these four criteria apply only to distributed applications.

Important 

Note that none of these criteria justifying the use of SFSBs applies to web applications - the most common use of J2EE.

Session Synchronization

Stateful session beans have one unique property: a stateful session bean can implement the javax.ejb.SessionSynchronization interface to receive callbacks about the progress of transactions they are enlisted in but do not control (EJB §7.5.3, §17.4.1). This interface contains three methods:

     public void afterBegin();     public void beforeCompletion();     public void afterCompletion (boolean success); 

The most important method is afterCompletion(). The success parameter is true if the transaction is committed, false if it is rolled back.

The SessionSynchronization interface allows SFSBs to reset conversational state on transaction rollback, indicated by an afterCompletion() callback with a parameter value of false. We can also use this interface to control data caching: while the bean's state indicates that we're still within a transaction, we can treat cached data as up-to-date. We might also choose to cache resources, such as database connections.

A stateful session bean may only implement the javax.ejb.SessionSynchronization interface if it uses CMT (it wouldn't make sense for a bean with BMT to implement this interface, as it handles transaction delimitation itself and doesn't need to be informed about it). Only three transaction attributes are allowed: Required, RequiresNew, or Mandatory. Stateful session beans implementing the SessionSynchronization interface are passive users of CMT, being informed of the progress of transactions delimited elsewhere (we've so far considered the use of CMT to delimit transactions).

Session synchronization raises serious design concerns. If the stateful session bean doesn't control its own transactions, who does? Stateless session beans don't normally call stateful session beans. Clearly, we don't want entity beans to call stateful session beans. So the likelihood is that the transactions will be managed outside the EJB tier.

Allowing transactions to be managed by remote clients is very dangerous. For example, we must trust the remote caller to end the transaction before it times out - if it doesn't, any resources the stateful bean keeps open may be locked until the container eventually cleans up. Imagine, for example, that the remote client experiences a connection failure after beginning a transaction. The transaction will be left to time out, probably locking valuable resources from other components.

This last danger doesn't apply to transactions managed by objects in the web tier of the same server instance in an integrated application deployment. However, this indicates a likely design mistake: if we are using EJB, why are we using JTA to manage transactions, rather than EJB CMT, which is arguably the biggest benefit that EJB provides?

Important 

Don't use the javax.ejb.SessionSynchronization interface for stateful session beans. This promotes poor application design, with remote clients of the EJB tier delimiting transactions, or the use of JTA for transaction management when EJB CMT would be a better option.

Protecting Stateful Session Beans from Concurrent Calls

It's illegal to make concurrent calls to stateful session beans. When using SFSBs, we need to take care to ensure that this cannot happen.

This situation can easily arise in practice. Imagine that a user has brought up two browser windows on a web site, and is browsing simultaneously in both. In this situation, if user state is held in an SFSB, it's possible that it may experience illegal concurrent invocations. The result will be that some calls will fail.

This problem isn't unique to SFSBs. However we handle user state in web applications, we will need to address it. However, it does show that even when we use SFSBs, we'll need to solve some tricky problems ourselves (in this case, probably by synchronization in web-tier code).

Patterns for Achieving Stateful Functionality with SLSBs

Since SLSBs offer superior scalability and are more robust in a cluster, it's a good idea to tweak design to replace SFSBs with SLSBs if possible. Let's consider some approaches.

Object Parameter

The usual way of mimicking stateful behavior is to pass state from client to SLSB as necessary with each method call. Often, instead of a number of parameters, we pass a single, larger object In web applications, the state will be held in the web tier in HttpSession objects. Passing state objects with each SLSB call isn't usually a problem in collocated applications, but may affect performance in distributed applications.

If the volume of state is very large, we may need to pass a lot of state with each method call. In such cases we may need to re-examine the design. Often holding an excessive amount of user state indicates poor design, which should be questioned. As we've seen in Chapter 6, however we go about implementing our application, holding a large amount of state per user will limit its scalability.

The need for large amounts of state per user may be an indication to use an SFSB. However, if we can implement each use case in a single SLSB method call, the system will still probably be more scalable if we pass state up with each method call. Often state is limited (for example, a primary key that will drive a database lookup).

Using a "Required Workflow Exception" to Mimic an SFSB State Machine

With a little inventiveness, we can sometimes make stateless session beans do the work of stateful session beans. The following example illustrates the use of stateless beans to manage client-side workflow.

I once had to design a registration system for a web portal that enforced new username, password, and profile requirements. Most existing users could simply log in and be marked as active on the new system. Others, whose logins were illegal in the new system, were forced to perform one or more steps to update their account before successful login. For example, some users might be required to change invalid usernames and supply additional profile information in separate operations before logging in. This migration process needed to be managed in the EJB tier, as it was possible that other client types would be added to the initial web application.

My initial design used a stateful session bean - essentially a state machine - to represent the login process. Each user's state largely consisted of the point in the process they had reached. Unfortunately, it emerged that the application had to run on a geographically dispersed cluster and that SFSB replication wasn't a viable option.

I was forced to design a system using stateless session beans. By using exceptions as alternative return values, I succeeded in directing client-side workflow from the EJB tier. No state was required in the web application or EJB tier, and the application proved to scale very well.

The login workflow began with the client (a web-tier controller accessing the UserManager SLSB via a business delegate) invoking the following method on the UserManager SLSB:

    UserProperties login (String login, String password)         throws NoSuchUserException, RequiredWorkflowException, RemoteException; 

A successful return meant that login was complete, and the client had valid user data. The client was forced to catch the two application exceptions. The NoSuchUserException meant that the login process was over; the user's credentials had been rejected. If a RequiredWorkflowException was encountered, the client checked a code in the exception. For example, in the case of duplicate logins, the code was the constant value MUST_IDENTIFY_URTHER. In this case, the controller forwarded the user to a form prompting them to enter their e-mail address. On submission of this form, the client issued another login request, to the following method on the stateless UserManager:

    void identifyUserFromEmail (String login, String password, String email)          throws NoSuchUserException, RequiredWorkflowException, RemoteException; 

This method also threw a NoSuchUserException (if the user could not be identified), or a RequiredWorkflowException with a code indicating whether the user should be forced to change their usename or password. One or other change was always required, but the UserManager implemented rules for determining which.

This method succeeded in concealing the details of account migration in the EJB tier, in a stateless session bean. State was effectively held in the client's browser - the URL of each form submission and attached data prompted calling of the correct EJB method.

Using a Stateful Session Bean as Controller

It's generally agreed that the Model View Controller architectural pattern is most appropriate for web interfaces and many other GUIs. In typical web application architectures, the controller component is a servlet or MVC framework-specific class.

A plausible alternative implementation for web or other GUI applications using EJB - advocated by Sun Java Center - moves the controller inside the EJB container, as a stateful session bean. This gathers controller code in the EJB tier (although some will inevitably remain in the UI tier). This means that the same controller may be usable by different client types, if the SFSB exposes a remote interface.

I feel that this approach illustrates the wishful thinking that characterizes a lot of J2EE literature. It should work. If it did work, the world would be a better place, but I don't know of any real, heavily used, application that adopts it, because of its performance implications. (The Java Pet Store, which does, performs very poorly.)

Performance issues are a key consideration in J2EE design tradeoffs. It's important to minimize the depth of calling down the layers of J2EE architecture to service each client request. If we force each request to call down into the EJB tier, performance and scalability will be seriously affected.

Important 

Use a stateful session bean only after considering reasonable alternatives. Usually, the indications for using a stateful session bean are that: (a) trying to solve the problem with a stateless interface leads to excessive complexity; and (b) the requisite state cannot be held in an HttpSession object in the web tier (perhaps because the application has no web tier).

Note 

The value of stateful session beans is hotly debated among EJB experts. Many share my skepticism. See, for example, http://www.onjava.com/pub/a/onjava/2001/10/02/ejb.html for a highly polemical article criticizing stateful session beans by TylerJewell, a Principal Technology Evangelist for BEA Systems. On the other hand, Sun Java Center architects favor the use of stateful session beans.



Expert One-on-One J2EE Design and Development
Microsoft Office PowerPoint 2007 On Demand
ISBN: B0085SG5O4
EAN: 2147483647
Year: 2005
Pages: 183

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net