The Stateless Session Bean (Enterprise JavaBeans 3.1)

 

The stateless session bean is designed for efficiency and simplicity. Central to its design is the absence of conversational state, which is a topic worth exploring a bit further before delving into SLSB mechanics.

When we say conversational state, we refer to information exchanged and remembered within the context of a series of requests between the Client (caller) and EJB (service). We can illustrate this simply:

tmp95-37_thumb

Here, the state variable holding the name of the client is stored for the duration of the conversation. Stateless session beans are incapable of handling this exchange correctly; each invocation upon a SLSB proxy operates independently from those both before and after (see Figure 5-1). In fact, its underlying bean instances may be swapped interchangeably between requests.

Stateless session beans are EJB’s answer to traditional transaction-processing applications, which are executed using a procedure call. The procedure runs from beginning to end, finally returning the result. Once the invocation completes, nothing about the data that was manipulated or the details of the request is available. When finished servicing a method invocation, an SLSB instance may be reused as the target for a new request. In short, stateless session beans are engineered to be lightweight and fast at the expense of conversational state. A much better usage of a SLSB looks like:

tmp95-38_thumb

Here Mike has supplied the SLSB with all the information it needs to process a request within the request itself.

Two invocations upon the same proxy, where each invocation uses a different underlying bean instance as picked from the pool

Figure 5-1. Two invocations upon the same proxy, where each invocation uses a different underlying bean instance as picked from the pool

Bypassing conversational state doesn’t mean that a stateless session bean can’t have instance variables or maintain any kind of internal state. Nothing prevents you from keeping a variable that tracks the number of times a bean instance has been called or a variable that saves data for debugging. However, it is important to remember that this state may never be visible to a client. The caller of an SLSB can’t assume that the same bean instance will service all of its requests. Instance variables may have different values in different bean instances, so their state is likely to appear to change randomly as stateless session beans are swapped from one client to another. Therefore, anything you reference in instance variables should be generic and not leaked out through the API.

For example, an SLSB might reasonably record debugging messages—that might be the only way to figure out what is happening on a large server with many bean instances. The client doesn’t know or care where debugging output is going. However, it would clearly be inappropriate for a stateless bean to remember that it was in the process of making a reservation for Madame X; the next time it is called, it may be servicing another client entirely. An SLSB used improperly might act a little like this:

tmp95-40_thumbtmp95-41_thumb

This kind of behavior stems from an incorrect application of the SLSB type. Either the bean provider has leaked out internal state variables or a stateful session bean should have been used instead. As a result, bean instances that have previously serviced Jason and Dave are now being used for Mike’s requests, and the system is in some inconsistent state.

Stateless session beans may be used for report generation, batch processing, or some stateless services such as validating credit cards. Another good application might be a StockQuoteEJB that returns a stock’s current price. Any activity that can be accomplished in one method call is a good candidate for the high-performance stateless session bean.

Let’s examine the semantics and grammars used to define session beans, and in particular, the SLSB.


The XML Deployment Descriptor

EJB has an optional XML deployment descriptor defined in the META-INF/ejb-jar.xml file of the EJB’s JAR file. You can use this descriptor as an alternative to annotations, to augment metadata that is not declared as an annotation, or to override an annotation. The choice is up to you. While annotations are a quick, simple way to prototype or define default metadata, they do make for tight coupling as information becomes embedded into the bytecode.

What’s interesting about an XML-only deployment is that your Java code may contain no references to any EJB-specific APIs. If you looked at the Java code, you wouldn’t even know that it was an EJB.

The <enterprise-beans> element contained in <ejb-jar> defines the set of EJBs you are deploying. The <session> element denotes that you are deploying a session bean. <ejb-name> gives the session bean an identity that you can reference. The <remote> and <local> elements identify the business interfaces of the bean, and <ejb-class> declares the bean class. The <session-type> element identifies the session bean as a stateless session bean. <env-entry> initializes the values externalized from the bean class.

The XML deployment descriptor schema also supports partial XML definitions. This may be used to augment or override metadata provided via annotations, as we’ll soon see.

Session Context

The javax.ejb.Session Context interface provides a view into the EJB container’s environment. The SessionContext object can be used as the bean instance’s interface to the EJB container to obtain information about the context of the method invocation call and to provide quick access to various EJB services. A session bean can obtain a reference to its SessionContext by using the @Resource annotation:

tmp9542_thumb_thumb1

