Search code examples
spring-bootactivemq-artemis

What is missing in my configuration ActiveMQSslConnectionFactory


I need to connect to Artemis node with ssl. If I use org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory and put into brokerURL all parameters:

String brokerURL = "tcp://*:61617?sslEnabled=true&keyStorePath=" + keyStorePath + "&keyStorePassword=" + keyStorePassword + "&trustStorePath=" + pathToTrustStore + "&trustStorePassword=" + trustStorePassword;

It works fine, I can send messages. This factory creates javax.jms.Connection.

I want to work with jakarta.org.apache.activemq.ActiveMQSslConnectionFactory or another ActiveMQSslConnectionFactory.
Application started, but doesn't send/receive messages.

In log file all errors are like:

ERROR \[org.springframework.jms.JmsListenerEndpointContainer#0-1\] Could not refresh JMS Connection for destination 'queue.payments.v3.batch.response' - retrying using FixedBackOff{interval=5000, currentAttempts=9, maxAttempts=unlimited}. Cause: Cannot send, channel has already failed: tcp://*\*.\**:61617

and debug messages like:

DEBUG [ActiveMQ Transport: tcp://*/:61617@50959] Async exception with no exception listener: {}
java.io.EOFException: null
    at java.base/java.io.DataInputStream.readInt(DataInputStream.java:398)
    at org.apache.activemq.openwire.OpenWireFormat.unmarshal(OpenWireFormat.java:280)
    at org.apache.activemq.transport.tcp.TcpTransport.readCommand(TcpTransport.java:240)
    at org.apache.activemq.transport.tcp.TcpTransport.doRun(TcpTransport.java:232)
    at org.apache.activemq.transport.tcp.TcpTransport.run(TcpTransport.java:215)
    at java.base/java.lang.Thread.run(Thread.java:833)

Here is my configuration class:

package com.bottomline.cfrm.ps.notification;

import javax.validation.constraints.NotEmpty;

import org.apache.activemq.ActiveMQSslConnectionFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;

import jakarta.jms.Connection;
import jakarta.jms.ConnectionFactory;
import jakarta.jms.JMSException;

@Configuration
@EnableJms
public class ActiveMQConfiguration {
    
    @NotEmpty(message = "Name may not be empty")
    @Value("${spring.activemq.user}")
    private String username;

    @NotEmpty(message = "Password may not be empty")
    @Value("${spring.activemq.password}")
    private String password;

    @NotEmpty(message = "Broker URL cannot be empty")
    @Value("${spring.activemq.broker-url}")
    private String brokerURL;
    
    @NotEmpty(message = "Password cannot be empty")
    @Value("${spring.activemq.ssl.keyStorePass}")
    private String keyStorePass;
    
    @Value("${spring.activemq.ssl.keyStorePath}")
    private String keyStorePath;
    
    @Value("${spring.activemq.ssl.trustStorePath}")
    private String trustStorePath;
    
    @Bean
    public ActiveMQSslConnectionFactory activeMQSslConnectionFactory()  {
    
        ActiveMQSslConnectionFactory factory = new ActiveMQSslConnectionFactory(brokerURL);
        try {
            factory.setTrustStore(trustStorePath);
        } catch (Exception e) {         
            e.printStackTrace();
        }
        factory.setTrustStorePassword(keyStorePass);
        try {
            factory.setKeyStore(keyStorePath);
        } catch (Exception e) {         
            e.printStackTrace();
        }
        factory.setKeyStorePassword(keyStorePass);         
        factory.setUserName(username);
        factory.setPassword(password);
       
        Connection connection;
        try {
            connection = factory.createConnection();
             connection.start();
        } catch (JMSException e) {
            e.printStackTrace();
        }              
        return factory;
    }
    
    @Bean
      public JmsListenerContainerFactory<?> jmsListenerContainerFactory(
              @Qualifier("activeMQSslConnectionFactory") ConnectionFactory connectionFactory, 
              DefaultJmsListenerContainerFactoryConfigurer configurer) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();     
        configurer.configure(factory, connectionFactory);      
        return factory;
      }
}

And pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.2.1</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    
    <groupId>com.intellinx.solutions</groupId>
    <artifactId>TestArtemisMQApplication</artifactId>
    <version>1.0.3</version>
    <name>TestArtemisMQApplication</name>
    <description>Send and Read from Artemis queue</description>

    <properties>
        <maven.compiler.target>17</maven.compiler.target>
        <maven.compiler.source>17</maven.compiler.source>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
    </properties>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>                                       
            </plugin>                   
        </plugins>
    </build>
    
    <dependencies>
    
        <!-- For Spring RestClient -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>    
        </dependency>
  <!--      
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-artemis</artifactId>
        </dependency> -->
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>           
  <!--      
        <dependency>
            <groupId>org.apache.activemq</groupId>
            <artifactId>activemq-artemis-native</artifactId>
            <version>2.0.0</version>
        </dependency>-->
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.integration</groupId>
            <artifactId>spring-integration-core</artifactId>
        </dependency>
          
      <dependency>
        <groupId>org.apache.activemq</groupId>
         <artifactId>artemis-jms-client-all</artifactId>
         <version>2.6.3</version>
      </dependency>
         
      <dependency>                
         <groupId>javax.json</groupId>                
         <artifactId>javax.json-api</artifactId>                
         <version>1.0</version>
      </dependency>
      
      <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
      </dependency>
          
      <dependency>                
         <groupId>org.glassfish</groupId>                
         <artifactId>javax.json</artifactId>                
         <version>1.0.4</version>             
      </dependency>
    </dependencies>
</project>

I removed jmsListenerContainerFactory, just to test ability to connect and send. Tried to use javax instead of jakarta, but I need all Spring Boot features for other parts of application.


Solution

  • The EOFException you're getting and its associated stack-trace indicate you're attempting to use the OpenWire JMS client (i.e. org.apache.activemq.ActiveMQSslConnectionFactory) to connect to a port on ActiveMQ Artemis that only supports the Core protocol (i.e. not OpenWire).

    I recommend that you switch back to using org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory and if you want to get a jakarta.jms.Connection instead of a javax.jms.Connection then you just need to use the proper dependencies. Right now you're using this:

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-activemq</artifactId>
            </dependency> 
    

    You should remove this and use this one (which has been commented-out):

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-artemis</artifactId>
            </dependency>
    

    This dependency has a dependency on org.apache.activemq:artemis-jakarta-client which will give you a Jakarta implementation of Connection.

    Also, you need to remove this as it will interfere with the Artemis Jakarta client dependency:

          <dependency>
            <groupId>org.apache.activemq</groupId>
             <artifactId>artemis-jms-client-all</artifactId>
             <version>2.6.3</version>
          </dependency>