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.
Session beans implement the javax.ejb.SessionBean interface. This interface extends the javax.ejb.EnterpriseBean interface and specifies ejbActivate(), ejbPassivate(), ejbRemove(), and setSessionContext() methods.
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()).
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.
import javax.ejb.*; import java.rmi.Remote; import java.rmi.RemoteException; public interface Profile extends Remote, EJBObject { public String getName() throws RemoteException; public void setName(String name) throws RemoteException; public String getEntry(String key) throws RemoteException; public void setEntry(String key, String value) throws RemoteException; }
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.
import javax.ejb.*; import java.rmi.RemoteException; import java.util.Properties; public class ProfileBean implements SessionBean { // Name of the person owning the profile private String mName = ""; // Entries in the profile (name/value pairs) private Properties mEntries = new Properties(); // Store session context private SessionContext mContext = null; // Session bean methods public void ejbActivate() { System.out.println("ProfileBean activated."); } public void ejbRemove() { System.out.println("ProfileBean removed."); } public void ejbPassivate() { System.out.println("ProfileBean passivated."); } public void setSessionContext(SessionContext context) { System.out.println("ProfileBean context set."); mContext = context; } public void ejbCreate() { System.out.println("Nameless ProfileBean created."); } public void ejbCreate(String name) throws NoSuchPersonException { mName = name; System.out.println("ProfileBean created for " + mName + "."); } // Business methods public String getName() { return mName; } public void setName(String name) { mName = name; } public String getEntry(String key) { return mEntries.getProperty(key); } public void setEntry(String key, String value) { mEntries.put(key, value); } }
import javax.ejb.*; import java.rmi.RemoteException; public interface ProfileHome extends EJBHome { public Profile create() throws RemoteException, CreateException; public Profile create(String name) throws RemoteException, CreateException; }
This stateful bean is used by clients to maintain a set of application-specific profile entries for a named user. Here is an example client scenario:
// Get the Profile bean's home interface ProfileHome pHome = ... // Create a profile for a person System.out.println("Creating profile for " + name); Profile profile = pHome.create(name); // Get/set some entries in the profile System.out.println("Setting profile entries for " + name); profile.setEntry("favoriteColor", "blue"); profile.setEntry("language", "German"); System.out.println("Getting profile entries for " + name); System.out.println("\tFavorite color: " + profile.getEntry("favoriteColor")); System.out.println("\tLanguage: " + profile.getEntry("language"));
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.
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.
Copyright © 2001 O'Reilly & Associates. All rights reserved.
This HTML Help has been published using the chm2web software. |