WebLogic JMS Programming


In this section, we are going to look at how to use WebLogic JMS in your application. We start out by talking about the WebLogic JMS resource pooling and how to leverage that support with Web applications and EJBs. This is a new feature added in WebLogic Server 8.1. Next, we discuss how to use WebLogic Server s message-driven bean support to consume JMS messages from server-side applications. We finish up this section with a discussion of strategies for integrating foreign JMS providers into WebLogic Server applications.

Using WebLogic JMS with Servlets and EJBs

Using WebLogic JMS from within your server-side application is as simple as using it from within stand-alone client applications. By making use of the J2EE-defined mechanisms for referencing JMS objects through deployment descriptor resource reference mappings, WebLogic JMS now transparently substitutes the real JMS objects for wrappers that pool JMS resources like connections and sessions. This is new in WebLogic Server 8.1 and also works with foreign JMS providers. To use this, you simply add a resource-ref into your standard J2EE deployment (that is, web.xml or ejb-jar.xml ):

 <resource-ref>   <res-ref-name>jms/BigRezEmailConnectionFactory</res-ref-name>   <res-type>javax.jms.QueueConnectionFactory</res-type>   <res-auth>Container</res-auth>   <res-sharing-scope>Shareable</res-sharing-scope> </resource-ref> 

Then, you add a matching resource-description entry in our WebLogic Server-specific deployment descriptor (that is, weblogic.xml or weblogic-ejb-jar.xml ):

 <resource-description>   <res-ref-name>jms/BigRezEmailConnectionFactory</res-ref-name>   <jndi-name>BigRezEmailConnectionFactory</jndi-name> </resource-description> 

Finally, you simply look up the connection factory and write standard JMS code, as shown in this excerpt from the ReservationSessionBean in our bigrez.com example:

  QueueConnectionFactory factory = (QueueConnectionFactory)   jndiCtx.lookup("java:comp/env/jms/BigRezEmailConnectionFactory");  Queue queue = (Queue)     jndiCtx.lookup("java:comp/env/jms/BigRezEmailQueue"); QueueConnection connection = null; try {  connection = factory.createQueueConnection();  ... } catch (JMSException jmse) {     ... } finally {     if (connection != null) {         try {  connection.close();  } catch (JMSException ignore) { }     } } 

Notice that we are closing our connection at the end of each use inside the finally block. This is critical when using pooled resources and is just like what you would do when using JDBC connection pools. In our example, we are looking up the connection factory and queue each time. You could cache the results of these two lookups if you choose, though the overhead for a local JNDI lookup should be small. Do not try to cache any of the intermediate objects like the connection, session, or sender. WebLogic JMS is already pooling these objects so it is important to release them back to the pool when you have finished using them.

Best Practice  

From your server-side applications, take advantage of JMS resource pooling by binding your JMS connection factory into your component-local JNDI tree. Always close your connection objects at the end of each use to allow WebLogic JMS to release these pooled objects back into the pool.

If you use JMS within XA transaction, you do not need to reference the XA versions of the JMS objects when using the WebLogic JMS pooling mechanism. The wrapper object is smart enough to detect the presence of a JTA transaction and will automatically use the XA capabilities of the JMS provider to enlist it in the XA transaction. If the underlying JMS provider does not support XA, then you will need to suspend the JTA transaction either by telling the container that the EJB does not support transactions or by using the JTA APIs. We will talk more about integrating with foreign JMS providers in the last section of this chapter.

If you happen to be using an older version of WebLogic Server, then you will not have the benefits of the new JMS resource pooling and you may need to come up with a caching strategy to cache the connections, sessions, and producers because you will be working with the actual JMS objects, rather than a pooling wrapper.

Best Practice  

When working with older versions of WebLogic Server, you should consider caching the JMS connection, session, and producer objects to improve performance. WebLogic Server 8.1 takes care of the caching through the new JMS resource-pooling facilities.

