Search code examples
jbossactivemq-artemis

JBoss EAP 7.4 and ActiveMQ Artemis 2.18.0 JMS bridge


I've been working with OpenMQ to ActiveMQ Artemis migration, and I'm having a bit of a problem getting my head around the JMS bridge concept. In OpenMQ JBoss's configuration we had JMS bridges but while migrating to ActiveMQ Artemis and re-making the standalone configuration I came to a problem where I can't get the JMS bridges working. Maybe there is a chance that someone could give me an example of how they are written properly or just do it for my configuration so I have a good example? (Don't mind only 1 ip, I've changed everything since I can't share ip's etc.)

Broker is a remote Artemis cluster which has 2 queues - sync.Trigger.gg and dlq.sync.Trigger.gg. I want a JMS bridge on JBoss to consume the message from the remote ActiveMQ Artemis (which in configuration is 1.0.0.1:61616) and send that message in the local queue named SyncTriggerQueue, and if the consumption fails by my deployed App then another bridge should send it back to dlq.sync.Trigger.gg. I know that I need 2 bridges for this (i.e. 1 in bridge for reading and writing to inbuilt queue and 1 out bridge for sending it to the DLQ).

I have 2 bridges in the configuration, and I've put "??" in the values where I don't know what to put inside. Could someone maybe fill it with the values it has to have from the story I've told? I would really appreciate help from someone who works with/knows these configurations.

P.S. I've achieved a working configuration without the JMS bridges, but our Architect wants to keep it the way it was - with the bridges, so that's why I'm fighting with this.

P.S.S. I have no idea if the in-built ActiveMQ queues are written properly, so feel free to re-make them.

Since I can't find how to add files here (probably not possible) then I'll just add my whole configuration as a code block, with some deleted parts that seem unnecessary.

<subsystem xmlns="urn:jboss:domain:messaging-activemq:13.0">
    <server name="default">
        <cluster password="${jboss.messaging.cluster.password:CHANGE ME!!}"/>
        <statistics enabled="${wildfly.messaging-activemq.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
        <security-setting name="#">
            <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-durable-queue="true"/>
        </security-setting>
        <address-setting name="#" dead-letter-address="jms.queue.DLQ" expiry-address="jms.queue.ExpiryQueue" max-size-bytes="10485760" page-size-bytes="2097152" message-counter-history-day-limit="10" redistribution-delay="1000"/>
        
        <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor"/>
        <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput">
            <param name="batch-delay" value="50"/>
        </http-connector>
        <in-vm-connector name="in-vm" server-id="0">
            <param name="buffer-pooling" value="false"/>
        </in-vm-connector>
        <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">
            <param name="buffer-pooling" value="false"/>
        </in-vm-acceptor>
        <jgroups-broadcast-group name="bg-group1" jgroups-cluster="activemq-cluster" connectors="http-connector"/>
        <jgroups-discovery-group name="dg-group1" jgroups-cluster="activemq-cluster"/>
        <cluster-connection name="my-cluster" address="jms" connector-name="http-connector" discovery-group="dg-group1"/>
        <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
        <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
        <jms-queue name="SyncTriggerQueue" entries="java:jboss/exported/SyncTriggerQueue" durable="false"/>
        <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
        <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector" ha="true" block-on-acknowledge="true" reconnect-attempts="-1"/>
        <pooled-connection-factory name="activemq-ra" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm" transaction="xa"/>
        <connection-factory name="SyncTriggerConnectionFactory" entries="java:jboss/exported/jms/TriggerRes" connectors="http-connector" client-id="TriggerMDB1" scheduled-thread-pool-max-size="8" thread-pool-max-size="32" factory-type="XA_GENERIC"/>
    </server>
            
    <jms-bridge name="SyncTriggerInBridge"
                module="org.apache.activemq.artemis"
                add-messageID-in-header="true"
                max-batch-time="500"
                max-batch-size="10"
                max-retries="-1"
                failure-retry-interval="30000"
                quality-of-service="ONCE_AND_ONLY_ONCE">
        <source destination="??"
                connection-factory="??"
                user="appusertest"
                password="appusertest">
            <source-context>
                <property name="java.naming.factory.initial" value="org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"/>
                <property name="java.naming.provider.url" value="??"/>
            </source-context>
        </source>        
      
        <target destination="??"
                connection-factory="??">
        </target>
    </jms-bridge>
    
    <jms-bridge name="SyncTriggerOutBridge"
                module="org.apache.activemq.artemis"
                add-messageID-in-header="true"
                max-batch-time="500"
                max-batch-size="10"
                max-retries="-1"
                failure-retry-interval="30000"
                quality-of-service="ONCE_AND_ONLY_ONCE">
        <source destination="??"
                connection-factory="??">
        </source>        
      
        <target destination="??"
                connection-factory="??"
                user="appusertest"
                password="appusertest">
            <target-context>
                <property name="java.naming.factory.initial" value="org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"/>
                <property name="java.naming.provider.url" value="??"/>
            </target-context>
        </target>
    </jms-bridge>
