Search code examples
javaspring-jmsactivemq-artemisjmstemplate

JmsTemplate fails to send message to ActiveMQ Artemis with "AMQ219007: Cannot connect to server" but can send message with JMS classes


I am trying to send a JMS messages with Spring's JmsTemplate but it fails with this as the root cause shown in the stack trace:

AMQ219007: Cannot connect to server

I can, however, send a message programming directly against the JMS classes using the same connection factory which is passed to JmsTemplate. I'm sure I'm missing something obvious, but I just can't figure out what configuration is missing to make JmsTemplate work. I think the configuration of the ActiveMQInitialContextFactory is correct since I can send messages using the javax.jms.* classes. FWIW, I've tried configuring the JmsTemplate with a destination resolver but that didn't help.

Here is the configuration:

<bean id="artemisJndiTemplate"
        class="org.springframework.jndi.JndiTemplate">
    <property name="environment">
        <props>
            <prop key="java.naming.factory.initial">org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory</prop>
            <prop key="java.naming.provider.url">(tcp://artemisServer1:61616,tcp://artemisServer2:61616)?user=$jmsArtemisSetup{user}&amp;password=$jmsArtemisSetup{password}&amp;jms.prefetchPolicy.all=1&amp;consumerWindowSize=0&amp;clientID=${NODENBRTAG}</prop>
        </props>
    </property>
</bean>

<!-- look up the JMS ConnectionFactory in JNDI -->
<bean id="artemisConnectionFactory"
        class="org.springframework.jndi.JndiObjectFactoryBean">
    <property name="jndiTemplate" ref="artemisJndiTemplate" />
    <property name="jndiName" value="ConnectionFactory" />
</bean>

<bean id="qFromSvcToESB"
        class="org.springframework.jms.core.JmsTemplate">
    <property name="connectionFactory" ref="artemisConnectionFactory" />
    <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
    <property name="sessionTransacted" value="true" />
    <property name="defaultDestination">
        <bean class="org.springframework.jndi.JndiObjectFactoryBean">
            <property name="jndiTemplate" ref="artemisJndiTemplate" />
            <property name="jndiName" value="dynamicQueues/qFromSvcToESB" />
        </bean>
    </property>
</bean>

When I send a message using the JmsTemplate, I get the following stack trace:

2021-08-18 10:06:31,796 [ERROR] [com.example.emailassistant.msghdlr.EmailAssistantMsgHdlr]:171  - 1629306356191 - jmsTemplate.send(...) failed.
org.springframework.jms.UncategorizedJmsException: Uncategorized exception occurred during JMS processing; nested exception is javax.jms.JMSException: Failed to create session factory; nested exception is ActiveMQNotConnectedException[errorType=NOT_CONNECTED message=AMQ219007: Cannot connect to server(s). Tried with all available servers.]
        at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:311) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:185) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:507) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:576) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:567) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at com.example.emailassistant.msghdlr.EmailAssistantMsgHdlr.execute(EmailAssistantMsgHdlr.java:151) ~[com.example.emailassistant.msghdlr-0.0.1-SNAPSHOT.jar:?]
        at com.example.framework2.activemq.artemis.EpmServiceHandlerAdapter.dispatchToListener(EpmServiceHandlerAdapter.java:71) ~[com.example.framework2.activemq.artemis-5.1-SNAPSHOT.jar:?]
        at com.example.framework2.AbstractEpmMsgHdlrAdapter$EpmMessageListener.onMessage(AbstractEpmMsgHdlrAdapter.java:615) ~[com.example.framework2-5.1-SNAPSHOT.jar:?]
        at org.apache.activemq.artemis.jms.client.JMSMessageListenerWrapper.onMessage(JMSMessageListenerWrapper.java:110) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.callOnMessage(ClientConsumerImpl.java:1031) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl.access$400(ClientConsumerImpl.java:50) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.core.client.impl.ClientConsumerImpl$Runner.run(ClientConsumerImpl.java:1154) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.utils.actors.OrderedExecutor.doTask(OrderedExecutor.java:42) ~[artemis-commons-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.utils.actors.OrderedExecutor.doTask(OrderedExecutor.java:31) ~[artemis-commons-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.utils.actors.ProcessorBase.executePendingTasks(ProcessorBase.java:65) ~[artemis-commons-2.12.0.jar:2.12.0]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128) [?:?]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628) [?:?]
        at org.apache.activemq.artemis.utils.ActiveMQThreadFactory$1.run(ActiveMQThreadFactory.java:118) [artemis-commons-2.12.0.jar:2.12.0]
