Modifying the Application

The design of our application is going to focus on the problems involved with using EJBs as JMS consumers. We will not focus on the use of JMS with EJBs where the EJB acts as a message producer as there is little difference in the process whether an EJB is used or a standard Java class.

Note 

As this application utilizes much of the same underlying JMS code (the JMSManager class, etc.) from the Chapter 6 for the details.

We will first focus our discussion on the EJB 1.1 specification and layout the difficulties presented by the lack of any defined integration with JMS, and then present a solution for this in our first EJB code example. Following that we will focus on the new EJB 2.0 specification, which does provide a definition of JMS and EJB integration, and present our same application solution converted to use the new EJB 2.0 specification–and demonstrate the ease with which this specification now allows EJBs to act as JMS consumers.

The application is a simple modification to the Case Logging application from the previous chapter. This time, as well as sending new case logs to a queue we will also publish them to a topic. The EJBs for this application will act as JMS consumers and monitor the topic, picking up submitted entries, and sending e-mail alerts to the user of the Case Management system so that they will know when they have to log on and check the latest submission.

In most applications involving EJBs and JMS, you will use EJBs in just such a fashion; where they act as manager objects that perform asynchronous operations, and delegations based on message types and properties, received from a given destination.

While our application will be fairly straightforward, the concepts presented can be utilized to create some fairly complex systems in which EJBs act as routing managers to asynchronously invoke operations on other EJBs in a "service" framework, for example.

The Help Request

The diagram below demonstrates how the application will work with the inclusion of an EJB:

click to expand

As you can see, the application is a simple one, involving the submission of a "help request" form using many of the same properties utilized by the previous chapter's Case Logging system, and which alerts support personnel via e-mail notifications. A JSP tag packages the help request details into a JMS message and publishes it on our topic.

In the EJB 1.1 example, we will use a WebLogic Startup class to act as a consumer for the topic. When a message is published to the topic, the Startup class will receive the message in its onMessage() method, which in turn locates and creates our EJB 1.1 bean, and delegates the message to the EJB for processing. The reasons for using the Startup class to delegate method calls will be explained below, and should become readily apparent.

Note 

Our EJB 2.0 example will show how the 2.0 specification simplifies this example even further, and we will modify our architecture to use an EJB 2.0 message-driven bean.

JMS and EJB 1.1

We'll now focus our discussion on the design we presented, and the issues involved in using JMS with EJB 1.1 beans to create the modified Case Logging application.

The EJB 1.1 specification does not provide any integration points for JMS. Due to this, 1.1 EJBs cannot act as direct JMS consumers. The reason for this is that Enterprise JavaBeans are designed to be remotely accessible objects and run within the context of an EJB container.

One of the cardinal rules of EJBs is that the client of an EJB never directly accesses the bean class itself. Since this class's lifecycle, and access, is managed by the EJB container, as a client of the bean you are only allowed to access the bean indirectly through its remote interface.

However, in our application design we want our e-mail EJB 1.1 bean to be able to receive asynchronous JMS messages as a JMS consumer, and the lack of defined integration between the two architectures, as we have discussed, presents us with a design problem.

Before discussing our solution to this problem, let's look at the problem itself, to understand the actual issues involved here.

At first glance, many developers might think the obvious solution to our issue a simple one. It would seem that we should implement the JMS MessageListener interface (and its onMessage() method) directly in our EJB bean class, along with any of our other business logic methods, and we should be all set.

But this solution will not work. Why? Because it violates the EJB specification's contract for accessing an EJB's bean class. When JMS makes an asynchronous call to our EJB, it must act as an EJB client the same as any other EJB client - meaning it must access the EJB through its remote interface (which implies a series of JNDI lookup operations, as well). So if the bean class were directly implementing the MessageListener interface, how would JMS locate the home or remote interfaces, to call the bean's onMessage() method? What's more, since the EJB's bean class may not even be instantiated, how would it be registered with JMS, and by what mechanism?

Even assuming that the EJB's bean class was perhaps registered as a MessageListener through some other external Java class accessing the EJB as a proper client, and that it may still exist as an instantiated instance in the object pool after the access, there is still no way for JMS to know that it is calling into an EJB when it calls the onMessage() method of the bean class asynchronously — and there is no way for JMS to know that it should use a JNDI lookup, and a remote interface to access the EJB.

