As much as I hate to admit it, Microsoft was a pioneer in server-side component architectures. Its COM/DCOM (Distributed Component Object Model) server-side component model for building and deploying components in the Microsoft Transaction Server (MTS) environment already had applications in production by the time the Enterprise JavaBeans specification was publicly released in March 1998.
MTS's approach toward declarative, container-managed components is reflected in the specification's notions of EJB servers and containers, and declarative, transactional properties of enterprise beans through deployment descriptors.
Also, Microsoft's distributed component architecture, DCOM, is based on remote procedure calls to stateless components running within the MTS. This model takes advantage of resource pooling - such as connection pools to the database and instance pooling - to provide a highly scalable distributed component environment. Microsoft's core argument for its stateless component architecture is that stateful servers can't scale. A stateful architecture maintains information between calls from a client, thereby holding resources within the server that could be used for other purposes or freed altogether. An application is scalable when it maintains a certain level of performance even as the number of users and complex transactions per second grows. Thus, from Microsoft's point of view, scalability is achieved by stateless business components that don't hold precious resources from others in need of their services.
The initial release of the EJB specification required session bean component support for 1.0 compliance. However, unlike Microsoft's server-side component model, the 1.0 specification offered two types of components: stateful and stateless session beans. With support for stateful server architectures, EJB split from its Microsoft lineage. In this month's EJB Home, however, I discuss the common genes between the two component models, focusing strictly on stateless components.
Stateless Session Beans
Unlike your neighborhood coffee beans, session beans come in only two flavors: stateful and stateless. A stateful session bean holds conversational state between method invocations in the form of instance variables from a single client while a stateless session bean maintains no state between method invocations.
Stateful and stateless session Beans have some commonalities:
In other words, all session beans rely on their container to manage thread access to the bean, ensuring it has only one thread manipulating the bean at a time. This restriction (container-managed threading) is actually enforced on all enterprise beans, not just session beans. Spawning threads in your enterprise beans isn't a good practice!
- They are relatively short-lived, nonpersistent components.
- They generally do not survive an EJB server crash.
- They are transaction aware (restrictions in stateless session beans will be covered shortly).
- They are single-threaded, servicing a single client at a time.
Despite their common features, there are some major differences. The lack of conversational state inherent in stateless session beans binds them closely to Microsoft's COM/DCOM. Also, while stateful session beans remain uniquely identifiable within their container, all instances of stateless session beans are equally capable of handling a client request. When a client compares two EJB objects of the same stateless session bean, they will always be equal. The following code provides an example of this comparison:
CustomerMgrHome home = (CustomerMgrHome)
CustomerMgr mgr1 = home.create();
CustomerMgr mgr2 = home.create();
return mgr1.isIdentical (mgr2) ; // return value will be true!
Because each stateless session bean of the same class is equal, any method-ready instance is equally qualified to service a request from a client. This allows for a highly scalable middle-tier environment in which the server can instantiate and destroy beans at will to support extreme fluctuations in user volume. Speaking of creating and destroying bean instances, let's take a look at the life cycle of a stateless session bean for a clear understanding of just how simple it is for the server to support stateless components.
Life Cycle of a Stateless Session Bean
A stateless session bean basically has two states: (1) nonexistent, and (2) method-ready. The EJB container does three steps to bring your stateless bean into a position where it is ready to satisfy a client request. First, the container allocates a new instance of the bean into memory. Next, the setSessionContext(javax.ejb.SessionContext ctx) method is called to set the bean's SessionContext. Last, the container calls the bean.ejbCreate() method, allowing the bean to perform any initialization code.
Enterprise JavaBeans uses "passivation" to manage large numbers of enterprise beans. In EJB, passivation is the process of storing a bean's state to a persistent store to release resources in the server. During the life cycle of an enterprise bean, passivation may occur if the bean hasn't been accessed by the client for some period of time; however, because stateless session beans have no state to store, they aren't passivated by their container. Instead, ejbRemove() is called on the instance of the bean to allow it to release any resource(s) it may have opened, and the instance is simply destroyed (or pooled).
While stateful server architectures are necessary in some lines of business, such as real-time quote systems or military command-and-control centers, higher scalability is generally achieved through stateless server components or session beans.
Stateless session beans are more scalable than stateful beans due to less resource allocation and management, and the clustering capabilities of stateless session beans.
With stateless session beans there's no need to hold resources or state longer than a method call. This way, the server isn't overburdened by maintaining information for an extended period of time. While passivation relieves the server of unused resources, the process requires resources to monitor the bean activity and to store the bean state to a pseudopersistent storage until the bean's services are needed again. Stateless beans aren't passivated; thus they demand no maintenance overhead.
The problem with stateful, distributed components occurs when the component's client either disconnects or fails to disconnect from the component. In some cases your server will meet a gradual degradation in performance until the server is recycled, thereby releasing the locked resources. For example, let's say the stateful session bean you're coding connects to a database in its ejbActivate() method and holds on to the connection to handle subsequent calls from its client. When ejbRemove() is invoked on the bean, it releases its connection. However, if the client forgets to call remove() on the bean, the database connection will be tied up until the bean is passivated. Worst of all, if a stateful bean is within a transaction, the passivation service isn't allowed to store the bean. This scenario may prevent other clients from performing their work efficiently if the bean's transaction locked rows in the database.
Instance pooling is another technique used by stateless architectures to lower the demands placed on the server and provide faster allocation of stateless components. Instance pools contain "waiting" components that are pulled from the pool on demand, then placed back into the pool when they're finished servicing the request (see Figure 1).
EJB containers in the market today usually provide some form of instance pooling to achieve scalability while decreasing the amount of garbage collection needed to clean up destroyed bean instances. Recall the life cycle of a stateless session bean. The bean is either nonexistent or method-ready. When moving from a state of nonexistent to method-ready, the container allocates memory for the bean. The container may create a new instance each time; however, a more efficient way to obtain a bean instance would be to grab preallocated memory from an instance pool of empty beans. Instance pooling is purely implementation and not mandatory in the EJB specification.
Last, stateless components scale well because they're easy to deploy in clustered EJB servers. Clustering allows components to be replicated across multiple servers on multiple machines, providing fault tolerance and load balancing. Clustering typically makes it harder for the EJB server to send requests back to the same instance over multiple calls from a client. Because stateless beans don't manage conversational state, it doesn't matter which instance services the client's next request.
Stateless beans scale better than stateful beans in a clustered environment in which 24/7 support is a necessity. If you need your EJB application to provide such support, failover is a main concern for your architecture team. In stateless server architectures any component can service a request.
Figure 2 shows how a client request on a clustered session bean can be transparently serviced during a software failure.
When the client calls foo() on the session bean's remote interface, a smart object implementing the bean's remote interface intercepts the call and routes it to a clustered bean. During the method invocation, a software failure occurs; however, because every stateless session bean in the cluster is equally capable of servicing the client, the router simply calls the real remote interface of a functional bean in another EJB container.
Transparent failover and clustering in stateful architectures isn't so easy. While any stateless bean can service a client request in the event of a failure, in 24/7 stateful server architecture the server must perform a secondary, "hot" backup of the primary stateful bean in case the primary fails. Tracking deltas and passing changes from a primary bean to a secondary bean is time consuming and results in unnecessary chatter between the two components. I recommend stateless business components over stateful components in a clustered, 24/7 environment.
There's Always a Downside
There are a few downsides to building a distributed application on the foundation of stateless session beans. Typically, there's a need to hold conversational state across method invocations, especially in today's distributed applications, which may hold session data in an HttpSession, a stateful session bean, a singleton RMI server, a servlet or JNDI, to name a few possibilities. State must be maintained either in the client and passed everywhere it may be referenced, or in a persistent store and retrieved on an as-needed basis. Understanding how best to handle state between calls when using stateless session beans can absorb a significant amount of time in the design phases of your application.
The programming model of stateless components isn't appealing to object-oriented fanatics, who'd rather maintain references to objects that are locally accessible everywhere the objects are passed. This approach was fine in the client/server world, where the majority of processing occurred in a single executable. In distributed systems, however, it's critical to keep the amount of information you're passing between components to a minimum. Likewise, it's critical to keep the number of distributed references to other components to a minimum. Now take into account the design considerations necessary to support a stateless server architecture and you're looking at a tough paradigm shift for true OO practitioners!
Another restriction I've seen throw a wrench into many designs is the inability for stateless session beans to partake in a transaction beyond a method call. While these beans are transaction aware, the transaction participation for the session bean begins with the method invocation and ends with the return of the method. Depending on when the transaction is eventually committed or rolled back, this bean has no option to synchronize with the results. For instance, stateless session beans can't implement the SessionSynchronization interface, which allows a stateful session bean to receive notification of a coordinated transaction's results. Since each call to a stateless bean could be serviced by a different instance, and the bean's container can destroy an instance at the end of its life cycle, the transaction has no way to reliably communicate to a particular instance.
While stateful server architectures have their niche, many high-volume distributed systems demand architectures built around stateless business components. These beans, available since the EJB specification 1.0, are capable of meeting this demand. They offer you the ability to build declarative, server-side components in Java that can scale to meet the needs of your enterprise application.
Microsoft has bet the farm on stateless components, believing enterprise-wide scalability can be met by taking advantage of features such as instance pooling, connection pooling and server clustering. Thankfully, the architects behind the EJB specification have included stateless session bean support in their design as well. If I had a farm to bet, I'm sure I'd gamble on building my high-volume, 24/7 application on these beans and win big!
For more information on comparisons of MTS versus EJB, check out Sun's portal
Jason Westra is the
CTO of Verge
Technologies Group, Inc. www.vergecorp.com. Verge is a Boulder,
Colorado, based firm
specializing in e-business solutions with Enterprise JavaBeans.