7.5. Implementing Session Beans

Now that we've seen a simple bean example, let's move on and talk about the specifics of implementing session beans (we'll get to entity beans after that). A session bean is much like a regular remote object, with the added benefit of being a JavaBeans component. The session bean serves as a remote extension of the client, running on a remote EJB server. Usually, a session bean is used by a single client, and the state data maintained by the session bean is owned by this client. The client acquires a reference to a session bean, and asks it to perform services by calling methods on the bean. These method calls might retrieve or update data in a remote database, filter data to be returned to the client, or update the session-related state information (if any) that the client is maintaining with the bean.

A session bean doesn't live beyond the lifetime of its server. If your client has a reference to a session bean, and the server restarts, that session bean reference is no longer valid. You can reacquire a session bean of the same type from the same server, but it's not guaranteed to be in the same state as the bean you had before the server restart. An EJB container also has the option of destroying a session bean after some timeout period while the bean is in an inactive state on the server (i.e., if there are no client references to the session bean for a period that exceeds the session timeout for the bean).

Stateful session beans can optionally receive notification of transaction boundaries from the EJB container. The container notifies the bean when a new client transaction is beginning and when the client transaction has either been completed or rolled back. If the session bean receives a rollback notification, it should manually reset its state information.

In addition to the standard EJB object methods mentioned in the
previous section, a session bean also needs to implement a
setSessionContext() method, as specified in the
SessionBean interface. The container calls this
method on the session bean just after the bean has been created,
passing in a javax.ejb.SessionContext object that
represents the runtime context for the bean. The session context is
valid for the life of the bean. The session bean can use the
SessionContext to get a reference to the remote
object associated with the bean, by calling the
getEJBObject() method on the context object. Since
the bean is not required to implement the remote interface for the
bean, this object may be different from the bean itself, and may
implement a class generated by the server based on the remote
interface, the home interface, and the bean implementation you
provided. More about that later, when we talk about deploying EJB
objects.

The SessionContext that the container passes to a
session bean is also an EJBContext, which is a
general representation for runtime context information, regardless
of whether the bean is an entity or session bean. Among other
things, the EJBContext has accessors that allow
the bean to get a reference to its home interface
(getEJBHome()), a list of environment properties
used to deploy the bean (getEnvironment()), and
the identity of the client that is currently executing a transaction
with the bean (getCallerIdentity()).

7.5.1. Stateless Versus Stateful Session Beans

Session beans can be either stateful or stateless. A
stateless session bean does not maintain state
across method calls. If a client makes a series of remote method
calls and/or transactions with the stateless bean, the bean is in
the same state at the start of each method call or transaction. Our
ProfileServerBean is such a bean. Stateless
session beans of the same type can be considered identical to each
other, and can be pooled and reused by multiple clients. A stateless
session bean can be used concurrently by multiple remote clients
without fear of conflicting with each other, since there is no
shared state data that can be corrupted. Stateless beans don't
need to be passivated since they have no state that needs to be
restored when they're reactivated. The container simply
destroys any stateless session beans it feels are no longer needed.

A stateful session bean, on the other hand, does
maintain state that can be accessed and changed directly by the
client's interactions with the bean. A stateful session bean
is generally not intended to be accessed by more than a single
remote client; the state of the stateful session bean along with its
remote methods act as an extension of the client that created the
bean.

To illustrate the difference between stateless and stateful
session beans, let's take our
ProfileServerBean and convert it to a stateful
session bean. The ProfileServerBean is stateless
because all it does is accept requests for user profiles and return
the profiles directly to the client as RMI object references. The
client then interacts with the Profile object
directly, and the Profile manages the state of the
interaction, in the form of the values of the profile entries. If the
profile were a stateful enterprise bean itself, we wouldn't need
the ProfileServer at all.

Example 7-5 shows the remote interface for a stateful Profile bean. It's similar to the remote interface for the RMI-based Profile we used in the stateless ProfileServerBean example. It has setEntry() and getEntry() methods that access entries using their names. The Profile bean also has accessors for the name of its user.

The implementation of the stateful ProfileBean is shown in Example 7-6. It has the requisite implementations for the bean methods needed by the container and includes two ejbCreate() methods: one with no arguments that creates an unnamed profile and another that takes the name of the user of the profile. The corresponding create() methods on the ProfileHome interface are shown in Example 7-7. The state of this stateful session bean is maintained in a String field that holds the profile user's name and a Properties object that keeps the profile entries. The principal design difference between the ProfileBean and the stateless ProfileServerBean is the state information stored on the ProfileBean in its data members. The get/set accessors from the remote Profile interface are implemented here as operations on these fields.

After getting the home interface for the ProfileBean, the client creates a profile for a named user, sets the values for some profile entries, and gets them back again.

An EJB container must be told at deployment time whether a session bean is stateful or stateless. The container uses this information to determine how to handle pooling of the session beans and whether to passivate the bean or not, among other things. Since stateless beans can be used by any client, the container pools stateless beans and doles them out to clients as needed. If new stateless beans are needed, the container creates them, and when they aren't needed (e.g., the rate of client requests decreases), they are simply destroyed. In order to allow the container to fill its pool, any stateless session bean must provide a single create() method with no arguments. Stateless beans implement only the no-argument creation method, since they have no client state that could be affected by arguments. An additional restriction on stateless beans is that they cannot participate in transaction synchronization and cannot implement the SessionSynchronization interface, which is described in the next section.

7.5.2. Optional Transaction Support

Since session beans don't typically represent persistent shared data, and stateful session beans can only be accessed by a single client at a time, user transaction boundaries may not be important to such a bean. If, however, the session bean is managing database data for the user, it may want to know about the beginning and ending of user transactions, so that it can cache data at the start and commit its database updates at the end. For this reason, the EJB specification allows session beans to optionally implement the javax.ejb.SessionSynchronization interface. By implementing this interface, the session bean indicates that it wants the container to notify it about the beginning and end of transactions.

In this case, the bean must implement the three methods declared on the interface: afterBegins(), beforeCompletion(), and afterCompletion(). The container calls the bean's afterBegin() method just after a new transaction begins. This lets the bean allocate any resources it might need during the transaction and cache database data, for example. Just before the transaction completes, the container calls the bean's beforeCompletion() method. In this method, the bean can release any resources or cached data it may have initialized during the transaction. The afterCompletion() method is called just after the transaction has completed. The container passes in a boolean value that is true if the transaction was committed and false if the transaction was rolled back. The bean can use this notification to deal with rollbacks, for example, allowing the bean to undo any changes made during the transaction.