Spring JMS and WebSphere
Using Spring JMS in our application which needs to be running on WebSphere proved to be somewhat of a challenge. And since googling provided a lot of information but just a small ‘easy to miss’ piece of text to put the pieces together, i decided to write up this blog.
The need for using Spring JMS opposed to ‘normal’ MessageDrivenBeans came out of the following requirement:
On receiving messages from the Queue we need to send these messages to another system through a Socket connection.
This means that we cannot start processing the messages from the queue until the socket connection is available. This socket connection is created on application startup in a spring bean so it isn’t possible to guarantee that the connection is available. And also the application will be deployed inside a WebSphere cluster so one of the two servers in the cluster will never get a connection to the socket.
So we need some kind of programmatic control over the starting of the message listener.
Luckily this is possible in Spring with the start (and stop) methods of the AbstractJmsListeningContainer or the GenericMessageEndpointManager. Since we already had an implementation with MessageDrivenbeans and ActivationSpecifications to distribute the messages we decided to go with the JmsMessageEndpointManager because we could reuse our ActivationSpecifications configured in WebSphere (well we thought we could do that…..)
Reusing the configured ActivationSpecification was not possible see this topic on the springsource forum so we configured the ActivationSpecification in the spring config files together with the message endpoint handler the resource adapter and the workmanager as described in the using Spring JMS on WebSphere article.
No messages were picked up and no error messages were found to guide us in the right direction. So without more information it became some kind of trial and error exercise (which i hate, and become to hate even more because of the long /try to fix/deploy/test cycle).
So after a few tries i decided to take a closer look at the sources i had used to develop the configuration and after reading over it more than once my eyes stopped at the following piece of text hidden in the using Spring JMS on WebSphere article:
…Be aware that no other Spring JMS MessageListenerContainer types are supported…
Did this mean…..? Yes it did. By making the decision to use the message endpoint handler i had used the wrong type. So after a few minutes of rewriting the configuration to use the DefaultMessageListenerContainer (together with a messageselector to replace the activationspecification), setting the autoStartup property to false and injecting it into my connection manager bean to programmatically start the listener after the connection is created, one more long /try to fix/deploy/test cycle………..
and then it almost worked. A ClassCastException in the onMessage method stating that class A could not be cast to class A. Why isn’t it possible to cast something to itself. Ok i admit it seems a bit strange to want to do this and i didn't do it on purpose really. The onMessage method was supposed to receive a Serialized message as input parameter so i thought i needed to cast it. But it turned out to be a class A already. So i changed the method signature to take a class A as input parameter.
So we got rid of the ClassCastException this way but now we got an NoSuchMethodError (i’m not exactly sure what i prefer). This is going to look like a ClassLoader issue and if it looks like a ClassLoader issue and behaves like a ClassLoader issue it is a ClassLoader issue!
But how can that be? I have an ear file with a war file which loads all the spring bean with the contextLoaderListener and nothing else. Well almost nothing else. On the same server a different ear is deployed which also makes use of class A. therefore class A is in the core module (its called core module for a reason!). Could it be that?
Yes it can be that! The Websphere server can decide to use a ApplicationClassloader for multiple applications so on server level we need to set the classloading policy to single to prevent the ClassCastException or the NoSuchMethodError.
Hope this helps anyone out there to avoid these pitfalls.