So what would happen if we were to implement the MessageListener interface directly in the EJB's bean class? It is uncertain, since firstly, JMS will not access the EJB bean class through a remote interface but will call directly into the instantiated bean class, thus violating the contract for accessing the bean class only through the remote interface.

Secondly, if the particular bean instance is being reused from the pool, is in the middle of a transaction or some other internal operation, etc., it could be catastrophic. Since the container serializes access to a bean class and its methods, and provides thread safety (an EJB is single-threaded, and as per the EJB spec, must never create its own threads), when JMS calls into the bean instance's onMessage() method it has just violated the boundary of the EJB container and the protections it provides. The result could be unforeseen and most likely disastrous if, for example, your EJB is participating in a transaction and JMS suddenly calls directly into the same bean instance (violating the ACID properties of the transaction, that the container helps to guarantee):

click to expand

This scenario obviously is not allowed, and therefore we cannot use it. Our next question then becomes, is there any way to allow your EJB to act as a JMS consumer? In EJB 2.0 this answer is yes, but for EJB 1.1 the answer is no.

There is, however, a workaround solution, and this will make up the first code example that we present. The way to solve this problem in EJB 1.1 is to create a "Delegator" class.

Note 

A Delegator class acts as a surrogate for operations to be performed on a delegate class. In our case, for instance, our Delegator class will act as a surrogate for receiving JMS messages through the implementation of the MessageListener interface, and delegate those messages to our EJB.

Most application servers provide the ability register and load any number of specified Java class instances upon startup of the server. This allows you to create your own Java classes that can perform any number of operations as the application server is started, and as importantly, which run within the same VM instance as the server.

For our "Delegator" example we will be using the WebLogic 6.0 application server. The WebLogic server provides such a mechanism, called a Startup class.

Note 

It is important to note that the WebLogic Startup class feature is specific to the WebLogic application server only and that this workaround can only be used against the WebLogic Server. However, if you wished to create this same solution in a more platform-neutral manner, you could add the functionality of the WebLogic Startup class to a simple Java application, and run this separately. While this solution would require running a separate VM instance for the Delegator message handler class, it would be a more platform-neutral solution.

The WebLogic Startup class is nothing more than a Java class which you create that implements a special interface called WebLogic.common.T3StartupDef, which contains a couple of methods. The most important method of which is:

 public String startup (String name, Hashtable args) throws Exception 

When the WebLogic server starts, any registered Startup classes that are found within the classpath, are instantiated (once instance per Startup class defined) by the WebLogic server and the startup() method is called. The name and args parameters are defined in the WebLogic configuration file (config.xml) and can be set through the WebLogic console.

The Startup class can perform any operation it needs to, implement its own methods, etc.

We will use this feature of the WebLogic server (along with the accompanying WebLogic Shutdown class, which is instantiated and its shutdown() method invoked upon an initiated shutdown of the WebLogic server) to create a mechanism to delegate JMS messages to our EJB.

Note 

An initiated shutdown is when the Application Server is shutdown via the WebLogic console, or via the WebLogic SHUTDOWN command available via the WebLogic command line utility weblogic.Admin.

The way the application works is thus: We will create a WebLogic Startup class that will implement the JMS MessageListener interface and its associated onMessage() method. We will register the class as a JMS subscriber on our e-mail topic, and delegate all of the asynchronously received messages on this topic to our Mail EJB via the proper mechanisms of EJB interface lookup via JNDI, and access of the EJB through its remote interface.

The following diagram shows the conceptual layout of this architecture:

click to expand

Our application will thus consist of the following components:

  • A WebLogic Startup class to register as a consumer with JMS and receive asynchronous messages on our "Mail" topic. The Startup class will be coded to delegate incoming JMS messages to our EJB - the "Mail Bean".

  • A stateless Session EJB — our "Mail Bean" that implements its own method to receive a JMS message and contains all of the code to extract the data from the message and send an e-mail alert via the JavaMail API.

  • JSPs for displaying and submitting the Help Request input form.

  • A JSP tag library to handle the POST of the form and to send the request to our topic as a JMS MapMessage.

  • JMSManager and the associated support classes used in the previous chapter. We will not cover these classes again, here.

The sequence of events in the application is shown in the following diagram:

click to expand



Professional JMS
Professional JMS
ISBN: 1861004931
EAN: 2147483647
Year: 2000
Pages: 154

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net