EJB lifecycles

  Kevin Boone, Web-Tomorrow

This article describes the lifecycles of the various types of enterprise JavaBean (EJB). It is intended as a guide to help EJB developers understand exactly how their EJBs will be interacting with the EJB container. It aims also to clear up some of the widespread misunderstandings about which EJB methods get called in which circumstances. This document is based on the 1.1 version of the EJB specification.

Session EJB

Session EJBs are intended to represent some kind of service provided to a client, rather than being a model of some entity in the business domain. Session EJBs have a relatively short lifespan, usually no longer than the duration of a particular client-provider interaction. The EJB specification does not stipulate that the state of an EJB should be able to persist in the event of a system failure, although some container providers may have a system to enable this. In any event, a session EJB will normally have a lifespan measured in seconds or minutes, rather that days or years.
     Session EJBs come in two varieties: stateful and stateless. The stateless variety is the simpler, and it has absolutely no conversational state. From the client perspective, every interaction with a stateless session EJB must be complete and self-contained in a single method call. There is no guarantee that if the client calls a different method, or even the same method twice, the method will be called on the same instance of the EJB class. In practice, the container will normally maintain a pool of stateless session beans, and each method call will be dispatched to a free instance in the pool.
     Stateful session EJBs do have a conversational state; once instantiated each instance is associated with a particular client. The client does have create/destroy control over the EJB instance, but the container may also destroy the instance if it detects that it is unused.
     Whether a session EJB is stateful or stateless, the EJB specification dictates that any instance of the EJB can only serve one client at a time. That is, session EJBs are (by design) not re-entrant. If a client gets the remote interface to a session EJB, and calls methods on it in two different threads, this will result in an exception being thrown. The two threads should each get a reference to a remote interaface by calling create on the home interface.

Stateless session EJB

