My experiences about a JMS interchange between HornetQ and ActiveMQ over Apache ServiceMix

I recently had a project where I had to deploy some Apache Camel routes, deployed over ServiceMix, that had to connect to a HornetQ messaging system, deployed on a JBoss Application Server.

My first intention was to wrap the HornetQ libraries as OSGi bundles and deploy them on Apache ServiceMix, but it was a real nightmare, with numerous class loading problems, terrible! There is an example of a similar use case of mine, written by Torsten Mielke, but I couldn’t establish the connection between Apache ServiceMix 6.0.0 and JBoss EAP 6.3. On the other hand, there is a HornetQ issue to make it OSGi compliant, but it also didn’t work out in my case.

My final approach to the problem was to set up HornetQ bridges between JBoss EAP 6.3 and the JMS installation of ServiceMix: Apache ActiveMQ. You can find the code of my proof of concept in one of my GitHub repositories. I honestly think that this solution desvirtues the concept of an ESB, because the routing is made outside, but it works!

Advertisements

JAAS, Wildfly and Microsoft Active Directory

Some time ago, I wrote about a Java EE Web application that made use of Microsoft Active Directory, through Java Authentication and Authorization Service (JAAS), as its security mechanism. The program was deployed in a Glassfish 4.0 application server. I’ve recently moved this application to a Red Hat Wildfly 9.0.1 server and I’d like to share a couple of issues I consider can be helpful for those involved in the same topic.

The fist one is the configuration of the LDAP realm. Here you have an excerpt of my standalone-full.xml file:


<management>
  <security-realms>
  ...
    <security-realm name="LdapRealm">
      <authentication>
        <ldap connection="AdConnection" base-dn="OU=TestOU,DC=test,DC=local" recursive="true">
          <username-filter attribute="sAMAccountName"/>
        </ldap>
      </authentication>
      <authorization>
        <ldap connection="AdConnection">
          <group-search group-name-attribute="cn">
            <principal-to-group group-attribute="memberOf"/>
          </group-search>
        </ldap>
      </authorization>
    </security-realm>
  ...
  </security-realms>
  <outbound-connections>
    <ldap name="AdConnection" url="ldap://127.0.0.1:389" 
      search-dn="CN=testapp,CN=Users,DC=test,DC=local" search-credential="password"/>
  </outbound-connections>
  ...
</management>

The CLI script that creates this XML structure on Wildfly 9.0.1 is:


connect
/core-service=management/ldap-connection=AdConnection:add(url="ldap://127.0.0.1:389", \
  search-dn="CN=testapp,CN=Users,DC=test,DC=local",search-credential="password")
/core-service=management/security-realm=LdapRealm:add
/core-service=management/security-realm=LdapRealm/authentication=ldap:add(connection=AdConnection, \
  base-dn="OU=TestOU,DC=test,DC=local", \
  recursive="true", \
  username-attribute="sAMAccountName")
batch
/core-service=management/security-realm=LdapRealm/authorization=ldap:add(connection="AdConnection")
/core-service=management/security-realm=LdapRealm/authorization=ldap/ \
  group-search=principal-to-group:add(group-name-attribute="cn", \
  group-attribute="memberOf")
run-batch
reload

The second issue is the configuration of a security domain, which is a concept that Glassfish not requires, there you just set up the name of the realm in the login-config element of the file web.xml Wildfly ignores this configuration, but requires instead its specific jboss-web.xml file in the WEB-INF folder of the application:


<?xml version="1.0" encoding="UTF-8"?>
<jboss-web version="7.1"
  xmlns="http://www.jboss.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/schema/jbossas/jboss-web_7_1.xsd">
  <context-root>/ifaztelep/>context-root>
  <security-domain>test-domain/>security-domain>
</jboss-web>

Finally, here you have the CLI script that creates the security domain in Wildfly:


connect
/subsystem=security/security-domain=test-domain:add(cache-type="default")
/subsystem=security/security-domain=test-domain/authentication=classic:add \
 (login-modules=[{code="RealmDirect", \
  flag="required", \
  module-options={password-stacking="useFirstPass",realm="LdapRealm"}}])
reload


SQL Server data sources in JBoss AS 7

Last week, I set up a SQL Server 2008 data source on a JBoss AS 7.1.1 server, in order to be used by a Java EE application, so I’d like to share what I’ve learned.

The first step was to install the driver. There are two ways to do this, the quick one is simply to deploy the jdbc driver (sqljdbc4.jar) as a regular deployment, by typing this command in CLI (the name parameter is optional, but I found it useful):

deploy C:\software\drivers\sqljdbc4.jar –name=sqlserver

The second option is to install the jdbc driver as a core module, which it was what I finally did. This one was a bit more laborious. First of all, I turned off the server and I set up a directory structure under JBoss modules folder, in my case C:\jboss-as-7.1.1.Final\modules\com\microsoft\sqlserver\main, after that, I copied the driver sqljdbc4.jar there and I created a file called module.xml with the following content:

  <?xml version="1.0" encoding="UTF-8"?>
  <module xmlns="urn:jboss:module:1.0" name="com.microsoft.sqlserver">
    <resources>
      <resource-root path="sqljdbc4.jar"/>
    </resources>
    <dependencies>
      <module name="javax.api"/>
      <module name="javax.transaction.api"/>
    </dependencies>
  </module>

The key here is to create a directory structure that matches the module name. The final step of this option was to start the server and run the following CLI command:

/subsystem=datasources/jdbc-driver=sqlserver:add(driver-name=sqlserver,driver-module-name=com.microsoft.sqlserver,driver-xa-datasource-class-name=com.microsoft.sqlserver.jdbc.SQLServerXADataSource)

Once I had the driver configured, I created the data source by using this CLI command (for this sample, I set up a local SQL Server EXPRESS instance, with a test database and a test user):

data-source add –name=TestDS –jndi-name=java:/jdbc/Test –driver-name=sqlserver –connection-url=jdbc:sqlserver://localhost\SQLEXPRESS;databaseName=Test –user-name=test –password=test –min-pool-size=10 –max-pool-size=50 –pool-use-strict-min=true –pool-prefill=true –jta=true –use-ccm=true –prepared-statements-cache-size=32

 The data source has to be enabled:

data-source enable –name=TestDS

Finally, I tested the configuration with the following command:

/subsystem=datasources/data-source=TestDS/:test-connection-in-pool

Creating a XA data source was slightly different. The first step was to check out that my SQL Server installation was properly configured, by reviewing the chapter titled Configuration Instructions of this article. After that, I ran these commands:

xa-data-source add –name=TestDS–jndi-name=java:/jdbc/Test/XA –driver-name=sqlserver –user-name=test –password=test–min-pool-size=10 –max-pool-size=50 –pool-use-strict-min=true –pool-prefill=true –jta=true –use-ccm=true –prepared-statements-cache-size=32 –same-rm-override=false
/subsystem=datasources/xa-data-source=TestDS/xa-datasource-properties=ServerName:add(value=localhost\SQLEXPRESS)
/subsystem=datasources/xa-data-source=TestDS/xa-datasource-properties=DatabaseName:add(value=Test)
/subsystem=datasources/xa-data-source=TestDS/xa-datasource-properties=SelectMethod:add(value=cursor)
xa-data-source enable –name=TestDS
/subsystem=datasources/xa-data-source=TestDS/:test-connection-in-pool

A final tip, if you decide to deploy the driver, instead of installing it as a core module, you have to add the parameter –xa-datasource-class to the command xa-data-source add with the value com.microsoft.sqlserver.jdbc.SQLServerXADataSource


References:


A .NET class library for accessing to the HornetQ REST interface

This is the third article of my series about the HornetQ REST interface, in the first post I wrote about the building and deployment of a customized version of the Web application that implements the interface, in the second post I talked about the development of a Java class library for accessing to the HornetQ REST interface, this time I want to develop a .NET API with the same goal.