One other thing to be aware of is that these new wrapper objects now enforce some J2EE restrictions on these pooled objects that were never enforced when working with the real JMS objects. These restrictions basically prevent you from calling certain JMS methods that require asynchronous delivery and thus require a thread to be created. For example, you are not allowed to associate a MessageListener with a consumer. What this means is that the only way to asynchronously consume messages from a J2EE application is to use either a message-driven bean or a server session pool. Because both of these mechanisms are using pooled objects that are not specific to a particular client or request, the main thing that you lose through this is the ability to create a MessageListener that contains state about the specific client or request. This simply means that any state that you require the asynchronous listener to have must be passed through or accessible using the contents of the message. For more information on the methods that are not allowed, see the J2EE Compliance section of http://edocs.bea.com/wls/docs81/jms/j2ee_components.html.

Warning  

WebLogic JMS resource pooling now enforces the restrictions laid out for server-side applications in Section 6.7 of the J2EE 1.3 specification. This means that existing server-side applications that use the asynchronous MessageListener pattern will no longer work properly if resource pooling is in use.

Consuming Asynchronous Messages on the Server

When building server-side applications that asynchronously consume JMS messages, you have two primary options for how to do this: message-driven beans (MDBs) and server session pools. While WebLogic Server supports both mechanisms, there are few reasons left for using server session pools now that message-driven beans have arrived. As a result, we will focus our discussion on MDBs. For more information on using WebLogic JMS support for server session pools, please refer to the WebLogic JMS documentation at http://edocs.bea.com/wls/docs81/jms/implement.html .

Message-Driven Beans

Our coverage of MDBs is not intended to be exhaustive. If you want to learn more about MDBs, please refer to the WebLogic Server documentation at http://edocs.bea.com/wls/docs81/ejb/message_beans.html .

Understanding Concurrency

Like stateless session beans, the WebLogic Server EJB container pools message-driven bean instances in memory. You can control the size of the pool using the initial-beans-in-free-pool and max-beans-in-free-pool parameters found in the weblogic-ejb-jar.xml deployment descriptor. When messages arrive at the associated destination, the EJB container tries to find a bean in the free pool to handle the message. If no instance is available, the container will create a new instance if the size of the pool is currently less than max-beans-in-free-pool . If the pool is already at its maximum size, the message will remain in the destination until a bean in the pool becomes available. Of course, the maximum amount of parallelism, and therefore the maximum number of beans the EJB container will ever create, is also controlled by the maximum number of threads available for use by the MDB instances. Unlike stateless session beans, the maximum number of available threads varies depending on how the MDB is deployed.

When you deploy an MDB, the WebLogic EJB container associates it with an execute queue and its associated execute threads. By default, all MDBs are associated with the default execute queue, now known as the weblogic.kernel.Default queue. When MDBs are using the default queue, WebLogic Server determines the maximum number of threads used by all MDBs associated with the default execute queue using the following formula. This limit is imposed to prevent deadlocks between the MDBs and other components that share the default execute queue.

click to expand

A recent addition to WebLogic Server is the ability to control the execute queue MDBs use through the use of the dispatch-policy element in the weblogic-ejb-jar.xml deployment descriptor. When you associate MDBs with an application-defined execute queue, then WebLogic Server will allow the MDBs to use up to the number of threads in the execute queue s thread pool. What this means is that you can dedicate a thread pool for the exclusive use of one or more MDBs. We strongly recommend that you take advantage of this as it will give you more control over the concurrency and allow you to partition your MDB message processing from other nonmessage- related activities.

Best Practice  

Always deploy your message-driven beans to a dedicated execute queue.

When you deploy an MDB to listen for messages on a queue, WebLogic Server uses one JMS session and consumer per bean instance in the pool. This allows for parallel processing of queued messages. In contrast, WebLogic Server uses one JMS session and consumer per pool for MDBs listening for messages on topics. Although this means that the EJB container receives one message at a time, it actually dispatches the messages to the bean instances in parallel.