The lifecycle of a stateless session EJB is shown in the state diagram below. Note that these states are of the EJB instance loaded in the container. state diagram Stateless session EJBs are not associated with a particular client. The container can maintain a pool of them, and direct method invocations to any free instances. Therefore, creation and deletion of instances has little connection with the client. When a client calls create() on the home interface, the container need not do anything in particular to any EJB instance. The container must ensure that, when a business method is called, there is at least one instance in the pool. Similarly, the container will delete instances when it sees fit.
     Stateless session EJBs cannot be activated or passivated (as they have no state to save). They cannot take part in transactions in any sense that requires programmatic support (e.g., they cannot implement SessionSynchronization because there is no code to synchronize. Therefore, the stateless session has a very simple lifecycle.

Stateful session EJB

In sharp contrast, the stateful session EJB probably has the most complex lifecycle. This is because it provides programmatic support for transaction monitoring via the SessionSynchronization interface. state diagram The stateful session EJB is associated with a particular client, and will be created and destroyed (indirectly) at the behest of the client. Moreover, because it may persist for some time, it provides an activation/passivation mechanism. Note that the instance may be activated to serve a different client than the one it was talking to when it was passivated. This means that the passivate method must leave the EJB in a state at which the container can serialize it.
     Method calls may be transactional or non-transactional (according to the deployment descriptor). A non-transactional method can be satisfied immediately, and does not involve the SessionSynchronization methods. Note that the container can activate a passivated session to serve a particular method call, if there is no active session currently available.
     If a method is transactional, then the EJB container interacts with a transaction manager. Transactions are committed when the method returns, but bear in mind that the method may call other methods, but the SessionSynchronization methods are only called at the beginning and end of a complete transaction.

Entity EJB

The entity bean represents some form of persistent entity in the business domain, and very often maps onto a row of a database table. In the state diagram below, the lifecycle shown is that of the EJB instance in the container; the database row with which it may be associated has a separated lifecycle. state diagram Note that entity EJBs do not have a conversational state with a client. They are persistent, but not sessional. Thus the container can (and probably will) maintain a pool of EJBs, and associate each with a particular database item when referenced by a client. The ejbActivate() and ejbPassivate() methods are called when the container has changed the mapping of the instance onto the databse; unlike with session EJBs, the entity need not free resources when passivated, or reclaim them when activated.
     The container associates the instance with a data item when the client calls create(...) on the home interface, or calls a business method on the remote interface. It does not do so when the client calls findByPrimaryKey(), or any other finder method. Note on the diagram that the `findXXX' event does not cause a transition to the `ready' state.
     Although entity EJB methods may be transactional, there is no programmatic control of transactions, andtransactional progress is not part of the lifecycle.
     When an EJB instance is in the `ready' state, the container will call ejbLoad() and ejbStore() where necessary to store and load the instance variables to the persistent store. The sequence and frequency with which it does this depends on the `commit option' supported by the container. Typically the container will call ejbLoad() before each business method, and ejbStore() after.

EJB gotchas

EJB development is complicated by the architecture in which an EJB operates. Here are some of the common problems that developers should be aware of. A familiarity with the lifecycle of each of the different EJB types can help to avoid making the following mistakes.

Re-entrancy

SessionBeans are intentionally non-reentrant; the EJB container will thow a RemoteException if a client tries to call methods on the same remote interface with two different threads. Given that a session bean is intended to represent a conversational interaction with a single client, the specification that they be nonre-entrant is logical. Entity beans can, however, be re-entrant if so specified.
     If an entity bean is specified as being re-entrant, then multiple threads will be allowed concurrent access to the same methods in the same instances. However, the specification recommends that this not be allowed, unless it is essential.

Inconsistent use of terms

In the EJB specification, and the EJB API, the same terms are used to mean somewhat different things. A good example is the use of the terms `activation' and `passivation'; these terms mean different things to session and entity EJBs. For a session EJB, the container calls ejbPassivate() to reclaim resources that the session may be holding. The EJB should respond by freeing resources, but in such a manner that they can be reinstated when ejbActivate is called. An entity EJB, on the other hand, is created initially in the passive state. It interprets this to mean that the bean instance is not assoicated with any particular business data (i.e., no particular database row). The container calls ejbActivate() when the association is made between the EJB instance and the business data. In entity beans, activation and passivation are only loosely associated with resource management.

Widespead misconceptions about the specification

The EJB specification has gone through several revisions, and many people are still using code that complied strictly with earlier versions, and does not comply with recent revisions. For example, the current version of the EJB 1.1 specification (page 52) specifically states that an EJB must not store its context in a transient instance variable. And yet, lines like this:
private transient EntityContext ctx;
appear at the start of many EJB classes. Here are some similar examples.
  • There is no counterpart to the method setSessionContext in the SessionBean interface. That is, there is no `unsetSesssionContext()'. This is confusing because there is an unsetEntityContext to correspond with setEntityContext. If the developer provides an unsetSessionContext in the code, it will never be called.
  • Stateless session beans are never passivated; the ejbActivate() and ejbActivate() methods are never called. There is no reason why they should be; if the container wants to free resources held by stateless EJBs, it can just delete them.
  • The ejbFindByPrimaryKey() method does not cause an EJB instance to become associated with a particular database row; it merely verifies that the row exists. The method does not have to do anything except return the primary key passed as a parameter, or throw an exception. It does not have to load the instance variable from the database (it won't hurt, but it's a waste of time).

Use of the same interfaces to specify behaviour in logically different classes

When implementing an EJB class, the code will specify that the class impements one of the EJB interfaces, either SessionBean or EntityBean. The fact that these interfaces are implemented means that the specified methods must be provided by the developer. However, there is no guarantee that any of them will be called. The needs of a stateless session EJB are somewhat different to those of a stateful session, but they implement the same interfaces. In addition, the EJB must provide other methods which are not specified in the interface, but will be called (e.g., ejbCreate()). The section below summarises the methods that must be implemented in each of the EJB types, and when they will be called, if ever.

Summary of EJB-class method requirements

When implementing an EJB class, the code will specify that the class impements one of the EJB interfaces, either SessionBean or EntityBean. The fact that these interfaces are implemented means that the specified methods must be provided by the developer. However, there is no guarantee that any of them will be called. In addition, the EJB must provide other methods which are not specified in the interface, but will be called (e.g., ejbCreate()). The table below shows the methods that must be implemented in each of the EJB types, and when they will be called, if ever.

Stateless session bean (implements SessionBean)
ejbCreate() Called when container wants to create an instance; not directly linked to a client call
setSessionContext() called by the container just before ejbCreate(). The EJB should save a reference to the session context for future use.
ejbActivate() Never called
ejbPassivate() Never called
ejbRemove() Called when container wants to remove the instance; not directly linked to a client call
Stateful session bean (implements SessionBean, and optionally SessionSynchronization)
ejbCreate(...) Called when the client calls create on the home interface
setSessionContext() called by the container just before ejbCreate(). The EJB should save a reference to the session context for future use.
ejbActivate() Called by the container when any method is called and the EJB is in the `passive' state.
ejbPassivate() Called by the container when resources are to be reclaimed. The EJB specification dictates that this method must leave the instance in a state ready to be serialized. In particular, it must close all open JDBC connections.
ejbRemove() Called when container wants to remove the instance; not directly linked to a client call
afterBegin() (if specified) Called when the container is about to execute a method in a new transactional context. The EJB should save its state so that it can be recovered if a transaction fails and rolls back.
beforeCompletion() (if specified) Called when the container is about to request the transaction manager to commit a transaction. This is the last chance that the EJB has to prevent the transaction being committed (by calling setRollbackOnly).
afterCompletion(boolean commit) (if specified) Called by the container when a transaction has completed. If the commit flag if false, the transaction failed and the EJB should restore the state it saved in afterBegin.
Entity bean with bean-managed persistence (implements EntityBean)
ejbCreate(...) Called by the container when the client calls a create() method. The EJB should initialize instance variables from the parameters, and create a new representation of the object in the persistent store
ejbPostCreate(...) Called by the container directly after ejbCreate(). Anything done in this method could, in principle be done in ejbCreate(), as the two methods are always called sequentially. However, the method must be implemented, even if empty.
setEntityContext() called by the container just before ejbCreate(). The EJB should save a reference to the entity context for future use.
ejbActivate() Called by the container when a business method is called and the EJB is in the passive (pooled) state, or whenever the container has just associated the instance with a particular database entry. This method is very often empty, but must be implemented.
ejbPassivate() Called by the container when it is about to dissociate the instance from a particular database entry. This method is very often empty, but must be implemented.
ejbLoad() Called by the container when the instance should take on the identity of a particular database item. The method should load the instance variables from the persistent store, perhaps using JDBC calls
ejbStore() Called by the container when the instance must write its identity to the persistent store. The method should do this, using JDBC calls for example.
ejbRemove() Called when container wants to remove the instance; not directly linked to a client call. This method should remove the instance's representation from the persistent store.
ejbFindByPrimaryKey() Called by the container when the client calls findByPrimaryKey() on the home interface. This method should simply verify that the primary key supplied corresponds to a real database entry. It does not need to set the instance variables (contrary to popular opinion). Must be implemented.
ejbFindXXX() Called by the container when the client calls findXXX() on the home interface. This method should return a collection of primary key class instances. Must be implemented if specified in the home interface.
Entity bean with container-managed persistence (implements EntityBean)
ejbCreate(...) Called by the container when the client calls a create() method. The EJB should initialize instance variables from the parameters and return `null'. The container will create a corresponding database entry. At least one ejbCreate(...) must be implemented, and should match the signature in the home interface.
ejbPostCreate(...) Called by the container directly after ejbCreate(). Anything done in this method could, in principle be done in ejbCreate(), as the two methods are always called sequentially. Must be implemented, to match the signature of ejbCreate(), even if unused.
setEntityContext() Called by the container just before ejbCreate(). The EJB should save a reference to the entity context for future use.
ejbActivate() Called by the container when a business method is called and the EJB is in the passive (pooled) state, or whenever the container has just associated the instance with a particular database entry. This method is very often empty, but must be implemented.
ejbPassivate() Called by the container when it is about to dissociate the instance from a particular database entry. This method is very often empty, but must be implemented.
ejbLoad() Called by the container when the instance should take on the identity of a particular database item. With CMP, normally empty, but must be implemented.
ejbStore() Called by the container when the instance must write its identity to the persistent store. With CMP, normally empty, but must be implemented.
ejbRemove() Called when container wants to remove the instance; not directly linked to a client call. Normally empty, but must be implemented.
ejbFindByPrimaryKey() Never called; need not be implemented.
ejbFindXXX() Never called; need not be implemented.

©1994-2003 Kevin Boone, all rights reserved