I was an enthusiastic VB6 programmer long, long time ago. I also participated in some projects that made use of Visual Basic .NET and I even made some things in ASP.NET, but, to be perfectly honest, I’ve been focused in Java based technologies last years, so I’ve had to bring myself up to date on .NET!

The first task was to setup a development environment. I decided to use the free tool Microsoft Visual Web Developer 2010 Express and I also installed the extension NuGet Package Manager, which made the setup of Microsoft ASP.NET Web API Client Libraries easier. These libraries helped me to access to the HornetQ REST interface, like RESTEasy helped me in Java. This post of Mike Wasson was very useful to get a quick introduction, although he programs in C#!

My idea was to rewrite the library I created in Java using Visual Basic .NET, as simple as that. The first thing I realized is that nowadays both languages have similar capacities, a quick proof, this is the messaging interface in Java:


public interface MessagingInterface {

    void start() throws MessagingException;

    <T> void sendMessage(T message) throws MessagingException;

    <T> T receiveNextMessage(Class<T> type) throws MessagingException;

    void ackLastMessageReceived() throws MessagingException;

    void stop() throws MessagingException;
}

And this .NET:


Public Interface MessagingInterface(Of T)

    Sub ClientStart()

    Sub SendMessage(ByRef Mensaje As T)

    Function ReceiveNextMessage() As T

    Sub AckLastMessageReceived()

    Sub ClientStop()

End Interface

The HTTP messages to send in each case are determined by the HornetQ REST Interface user’s manual, so the algorithms are the same in Java and in Visual Basic .NET, I just had to adapt the code to the language and the special features of .NET
Another interesting comparison, this the Java method to send a message:


@Override
public <T> void sendMessage(T message) throws MessagingException {
    ClientResponse response;

    try {
        if (!this.isStarted) {
            throw new IllegalStateException("The client is not started");
        }

        response = this.msgCreateLink.request().body(MediaType.APPLICATION_XML, message).post();

        if (response.getStatus() == 307) {
            this.msgCreateLink = response.getLocation();

            response = this.msgCreateLink.request().body(MediaType.APPLICATION_XML, message).post();
        }

        if (response.getResponseStatus().equals(Response.Status.CREATED)) {
            this.msgCreateLink = response.getHeaderAsLink("msg-create-next");
        } else {
            throw new MessagingException("Response code  " + response.getStatus() + " not supported");
        }
    } catch (MessagingException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new MessagingException(ex);
    }
}

And this is the .NET one:


Public Sub SendMessage(ByRef Message As T) Implements MessagingInterface(Of T).SendMessage
    Dim Response As HttpResponseMessage
    Dim XmlFormatter As XmlMediaTypeFormatter

    Try
        If Not Me.IsStarted Then
            Throw New InvalidOperationException("The client is not started")
        End If

        XmlFormatter = New XmlMediaTypeFormatter
        XmlFormatter.UseXmlSerializer = True

        Response = Me.HornetQHttpClient.PostAsync(Me.MsgCreateUri, 
                                                  Message, 
                                                  XmlFormatter).Result

        If Response.StatusCode = HttpStatusCode.RedirectKeepVerb Then
            Me.MsgCreateUri = Response.Headers.GetValues("Location").First
            Response = Me.HornetQHttpClient.PostAsync(Me.MsgCreateUri, 
                                                      Message, 
                                                      XmlFormatter).Result
        End If

        If Response.StatusCode = HttpStatusCode.Created Then
            Me.MsgCreateUri = Response.Headers.GetValues("msg-create-next").First
        Else
            Throw New MessagingException("Response code " + Response.StatusCode + " not supported")
        End If
    Catch ex As MessagingException
        Throw ex
    Catch ex As Exception
        Throw New MessagingException(ex.Message, ex)
    End Try
End Sub

References


A Java class library for accessing to the HornetQ REST interface