</subsystem>

<subsystem xmlns="urn:jboss:domain:resource-adapters:6.0">
    <resource-adapters>
        <resource-adapter id="artemis-ra.rar">
            <module slot="main" id="gg.go.artemis.rar" />
            <transaction-support>XATransaction</transaction-support>
    
            <config-property name="CallFailoverTimeout">30000</config-property>
            <config-property name="CallTimeout">30000</config-property>
            <config-property name="ClientFailureCheckPeriod">30000</config-property>
            <config-property name="ConnectionLoadBalancingPolicyClassName">gg.go.artemis.loadbalancing.OrderedConnectionLoadBalancingPolicyLoggingImpl</config-property>
            <config-property name="ConnectionParameters">host=1.0.0.1;port=61616</config-property>
            <config-property name="ConnectorClassName">org.apache.activemq.artemis.core.remoting.impl.netty.NettyConnectorFactory</config-property>
            <config-property name="ConsumerMaxRate">1</config-property>
            <config-property name="HA">true</config-property>
            <config-property name="InitialConnectAttempts">9</config-property>
            <config-property name="MinLargeMessageSize">5120000</config-property>
            <config-property name="Password">admin</config-property>
            <config-property name="ReconnectAttempts">-1</config-property>
            <config-property name="RetryInterval">-1</config-property>
            <config-property name="ThreadPoolMaxSize">10</config-property>
            <config-property name="UserName">admin</config-property>
      
            <connection-definitions>
                <connection-definition class-name="org.apache.activemq.artemis.ra.ActiveMQRAManagedConnectionFactory" jndi-name="java:jboss/DefaultArtemisConnectionFactory" enabled="true" connectable="true" pool-name="DefaultArtemisConnectionFactory" use-java-context="true" use-ccm="true" />
            </connection-definitions>
        </resource-adapter>
    </resource-adapters>
</subsystem>

jms-bridge TRACE logs

TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$SourceReceiver@6c989ca received message ActiveMQMessage[null]:PERSISTENT/ClientMessageImpl[messageID=2145, durable=true, address=rekku.SyncTrigger.virre,userID=null,properties=TypedProperties[_AMQ_ROUTING_TYPE=1]]
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$SourceReceiver@6c989ca rescheduled batchExpiryTime to 1660722704869
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@62859582 woke up
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@62859582 waited enough
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@62859582 waiting for 500
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@62859582 woke up
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@62859582 waited enough
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@62859582 waiting for 500
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@57e98840 woke up
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@57e98840 waited enough
TRACE ... org.apache.activemq.artemis.jms.bridge.impl.JMSBridgeImpl$BatchTimeChecker@57e98840 got some messages so sending batch
TRACE ... Sending batch of 1 messages
TRACE ... Adding old message id in Message header
TRACE ... Sending message ActiveMQMessage[null]:PERSISTENT/ClientMessageImpl[messageID=2145, durable=true, address=sync.Trigger.gg,userID=null,properties=TypedProperties[_AMQ_ROUTING_TYPE=1,AMQ_BRIDGE_MSG_ID_LIST=NULL-value,JMSXDeliveryCount=1]]
TRACE ... Sent message ActiveMQMessage[ID:70bf4ffb-1e01-11ed-a09e-00059a3c7a00]:PERSISTENT/ClientMessageImpl[messageID=2145, durable=true, address=jms.queue.SyncTriggerQueue,userID=70bf4ffb-1e01-11ed-a09e-00059a3c7a00,properties=TypedProperties[__AMQ_CID=54a01028-1e01-11ed-a09e-00059a3c7a00,_AMQ_ROUTING_TYPE=1,AMQ_BRIDGE_MSG_ID_LIST=NULL-value,JMSXDeliveryCount=1]]
TRACE ... Delisting resources from tx
TRACE ... Delisted resources from tx
TRACE ... Committing JTA transaction
TRACE ... Committed JTA transaction
TRACE ... Starting JTA transaction
TRACE ... Enlisted resources in tx
TRACE ... Enlisting resources in tx
TRACE ... Started JTA transaction