Session Context allows you to obtain information such as the current user that is invoking on the EJB, or to look up entries within the EJB’s Enterprise Naming Context (ENC). Let’s look at the javax.ejb.Session Context interface:

tmp9543_thumb_thumb

The getEJBObject() and getEJBLocalObject() methods are obsolete and will throw an exception if invoked upon. They are objects that are specific to the EJB 2.1 style of defining EJBs.

The SessionContext.getBusinessObject() method returns a reference to the current EJB that can be invoked by other clients. This reference is the EJB equivalent to Java’s this pointer, but it returns a proper EJB proxy. The businessInterface parameter must be one of the EJB’s BusinessRemote or BusinessLocal interfaces so that the container knows whether to create a remote or local reference to the current EJB. The getBusinessObject() method allows the bean instance to get its own EJB object reference, which it can then pass to other beans. Here is an example:

tmp9544_thumb_thumb

It is illegal for a bean instance to pass a this reference to another bean; instead, it passes its remote or local EJB object reference, which the bean instance gets from its Session Context.

The SessionContext.getInvokedBusinessInterface() method allows you to determine whether your EJB was invoked on through its remote, local, or web service interface. It returns the invoked business interface as a class.

EJBContext

SessionContext extends the javax.ejb.EJBContext class. EJBContext defines several methods that provide useful information to a bean at runtime.

Here is the definition of the EJBContext interface:

tmp9545_thumb1_thumb2

EJBContext.lookup() is a convenience method that allows you to look up entries in the EJB’s ENC. We’ve used it in the example just shown to obtain the cipher’s passphrase.

The EJBContext.getTimerService() method returns a reference to the container’s Timer Service, which allows the stateless bean to set up notifications of timed events for itself.

In other words, a session bean can set alarms so that the container will call it when a specific date arrives or some interval of time has passed. The Timer Service can also be injected using the @Resource annotation.

The EJBContext.getCallerPrincipal() method is used to obtain the java.security. Principal object representing the client that is currently accessing the bean. The Prin cipal object can, for example, be used by an EJB to track the identities of clients making updates:

tmp9546_thumb1_thumb

The EJBContext.isCallerInRole() method tells you whether the client accessing the bean is a member of a specific role, identified by a role name. This method is useful when more access control is needed than simple method-based access control can provide. In a banking system, for example, you might allow the Teller role to make most withdrawals but only the Manager role to make withdrawals of more than $10,000. This kind of fine-grained access control cannot be addressed through EJB’s security attributes, because it involves a business logic problem. Therefore, we can use the isCaller InRole() method to augment the automatic access control provided by EJB. First, let’s assume that all managers are also tellers. The business logic in the withdraw() method uses is CallerInRole() to make sure that only the Manager role can withdraw sums of more than $10,000:

tmp9547_thumb1_thumb1 

EJBContext contains some methods that were used in older EJB specifications but have been abandoned in EJB 3.0. The security methods that interact with Identity classes, as well as the getEnvironment(), EJBContext.getEJBHome(), and EJBContext.getEJBLocalHome() methods, are now obsolete. A RuntimeException is thrown if these methods are executed.

The material on EJBContext covered in this section applies equally well to message-driven beans. There are some exceptions.

The Lifecycle of a Stateless Session Bean

The lifecycle of a stateless session bean is very simple. It has only two states: Does Not Exist and Method-Ready Pool. The Method-Ready Pool is an instance pool of stateless session bean objects that are not in use. Because of all the injection and such that can happen, it can be more efficient to save stateless bean instances when they are not in use. This is an important difference between stateless and stateful session beans; stateless beans define instance pooling in their lifecycles and stateful beans do not.* Figure 5-2 illustrates the states and transitions an SLSB instance goes through.

Stateless session bean lifecycle

Figure 5-2. Stateless session bean lifecycle

* Some vendors may not pool stateless instances but may instead create and destroy instances with each method invocation. This is an implementation-specific decision that shouldn’t affect the specified lifecycle of the stateless bean instance.

The Does Not Exist State

When a bean 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 Pool

Stateless bean instances enter the Method-Ready Pool as the container needs them. When the EJB server is first started, it may create a number of stateless bean instances and enter them into the Method-Ready Pool. (The actual behavior of the server depends on the implementation.) When the number of stateless instances servicing client requests is insufficient, more can be created and added to the pool.

Transitioning to the Method-Ready Pool

