Stateful session beans (EJB 3)

Stateful session beans are guaranteed to maintain conversational state. They are not programmatically very different from their stateless cousins: as a matter of fact, the only real difference between stateless and stateful beans is how the container manages their lifecycle. Unlike with stateless beans, the container makes sure that subsequent method invocations by the same client are handled by the same stateful bean instance. Figure 3.6 shows the one-to-one mapping between a bean instance and a client enforced behind the scenes by the container. As far as you are concerned, this one-to-one relation management happens "automagically."

The one-to-one mapping between a client and a bean instance makes saving bean conversational state in a useful manner possible. However, this one-to-one correlation comes at a price. Bean instances cannot be readily returned to the pool and reused while a client session is still active. Instead, a bean instance must be squirreled away in memory to wait for the next request from the client owning the session. As a result, stateful session bean instances held by a large number of concurrent clients can have a significant memory footprint. An optimization technique called passivation, which we’ll discuss soon, is used to alleviate this problem. Stateful session beans are ideal for multistep, workflow-oriented business processes. In this section, we explore stateful beans by using the ActionBazaar bidder account creation workflow as an example.


Stateful bean session maintenance. There is a bean instance reserved for a client and each instance stores the client's state information. The bean instance exists until either removed by the client or timed out.

Figure 3.6 Stateful bean session maintenance. There is a bean instance reserved for a client and each instance stores the client’s state information. The bean instance exists until either removed by the client or timed out.

We describe a use case of our ActionBazaar application and implement it using a stateful session bean. We show you additional programming rules for stateful session beans, and then we examine stateful bean lifecycle callback methods and the @Remove annotation. Finally, we summarize differences between stateless and stateful session beans.

However, before we jump into code, let’s briefly examine the rules specific to developing a stateful session bean.

Additional programming rules

In section 3.1.3, we discussed the programming rules that apply to all session beans. Stateful session beans have a few minor twists on these rules:

■ Stateful bean instance variables used to store conversational state must be Java primitives or Serializable objects. We’ll talk more about this requirement when we cover passivation.

■ Since stateful session beans cannot be pooled and reused like stateless beans, there is a real danger of accumulating too many of them if we don’t have a way to destroy them. Therefore, we have to define a business method for removing the bean instance by the client using the @Remove annotation. We’ll talk more about this annotation soon.

■ In addition to the PostConstruct and PreDestroy lifecycle callback methods, stateful session beans have the PrePassivate and PostActivate lifecycle callback methods. A PrePassivate method is invoked before a stateful bean instance is passivated and PostActivate is invoked after a bean instance is brought back into the memory and is method ready.

You’ll see these rules applied when we explore a concrete stateful session beans example next. As we did for stateless beans, we’ll utilize the example as a jump-ing-off point to detail stateful features.

The BidderAccountCreatorBean example

The process to create an ActionBazaar bidder account is too involved to be implemented as a single-step action. As a result, account creation is implemented as a multistep process. At each step of the workflow, the would-be bidder enters digestible units of data. For example, the bidder may enter username/password information first; then biographical information such as name, address, and contact information; then billing information such as credit card and bank account data; and so forth. At the end of a workflow, the bidder account is created or the entire task is abandoned. This workflow is depicted in figure 3.7.

The ActionBazaar bidder account creation process is broken up into multiple steps: entering username/password, entering biographical information, entering billing information, and finally creating the account. This workflow could be implemented as a stateful session bean.

Figure 3.7 The ActionBazaar bidder account creation process is broken up into multiple steps: entering username/password, entering biographical information, entering billing information, and finally creating the account. This workflow could be implemented as a stateful session bean.

Each step of the workflow is implemented as a method of the BidderAccountCre-atorBean presented in listing 3.2. Data gathered in each step is incrementally cached into the stateful session bean as instance variable values. Calling either the cancelAccountCreation or createAccount method ends the workflow. The createAccount method creates the bidder account in the database and is supposed to be the last "normal" step of the workflow. The cancelAccountCreation method, on the other hand, prematurely terminates the process when called by the client at any point in the workflow and nothing is saved into the database.

