November 07, 2003
Why are JMS APIs restricted in J2EE and how to workaround it.
The J2EE specification forbids certain JMS APIs. Section 6.6 of the 1.3 specification documents which ones are forbidden. When you declare a JMS provider to an application server then the application looks up this provider, it actually receives a wrapped JMS provider (wrapped connection, wrapper sessions, wrapped everything). The wrapper serves to add XA support as the appropriate times, implement connection/session pooling and also to police the APIs which are callable on the provider. The same mechanism used to be used on JDBC providers but the JCA mechanism now takes care of this aspect for JDBC and JCA connectors in general. JCA 1.5 should remove this wrapper need for JMS providers in the J2EE 1.4 time frame.
Thus, if you want to call the restricted methods such as JMSConnection.setExceptionListener or APIs to do with ConnectionConsumer then these will fail with a compliance exception. Basically, the J2EE spec says you can't call these. When J2EE 1.4 comes along, then JMS providers will have to implement this 'forbidden API' support when they run in a JCA connector.
There is also a restriction of a single JMS session per connection and you can't call Session.setMessageListener. This basically means an application can't do async receiving of messages at all except using MDBs and MDBs are typically too static for the kinds of customers/applications I'm alluding to here.
There are times when this just plain gets in the way if you are an advanced customer, typically a financial customer. If you're using async beans in WAS-E 5.0 to build advanced messaging applications then you need to work around this problem. The reason the spec disallows these APIs is that they restrict the ability of an application server to provide a managed environment. The server may want to set it's own exception listener. When Connection.start is called when the JMS provider starts a thread for each JMS session associated with the connection and calls message listeners on those threads. Clearly, this thread isn't one made by the app server and hence the objection.
JCA 1.5 should help here as the JMS provider would use threads it obtained from the JCA WorkManager for these sessions. An issue with the JCA 1.5 specification here is that the provider has no way to tell the app server that the thread is long lived and thus the app server will likely take threads from a pool for the session threads which is a bad thing as they are clearly daemon threads so when J2EE 1.4 comes along, size your thread pools for connectors appropriately.
But, nevertheless. Some times, a developer wants to do this. The only workaround available to customers is to load the JMS provider directly, i.e. configure some properties in your application for "InitialContextFactory", "URL", "JNDI Name", "UserID", "Password" and directly create a TopicConnectionFactory or QueueConnectionFactory. This basically gives you a unwrapped/altered JMS provider which will allow you to make the forbidden calls. BUT, you will lose the builtin XA support and pooling if you do this because it's the wrappers which gave this capability but from what I've seen, the typical application that we're talking about here doesn't need this support anyway. This may also require your application to use a J2EE security policy to allow it to instantiate the JMS provider if Java 2 security is enabled.
If you're using WebSphere Enterprise then when a message listener is called on an unmanaged JMS session thread then you should use a WorkManager to process the message on a managed thread before accessing any WebSphere APIs. The pattern here is basically, use one or two sessions to pull the messages in and then execute them on a WorkManager using a pool, i.e. decouple the pulling of the messages from the message processing.
I'm going to shortly publish an article showing how JMS + async beans can be used to build some very common patterns in the kinds of advanced applications found in electronic trading systems. More later...
From what I read in the JavaDocs it is perfectly acceptable to call these methods. They are listed as optional in the specification, not restricted. Normally you use setMessageListener on the MessageConsumer though, not on the Session except in rare cases. I can only imagine that it doesn't work in the WebSphere case because of the weak integration of JMS with the application server.
Posted by: Sam Pullara | Nov 8, 2003 3:04:23 AM
The spec says these APIs cannot be used in portable applications as well as in containers that forbid threads, typically the EJB container. 6.7, Page 102 from the J2EE 1.3 spec. JMS is fully implemented in WebSphere, arguably as well as or better than any one elses. Other vendors have only just gotten around to what 5.0 had when it shipped, for example, XA support for any JMS provider rather than just the built in one as well as ASF support for any provider on the incoming side.
These restriction have nothing to do with WebSphere, it's just the spec.
Posted by: Billy | Nov 8, 2003 6:14:40 AM
Session.setMessageListener is ASF and implementation is optional. A 'normal' JMS client would use consumer.setMessageListener.
As for the JCA 1.5. The JMS provider now provides the 'wrapper' (a connection handle) and - at least here - will restrict usage of methods here as well. set/getExceptionListener on a handle doesn't make much sense in a managed environment.
Another problem I ran into is the Java2 security. What do you think, does it make sense for a JMS JCA 1.5 RA to start a JMS server intravm when the RA is started and stop it when the RA stops? The problem is that I ran into security issues because the JMS server basically hits any security restrictions (e.g. file read, thread pools, socket listens etc). For me it would be a portable way to start a JMS server intravm (in contrast to implement a service interface for any app server). However, it would require to give any permissions (e.g. no hot deploy of the RA possible then, I guess).
Posted by: Andreas | Nov 10, 2003 4:26:25 AM
There should be a way for an RA to specify a security policy for itself. I'm not sure whether this is part of the spec or vendor specific. I don't see a reason why you couldn't/shouldn't start an engine in the RA, the whole point of an RA is to connect to the outside world and of course, you'll need sockets and maybe files to do that.
Posted by: Billy | Nov 10, 2003 6:48:37 AM
I used unwrapped/altered JMS suggested in this article.
Basically i did have only one problem:
when you create it directly it uses default Queue Manager of WS MQ.
Sometimes customer does not like this limitation.
Looking from other side... actually i do not ned to have _J2EE_ application to setMessageLister().
I wanted just a service which extends WAS and could do this. Currently there is nop way to make suche service. No, MBean here does not help me unfortunately. In JBoss it does, in WAS it does not. Because in WAS MBeans must be started/registerd from again J2EE container. With the same limitations.
Posted by: Aleksey | Apr 21, 2004 8:46:28 AM
yes, I do find all the restrictions on JMS calls in websphere *extremely* annoying. We don't find such restrictions in Weblogic or JBoss at all and end up getting stuck trying to port our J2EE application onto WAS for a long time....
Posted by: kwong | Jul 22, 2005 4:54:22 PM