When an instance transitions from the Does Not Exist state to the Method-Ready Pool, three operations are performed on it. First, the bean instance is instantiated by invoking the Class.newInstance() method on the stateless bean class. Second, the container injects any resources that the bean’s metadata has requested via an injection annotation or XML deployment descriptor.

You must always provide a no-argument constructor. This is a constructor that accepts no parameters. The container instantiates instances of the bean class using Class.newInstance(), which requires a no-arg constructor. If no constructors are defined, the no-arg constructor is implicit.

Finally, the EJB container will fire a post-construction event. The bean class can register for this event by annotating a method with @javax.annotation.PostConstruct. This annotated method is called by the container after the bean is instantiated. The callback method can be of any name, but it must return void, have no parameters, and throw no checked exceptions. The bean class may define only one @PostConstruct method (but it is not required to do so).

Alternatively, you can declare your @PostConstruct method in the EJB’s XML deployment descriptor:

tmp9550_thumb1_thumb3

Stateless session beans are not subject to activation, so they can maintain open connections to resources for their entire lifecycles.t The @PreDestroy method should close any open resources before the stateless session bean is evicted from memory at the end of its lifecycle. You’ll read more about @PreDestroy later in this section.

Life in the Method-Ready Pool

Once an instance is in the Method-Ready Pool, it is ready to service client requests. When a client invokes a business method on an EJB object, the method call is delegated to any available instance in the Method-Ready Pool. While the instance is executing the request, it is unavailable for use by other EJB objects. Once the instance has finished, it is immediately available to any EJB object that needs it. Stateless session instances are dedicated to an EJB object only for the duration of a single method call.

When an instance is swapped in, its SessionContext changes to reflect the context of the EJB object and the client invoking the method. The bean instance may be included in the transactional scope of the client’s request and it may access SessionContext information specific to the client request: for example, the security and transactional methods. Once the instance has finished servicing the client, it is disassociated from the EJB object and returned to the Method-Ready Pool.

Clients that need a remote or local reference to a stateless session bean begin by having the reference injected (servlets support injection, for example) or by looking up the stateless bean in JNDI. The reference returned does not cause a session bean instance to be created or pulled from the pool until a method is invoked on it.

PostConstruct is invoked only once in the lifecycle of an instance: when it is transi-tioning from the Does Not Exist state to the Method-Ready Pool. It is not reinvoked every time a client requests a remote reference to the bean.

Transitioning out of the Method-Ready Pool: The death of a stateless bean instance

Bean instances leave the Method-Ready Pool for the Does Not Exist state when the server no longer needs them—that is, when the server decides to reduce the total size of the Method-Ready Pool by evicting one or more instances from memory. The process begins when a PreDestroy event on the bean is triggered. The bean class can register for this event by annotating a method with @javax.annotation.PreDestroy. The container calls this annotated method when the PreDestroy event is fired. This callback method can be of any name, but it must return void, have no parameters, and throw no checked exceptions. The bean class may define only one @PreDestroy method (but it is not required to do so). An @PreDestroy callback method can perform any cleanup operations, such as closing open resources.

The duration of a stateless bean instance’s life is assumed to be very long. However, some EJB servers may actually destroy and create instances with every method invocation, making this strategy less attractive. Consult your vendor’s documentation for details on how your EJB server handles stateless instances.

tmp9551_thumb_thumb1

Alternatively, you can declare your @PreDestroy method in the EJB’s XML deployment descriptor:

tmp9552_thumb1_thumb1

As with @PostConstruct, @PreDestroy is invoked only once: when the bean is about to transition to the Does Not Exist state. During this callback method, the SessionContext and access to the JNDI ENC are still available to the bean instance. Following the execution of the @PreDestroy method, the bean is dereferenced and eventually garbage-collected.

Example: The EncryptionEJB

Often we write applications that require users to choose a password or enter a credit card number. Storing this sensitive data in its raw form (called cleartext) represents a security risk: if someone were to gain unauthorized access to our database, he or she could query for this information and we’d be liable for any damage done. A nice alternative to persisting these fields in human-legible format is to encrypt them using our application. Because this is a simple request-response model requiring no conversational state, the stateless session bean is ideally suited to handle these requirements efficiently. Other EJBs or standalone clients may later leverage the generic encryption service we provide in this component.

Cryptographic hashing is the process in which some input is transformed into a reproducible, fixed-size result. The algorithm is unidirectional, meaning that it’s not mathematically possible to un-hash the output back to its original form. By storing the hash of passwords only, we may compare hashes of login attempts against the stored value (see Figure 5-3).