Using Transactions

MDBs provide a declarative mechanism to tell the EJB container to start a transaction before delivering a message to them. As we discussed earlier, JMS does not generically support the concept of transactional delivery of asynchronously received messages. For JMS providers whose session objects implement the weblogic.jms.extensions.MDBTransaction interface, WebLogic Server will support truly asynchronous transactional delivery by receiving the message using the CLIENT_ACKNOWLEDGE mode, start a JTA transaction, and then use this callback interface to associate the message delivery with the JTA transaction. Obviously, this interface is specific to WebLogic Server, but at least one other third-party JMS vendor implements this interface. For JMS providers that do not implement this interface, WebLogic Server uses a synchronous mechanism under the covers in order to support transactional delivery of JMS messages to MDBs.

MDBs support both container-managed and bean-managed transactions. You can control the transactional semantics for your MDBs through the ejb-jar.xml deployment descriptor, just as you do for any other type of EJB. When using container-managed transactions, WebLogic Server will automatically start a JTA transaction before invoking to MDB s onMesssage() method so that the incoming message delivery is part of the JTA transaction. If you want to force the container to roll back the transaction, you should call the setRollbackOnly() method on the MessageDrivenContext object. In general, you should avoid throwing a RuntimeException like EJBException from the onMessage() method. While this will cause the container to roll back the transaction, it also forces the container to remove the MDB instance from the pool, as required by the EJB specification. Of course, the container is free to create another instance should it need to do so.

Best Practice  

Avoid throwing a RuntimeException , such as EJBException from an MDB s onMessage() method to roll back transactions. If an MDB does throw a RuntimeException , the EJB specification required the container to remove the instance that threw the exception from memory. Calling setRollbackOnly() works just as well and does not force the container to remove the instance from memory.

To deploy an MDB that uses container-managed transactions, the MDB must use an XA connection factory. If the referenced connection factory does not support XA, WebLogic Server will not deploy the MDB.

Tip  

MDBs that use container-managed transactions must use XA connection factories.

MDBs also support bean-managed transactions. When using bean-managed transactions, the incoming message delivery cannot be included as part of the transaction. In the onMessage() method, the WebLogic EJB container gives you access to the JTA UserTransaction object through the MessageDrivenContext so that your application can begin, commit, and roll back transactions as necessary. In all cases, you must end your transaction before the onMessage() method returns. Once the onMessage() method returns, the EJB container will acknowledge the message. To prevent this message acknowledgment from occurring, you must throw a Runtime_Exception after ending the transaction.

You have several choices for controlling the type of message acknowledgment that the container uses. By setting the acknowledge-mode element in the ejb-jar.xml deployment descriptor, you can control the acknowledgment mode for any MDB that is not using a container-managed transaction. When container-managed transactions are being used, this attribute is ignored. By default, the container uses AUTO_ACKNOWLEDGE mode when container-managed transactions are not in use (or the transaction type is set to NotSupported ). You can also choose to use DUPS_OK_ACKNOWLEDGE or one of the WebLogic JMS-specific modes, NO_ACKNOWLEDGE or MULTICAST_NO_ACKNOWLEDGE . An MDB is prohibited from using client acknowledgment by the EJB specification.

Dealing with Durable Subscriptions

MDBs can also use durable subscriptions; however, there is a problem with deploying a message-driven bean that uses durable subscriptions to a cluster that warrants discussion. To create an MDB with a durable subscription, you are required to specify the connection factory that sets the durable subscription s client identifier in the MDB deployment descriptor. WebLogic JMS supports clustering through the process of defining a JMS server in each WebLogic Server instance in the cluster. When you target an MDB to a cluster, WebLogic Server deploys a copy of the MDB in each JMS server. Because each copy of the MDB deployed to the individual servers is treated as a separate deployment, this causes a problem because, as far as WebLogic JMS is concerned , you have just deployed multiple durable subscriptions that are using the same client identifier.