This is the second post of the series about the HornetQ REST interface, which I started to write last month. This time I’ve developed a Java class library that pretends to simplify the access to the HornetQ REST interface. You may be wondering, why not to use JMS if you’re programming in Java language? The main reason is that it’s difficult to communicate through firewalls using JMS in JBoss 4.2.1, which it’s a requisite in my case. You might implement HTTP tunnelling, but this could be inefficient and not scalable, so I think that an architecture that mixes JMS and REST may be a good solution when you have to send/receive messages to/from multiple partners. For example, a provider can send you a message through the REST interface and you can consume with JMS, and vice versa.

First of all, I’d like to point out two additional requisites of my case:

  • All messages must be persistent.
  • Messages must not be duplicated.

These goals can be achieved by setting up the elements default-durable-send and dups-ok of the HornetQ REST Interface Web Application configuration file (rest-config.xml) to the values true and false respectively.

The HornetQ REST Interface user’s manual define a protocol for sending and receiving messages, where you have to send several HTTP messages, read different HTTP headers, interpret return codes and so on. The idea is to write a class library that hides this complexity and offers a simple interface that allows sending and receiving messages:


public interface MessagingInterface {

    void start() throws MessagingException;

    <T> void sendMessage(T message) throws MessagingException;

    <T> T receiveNextMessage(Class<T> type) throws MessagingException;

    void ackLastMessageReceived() throws MessagingException;

    void stop() throws MessagingException;
}

The class HornetQRESTClient implements the former interface. It uses the RESTEasy supporting library in order to simplify the REST calls and has three main properties:

  • serverURL: URL where is deployed the HornetQ REST Interface Web Application
  • queue: the queue or topic where you want to send messages or receive from.
  • msgSubscriberId: the id of the subscriber for the case of the topics.

The start method setup the environment and send an HTTP HEAD request, which returns the headers you need for sending and receiving messages. Here you have an excerpt of the most interesting code:


    ...
    request = new ClientRequest(this.serverURL + (this.isTopic ? "/topics/" : "/queues/") + this.queue);

    response = request.head();
    if (response.getResponseStatus().equals(Response.Status.OK)) {
        this.msgCreateLink = response.getHeaderAsLink("msg-create");                
        this.msgPullConsumerCreationLink = response.getHeaderAsLink(this.isTopic ? 
                                                                        "msg-pull-subscriptions" : 
                                                                        "msg-pull-consumers");
    } else if (response.getResponseStatus().equals(Response.Status.NOT_FOUND)) {
        throw new MessagingException("The queue " + queue + " is not registered");
    } else {
        throw new MessagingException("Response code  " + response.getStatus() + " not supported");
    }
    ...

The method sendMessage allows you to send a message encapsulated on an object susceptible to be marshalled with JAXB:


@Override
public <T> void sendMessage(T message) throws MessagingException {
    ClientResponse response;

    try {
        if (!this.isStarted) {
            throw new IllegalStateException("The client is not started");
        }

        response = this.msgCreateLink.request().body(MediaType.APPLICATION_XML, message).post();

        if (response.getStatus() == 307) {
            this.msgCreateLink = response.getLocation();

            response = this.msgCreateLink.request().body(MediaType.APPLICATION_XML, message).post();
        }

        if (response.getResponseStatus().equals(Response.Status.CREATED)) {
            this.msgCreateLink = response.getHeaderAsLink("msg-create-next");
        } else {
            throw new MessagingException("Response code  " + response.getStatus() + " not supported");
        }
    } catch (MessagingException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new MessagingException(ex);
    }
}

On the other hand, I’ve decided to implement the manual acknowledgement method for receiving messages via pull, so I’m continuously polling the server to see if messages are available by calling the method receiveNextMessage and I call the method ackLastMessageReceived, once I’ve processed a received message.  I’ll analyse consuming messages via push in a forthcoming post. Here you have the code of both methods:


@Override
public <T> T receiveNextMessage(Class<T> type) throws MessagingException {
    T message = null;
    ClientResponse response;

    try {
        if (!this.isStarted) {
            throw new IllegalStateException("The client is not started");
        }

        if (this.msgAcknowledgementLink != null) {
            throw new IllegalStateException("There is a message waiting for acknowledgement");
        }

        // Creates the consumer, if needed
        if (this.msgPullConsumerLink == null) {
            if (this.isTopic) {
                response = this.msgPullConsumerCreationLink.request()
                        .formParameter("autoAck", "false")
                        .formParameter("durable", "true")
                        .formParameter("name", this.msgSubscriberId)
                        .post();
            } else {
                response = this.msgPullConsumerCreationLink.request().formParameter("autoAck",
                                                                                    "false").post();
            }

            if (response.getResponseStatus().equals(Response.Status.CREATED)) {
                this.msgPullConsumerLink = response.getLocation();                    
                this.nextMsgLink = response.getHeaderAsLink("msg-acknowledge-next");
            } else if (response.getResponseStatus().equals(Response.Status.NO_CONTENT)) {
                this.nextMsgLink = response.getHeaderAsLink("msg-acknowledge-next");              
            } else {
                throw new MessagingException("Response code  " + response.getStatus() + " not supported");
            }
        }

        response = this.nextMsgLink.request()
                .header("Accept-Wait", SECONDS_WAITING)
                .header("Accept", MediaType.APPLICATION_XML)
                .post();

        if (response.getResponseStatus().equals(Response.Status.OK)) {
            message = (T) response.getEntity(type);
            this.msgAcknowledgementLink = response.getHeaderAsLink("msg-acknowledgement");
        } else if (response.getResponseStatus().equals(Response.Status.SERVICE_UNAVAILABLE)) {
            this.nextMsgLink = response.getHeaderAsLink("msg-acknowledge-next");
        } else if (response.getResponseStatus().equals(Response.Status.PRECONDITION_FAILED)) {
            this.nextMsgLink = response.getHeaderAsLink("msg-acknowledge-next");
        } else {
            throw new MessagingException("Response code  " + response.getStatus() + " not supported");
        }
    } catch (MessagingException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new MessagingException(ex);
    }

    return message;
}

@Override
public void ackLastMessageReceived() throws MessagingException {
    ClientResponse response;

    try {
        if (!this.isStarted) {
            throw new IllegalStateException("The client is not started");
        }

        if (this.msgAcknowledgementLink == null) {
            throw new MessagingException("No messages waiting for acknowledgement");
        }

        response = this.msgAcknowledgementLink.request().formParameter("acknowledge", "true").post();

        if (response.getResponseStatus().equals(Response.Status.NO_CONTENT)) {
            this.nextMsgLink = response.getHeaderAsLink("msg-acknowledge-next");                
            this.msgAcknowledgementLink = null;
        } else {
            throw new MessagingException("Response code  " + response.getStatus() + " not supported");
        }
    } catch (MessagingException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new MessagingException(ex);
    }
}

The method stop cleans up the consumer created for consuming messages just in the case of queues. I’ve decided not to do it for topics, because I need durable subscriptions and I’ve realized through testing that if you delete the consumer, the subscription is deleted too, and the manual says:

“…A consumer timeout for durable subscriptions will not delete the underlying durable JMS subscription though, only the server-side consumer resource (and underlying JMS session)…”

So, I just wait for the timeout. Here you have the code:


@Override
public void stop() throws MessagingException {
    ClientResponse response;

    try {
        if (!this.isStarted) {
            throw new IllegalStateException("The client is not started");
        }

        if (this.msgPullConsumerLink != null && (!this.isTopic)) {
            response = this.msgPullConsumerLink.request().delete();

            if (!response.getResponseStatus().equals(Response.Status.NO_CONTENT)) {
                throw new MessagingException("Response code  " + response.getStatus() + " not supported");
            }
        }

        this.isStarted = false;
        this.initLinks();
    } catch (MessagingException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new MessagingException(ex);
    }
}

