Search code examples
activemq-artemis

ActiveMQ Artemis broker connection


I have a very simple Spring Boot 2.7.6 ActiveMQ Artemis app which listens for messages.

package broker.consumer;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.apache.activemq.broker.BrokerService;
import org.springframework.jms.annotation.JmsListener;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public BrokerService broker() throws Exception {
        BrokerService broker = new BrokerService();
        broker.addConnector("tcp://localhost:61616");
        broker.setPersistent(false);
        broker.start();
        return broker;
    }

    @JmsListener(destination = "foo")
    public void listen(String in) {
        System.out.println(in);
    }
}

Then I have another Spring Boot app which produces messages and sends them to the broker address.

package broker.producer;

import org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.destination.JndiDestinationResolver;
import org.springframework.stereotype.Service;

@Service
public class JmsProducer {

  @Value("${spring.artemis.broker-url}")
  private String brokerUrl;

  @Value("${spring.jms.template.default-destination}")
  private String defaultDestination;

  Logger log = LoggerFactory.getLogger(JmsProducer.class);

  @Bean
  public ActiveMQConnectionFactory activeMQConnectionFactory() {
      log.info("BrokerUrl: {}", brokerUrl);
      ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory(brokerUrl);

      return activeMQConnectionFactory;
  }

  @Bean
  public JndiDestinationResolver jndiDestinationResolver() {
    return new JndiDestinationResolver();
  }

  @Bean
  public JmsTemplate jmsTemplate() {
    JmsTemplate template = new JmsTemplate();
    template.setConnectionFactory(activeMQConnectionFactory());
    template.setPubSubDomain(false); // false for a Queue, true for a Topic
    template.setDefaultDestinationName(defaultDestination);

    return template;
  }


  public void send(String message) {
    JmsTemplate jmsTemplate = jmsTemplate();

    log.info("Sending message='{}'", message);
    jmsTemplate.convertAndSend(message);
    log.info("Sent message='{}'", message);
  }
}

With this simple application.properties for both apps.

spring.artemis.mode=EMBEDDED
spring.artemis.broker-url=tcp://localhost:61616
spring.artemis.user=admin
spring.artemis.password=secret
spring.artemis.embedded.enabled=true

spring.jms.template.default-destination=my-queue-1

Then I start each of the apps and try calling send method and I keep getting this error from the producer app.

2024-01-15 15:50:28.462 ERROR 1012 --- [-netty-threads)] org.apache.activemq.artemis.core.client : AMQ214013: Failed to decode packet
java.lang.IllegalArgumentException: AMQ219032: Invalid type: 1
        at org.apache.activemq.artemis.core.protocol.core.impl.PacketDecoder.decode(PacketDecoder.java:499) ~[artemis-core-client-2.19.1.jar:2.19.1]

Consumer app shows this error message:

2024-01-15 15:50:28.464  WARN 986 --- [0.1:57714@61616] o.a.a.b.TransportConnection.Transport : Transport Connection to: tcp://127.0.0.1:57714 failed: Unknown data type: 77

Solution

  • The problem is that your consumer app is starting an embedded instance of ActiveMQ Classic. See this method:

        @Bean
        public BrokerService broker() throws Exception {
            BrokerService broker = new BrokerService();
            broker.addConnector("tcp://localhost:61616");
            broker.setPersistent(false);
            broker.start();
            return broker;
        }
    

    Then your producer app is using the ActiveMQ Artemis JMS client implementation (i.e. org.apache.activemq.artemis.jms.client.ActiveMQConnectionFactory) to connect and send a message. This client uses the "core" protocol which ActiveMQ Classic doesn't understand.

    Your consumer app needs to start an embedded instance of ActiveMQ Artemis instead of ActiveMQ Classic. Spring itself should be doing this for you, but it may not be since you're starting your own already.