What this means is that you need to make sure that each server s MDB is using a unique client identifier. WebLogic Server lets you set the client identifier for an MDB s durable subscription in two ways: using the jms-client-id element in the weblogic-ejb-jar.xml deployment descriptor or using the Client ID attribute on the connection factory s General Configuration tab in the WebLogic Console. This is where the trouble begins.

Because each server in the cluster must use a unique client ID for their individual MDB deployment, you need some way of making your MDB application use either a different deployment descriptor for each server or a different connection factory. To set the client ID explicitly in the deployment descriptor, you would need to create one MDB deployment unit per WebLogic Server instance. Of course, most applications do not deploy MDBs separately from the rest of the application so this means that you end up creating separate application deployment units (for example, separate EAR files) for every server in the cluster just to handle this shortcoming.

To use the connection factory to solve the problem, you need each server to use a different connection factory. The problem is that the JNDI name to use to locate the connection factory is specified using the connection-factory-jndi-name element in the weblogic-ejb-jar.xml deployment descriptor. The only way to make this work currently without having to create separate application deployment units for each server is to create a separate connection factory for each server and have them all use the same JNDI name. While this works, it does prevent you from migrating one JMS server to another WebLogic Server instance that already has a JMS server deployed because of the JNDI naming conflict when both servers need to deploy two different connection factories with the same JNDI name.

In addition, don t forget that WebLogic JMS does not support creating durable subscriptions to a distributed topic. As we discussed previously, you need to create the durable subscriptions directly against the distributed topic s member destinations. Again, this creates a problem for a clustered deployment of MDBs because the durable subscriber s target topic must be identified in the MDB s ejb-jar.xml deployment descriptor. Because each member topic must have a unique JNDI name, this also forces you to create a separate MDB deployment unit for each member destination. We expect that both of these shortcomings will be addressed in a future release.

Warning  

Avoid using durable subscriptions with message-driven beans in WebLogic Server. Due to limitations in the implementation, you will need to create a separate copy of the MDB for each server in the cluster. Because MDBs are usually packaged with the rest of the application, this will mean that you will need to create a separate copy of your application for every server in the cluster or separate the MDB into its own application. Moving the MDBs into a separate application has performance implications because the calls from the MDBs to other EJBs can no longer take advantage of enable-call-by-reference and EJB local interface optimizations.

Application Design Considerations

When designing your MDB-based application, there are several things to keep in mind. First, it is generally better to use a delegation model to keep the business logic inside the onMessage() method to a minimum. By delegating the actual message processing to another component, you can turn the MDB into a controller that does nothing more than dispatch messages to the right business component. This promotes modular design and component reusability.

Remember that an MDB instance can process only one message at a time. This creates a problem if the business logic takes a relatively long time to process a message. As we have mentioned several times throughout this chapter, a well-designed messaging application requires consumers to be able to keep up with producers over long periods of time. If your message processing takes a long time, you need to make sure that you have enough concurrency to handle the incoming message volume.

When deploying MDBs, it is always better to deploy them to the same WebLogic Server instance that hosts the destination whenever possible. When deploying to a cluster where the MDBs and the JMS destinations are both hosted in the same cluster, you should make sure that the MDBs are listening only on destinations in the same server instance. Remember that by default with distributed destinations, WebLogic Server always uses server affinity to help enforce this pattern. In addition, when deploying MDBs that listen to JMS destinations in the same cluster, WebLogic Server starts the MDB instances only on servers that contain the actual destination, or a member destination in the case of distributed destinations. Of course, all of the other considerations we discussed previously in the WebLogic JMS Application Design section apply to MDBs as well.




Mastering BEA WebLogic Server. Best Practices for Building and Deploying J2EE Applications
Mastering BEA WebLogic Server: Best Practices for Building and Deploying J2EE Applications
ISBN: 047128128X
EAN: 2147483647
Year: 2003
Pages: 125

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