Finally, I’d like to show the code of a simple unit test:


@Test
public void queueTest() throws MessagingException {
    MessagingInterface messaging;
    Order orderToSend, orderReceived;

    messaging = RESTMessagingClientFactory.getHornetQRESTClient(SERVER_URL, QUEUE);

    messaging.start();

    // Purge the queue
    while((orderReceived = messaging.receiveNextMessage(Order.class)) != null) {
        messaging.ackLastMessageReceived();
    }

    orderToSend = new Order("Test item", 5, 10.2f);        
    messaging.sendMessage(orderToSend);

    orderReceived = messaging.receiveNextMessage(Order.class);        
    messaging.ackLastMessageReceived();

    messaging.stop();

    assertNotNull(orderReceived);
    assertEquals(orderToSend, orderReceived);                
}

In order to run the test, I had to deploy the following queue and topic by adding this configuration to my hornetq-jms.xml file:


   <queue name="orders">
      <entry name="/queues/orders"/>
   </queue>
   <topic name="orders">
      <entry name="/topics/orders"/>
   </topic>

The source code of this library can be downloaded following this link.


References


Researching the HornetQ REST interface

I’ve been studying the HornetQ REST interface during the last weeks, because I consider it’s a very good solution if you have an application that uses JMS and you want to integrate messages from a third party that doesn’t use Java language. This is the first post of the ones I’ve decided to write about this issue and, in this case, the first task is to configure and install the interface.

As usual, it’s important to review the user’s manual of the technology, this time the HornetQ REST interface one.  One important restriction in my case was that I had to use the version 4.2.1 GA of the JBoss Application Server, with HornetQ 2.2.5 as its messaging provider. Therefore, the pom.xml file proposed by the manual had to be changed, in order to add the dependency on the RESTEasy libraries and the 1.5 Java version for both the source and target code:

<project xmlns="http://maven.apache.org/POM/4.0.0" 
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.wordpress.fcosfc.hornetq</groupId>
  <artifactId>HornetQRESTInterface</artifactId>
  <packaging>war</packaging>
  <version>1.0</version>
  <name>HornetQRESTInterface</name>
  <repositories>
    <repository>
      <id>jboss</id>
      <url>http://repository.jboss.org/nexus/content/groups/public/</url>
    </repository>
  </repositories>

  <dependencies>
    <dependency>
      <groupId>org.hornetq.rest</groupId>
      <artifactId>hornetq-rest</artifactId>
      <version>2.2.2.Final</version>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxrs</artifactId>
      <version>2.2.2.GA</version>
      <exclusions>
        <exclusion>
          <artifactId>commons-logging</artifactId>
          <groupId>commons-logging</groupId>
        </exclusion>
      </exclusions>
    </dependency>
    <dependency>
      <groupId>org.jboss.resteasy</groupId>
      <artifactId>resteasy-jaxb-provider</artifactId>
      <version>2.2.2.GA</version>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>2.0.2</version>
        <configuration>
          <source>1.5</source>
          <target>1.5</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

Here you have a link to my Maven project, I hope it will be useful for you.


References


Trying to set up a JMS bridge between HornetQ and Oracle AQ

I’ve been interested in setting up a JMS bridge between HornetQ and Oracle Advanced Queuing (AQ) for a while, even I wrote about this issue in a response to a question of the HornetQ user’s forum. I resume the matter in this post with some improvements, although, to be perfectly honest, I am not happy with the configuration.

In theory, the bridge is possible because both systems are JMS 1.1. compliant. If you use a HornetQ 2.2.5 standalone installation, you simply have to add some managed beans to the hornetq-beans.xml file, as is described in the user manual. The key point here is to use the proper connection factory and set up the destination following the rules of the Weblogic manual: “Interoperating with Oracle AQ JMS”. Here you have an excerpt of the hornetq-beans.xml file:

<bean name="JMSBridge">
  <constructor>
  ...
    <!-- concatenate JMS messageID to the target's message header -->
    <parameter>true</parameter>
  ...
</bean>
...
<!-- TargetCFF describes the ConnectionFactory used to connect to the target destination -->
<bean name="TargetCFF">
  <constructor>
    <parameter>
      <inject bean="TargetJNDI" />
    </parameter>
    <parameter>QueueConnectionFactory</parameter>
  </constructor>
</bean>
...
<!-- TargetDestinationFactory describes the Destination used as the target -->
<bean name="TargetDestinationFactory">
  <constructor>
    <parameter>
      <inject bean="TargetJNDI" />
    </parameter>
    <parameter>Queues/testQueue</parameter>
  </constructor>
</bean>
...
<!-- JNDI is a Hashtable containing the JNDI properties required -->
<!-- to connect to the *target* JMS resources                    -->
<bean name="TargetJNDI">
  <constructor>
    <map keyClass="java.lang.String" valueClass="java.lang.String">
      <entry>
        <key>java.naming.factory.initial</key>
        <value>oracle.jms.AQjmsInitialContextFactory</value>
      </entry>
      <entry>
        <key>db_url</key>
        <value>jdbc:oracle:thin:@oraclehost:1521:test</value>
      </entry>
      <entry>
        <key>java.naming.security.principal</key>
        <value>test</value>
      </entry>
      <entry>
        <key>java.naming.security.credentials</key>
        <value>secret</value>
      </entry>
    </map>
  </constructor>
</bean>

The former configuration runs properly with the DUPLICATES_OK quality of service, but when you moves to a ONCE_AND_ONLY_ONCE one, changing the TargetCFF to XAQueueConnectionFactory, you get a NullPointerException:

  oracle.jms.AQjmsException: Error creating the db_connection
    at oracle.jms.AQjmsDBConnMgr.getConnection
    at oracle.jms.AQjmsDBConnMgr.
    at oracle.jms.AQjmsXAConnection.
    at oracle.jms.AQjmsXAQueueConnectionFactory.createAllXAConnection
    at oracle.jms.AQjmsXAQueueConnectionFactory.createXAQueueConnection

After a research on Oracle Support notes, I found that the Oracle API has a bug (10102373) that it will be fixed on the future release 12.1 There are workarounds for not receiving duplicate messages, for example: to save the JMS Message ID in a Oracle table and implement a unique index on that column, you have to take into account that the JMS Messege ID sent by HornetQ is a property that can be recovered with the method get_string_property of the Oracle type aq$_jms_message.

But, why Weblogic can interoperate with Oracle AQ in XA configurations? I suspect that the reason is because uses datasources with pre-created database connections. So, I’ve tried to deploy the bridge within JBoss AS 6.0.0, with HornetQ as its default JMS provider. First of all, I created an Oracle XA datasource. After that, I set up the configuration file jms-bridge-jboss-beans.xml in a similar way as before, the only change is to change the db_url parameter by the parameter datasource:

<bean name="TargetJNDI">
  <constructor>
    <map keyClass="java.lang.String" valueClass="java.lang.String">
      <entry>
        <key>java.naming.factory.initial</key>
        <value>oracle.jms.AQjmsInitialContextFactory</value>
      </entry>
      <entry>
        <key>datasource</key>
        <value>jdbc/testOracleXA</value>
      </entry>
    </map>
  </constructor>
</bean>

But, when I tried to run the former configuration, the bridge didn’t start. Another bug, a HornetQ one in this case, prevented me from reaching my goal. To be perfectly honest, I didn’t patch the installation, but I restart the managed bean through the JMX console and I got another exception, a ClassCastException, because JBoss pass a wrapped Oracle connection to the class AQjmsInitialContextFactory, which is waiting for an native internal connection. I really felt powerless!

Finally, I adopted the solution of setting up an Oracle Weblogic server as a mediator,  the guru Edwin Biemond describes this configuration in this article.