Comparing input with a hash of the expected result

Figure 5-3. Comparing input with a hash of the expected result

Sometimes we need to be able to get the cleartext back out of an encrypted result. For example, perhaps our application offers storage of a previously used credit card number for future purchases. For this, we may employ symmetrical encryption, which dictates that some key (backed by a passphrase) may both encrypt and decrypt the data. Without the correct passphrase, attempts to decode become very tricky for potential attackers.

The Contract: Business Interfaces

Using the requirements described in the previous section, we can flesh out the contract for our EncryptionEJB:

tmp9554_thumb_thumb1

Here we account for our symmetrical encryption functions:

tmp9555_thumb1_thumb

And these methods will handle our one-way cryptographic hashing. The Encryption CommonBusiness interface again becomes the base for both our local business and remote business views:

public interface EncryptionLocalBusiness extends EncryptionCommonBusiness {} public interface EncryptionRemoteBusiness extends EncryptionCommonBusiness {}

A business interface can be a remote or local interface but not both. Remote business interfaces are able to receive method invocations from networked clients. When a client invokes a method on a session bean’s remote interface, the parameter values and return value are copied. This is true regardless of whether the client is running in the same VM or on another machine in the network. This is known as call-by-value semantics.

Local interfaces are available only within the same JVM as the session bean. Invoking on a local interface does not copy the parameters or return value. Because of this, local interfaces are said to follow what is termed call-by-reference semantics.

Application Exceptions

Any business interface can throw application exceptions. These should describe a business logic problem—in this case, a problem in encryption/decryption. Application exceptions should be meaningful to the client, providing a brief and relevant identification of the error.

Our new EncryptionException will describe a specific business problem that is possibly recoverable; this makes it an application exception. The EJB container treats any exception that does not extend RuntimeException as an application exception. Here is the definition of EncryptionException:

tmp9556_thumb1_thumb2

An application exception is propagated to the calling client as is. Any instance variables you include in these exceptions should be serializable. Nonapplication exceptions are always wrapped, by the Container, in an EJBException. This means that any exception you throw that is or extends RuntimeException will be caught by the EJB container and wrapped in an EJBException. This is especially important for session beans that interact with entity beans. All exceptions thrown by Java Persistence interfaces are RuntimeExceptions. Your client code must be aware of this if it needs to take action on specific persistence exceptions. Exception behavior, preventing the wrapping in EJBException, can be declared explicitly using the @javax.ejb.ApplicationException and the <application-exception> XML deployment descriptor metadata. 

Bean Implementation Class

The EncryptionEJB models a specific business process, so it is an excellent candidate for a stateless session bean. This bean really represents a set of independent operations—another indication that it is a good candidate for a stateless session bean.

First we’ll reveal any nonobvious types to be imported (all will be discussed in turn):

tmp9557_thumb_thumb

Annotations upon the bean implementation class provide the metadata declaring our SLSB and business interfaces:

tmp9558_thumb_thumb1

The bean class is annotated with the @javax.ejb.Stateless annotation to identify that it is a stateless session bean. The name() attribute identifies the EJB name of the session bean. The EJB name defaults to the unqualified (simple) name of the bean class if you initialize this attribute; in this case, the EJB name would default to EncryptionBean. In most cases, you don’t have to be aware of the concept of an EJB name, but it’s useful when you want to override or augment metadata with an XML deployment descriptor. This will allow us to externalize properties and provide configuration for the service without requiring recompilation of the Java sources.

When used on the bean class, the @Local and @Remote annotations take an array of interface classes. It is not recommended that you use this approach unless you have to, as implementing your business interfaces directly enforces the contract between the bean class and these interfaces. These examples opt for the bean implementation class approach simply to centralize annotation metadata for readability, and the contract is honored by ensuring the bean implementation class explicitly implements all business interfaces.

The EncryptionEJB identifies its remote and local interfaces via the class-level annotations upon the bean implementation class. As we’ve mentioned before, @Remote and @Local could also go directly upon the EncryptionBusinessRemote and EncryptionBusinessLocal interfaces:

tmp9559_thumb_thumb

When an EJB is deployed, the container looks at the interfaces of the bean class to see whether they are annotated as business interfaces. This introspection may determine the business remote and business local interfaces of the bean class.

