How to send JMS messages from WildFly 10 to remote ActiveMQ

Sayo Oladeji picture Sayo Oladeji · Sep 2, 2016 · Viewed 10.9k times · Source

After so much fumbling around the internet, it's a surprise that I can't find a sample configuration for pushing to a remote message queue using JMS in WildFly 10 with ActiveMQ (Artemis). To worsen the situation standalone-full.xml is not bound to a schema (why???) and when I finally found the XSD for it here on GitHub, it contains no documentation stating what each node/attribute means and what values can be put in what.

Below is the original configuration from standalone-full.xml.

    <subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
      <server name="default">
        <security-setting name="#">
          <role name="guest" delete-non-durable-queue="true" create-non-durable-queue="true" consume="true" send="true"/>
        </security-setting>
        <address-setting name="#" message-counter-history-day-limit="10" page-size-bytes="2097152" max-size-bytes="10485760" expiry-address="jms.queue.ExpiryQueue" dead-letter-address="jms.queue.DLQ"/>
        <http-connector name="http-connector" endpoint="http-acceptor" socket-binding="http"/>
        <http-connector name="http-connector-throughput" endpoint="http-acceptor-throughput" socket-binding="http">
          <param name="batch-delay" value="50"/>
        </http-connector>
        <in-vm-connector name="in-vm" server-id="0"/>
        <http-acceptor name="http-acceptor" http-listener="default"/>
        <http-acceptor name="http-acceptor-throughput" http-listener="default">
          <param name="batch-delay" value="50"/>
          <param name="direct-deliver" value="false"/>
        </http-acceptor>
        <in-vm-acceptor name="in-vm" server-id="0"/>
        <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
        <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
        <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
        <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
        <pooled-connection-factory name="activemq-ra" transaction="xa" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm"/>
      </server>
    </subsystem>

Below is my CDI queue client which is able to post messages to the local Artemis instance in the WildFly.

@ApplicationScoped
public class QueueClient {

  private static final Gson GSON = new Gson();

  @Resource(mappedName = "java:jboss/DefaultJMSConnectionFactory")
  private ConnectionFactory connectionFactory;

  public void sendMessage(String destinationName, Object message) throws JMSException {
    try (Connection conn = connectionFactory.createConnection();
        Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE)) {

      Queue queue = session.createQueue(destinationName);
      final Message consignment = session.createMessage();
      consignment.setStringProperty("MEDIA_TYPE", "application/json");
      consignment.setStringProperty("BODY", GSON.toJson(message));
      session.createProducer(queue).send(consignment);
    }
  }
}

My goal: to post messages to a remote ActiveMQ instance.

What I have: server url, topic name, username and password.

My question: how do I modify the configuration to achieve this goal?

Alternative question: if the above can't be answered, how else do I achieve this goal?

Thanks!

Answer

99Sono picture 99Sono · Nov 22, 2016

Have you checked the following documentation.

https://docs.jboss.org/author/display/WFLY10/Connect+a+pooled-connection-factory+to+a+Remote+Artemis+Server

It probably addresses your current concern.

Thanks.

EDIT: Upon negative flagging of this post.

As the documentation that was listed above clearly describes. You need to create a pooled connection factory, that uses a remote connector and the remote connector references the IP x Socket of the remote Active MQ server. Please see the documentation on how to this, these the first three steps.

The configuration of such a pooled-connection-factory is done in 3 steps, and I quote:

  1. create an outbound-socket-binding pointing to the remote messaging server: /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=remote-artemis:add(host=, port=61616)

  2. create a remote-connector referencing the outbound-socket-binding created at step (1). /subsystem=messaging-activemq/server=default/remote-connector=remote-artemis:add(socket-binding=remote-artemis)

  3. create a pooled-connection-factory referencing the remote-connector created at step (2). /subsystem=messaging-activemq/server=default/pooled-connection-factory=remote-artemis:add(connectors=[remote-artemis], entries=[java:/jms/remoteCF])

Finally, once you have your pooled connection factory properly configured, you can use it as just any JNDI resource available to a JEE container. Simply inject the pooled connction factory into your bean and use it to create a session.

Here, you have an example on how to send a JMS message to a server. In your case, it is only a matter of fixing the connection factory JNDI reference to your pooled connection factory associated to remote active MQ.

http://www.mastertheboss.com/jboss-server/jboss-jms/sending-jms-messages-over-xa-with-wildfly-jboss-as

And I quote:

@Stateless
public class JMSService {


    @Resource(mappedName = "java:jboss/jms/queue/exampleQueue")
    private Queue queueExample;

    @Resource(mappedName = "java:/JmsXA")
    private ConnectionFactory cf;

    private Connection connection;
    private MessageProducer publisher;
    private Session session;

    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void sendMessage(String txt) {

        try {         

            connection = cf.createConnection();
            session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            publisher = session.createProducer(queueExample);

            connection.start();

            TextMessage message = session.createTextMessage(txt);
            publisher.send(message);


        }
        catch (Exception exc) {
            exc.printStackTrace();
        }
        finally {         

          if (publisher != null) try { publisher.close(); } catch (Exception ignore) { }
          if (session != null) try { session.close(); } catch (Exception ignore) { }
          if (connection != null) try { connection.close(); } catch (Exception ignore) { }


         }
    } 
}

In this case you connection factory should:

@Inject

    @JMSConnectionFactory("java:/jms/remoteCF")

    private JMSContext context;

One more thing you need to be aware is that the queue you would like to inject into your bean is a remote queue. The way you get access to a remote queue is by doing a JNDI lookup on the the remote active MQ server. This is explained here. http://activemq.apache.org/artemis/docs/1.1.0/using-jms.html#jndi-configuration

And I quote the interesting part of the documentation starts. If you follow through, you can see how the JNDI initial context can be used to lookup the artemis queues.

And if the client wanted to bind this queue to "queues/OrderQueue" then the JNDI properties would be configured like so:

java.naming.factory.initial=org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory java.naming.provider.url=tcp://myhost:5445 queue.queues/OrderQueue=OrderQueue

It is also possible to look-up JMS destinations which haven't been configured explicitly in the JNDI context environment. This is possible using dynamicQueues/ or dynamicTopics/ in the look-up string. For example, if the client wanted to look-up the aforementioned "OrderQueue" it could do so simply by using the string "dynamicQueues/OrderQueue". Note, the text that follows dynamicQueues/ or dynamicTopics/ must correspond exactly to the name of the destination on the server.

Please read through the artemis configuration on this.

Finally, there is another approach that you can try to explore. And these are JMS Queue Bridges. You have too google this up. But essentially, a JMS bridge can allow the contents of a queue to tunneled to a remote queue. So this mechanism of bridging should give you the possibility of programming your code as if against a local artemis JMS server, but with all the queues being bridged the data Being READ or being published is actually coming from or going to a remote server.

But you have to inform yourself to see what works best for you.

I sincerly hope this helps.