Posts Tagged JCA

Something works!

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!

Advertisements

, , , , ,

Leave a comment

JCA Resource Adapters: outbound vs. inbound

In the ra.xml configuration file in the ActiveMQ resource adapter .rar file, I noticed these tags:

<outbound-resourceadapter>

<inbound-resourceadapter>

It seemed like most of the interesting-looking parts of the configuration were inside one or the other of these tags, but I wasn’t sure what they meant.  This helpful blog post clarifies it for me:

There’s quite a big difference in how a Resource Adapter is used to provide outbound connectivity versus inbound connectivity. With outbound connectivity, I mean a situation where an application obtains a connection to an external system and reads or writes data to it. With inbound I mean a situation where the Resource Adapter listens for events from the external system and calls into your application when such an event occurs.

Leave a comment

Trying out XA, part 3: dead ends

In our last episode, we got our little .war file to deploy to JBoss with the JtaTransactionManager bean and QueueConnectionFactory defined in the Spring bean file.  Next we need to get the DefaultMessageListenerContainer bean to work.

First let’s try just uncommenting what we have, and see if it Just Works.

Here’s what we get:

13:15:37,740 ERROR [ContextLoader] Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘jmsContainer’ defined in class path resource [applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [org.apache.activemq.ra.ActiveMQConnectionFactory] to required type [javax.jms.ConnectionFactory] for property ‘connectionFactory’; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.apache.activemq.ra.ActiveMQConnectionFactory] to required type [javax.jms.ConnectionFactory] for property ‘connectionFactory’: no matching editors or conversion strategy found

It looks like the org.apache.activemq.ra.ActiveMQConnectionFactory that the JNDI lookup got us is not a javax.jms.ConnectionFactory.  Assuming we’re on the right track, I wonder how we would get it to be a JMS ConnectionFactory?

A Different Track

I think we’re on the wrong track, though.  Looking in the activemq-jms-ds.xml file, which defines the datasource (if I have my terminology right) for the java:activemq/QueueConnectionFactory JNDI name, I see these tags at the same level:

<jndi-name>activemq/QueueConnectionFactory</jndi-name>

<connection-definition>javax.jms.QueueConnectionFactory</connection-definition>

Now a javax.jms.QueueConnectionFactory is not the same thing as a javax.jms.ConnectionFactory.  Spring’s DefaultMessageListenerContainer wants the javax.jms.ConnectionFactory, not just a QueueConnectionFactory, so I think we need to modify the activemq-jms-ds.xml file to expose a full-fledged ConnectionFactory.  I think when I was digging around in the activemq-ra.rar, I saw a ConnectionFactory sitting around in there… maybe in the meta-inf/ra.xml file?

Exposing a Full-Fledged ConnectionFactory

Let’s try making this change in the activemq-jms-ds.xml file and see what happens:

<tx-connection-factory>
<jndi-name>activemq/FullFledgedConnectionFactory</jndi-name>
<xa-transaction/>
<track-connection-by-tx/>
<rar-name>activemq-ra.rar</rar-name>
<connection-definition>javax.jms.ConnectionFactory</connection-definition>
<ServerUrl>tcp://localhost</ServerUrl>
<min-pool-size>1</min-pool-size>
<max-pool-size>200</max-pool-size>
<blocking-timeout-millis>30000</blocking-timeout-millis>
<idle-timeout-minutes>3</idle-timeout-minutes>
</tx-connection-factory>

For this to work, JBoss would have to be able to find some javax.jms.ConnectionFactory-implementing class in activemq-ra.rar.  It’s no more magic than before, just specifying a different interface than we did before.  The whimsical FullFledged… naming scheme is so it’s easy to notice if the new thing shows up.

Modifying the Spring Bean File Correspondingly

When I deploy the updated datasource configuration file to JBoss, I get a promising-looking message on the JBoss console:

13:42:39,475 INFO  [ConnectionFactoryBindingService] Bound ConnectionManager ‘jboss.jca:service=ConnectionFactoryBinding,name=activemq/FullFledgedConnectionFactory’ to JNDI name ‘java:activemq/FullFledgedConnectionFactory’

So let’s modify the Spring bean file accordingly:

<jee:jndi-lookup id=”connectionFactory” jndi-name=”java:activemq/FullFledgedConnectionFactory”/>

<bean id=”jmsContainer” class=”org.springframework.jms.listener.DefaultMessageListenerContainer”>
<property name=”connectionFactory” ref=”connectionFactory”/>
<property name=”destination” ref=”destination”/>
<property name=”messageListener” ref=”messageListener”/>
<property name=”transactionManager” ref=”transactionManager”/>
</bean>

And…

Not There Yet

When we deploy the .war file, we now get:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘jmsContainer’ defined in class path resource [applicationContext.xml]: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [org.apache.activemq.ra.ActiveMQConnectionFactory] to required type [javax.jms.ConnectionFactory] for property ‘connectionFactory’; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.apache.activemq.ra.ActiveMQConnectionFactory] to required type [javax.jms.ConnectionFactory] for property ‘connectionFactory’: no matching editors or conversion strategy found

Awwwww!  I really thought we had it this time, but a org.apache.activemq.ra.ActiveMQConnectionFactory is still not a javax.jms.ConnectionFactory.  I think the time has come to take a closer look at this org.apache.activemq.ra package.

Dead End and a New Try

[Time passes…]

I couldn’t find much of anything about this ra package.  Looking in the Spring manual, Section 19.5. Support for JCA Message Endpoints and 19.6. JMS Namespace Support, it looks like I should rename my JNDI bean id to reflect that it is actually (I think) a resource adapter and not a connection factory:

<jee:jndi-lookup id=”resourceAdapter” jndi-name=”java:activemq/FullFledgedConnectionFactory”/>

And then I think I can configure the (JCA-based, now) listener container something like this:

<jms:jca-listener-container resource-adapter="resourceAdapter"
                            transaction-manager="transactionManager">
    <jms:listener destination="destination" ref="messageListener"/>
</jms:jca-listener-container>

Is that still a DefaultMessageListenerContainer?  It’s kind of hard to tell…

Apparently It’s Not a ResourceAdapter Either…

When we deploy with these modifications, we get this error:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘org.springframework.jms.listener.endpoint.JmsMessageEndpointManager#0’: Initialization of bean failed; nested exception is org.springframework.beans.TypeMismatchException: Failed to convert property value of type [org.apache.activemq.ra.ActiveMQConnectionFactory] to required type [javax.resource.spi.ResourceAdapter] for property ‘resourceAdapter’; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type [org.apache.activemq.ra.ActiveMQConnectionFactory] to required type [javax.resource.spi.ResourceAdapter] for property ‘resourceAdapter’: no matching editors or conversion strategy found

Well, that’s enough for this episode.  In Episode 4 we’ll see if we can break through with a solution.

, , ,

Leave a comment