Listing 3.2 Stateful session bean example

Listing 3.2 Stateful session bean example

Listing 3.2 Stateful session bean example

Listing 3.2 Stateful session bean example

As we mentioned earlier, it should not surprise you that the code has a lot in common with the stateless session bean code in listing 3.1.

NOTE As before, we are using JDBC for simplicity in this example because we want you to focus on the session bean code right now and not JPA. We’ll cover JPA in the part 3 of this topic. An interesting exercise for you is to refactor this code using JPA and notice the radical improvement over JDBC!

We are using the @Stateful annotation to mark the BidderAccountCreatorBean POJO O. Other than the annotation name, this annotation behaves exactly like the @Stateless annotation, so we won’t mention it any further. The bean implements the BidderAccountCreator remote business interface. As per stateful bean programming rules, the BidderAccountCreatorBean has a no-argument constructor.

Just like in listing 3.1, a JDBC data source is injected using the @Resource annotation. Both the PostConstruct G and PostPassivate Q callbacks prepare the bean for use by opening a database connection from the injected data source. On the other hand, both the PrePassivate Q and PreDestroy Q callbacks close the cached connection.

The loginInfo, biographicalInfo, and billingInfo instance variables are used to store client conversational state across business method calls Q. Each of the business methods models a step in the account creation workflow and incrementally populates the state instance variables. The workflow is terminated when the client invokes either of the @Remove annotated methods Q.

There is no point to repeating our discussion of the features that are identical to the ones for stateless session beans, so we’ll avoid doing so. However, let’s explore the features unique to stateful session beans next, starting with the state-ful bean business interfaces.

Business interfaces for stateful beans

Specifying stateful bean business interfaces works in almost exactly the same way as it does for stateless beans—with a couple of exceptions. Stateful session beans support local and remote invocation through the @Local and @Remote annotations. However, a stateful session bean cannot have a web service endpoint interface. This is because SOAP-based web services are inherently stateless in nature. Also, you should always include at least one @Remove annotated method in your stateful bean’s business interface. The reason for this will become clear as we discuss the stateful bean lifecycle next.

Stateful bean lifecycle callbacks

As we mentioned in section 3.1, the lifecycle of the stateful session bean is very different from that of a stateless session bean because of passivation. In this section, we explain this concept in more depth. Let’s start by looking at the lifecycle of a stateful bean, as shown in figure 3.8. The container follows these steps:

1 Always creates new bean instances using the default constructor whenever a new client session is started.

2 Injects resources.

3 Stores the instance in memory.

4 Executes the requested business method invoked through the business interface by the client.

5 Waits for and executes subsequent client requests.

6 If the client remains idle for a period of time, the container passivates the bean instance. Essentially, passivation means that the bean is moved out of active memory, serialized, and stored in temporary storage.

7 If the client invokes a passivated bean, it is activated (brought back into memory from temporary storage).

8 If the client does not invoke a passivated bean instance for a period of time, it is destroyed.

9 If the client requests the removal of a bean instance, it is first activated if necessary and then destroyed.

The lifecycle of a stateful session bean. A stateful bean maintains client state and cannot be pooled. It may be passivated when the client is not using it and must be activated when the client needs it again.

Figure 3.8 The lifecycle of a stateful session bean. A stateful bean maintains client state and cannot be pooled. It may be passivated when the client is not using it and must be activated when the client needs it again.

Like a stateless session bean, the stateful session bean has lifecycle callback methods, or callbacks, that are invoked when the PostConstruct event occurs (as an instance is created) and when the PreDestroy event occurs (before the instance is destroyed). But now we have two new callback events for which we can have callbacks: PrePassivate and PostActivate, which are part of the passivation process. We’ll discuss them next.

