The biggest difference between the stateful session bean and the other bean types is that stateful session beans don’t use instance pooling. Stateful session beans are dedicated to one client for their entire life, so there is no swapping or pooling of instances.[34] Instead of pooling instances, stateful session beans 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 a 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 any detail yet, so we will not consider
this view of the stateful session bean’s life cycle until Chapter 8. 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. Figures Figure 7.2 and Figure 7.3 show the state diagrams for stateful session beans in EJB 1.1 and EJB 1.1.
Like the entity bean and stateless session bean, when a 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.
When a
client invokes the
create()
method on
an EJB home of a stateful session bean, its 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. At this point, the bean
instance is assigned to its EJB object. Next, the container invokes
setSessionContext()
on the instance, handing it
its reference to the SessionContext
, which it must
maintain for life. 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 remote 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. During its lifetime, a
bean instance will be passivated and activated zero or more times.
It’s likely that it will be passivated at least once, passing
into the Passivated state. The bean enters the Does Not Exist state
if it is removed. 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
remove the bean instance from the Method-Ready State if the bean times out. Timeouts are declared at
deployment time in a manner specific to the EJB vendor. When a
timeout occurs, the ejbRemove()
method is
not invoked. A stateful bean cannot time out
while a transaction is in progress.
The container removes the bean if it times out (the timeout period is
set in the deployment descriptor). When a bean is removed, its
ejbRemove()
method is invoked, giving the bean
instance an opportunity to close any open resources and invoke
remove()
on any session beans it has referenced.
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.
javax.ejb.SessionContext
javax.ejb.EJBHome
(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)
The types in this list (and their subtypes) are handled specially by the passivation mechanism. They don’t 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 will be 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, will 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.
A bean instance can time out while it is passivated. If a timeout
occurs, the container will discard the instance, returning it to the
Does Not Exist state. The ejbRemove()
method will
not be called on an instance that times out.
With the exception of the SessionContext
and
remote references to other beans, conversational state must be
primitive values or objects that are serializable. This is because
the bean instance’s conversational state will be written to
secondary storage to preserve it when the instance is 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, will 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 the
SessionContext
reference automatically.[35]
Nonserializable object references and variables labeled as
transient
will not be preserved when the bean is
passivated. Fields that are nontransient and nonserializable must be
set to null
before the instance is passivated or
the container can destroy the bean, making it unavailable for
continued use by the client. References to beans and the
SessionContext
must be automatically 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 and bean references held
by the instance before it was passivated. When a bean’s
conversational state has been successfully activated, the
ejbActivate()
method is invoked. The bean instance should open any resources needed
and initialize the value 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.
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, Booleans
false
, and object references
null
. In EJB, transient fields do not have to be
set to their initial values; therefore, they could contain arbitrary
values when the bean is activated. The value held by transient fields
following activation is unpredictable across vendor implementations,
so don’t depend on them to be initialized. Instead, use
ejbActivate()
to reset their
values
.
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 nonapplication exception including
RemoteException
, EJBException
,
and any unchecked exceptions.
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 a EJBException
.
[34] Some vendors use pooling with stateful session beans, but that is a proprietary implementation and shouldn’t impact the specified life cycle of the stateful session bean.
[35] References to SessionContext
or
EntityContext
in a bean class should not be
transient. At the time of this writing, however, at least one major
vendor required that references to
SessionContext
in session beans be transient. This
is a proprietary requirement and is noncompliant with the
specification.