Read all about EJB 2.0

Dramatic EJB 2.0 changes enhance flexibility and portability in your application development

1 2 3 4 Page 3
Page 3 of 4
SELECT invest FROM invest IN benefit.investments WHERE invest.type = ?1

The ejbSelect<METHOD> methods, on the other hand, are global in scope, so the same query would return all the risky investments of all the employees in the entire enterprise.

ejbSelect<METHOD>InEntity select methods can return the remote types of beans (as in the above query), dependent objects, or any other Java type. Global select methods, on the other hand, are not allowed to return the dependent object types of beans.

The EJB QL statements for select methods require the use of the SELECT clause, since they can return a wider range of values.

The new ejbHome methods

In EJB 2.0, entity beans can declare ejbHome methods that perform operations related to the EJB component but that are not specific to a bean instance. The ejbHome methods defined in the bean class must have a matching home method in the home interface. The code below illustrates a home method as defined for the Employee bean's home interface. The applyCola() method is used to update the salaries of all employees with the most recent COLA (cost of living adjustment) increase.

public interface EmployeeHome extends javax.ejb.EJBHome {
      // a home method
      public void applyCola(double increate) throws RemoteException;
      ...
}

The applyCola() method must have a matching ejbHome method in the bean class, which is declared ejbHomeApplyCola(). The ejbHomeApplyCola() method is not specific to one bean instance, its scope is global, so it will make a COLA to the salaries of all the employees.

public abstract class EmployeeBean implements javax.ejb.EntityBean {
      ...
            // ejbHome method 
      public void ejbHomeApplyCola (double increase ){
            Collection col = ejbSelectAllEmployees ();
            Iterator employees = col.iterator();
            while(employees.next()){
                  Employee emp = (Employee)employees.next();
                  double salary = emp.getAnnualSalary();
                  salary = salary + (salary*increase);
                  emp.setAnnualSalary(salary);
            }
      }
}

The bean developer is required to implement the ejbHome methods for both BMP and CMP entity beans. CMP implementations will probably rely heavily on global select statements (as illustrated above) and finder methods, while BMP implementations of ejbHome will use direct database access and the finder methods of beans to query data and apply changes.

MessageDrivenBean

A fundamental change to the specification in EJB 2.0 is the addition of a completely new enterprise bean type, the MessageDrivenBean. The MessageDrivenBean is designed specifically to handle incoming JMS messages. JMS is a new paradigm for many developers, so this article will spend a little time developing an understanding of JMS and how it's used in EJB 2.0.

What is JMS?

JMS is a vendor agnostic API for accessing messaging systems. It's analogous to JDBC (Java Database Connectivity): where JDBC is an API that you can use to access many different relational databases, JMS provides the same vendor-independent access to messaging services. Many vendors currently support JMS including IBM's MQSeries, BEA's Weblogic JMS service, and Progress' SonicMQ to name a few.

JMS lets you send messages from one JMS client to another through a messaging service, sometimes called a message broker or router. A message is a type object in JMS, which has two parts: a header and a message body. The header is composed of routing information and metadata about the message. The message body carries the application data or payload. There are several message types depending on the type of payload, including message types that carry simple text (TextMessage), serializable objects (ObjectMessage), a collection of properties (MapMessage), byte streams (BytesMessage), primitive values streams (StreamMessage), or no payload (Message).

Messaging systems are asynchronous, which means that a JMS client can send a message without having to wait for a reply. That is completely different when compared to RPC-based (Remote Procedure-based) systems such as EJB 1.1, CORBA, and the Java RMI reference implementation. In RPC, the client invokes a method on a distributed object on a server. Until the method invocation returns, the client is blocked; it must wait for the method invocation to end before it can execute the next instruction. In JMS, a client delivers a message to a virtual channel (topic or queue) to which other JMS clients subscribe or listen. When a JMS client sends a message, it doesn't wait for a reply. It executes the send operation and moves on to the next instruction. The message might eventually be forwarded to one client or many, none of which need reply.

JMS in EJB 2.0

EJB 2.0 supports the integration of JMS in two ways: as a resource available to beans and as a MessageDrivenBean. When JMS is used as a resource, the bean using the JMS API is a message producer or sender. In that case, the bean is sending messages to a virtual channel called a topic or queue. A MessageDrivenBean, on the other hand, is a message consumer or receiver. It listens to a specific virtual channel (topic or queue), and handles messages delivered to that channel. To better understand the roles of both message producers and consumers, a SessionBean bean is used to send a message using JMS and then a new MessageDrivenBean is used to consume that same message.

JMS as an EJB 2.0 resource

Session and entity beans are RPC-based components, which is an excellent architecture for assembling transactional components. In some cases, however, the synchronous nature of RPC becomes a handicap, which is why access to the JMS API was included as a resource in EJB 1.1. Using the JNDI environment-naming context, a bean can obtain a JMS factory and deliver an asynchronous message to a topic or queue (also obtained from JNDI) without having to wait for a reply. Below is an example of a ShoppingCart bean that uses JMS to send the details of an Order to a messaging topic.