Just as in listing 3.1, we use a PostConstruct callback in listing 3.2 to open a database connection from the injected data source so that it can be used by business methods. Also as in listing 3.1, we close the cached connection in preparation for bean destruction in a PreDestroy callback. However, you should note that we invoke the very same method for both the PreDestroy and PrePassivate callbacks:

tmp2486_thumb

Similarly, the exact same action is taken for both the PostConstruct and Post-Activate callbacks:

tmp2487_thumb

To see why this is the case, let’s discuss activation and passivation in a little more detail.

Passivation and activation

If clients don’t invoke a bean for a long enough time, it is not a good idea to continue keeping it in memory. For a large number of beans, this could easily make the machine run out of memory. The container employs the technique of passivation to save memory when possible.

NOTE Passivation essentially means saving a bean instance into disk instead of holding it in memory. The container accomplishes this task by serializing the entire bean instance and moving it into permanent storage like a file or the database. Activation is the opposite of passivation and is done when the bean instance is needed again. The container activates a bean instance by retrieving it from permanent storage, deserializing it, and moving it back into memory. This means that all bean instance variables that you care about and should be saved into permanent storage must either be a Java primitive or implement the java.io. Serializable interface.

The point of the PrePassivate callback is to give the bean a chance to prepare for serialization. This may include copying nonserializable variable values into Serializable variables and clearing unneeded data out of those variables to save total disk space needed to store the bean. Most often the prepassivation step consists of releasing heavy-duty resources such as open database, messaging server, and socket connections that cannot be serialized. A well-behaved bean should ensure that heavy-duty resources are both closed and explicitly set to null before passivation takes place.

From the perspective of a bean instance, there isn’t much of a difference between being passivated and being destroyed. In both cases, the current instance in memory would cease to exist. As a result, in most cases you’ll find that the same actions are performed for both the PreDestroy and PrePassivate callbacks, as we do in listing 3.2. Pretty much the same applies for the PostConstruct-Post-Activate pair. For both callbacks, the bean needs to do whatever is necessary to make itself ready to service the next incoming request. Nine times out of ten, this means getting hold of resources that either are not instantiated or were lost during the serialization/deserialization process. Again, listing 3.2 is a good example since the java.sql.Connection object cannot be serialized and must be reinstan-tiated during activation.

Destroying a stateful session bean

In listing 3.2, the cancelAccountCreation and createAccount methods are marked with the @Remove annotation. Beyond the obvious importance of these methods in implementing vital workflow logic, they play an important role in maintaining application server performance. Calling business methods marked with the @Remove annotation signifies a desire by the client to end the session. As a result, invoking these methods triggers immediate bean destruction.

To gain an appreciation for this feature, consider what would happen if it did not exist. If remove methods were not an option, the client would have no way of telling the container when a session should be ended. As a result, every stateful bean instance ever created would always have to be timed out to be passivated (if the container implementation supports passivation) and timed out again to be finally destroyed. In a highly concurrent system, this could have a drastic performance impact. The memory footprint for the server would constantly be artificially high, not to mention how there would be wasted CPU cycles and disk space used in the unnecessary activation/passivation process. This is why it is critical that you remove stateful bean instances when the client is finished with its work instead of relying on the container to destroy them when they time out.

Believe it or not, these are the only few stateful bean-specific features that we needed to talk about! Before concluding this section on stateful beans, we’ll briefly summarize the differences between stateful and stateless session beans as a handy reference in table 3.2.

Table 3.2 The main differences between stateless and stateful session beans

Features

Stateless

Stateful

Conversational state

No

Yes

Pooling

Yes

No

Performance problems

Unlikely

Possible

Lifecycle events

tmp24-88 tmp24-89

Timer

(discussed in chapter 5)

Yes

No

tmp24-90

No

Yes

Web services

Yes

No

tmp24-91

No

Yes

Thus far we have explored how to develop session beans. In the next section, we discuss how session beans are actually accessed and used by clients.

Next post:

Previous post: