May 18, 2011, 4:22 p.m.
posted by hashspark
A large business system with many users can easily have thousands or even millions of objects in use simultaneously. As the number of interactions among these objects increases, concurrency and transactional concerns can degrade the system's response time and frustrate users. EJB servers increase performance by synchronizing object interactions and sharing resources.
There is a relationship between the number of clients and the number of distributed objects that are required to service them. Not surprisingly, the larger the client population, the more distributed objects are needed. At some point, the increase in clients affects performance and diminishes throughput. EJB explicitly supports two mechanisms that make it easier to manage large numbers of beans at runtime: instance pooling and activation. In addition, EJB supports the use of the Java EE Connector Architecture (Java EE Connectors) for managing resource connections. As the number of distributed objects and clients increases, the number of resource connections also increases. Java EE Connectors work with the EJB container to manage connections to databases, enterprise messaging, ERP, legacy systems, and other types of resources.
The concept of pooling resources is not new. It's common to pool database connections so that the business objects in the system can share database access. This trick reduces the number of database connections needed, which reduces resource consumption and increases throughput. The Java EE Connector Architecture (JCA) is frequently the mechanism employed by EJB containers when pooling connections to databases and other resources, and is covered a little later. Most EJB containers also apply resource pooling to server-side components; this technique is called instance pooling. Instance pooling reduces the number of component instancesand therefore resourcesneeded to service client requests.
As you already know, clients of session beans interact with the beans through the remote and local interfaces implemented by EJB objects. Client applications never have direct access to an actual session bean class instance. Similarly, JMS clients never interact with JMS-based message-driven beans (JMS-MDBs) directly. They send messages that are routed to the EJB container system. The EJB container then delivers these messages to the proper message-driven instance.
Instance pooling is possible because clients never access beans directly. Therefore, there's no fundamental reason to keep a separate copy of each enterprise bean for each client. The server can keep a much smaller number of enterprise beans around to do the work, reusing each enterprise bean instance to service different requests. Although this sounds like a resource drain, when done correctly, it greatly reduces the resources required to service all the client requests.
The stateless session bean life cycle
To understand how instance pooling works, let's examine the life cycle of a stateless session bean. Stateless beans exist in one of three states:
Because a stateless session bean does not maintain any state between method invocations, every method invocation operates independently, performing its task without relying on instance variables. This means that any stateless session instance can service requests for any EJB object of the proper type. The container can therefore swap bean instances in and out between method invocations.
Each EJB vendor implements instance pooling differently, but all instance-pooling strategies attempt to manage collections of bean instances so that they are quickly accessible at runtime. To set up an instance pool, the EJB container creates several instances of a bean class and then holds on to them until they are needed. As clients make business-method requests, bean instances from the pool are assigned to the EJB requests associated with the clients. After the request is complete, the EJB object doesn't need the instance anymore and it's returned to the instance pool. An EJB server maintains instance pools for every type of stateless session bean deployed. Every instance in an instance pool is equivalentthey are all treated equally. Instances are selected arbitrarily from the instance pool and are assigned to EJB requests as needed.
Figure illustrates instance swapping between stateless session bean method invocations. In Figurea, instance A is servicing a business method invocation delegated by EJB object 1. Once instance A has serviced the request, it moves back to the instance pool (Figureb). When a business method invocation on EJB object 2 is received, instance A is associated with that EJB object for the duration of the operation (Figurec). While instance A is servicing EJB object 2, another method invocation is received by EJB object 1 from the client and is serviced by instance B (Figured).
Stateless session beans in a swapping strategy
Using this swapping strategy allows a few stateless session bean instances to serve hundreds of clients, because the amount of time it takes to perform most method invocations is typically much shorter than the pauses between method invocations. When a bean instance is finished servicing a request for an EJB object, it is immediately made available to any other EJB object that needs it. This allows fewer stateless session instances to service more requests, which decreases resource consumption and improves performance.
Soon after the bean instance is placed in the pool, it's given a reference to a javax.ejb.EJBContext if the session bean has requested that this context object be injected (see Chapter 14 for more information). The EJBContext provides an interface that the bean can use to communicate with the EJB environment. This EJBContext becomes more useful when the bean instance moves to the Ready state.
When a bean instance is servicing a request, the EJBContext takes on new meaning. The EJBContext provides information about the client that is using the bean. It also provides the instance with access to its own EJB stub proxy, which is useful when the bean needs to pass references to itself or to other enterprise beans. So the EJBContext is not a static class; it is an interface to the container.
Stateless session beans are declared "stateless" via the annotation @javax.ejb.Stateless or in the deployment descriptor. Once a bean class is deployed as stateless, the container assumes that no conversational state is maintained between method invocations. A stateless bean can have instance variables, but because bean instances can be servicing several different EJB objects, they should not be used to maintain conversational state.
Message-driven beans and instance pooling
Message-driven beans, like stateless session beans, do not maintain state specific to a client request, which makes them excellent candidates for instance pooling.
In most EJB containers, each type of message-driven bean has its own instance pool that services incoming messages. JMS-MDBs subscribe to a specific message destination, which is a kind of address used when sending and receiving messages. When a JMS client sends an asynchronous message to a destination, the message is delivered to the EJB container of the beans that subscribe to the destination. The EJB container determines which JMS-MDB subscribes to that destination and then chooses an instance of that type from the instance pool to process the message. Once the JMS-MDB instance has finished processing the message (when the onMessage( ) method returns), the EJB container returns the instance to its instance pool. Figure illustrates how client requests are processed by an EJB container.
JMS-MDB instance pooling
In Figurea, the top JMS client delivers a message to destination A, and the bottom JMS client delivers a message to destination B. The EJB container chooses an instance of MessageDrivenBean_1 to process the message intended for destination A and an instance of MessageDrivenBean_2 to process the message intended for Destination B. The bean instances are removed from the pool and are used to process the messages.
A moment later, in Figureb, the middle JMS client sends a message to destination B. At this point, the first two messages have already been processed and the container is returning the instances to their respective pools. As the new message comes in, the container chooses a new instance of MessageDrivenBean_2 to process the message.
JMS-MDBs are always deployed to process messages from a specific destination. In Figure, instances of MessageDrivenBean_1 process messages only for destination A, and instances of MessageDrivenBean_2 process messages only for destination B. Several messages for the same destination can be processed at the same time. If, for example, 100 messages for destination A arrive at the same time, the EJB container simply chooses 100 instances of MessageDrivenBean_1 to process the incoming messages; each instance is assigned a message.
EJB 2.1 and its successors have expanded the role of message-driven beans beyond JMS so that they can support other messaging services and APIs. This opens up the message-driven bean to just about any kind of resource, including messaging systems other than JMS, ERP systems like SAP, and legacy systems like IMS. Regardless of the type of resource represented by the message-driven bean, the instances of the bean type will be pooled in the same way as the JMS-MDBs.
The Activation Mechanism
Unlike other enterprise beans, stateful session beans maintain state between method invocations. Conversational state represents the continuing conversation with the stateful session bean's client. The integrity of this conversational state needs to be maintained for the life of the bean's service to the client. Stateful session beans, unlike stateless session and message-driven beans, do not participate in instance pooling. Instead, stateful session beans use activation to conserve resources. When an EJB server needs to conserve resources, it can evict stateful session beans from memory. When a bean is evicted, its conversational state is serialized to secondary storage. When a client invokes a method on the EJB object, a new stateful session bean instance is instantiated and populated with the state from the initial bean.
Passivation is the act of disassociating a stateful bean instance from its EJB object and saving its state. Passivation requires that the bean instance's state be held relative to its EJB object. After the bean has been passivated, it is safe to remove the bean instance from the EJB object and evict it from memory. Clients are unaware of the deactivation process. Remember that the client uses the bean's remote reference, which is implemented by an EJB stub proxy, and therefore does not directly communicate with the bean instance. As a result, the client's connection to the EJB object can be maintained while the bean is passivated.
Activating a bean is the act of restoring a stateful bean instance's state relative to its EJB object. When a method on the passivated EJB object is invoked, the container automatically creates a new instance and sets its fields equal to the data stored during passivation. The EJB object can then delegate the method invocation to the bean as normal. Figure shows activation and passivation of a stateful bean. In Figurea, the bean is being passivated. The state of instance B is read and held relative to the EJB object it was serving. In Figureb, the bean has been passivated and its state preserved. Here, the EJB object is not associated with a bean instance. In Figurec, the bean is being activated. A new instance, instance C, has been instantiated and associated with the EJB object and is in the process of having its state populated with the state held relative to the EJB object.
The passivation and activation processes
Since a stateful bean class does not have to be serializable, the exact mechanism for activating and passivating stateful beans is up to the vendor. Note that the transient property is not treated as you might expect when activating a passivated bean. In Java serialization, transient fields are always set to the initial value for that field type when the object is deserialized. Integers are set to 0, Booleans to false, object references to null, and so on. In EJB, transient fields are not necessarily set back to their initial values but can maintain their original values, or any arbitrary value, after being activated. Take care when using transient fields, since their state following activation is implementation-specific.
The activation process is supported by life cycle callback methods. In EJB 3.0 (unlike in EJB 2.1), life cycle callback methods are added to the stateful bean class when they are needed and are not imposed on the bean class by a session bean interface. Bean developers can hook into life cycle callbacks in their stateful bean classes by annotating methods that are interested in these callbacks. A @javax.ejb.PostActivate annotated method is called immediately following the successful activation of a bean instance if it is defined in the bean class. It can be used to reset transient fields to an initial value. A @javax.ejb.PrePassivate annotated method is called immediately prior to passivation of the bean instance, again, if the bean developer has defined this method. These two methods are especially helpful if the bean instance maintains connections to resources that need to be closed or freed prior to passivation and reobtained following activation. Because the stateful bean instance is evicted from memory, open connections to resources are not maintained. The exceptions are remote references to other beans and the SessionContext , which must be maintained with the serialized state of the bean and reconstructed when the bean is activated. EJB also requires that the references to the JNDI environment context, component interfaces, the EntityManager service, and the UserTransaction object be maintained through passivation.
Java EE Connector Architecture
The Java EE Connector Architecture defines an interface between Enterprise Information Systems (EISs) and Java EE container systems (i.e., EJB and Servlet containers). EIS is a generic term for any information system, including relational database servers, message-oriented middleware (e.g., MQSeries and SonicMQ), CORBA, ERP systems (e.g., SAP, PeopleSoft, and JD Edwards), and legacy systems (e.g., IMS and CICS).
Java EE defines a number of standard enterprise APIs, including JDBC, JMS, JNDI, Java IDL, and JavaMail, in addition to EJB. Each API provides a vendor-neutral API for a specific kind of enterprise information system. JDBC is used to exchange information with relational databases; JMS is for message-oriented middleware; JNDI is for naming and directory services; JavaMail is for electronic mail systems; and Java IDL is for CORBA. Requiring support for these APIs ensures that the enterprise beans that use them are portable across EJB vendors.
Although the enterprise APIs are vendor-agnostic, the products behind the APIs are always proprietary. When an enterprise bean uses the enterprise APIs, it's the EJB container's responsibility to pool and maintain the EIS connections, enroll the EIS in transactions, propagate security credentials, etc. These tasks often require the EJB container to interact with the underlying EIS in ways not addressed by the generic APIs. In effect, each Java EE vendor had to write proprietary code to manage each brand of EIS. Faced with this situation, Java EE vendors chose which EISs they would support for each standard API. This situation had a significant impact on the brands of EIS an EJB vendor could be expected to support: for example, Vendor A might support JDBC connectivity to Oracle, and Vendor B might support only DB2.
Java EE Connectors 1.5
EJB 2.0 required support for the new Java EE Connector Architecture, which went a long way toward solving this problem. However, it didn't go far enough. In particular, it didn't support the push model for messaging, which is needed because several EISs push data to clients, without the clients explicitly making a requestfor example, JMS. Both EJB 2.1 and 3.0 require support for Java EE Connector Architecture 1.5, which supports the push model. To support the push model, JCA 1.5 uses the message-driven bean programming model. Specifically, it defines a container/connector interface that allows incoming messages, sent asynchronously from the EIS, to be processed by message-driven beans. For example, Vendor X could develop a Java EE Connector for a Mail Delivery Agent (MDA), which is software that delivers Internet email. Vendor X defines a message-listening interface, the EmailListener , which can be implemented to create an Email-Message Driven Bean (Email-MDB) for processing email. As the MDA receives email from the Internet, it pushes the email to the EJB container, which delegates each message to an instance of the Email-MDB. The application developer then writes an Email-MDB that is annotated with the @javax.ejb.MessageDriven annotation and implements the com.vendor.EmailListener interface. Once the Email-MDB is created and deployed, it can process incoming messages.