Caused by: javax.jms.JMSException: Failed to create session factory
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnectionInternal(ActiveMQConnectionFactory.java:886) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:299) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:294) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:196) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:494) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        ... 15 more
Caused by: org.apache.activemq.artemis.api.core.ActiveMQNotConnectedException: AMQ219007: Cannot connect to server(s). Tried with all available servers.
        at org.apache.activemq.artemis.core.client.impl.ServerLocatorImpl.createSessionFactory(ServerLocatorImpl.java:690) ~[artemis-core-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnectionInternal(ActiveMQConnectionFactory.java:884) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:299) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory.createConnection(ActiveMQConnectionFactory.java:294) ~[artemis-jms-client-2.12.0.jar:2.12.0]
        at org.springframework.jms.support.JmsAccessor.createConnection(JmsAccessor.java:196) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:494) ~[spring-jms-5.2.12.RELEASE.jar:5.2.12.RELEASE]
        ... 15 more

This code and configuration was previously working with the ActiveMQ "classic" connection factory (i.e. org.apache.activemq.jndi.ActiveMQInitialContextFactory).

Here is the destination resolver configuration I tried. I added it to the JmsTemplate config using <property name="destinationResolver" ref="artemisDestinationResolver" />:

<bean id="artemisDestinationResolver"
        class="org.springframework.jms.support.destination.JndiDestinationResolver">
    <property name="jndiTemplate" ref="amqJndiTemplate" />
    <property name="cache" value="true" />
</bean>

Solution

  • The root cause of the error is that the application is trying to make two JMS connections to the JMS server with the same client ID. This is not allowed by the JMS specification.

    The solution is to use connection pooling. In general, connection pooling should be included for performance reasons. In this particular situation, the application starts up, listens for one message, sends one reply message, and shuts down so pooling was not enabled. However, when using the artemis-jms-client library this meant two different JMS connections were being created with the same client ID leading to a failure.

    The following XML configuration using pooling allowing the service to work without error:

    <bean id="artemisJndiTemplate"
            class="org.springframework.jndi.JndiTemplate">
        <property name="environment">
            <props>
                <prop key="java.naming.factory.initial">org.apache.activemq.artemis.jndi.ActiveMQInitialContextFactory</prop>
                <prop key="java.naming.provider.url">(tcp://artemisServer1:61616,tcp://artemisServer1:61616)?user=$jmsArtemisSetup{user}&amp;password=$jmsArtemisSetup{password}&amp;consumerWindowSize=0&amp;clientID=${NODENBRTAG}</prop>
            </props>
        </property>
    </bean>
    
    <!-- look up the JMS ConnectionFactory in JNDI -->
    <bean id="artemisConnectionFactoryBase"
            class="org.springframework.jndi.JndiObjectFactoryBean">
        <property name="jndiTemplate" ref="artemisJndiTemplate" />
        <property name="jndiName" value="ConnectionFactory" />
    </bean>
    
    <!-- A cached connection to wrap the ActiveMQ connection -->
    <bean id="artemisConnectionFactory"
        class="org.springframework.jms.connection.CachingConnectionFactory">
        <property name="targetConnectionFactory">
            <ref bean="artemisConnectionFactoryBase" />
        </property>
        <property name="sessionCacheSize">
            <value>100</value>
        </property>
    </bean>
    
    <bean id="replyJmsQueue"
            class="org.springframework.jms.core.JmsTemplate">
        <property name="connectionFactory" ref="artemisConnectionFactory" />
        <property name="sessionAcknowledgeModeName" value="CLIENT_ACKNOWLEDGE" />
        <property name="sessionTransacted" value="true" />
        <property name="defaultDestination">
            <bean class="org.springframework.jndi.JndiObjectFactoryBean">
                <property name="jndiTemplate" ref="artemisJndiTemplate" />
                <property name="jndiName" value="dynamicQueues/replyJmsQueue" />
            </bean>
        </property>
    </bean>
    

    The raw JMS code and the JmsTemplate both use the connection pool and therefore end up using the same connection.

    As an aside, upgrading the artemis-jms-client library from 2.10.1 to 2.18.0 provided a much more useful message in the stack trace than the original.

    javax.jms.InvalidClientIDException: clientID=Node1 was already set into another connection
    

    With connection pooling, the JMS code works with either artemis-jms-client 2.10.1 or 2.18.0.