public class ShoppingCartBean implements SessionBean {
   // order detail is a serializable object that contains all the order information. 
   public OrderDetail orderDetail;
    public void processOrder(){
          // The logic to process the order goes here ....
          
          // ... after processing the order, send a message to other systems                  
          //     about the order
          InitialContext jndiEnc = new InitialContext();
          // Using JNDI ENC to obtain the JMS Factory and Topic identifier
          TopicConnectionFactory factory = 
          jndiEnc.lookup("java:comp/env/jms/topicfactory");
          Topic orderTopic = jndiEnc.lookup("java:comp/env/jms/ordertopic");
          // Obtain a publisher that is used to send messages
          TopicConnection con = factory.createTopicConnection();
          TopicSession session = 
                   con.createTopicSession(false, Session.AUTO_ACKNOWLEDGE );
          TopicPublisher publisher = session.createPublisher(orderTopic);
          // Send an ObjectMessage to the topic (virtual channel)
          ObjectMessage message = session.createObjectMessage();
          message.setObject(orderDetail);
          publisher.publish(message);
          con.close();
    }
    ...
}

In that case, JMS is used to notify other applications that an order has been processed. Those other applications are not critical to processing the order, but they benefit from being notified that an order has been processed. Examples include an inventory system that automatically adjusts inventory and a marketing application that adds customers to the mailing lists for product catalogs.

Using JMS lets the bean publish (send) the message without blocking. The bean doesn't know who will receive the message because it delivers the message to a topic (virtual channel) and not directly to another application. Applications can choose to subscribe to that topic and receive notification of new orders. That makes it possible to dynamically add and remove applications from virtual channels, which results in a more flexible system.

Applications that subscribe to the order topic will receive notification of a new order, which the applications can process in any way they see fit. Applications that subscribe to topics or receive messages from queues can be Java applications, EAI systems (for integrating legacy and ERP systems), or MessageDrivenBean components, all of which are considered, in JMS terminology, to be JMS clients.

JMS and the MessageDrivenBean

While most JMS vendors provide the message brokering facilities for routing messages from senders to receivers, it is the application developer's responsibility to build JMS clients that consume (receive) messages. In many cases the application receiving the message needs to be robust, secure, fast, and scalable; basically it needs the same infrastructure as an EJB application.

In recognition of that need, EJB 2.0 now includes the MessageDrivenBean type, which can consume JMS messages and process them in the same robust component-based infrastructure that session and entity beans enjoy. The MessageDrivenBean type (message bean) is an enterprise bean component that is designed to consume asynchronous JMS messages.

In addition to providing the container infrastructure, EJB also provides another important advantage: concurrent processing. In EJB, a deployed message bean represents a single message consumer, but the bean itself is serviced by many bean instances. Each bean instance can discretely consume messages received by the message bean. That means that a message bean doesn't have to consume messages serially as is the case with a normal JMS client. A message bean can consume the messages it receives concurrently, which enables much higher throughput and better scalability than is possible with a traditional JMS application.

To illustrate the role of a message bean, the MarketingBean class is developed and deployed to consume messages from the order topic. The MarketingBean will extract the OrderDetail object from the message and use it to add the customer to the appropriate catalog mailing lists. A junk mailing system at its finest.

The following is the definition of the MarketingBean class, which consumes messages published to the order topic.

public class MarketingBean implements javax.ejb.MessageDrivenBean {
      public void onMessage(Message message) {
          ObjectMessage orderMessage = (ObjectMessage)message;
            OrderDetail orderDetail = (OrderDetail)orderMessage.getObject();
       
          Integer customerID = orderDetail.getCustomerID();      
          InitialContext jndiEnc = new InitialContext();
          CatalogHome catalogHome = 
            (CatalogHome)jndiEnc.lookup("java:comp/env/ejb/catalog");
          Iterator productIDs = orderDetail.getProductsIDs();
          while(productIDs.hasNext()){
            Integer productID = (Integer)productIDs.next();
            Catalog cat = CatalogHome.findByProductID(productID);
            cat.addCustomerToMailingList(customerID);
          }
     }
}

The MessageDrivenBean is a complete enterprise bean just like the session and entity beans, but there are some important differences. A message bean does not have a remote or home interface. That is because the message bean is not an RPC component. It does not have business methods that are invoked by EJB clients. A message bean listens to a virtual message channel (topic or queue) and consumes messages delivered by other JMS clients to that channel.

Messaging beans are composed of a bean class that implements the MessageDrivenBean interface and an XML deployment descriptor. Below is the definition of the MessageDrivenBean interface that all message beans must implement.

package javax.ejb;
import javax.jms.Message;
import javax.jms.MessageListener;
public interface MessageDrivenBean extends MessageListener{
  public void onMessage(Message message);
  public void ejbCreate();
  public void ejbRemove();
  public void setMessageDrivenContext(MessageDrivenContext mdc);
}

When a message-driven bean is deployed, it's assigned to handle messages from a specific topic or queue. Any messages sent by a JMS client (Java application, bean, or native client) will be forwarded by the message router to the message bean assigned to receive messages from that virtual channel. When a message is delivered to a message bean, one instance of that bean is selected by the EJB container from a pool to handle the message. The bean instance will receive the message when its onMessage() method is invoked and can process the message any way it sees fit. Once the message is consumed, it won't be delivered to any other instance of that message bean, providing that the transaction is not aborted.

Message beans are similar to stateless session beans in that both beans maintain no state between requests. Message-driven beans are therefore stateless but, like stateless session beans, they may have instance variables, which are maintained throughout the bean instances' life.

1 2 3 4 Page 3
Page 3 of 4