After days of untested theories, head-scratching, dead ends, and rabbit trails, I’m happy to report that something now works.
The Beginning of Unstuckitude
First thing was, Keith got back, took a look at the class cast errors in the stack trace and said something along the lines of, “That looks like a dependency issue.”
And that is what it was: my .war project was including javax.jms.ConnectionFactory more than once. (There is also a Spring forum question and answer for this problem.)
Step 1: activemq-all to activemq-core
We browsed through the .jar files in the activemq-all .jar file (manually, by opening each enclosing .jar in WinZip — anyone have a better way of doing this?) and indeed it did have javax.jms.* classes in it. Keith knew that having the same classes multiple times in your .war can cause problems as the app server may load the wrong ones, so in my project’s pom.xml we replaced the dependency on activemq-all with a dependency on activemq-core instead:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.0.0</version>
</dependency>
When we tried to do a Maven Install of this, though, we got:
Missing:
----------
1) org.apache.activemq:activeio-core:jar:3.1-SNAPSHOT
...
2) org.apache.activemq:activeio-core:test-jar:tests:3.1-SNAPSHOT
...
----------
2 required artifacts are missing.
Step 2: Exclude activeio-core dependency
We don’t need the activeio-core tests anyway, so we put in an exclusion for this:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.0.0</version>
<exclusions>
<exclusion>
<groupId>org.apache.activemq</groupId>
<artifactId>activeio-core</artifactId>
</exclusion>
</exclusions>
</dependency>
This time Maven Install ran cleanly.
Step 3: Scope = Provided
When we deployed, though, we got errors like this:
10:12:59,417 WARN [JBossManagedConnectionPool] Throwable while attempting to get a new connection: null
javax.resource.ResourceException: Could not create connection.
...
Caused by: javax.jms.JMSException: Could not create Transport. Reason: java.io.IOException: Transport scheme NOT recognized: [tcp]
...
08:35:36,453 INFO [DefaultMessageListenerContainer] Could not refresh JMS Connection - retrying in 5000 ms
javax.jms.JMSException: Could not create Transport. Reason: java.io.IOException: Transport scheme NOT recognized: [tcp]
...
The DefaultMessageListenerContainer, finding itself unable to make a JMS connection, kept trying every five seconds — boom, bang, bang, boom, zowie! (I’m still getting acclimated to the spectacular errors you can get when deploying to an app server. :)
At first I didn’t notice one of the Caused Bys farther down, which said:
Caused by: java.lang.ClassCastException: org.apache.activemq.transport.tcp.TcpTransportFactory
But sure enough, it turns out that this error was caused by double-deployed classes. This time, it was the activemq-core jar that is already contained inside the activemq-ra.rar file. Our project already depends on that .rar file being there, and we don’t want two copies of the jar, so we updated our pom file’s activemq-core dependency definition by setting scope to provided (line 5), as follows:
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.activemq</groupId>
<artifactId>activeio-core</artifactId>
</exclusion>
</exclusions>
</dependency>
Success
This time, our .war file deploys without stack traces (woohoo!), and when I point my browser to the ActiveMQ Console I see that my “gloriousTest.Queue” queue shows up. I use the ActiveMQ Console to send a message to the queue and… my MessageListener gets the message and prints its debug output to the JBoss console!
The Files That Worked
applicationContext.xml, the Spring bean file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:amq="http://activemq.org/config/1.0"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
http://activemq.org/config/1.0 http://activemq.apache.org/schema/core/activemq-core-5.0.0.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd">
<jee:jndi-lookup id="queueConnectionFactory" jndi-name="java:activemq/QueueConnectionFactory" />
<amq:queue id="queue" physicalName="gloriousTest.Queue" />
<bean id="messageListener" class="com.something.dmn.MessageListener" />
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager" />
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="queueConnectionFactory" />
<property name="destination" ref="queue" />
<property name="messageListener" ref="messageListener" />
<property name="transactionManager" ref="transactionManager" />
</bean>
</beans>
pom.xml (relevant portions)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${springFramework.version}</version>
</dependency>
...
<dependency>
<groupId>org.apache.geronimo.specs</groupId>
<artifactId>geronimo-jms_1.1_spec</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-core</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
<exclusions>
<exclusion>
<groupId>org.apache.activemq</groupId>
<artifactId>activeio-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
...
</dependencies>
<properties>
...
<springFramework.version>2.5.4</springFramework.version>
</properties>
activemq-jms-ds.xml
This file I was able to use without modification from the example on the ActiveMQ site. I noticed, though, that the <ServerUrl> elements under connection-factories -> tx-connection-factory seem to be ignored. (Maybe that is because in our case the ServerUrl is already being pre-specified in the activemq-ra.rar’s ra.xml file.)
ra.xml (in activemq-ra.rar)
I think the only change I had to make to this resource adapter archive was to the connector -> resourceadapter -> config-property-value element for the ServerUrl config-property-name, to change it from vm://localhost to tcp://localhost:61616. (Or maybe it was already tcp… I don’t remember now.)
Next
Now that we have the JCA/JMS/JNDI/JBoss/Spring/ActiveMQ stuff playing nicely together, we can try to actually do some XA stuff!