Solution

  • Couple of things:

    • The bridge shouldn't use the ActiveMQ Artemis JCA RA so all the <resource-adapter> configuration is irrelevant. Therefore, I will not include it in my example.
    • There must be another local queue which the bridge can use to send the message back to the remote dlq.sync.Trigger.gg. This is the queue your application should use as a DLQ. I'll call this queue DlqSyncTriggerQueue. If you want the broker to handle sending failed messages to DlqSyncTriggerQueue then you should create a new address setting, e.g.:
    <address-setting name="jms.queue.SyncTriggerQueue" dead-letter-address="jms.queue.DlqSyncTriggerQueue"/>
    

    This assumes your application will be consuming messages from SyncTriggerQueue.

    Here's the rest of the relevant configuration:

    <subsystem xmlns="urn:jboss:domain:messaging-activemq:13.0">
        <server name="default">
            <cluster password="${jboss.messaging.cluster.password:CHANGE ME!!}"/>
            <statistics enabled="${wildfly.messaging-activemq.statistics-enabled:${wildfly.statistics-enabled:false}}"/>
            <security-setting name="#">
                <role name="guest" send="true" consume="true" create-non-durable-queue="true" delete-non-durable-queue="true"/>
            </security-setting>
            <address-setting name="#" dead-letter-address="jms.queue.DLQ" expiry-address="jms.queue.ExpiryQueue" max-size-bytes="10485760" page-size-bytes="2097152" message-counter-history-day-limit="10" redistribution-delay="1000"/>
            
            <http-connector name="http-connector" socket-binding="http" endpoint="http-acceptor"/>
            <http-connector name="http-connector-throughput" socket-binding="http" endpoint="http-acceptor-throughput">
                <param name="batch-delay" value="50"/>
            </http-connector>
            <in-vm-connector name="in-vm" server-id="0">
                <param name="buffer-pooling" value="false"/>
            </in-vm-connector>
            <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">
                <param name="buffer-pooling" value="false"/>
            </in-vm-acceptor>
            <jgroups-broadcast-group name="bg-group1" jgroups-cluster="activemq-cluster" connectors="http-connector"/>
            <jgroups-discovery-group name="dg-group1" jgroups-cluster="activemq-cluster"/>
            <cluster-connection name="my-cluster" address="jms" connector-name="http-connector" discovery-group="dg-group1"/>
            <jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
            <jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
            <jms-queue name="SyncTriggerQueue" entries="java:/SyncTriggerQueue" durable="false"/>
            <jms-queue name="DlqSyncTriggerQueue" entries="java:/DlqSyncTriggerQueue" durable="false"/>
            <connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory" connectors="in-vm"/>
            <connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/jms/RemoteConnectionFactory" connectors="http-connector" ha="true" block-on-acknowledge="true" reconnect-attempts="-1"/>
            <pooled-connection-factory name="activemq-ra" entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm" transaction="xa"/>
        </server>
                
        <jms-bridge name="SyncTriggerInBridge"
                    module="org.apache.activemq.artemis"
                    add-messageID-in-header="true"
                    max-batch-time="500"
                    max-batch-size="10"
                    max-retries="-1"
                    failure-retry-interval="30000"
                    quality-of-service="ONCE_AND_ONLY_ONCE">
            <source destination="sync.Trigger.gg"
                    connection-factory="ConnectionFactory"
                    user="appusertest"
                    password="appusertest">
                <source-context>
                    <property name="java.naming.factory.initial" value="org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"/>
                    <property name="java.naming.provider.url" value="tcp://1.0.0.1:61616"/>
                    <property name="queue.sync.Trigger.gg" value="sync.Trigger.gg"/>
                </source-context>
            </source>        
          
            <target destination="SyncTriggerQueue"
                    connection-factory="java:/ConnectionFactory">
            </target>
        </jms-bridge>
        
        <jms-bridge name="SyncTriggerOutBridge"
                    module="org.apache.activemq.artemis"
                    add-messageID-in-header="true"
                    max-batch-time="500"
                    max-batch-size="10"
                    max-retries="-1"
                    failure-retry-interval="30000"
                    quality-of-service="ONCE_AND_ONLY_ONCE">
            <source destination="DlqSyncTriggerQueue"
                    connection-factory="java:/ConnectionFactory">
            </source>        
          
            <target destination="dlq.sync.Trigger.gg"
                    connection-factory="ConnectionFactory"
                    user="appusertest"
                    password="appusertest">
                <target-context>
                    <property name="java.naming.factory.initial" value="org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory"/>
                    <property name="java.naming.provider.url" value="tcp://1.0.0.1:61616"/>
                    <property name="queue.dlq.sync.Trigger.gg" value="dlq.sync.Trigger.gg"/>
                </target-context>
            </target>
        </jms-bridge>
    </subsystem>
    

    On the remote ActiveMQ Artemis cluster you may need to add anycastPrefix=jms.queue.;multicastPrefix=jms.topic. to the configuration of your acceptor which is listening on 61616.