The art of creating a truly secure encryption service is beyond scope of this exercise, so we’ll leave the implementation of the business methods for you to explore in runnable examples detailed in the topic. Worthy of note, however, is that our bean implementation class must perform some initialization before it’s able to do any real work. For this purpose, we introduce:

tmp9560_thumb1_thumb1

The @PostConstruct annotation defines a callback method to be executed at the proper point within the SLSB lifecycle. In this case, initialize() will be invoked by the Container after it has created the bean instance and performed any injection operations.

We’ve also defined the internal method getCiphersPassphrase(), which we’ll use to obtain an externalized parameter as the passphrase (secret key) for use in symmetric encryption.

Accessing Environment Properties (Injection and Lookup)

Our bean implementation class declares a few instance members as part of its internal state:

tmp9561_thumb1_thumb1

Remember that the internal state for each instance is permissible, so long as these don’t leak out to the client. Stateless session beans may contain no conversational state!

Two of these properties, ciphersPassphrase and messageDigestAlgorithm, represent configurable attributes that we’ll provide via externalized variables called environment entries. An environment entry is a value placed into JNDI under a private namespace called the Enterprise Naming Context (ENC), which is visible only to our EJB. There are a variety of ways to get at the values within the ENC, and this example will illustrate both manual lookup and injection.

The messageDigestAlgorithm is annotated with @Resource; this tells the EJB container that when an instance of the bean class is created, this field must be initialized with values in the container’s ENC.

When the EJB container is deployed, the ENC is populated both with metadata embedded in annotations such as @Resource and with information stored in the optional EJB XML deployment descriptor. For example, the annotation alone suggests a named value within the ENC should be used to initialize the field externally. This named value can be configured by defining environment entries within the EJB’s XML deployment descriptor:

tmp9562_thumb_thumb1tmp9563_thumb1_thumb1

When this file, META-INF/ejb-jar.xml, is included in the packaging of the EJB, the Container will place the designated values within the ENC of our EncryptionEJB upon deployment. The messageDigestAlgorithm field may then be injected by the Container. But we’ve defined no annotation upon the field ciphersPassphrase, so we’ll look it up manually.

The @Resource annotation is very versatile, and when placed upon a known type such as SessionContext, the Container knows how to handle this injection request. Through the lookup() method upon a SessionContext, we can poke into the ENC to obtain the value we’re looking for:

tmp9564_thumb_thumb1tmp9565_thumb1_thumb1

We may use something similar to the previous code to extract out the cipher passphrase from the ENC in a manual fashion. Because of this extra burden upon the developer, it’s easy to see why injection is the preferred mechanism to obtain information from the Container.

Externalization of values is incredibly important. In this case, the application assembler or deployer of the EJB may choose a secret passphrase for the encryption scheme in production environments, and developers of the application never have to be exposed to this privileged information. We’ve also used environment entries to make our service configurable.

It’s worth noting: never, ever design to store passwords in a file to be placed on the filesystem. By definition, this is insecure. This example is in place to illustrate the absence of conversational state while maintaining internal state and should not be used as a real-world encryption system.

Asynchronous Methods

New in the EJB 3.1 Specification is the feature of fire-and-forget invocations upon session beans. Often we’ll have requirements where a request may take some time to be fully processed, and the client may not need to wait for the result or confirmation of completion. In these cases, we can take advantage of the new @javax.ejb.Asynchro nous annotation to return control to the client before EJB is invoked. When the client does need access to the return value, it can do so via the facilities provided by java.util.concurrent.Future.

In the case of our EncryptionEJB, perhaps we have a very intensive hashing function that takes some time to complete:  

tmp9567_thumb_thumb2

During implementation, the bean provider may mark this method (either on the business interface or the bean class) as @Asynchronous, wrapping the real return value in a convenience implementation of java.util.concurrent.Future called javax.ejb.AsyncResult:

tmp9568_thumb_thumb1tmp9569_thumb_thumb

Here control is returned to the client immediately after invoking upon hashAsync, and other work may be performed while the potentially expensive hashing function chugs along elsewhere concurrently. When we need the asynchronous result, we can use Future.get() or its derivatives to block until the result is available.

Methods with no return value (i.e., void) may also be marked as @Asynchronous. Any session bean business method (in Stateless, Stateful, or Singleton) is eligible to be made asynchronous, and @Asynchronous may be applied upon a business interface or bean class to denote that all methods should be handled async.

Next post:

Previous post: