The biggest difference between the stateful session bean and the other bean types is that stateful session beans do not use instance pooling. Stateful session beans are dedicated to one client for their entire lives, so there is no swapping or pooling of instances.[46] Instead of being pooled, stateful session bean instances are simply evicted from memory to conserve resources. The EJB object remains connected to the client, but the bean instance is dereferenced and garbage collected during inactive periods. This means that each stateful bean must be passivated before it is evicted to preserve the conversational state of the instance, and it must be activated to restore the state when the EJB object becomes active again.
The bean’s perception of its life cycle depends on whether or
not it implements a special interface called
javax.ejb.SessionSynchronization
. This interface
defines an additional set of callback methods that notify the bean of
its participation in transactions. A bean that implements
SessionSynchronization
can cache database data
across several method calls before making an update. We have not
discussed transactions in detail yet, so we will not consider this
part of the bean’s life cycle until Chapter 14. This section describes the life cycle of
stateful session beans that do not implement the
SessionSynchronization
interface.
The life cycle of a stateful session bean has three states: Does Not Exist, Method-Ready, and Passivated. This sounds a lot like a stateless session bean, but the Method-Ready state is significantly different from the Method-Ready Pool of stateless beans. Figure 12-2 shows the state diagram for stateful session beans.
When a stateful bean instance is in the Does Not Exist state, it is not an instance in the memory of the system. In other words, it has not been instantiated yet.
The Method-Ready state is the state in which the bean instance can service requests from its clients. This section explore the instance’s transition into and out of the Method-Ready state.
When a client invokes the
create()
method on an EJB home of a stateful
session bean, the bean’s life cycle begins. When the
create()
method is received by the container, the
container invokes newInstance()
on the bean class,
creating a new instance of the bean. Next, the container invokes
setSessionContext()
on the instance, handing it
its reference to the SessionContext
, which it must
maintain for life. At this point, the bean instance is assigned to
its EJB object. Finally, the container invokes the
ejbCreate()
method on the instance that matches
the create()
method invoked by the client. Once
ejbCreate()
has completed, the container returns
the EJB object’s reference to the client. The instance is now
in the Method-Ready state and is ready to service business methods
invoked by the client on the bean’s remote reference.
While in the Method-Ready state, the bean instance is free to receive method invocations from the client, which may involve controlling the workflow of other beans or accessing the database directly. During this time, the bean can maintain conversational state and open resources in its instance variables.
Bean instances leave the Method-Ready state to enter either the
Passivated state or the Does Not Exist state. Depending on how the
client uses the stateful bean, the EJB container’s load, and
the passivation algorithm used by the vendor, a bean instance may be
passivated (and activated) several times in its life or not at all.
If the bean is removed, it enters the Does Not Exist state. A client
application can remove a bean by invoking one of the
remove()
methods on the client API, or the
container can choose to remove the bean.
The container can also
move the bean instance from the
Method-Ready state to the Does Not Exist state if the bean times out.
Timeouts are declared at deployment time in a vendor-specific manner.
When a timeout occurs in the Method-Ready state, the container may,
but is not required to, call the ejbRemove()
method. A stateful bean cannot time out while a transaction is in
progress.
During the lifetime of a stateful session bean, there may be periods of inactivity when the bean instance is not servicing methods from the client. To conserve resources, the container can passivate the bean instance while it is inactive by preserving its conversational state and evicting the bean instance from memory.
When a stateful bean is passivated, the instance fields are read and then written to the secondary storage associated with the EJB object. When the stateful session bean has been successfully passivated, the instance is evicted from memory; it is destroyed.
When a bean is about to be passivated, its
ejbPassivate()
method is invoked, alerting the
bean instance that it is about to enter the Passivated state. At this
time, the bean instance should close any open resources and set all
nontransient, nonserializable fields to null
. This
will prevent problems from occurring when the bean is serialized.
Transient
fields will simply be ignored.
A bean’s conversational state may consist of only primitive values, objects that are serializable, and the following special types:
EJB 2.0 and 1.1
javax.ejb.SessionContext
|
javax.ejb.EJBHome (remote home interface types) |
javax.ejb.EJBObject (remote interface types) |
javax.jta.UserTransaction (bean transaction interface) |
javax.naming.Context (only when it references the JNDI ENC) |
EJB 2.0 only
javax.ejb.EJBLocalHome (local home interface types) |
javax.ejb.EJBLocalObject (local interface types) |
References to managed resource factories (e.g., javax.sql.DataSource ) |
The types in this list (and their subtypes) are handled specially by the passivation mechanism. They do not need to be serializable; they will be maintained through passivation and restored automatically to the bean instance when it is activated.
A bean instance’s conversational state is written to secondary storage to preserve it when the instance is passivated and destroyed. Containers can use standard Java serialization to preserve the bean instance, or some other mechanism that achieves the same result. Some vendors, for example, simply read the values of the fields and store them in a cache. The container is required to preserve remote references to other beans with the conversational state. When the bean is activated, the container must restore any bean references automatically. The container must also restore any references to the special types listed earlier.
Fields declared transient
will not be preserved
when the bean is passivated. Except for the special types listed
earlier, all fields that are nontransient and nonserializable must be
set to null
before the instance is passivated or
else the container will destroy the bean instance, making it
unavailable for continued use by the client. References to special
types must automatically be preserved with the serialized bean
instance by the container so that they can be reconstructed when the
bean is activated.
When the client makes a request on an EJB
object whose bean is passivated, the container activates the
instance. This involves deserializing the bean instance and
reconstructing the SessionContext
reference, bean
references, and managed resource factories (EJB 2.0 only) held by the
instance before it was passivated. When a bean’s conversational
state has been successfully restored, the
ejbActivate()
method is
invoked. The bean instance should open any resources that cannot be
passivated and initialize the values of any transient fields within
the ejbActivate()
method. Once
ejbActivate()
is complete, the bean is back in the
Method-Ready state and available to service client requests delegated
by the EJB object.
In EJB 1.1, open resources such as sockets or JDBC connections must be closed whenever the bean is passivated. In stateful session beans, open resources will not be maintained for the life of the bean instance. When a stateful session bean is passivated, any open resource can cause problems with the activation mechanism.
The activation of a bean instance follows the rules of Java
serialization. The
exception to this is transient fields. In Java serialization,
transient fields are set to their default values when an object is
deserialized; primitive numbers become zero, Boolean fields
false
, and object references
null
. In EJB, transient fields do not have to be
set to their initial values; therefore, they can contain arbitrary
values when the bean is activated. The values held by transient
fields following activation are unpredictable across vendor
implementations, so do not depend on them to be initialized. Instead,
use ejbActivate()
to reset their values.
The container can also move the bean instance from the Passivated
state to the Does Not Exist state if the bean times out. When a
timeout occurs in the
Passivated state, the
ejbRemove()
method is not invoked.
Whenever a system exception is thrown by a bean
method, the container invalidates the EJB object and destroys the
bean instance. The bean instance moves directly to the Does Not Exist
state and the ejbRemove()
method is
not invoked.
A system exception is any unchecked exception, including
EJBException
. Checked exceptions thrown from
subsystems are usually wrapped in an EJBException
and rethrown as system exceptions. A checked exception thrown by a
subsystem does not need to be handled this way if the bean can safely
recover from the exception. In most cases, however, the subsystem
exception should be rethrown as an EJBException
.
In EJB 1.1, the java.rmi.RemoteException
is also
considered a system exception for backward compatibly with EJB 1.0.
However, throwing the RemoteException
from a bean
class method has been deprecated and
is discouraged.
[46] Some vendors use pooling with stateful session beans, but that is a proprietary implementation and should not impact the specified life cycle of the stateful session bean.