Discussed in https://github.com/spring-projects/spring-amqp/discussions/2918

Originally posted by **bilak** December 4, 2024 Hey, is there a plan to support [rabbitmq amqp 1.0](https://github.com/rabbitmq/rabbitmq-amqp-java-client) library?

Comment From: csp1992

Hello, I have the same question. There is a official support of amqp 1.0 in spring boot 3?. Any plans of support in this library?

Comment From: artembilan

According to some resources in the Internet we can use JMS auto-configuration in Spring Boot to talk to AMQP 1.0 protocol: https://github.com/amqphub/amqp-10-jms-spring-boot/tree/main/amqp-10-jms-spring-boot-examples.

We don't deny this issue since it would bring a Spring-friendly API over the mentioned rabbitmq-amqp-java-client library.

Comment From: JimmyWang6

Hi @artembilan, as mention above, I am willing to handle this issue. Do you know if this issue already started?

Comment From: artembilan

Yes, my plan is to start looking into this from next Monday. However you are free to share your vision for this new module:

  1. What its name?
  2. How do we manage connection(s)?
  3. Do you foresee Amqp10Template (similar to RabbitTemplate) implementation?
  4. What is your thought about @RabbitListener support? Or we might have an @Amqp10Listener instead?

I'm opened for any suggestions

Thank you!

Comment From: artembilan

OK. I have started working on this. So far I have: 1. Module name is spring-rabbitmq-client - we kinda anticipate that this one would be a default in the future with slow 0.9.1 support sunset. 2. I wrote AmqpConnectionFactoryBean to manage AMQP connection object in Spring manner. This is the most important and shared component between the rest of the API. 3. The RabbitAmqpAdmin - similar to RabbitAdmin, but with the logic based on the AMQP 1.0 client. We cannot live without topology, so it is better to have it automatically initialized from respective Declarable beans like we do for 0.9.1 support. 4. Have started RabbitAmqpTemplate for the Spring-friendly Publisher API.

Wanted to push and share already, but stuck with so much code to do. Therefore expect something from me in the nearest future. Maybe tomorrow/after tomorrow.

Comment From: making

I'm keeping an eye on this effort.

It looks like Quarkus already supports AMQP 1.0. https://quarkus.io/guides/amqp-reference

I was hoping this client could be used to access other AMQP 1.0-enabled products, such as Azure Service Bus. Is that out of scope? RabbitAmqpTemplate sounds like it's for RabbitMQ.

Comment From: artembilan

That’s correct, @making . This module is based on the RabbitMQ Java client for only RabbitMQ AMQP 1.0 support. There is no guarantee that it may work with other AMQP 1.0 providers.

We always recommended to use JMS API to communicate with AMQP 1.0 brokers . With our experience there is just no reason for paradox of choice adding extra generic AMQP 1.0 capabilities to Spring.

Comment From: artembilan

See more info here: https://github.com/spring-projects/spring-amqp/issues/2217

Comment From: artembilan

So, I have just pushed spring-rabbitmq-client module with few important classes: 1. AmqpConnectionFactoryBean to expose Connection object configuration in Spring manner. Requires Environment from RabbitMQ AMQP Client. 2. RabbitAmqpAdmin implements AmqpAdmin for API to handle topology in Spring manner. The most important part that it can deal with Declarable bean. Just exactly similar to the RabbitAdmin, but over AMQP 1.0 protocol with RabbitMQ. 3. RabbitAmqpTemplate with async send and receive operation implementations (for now).

See more info in the linked sub-issues.

Comment From: artembilan

OK. Added listener side API together with tests. There is a RabbitAmqpListenerTests which demonstrates how this new module can be used to consume messages from RabbitMQ AMQP 1.0 via @RabbitListener.

I think that's enough for now and next stop will be docs which would close this issue. The rest of improvements after feedback.

Comment From: eddumelendez

Hi @artembilan, is possible to add RabbitAmqpTemplate#execute method similar to RabbitTemplate#execute? The idea is to perform something similar to RabbitHealthIndicator

Comment From: artembilan

Hi @eddumelendez ! Thank you for feedback! Well, there is no channel abstraction in AMQP 1.0, therefore no need to worry about context and Connection object can be used concurrently. However I see your (and already felt it myself) pain because there is no low-level driver (ProtonJ2) exposed from RabbitMQ AMQP Java Client. But we can ask @acogoluegnes to provide at least broker version property on the connection , or management …

Comment From: eddumelendez

Thanks! looking forward to it.

Also, would like to share that I started prototyping a spring boot auto-configuration and noticed a few things

  1. RabbitAmqpTemplate doesn't allow to configure RetryTemplate. Meanwhile, RabbitAmqpListenerContainerFactory allow it.
  2. RabbitAmqpTemplate#setKey could be renamed to setRoutingKey
  3. AmqpConnectionFactoryBean#createInstance creates a Connection using a ConnectionBuilder#build which performs an actual call to RabbitMQ instance and make the testing part a little bit complex.

Comment From: acogoluegnes

It is possible to register a StateListener on the connection to track its state. That should be enough for a health indicator.

Comment From: artembilan

Hi @acogoluegnes !

Yes, the StateListener is good to catch such events internally in the application logic. The health indicator is a Spring Boot high-level API to be called externally: https://docs.spring.io/spring-boot/reference/actuator/endpoints.html#actuator.endpoints.health.

The mentioned RabbitHealthIndicator for AMQP 0.9.1 does this:

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        builder.up().withDetail("version", getVersion());
    }

    private String getVersion() {
        return this.rabbitTemplate
            .execute((channel) -> channel.getConnection().getServerProperties().get("version").toString());
    }

Something similar we can see for JMS:


    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        try (Connection connection = this.connectionFactory.createConnection()) {
            new MonitoredConnection(connection).start();
            builder.up().withDetail("provider", connection.getMetaData().getJMSProviderName());
        }
    }

The MongoDB one is:

    @Override
    protected void doHealthCheck(Health.Builder builder) throws Exception {
        Document result = this.mongoTemplate.executeCommand("{ hello: 1 }");
        builder.up().withDetail("maxWireVersion", result.getInteger("maxWireVersion"));
    }

So, feels like for AMQP 1.0 we would expect the broker version info as well.

For now, @eddumelendez , the regular RabbitTemplate should be enough. Even if it is AMQP 0.9.1, it still connects to the same broker over the same port. So, from the application perspective, it does not matter how we check RabbitMQ broker connectivity. Only downside that there is going to be extra connection to the broker and that one is for 0.9.1 protocol.

Comment From: artembilan

@eddumelendez ,

RabbitAmqpTemplate doesn't allow to configure RetryTemplate.

That's correct, because all the operations in the RabbitAmqpTemplate are CompletableFuture-based. Therefore it is not possible to retry them with standard RetryTemplate. There is no async retry in Spring Retry yet: https://github.com/spring-projects/spring-retry/pull/176.

Perhaps Resilience4j can help some how, but that's different story: https://stackoverflow.com/questions/62084143/resilience4j-returning-a-completablefuture-around-tried-method

RabbitAmqpTemplate#setKey could be renamed to setRoutingKey

Will fix that today.

using a ConnectionBuilder#build which performs an actual call to RabbitMQ

Yes, I had those doubts as well. It is really not very good to have a low-level connection just during initialization phase. Wanted to stay away from ConnectionFactory abstraction.

I would like to know more about your tests and what really complexity are you facing. Look into my tests: https://github.com/spring-projects/spring-amqp/blob/main/spring-rabbitmq-client/src/test/java/org/springframework/amqp/rabbitmq/client/RabbitAmqpTestBase.java I just use Testcontainers for the latest RabbitMQ image and configure these two beans:


        @Bean
        Environment environment() {
            return new AmqpEnvironmentBuilder()
                    .connectionSettings()
                    .port(amqpPort())
                    .environmentBuilder()
                    .build();
        }

        @Bean
        AmqpConnectionFactoryBean connection(Environment environment) {
            return new AmqpConnectionFactoryBean(environment);
        }

Yes, it might connect immediately, but that is light operation as a handshake between client and broker. Or do you mean it cause a problem when no really broker running? And perhaps connection goes to its recovery cycle causing the whole application initialization phase to be blocked. Would you mind to elaborate?

Comment From: eddumelendez

Or do you mean it cause a problem when no really broker running?

Yes, auto-configuration tests don't use Testcontainers because it checks different configurations as part of the unit tests. See RabbitAutoConfigurationTests

I tried mocking the Connection, but then failed with PublisherBuilder is null. I didn't try that yesterday and since I was already providing some notes here I decided to share it here as well too.

Comment From: artembilan

OK. So, I'll bite a bullet and will introduce an AmqpConnectionFactory abstraction to initiate connection in lazy manner. Probably something like RoutingAmqpConnectionFactory could be implemented later on to allow to chose different connections at runtime.

Comment From: acogoluegnes

@artembilan The AMQP 0.9.1 health check is the only one returning the product version: JMS returns the provider name and MongoDB returns the wire protocol version (not the same as MongoDB version).

I think something like should do the trick (assuming there is a StateListener that tracks the connection state):

builder.up().withDetail("state", state.name());

WDYT?

Comment From: artembilan

Well, @acogoluegnes , that still would work for me. I'm not sure what are criteria in Spring Boot for those health details, but I see that it is really not consistent. For example, this one for Cassandra: https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/cassandra/CassandraDriverHealthIndicator.java.

I think this might easy go for discussion to Spring Boot project since there is really nothing I can do from Spring AMQP perspective for the current expose of the RabbitMQ library. I can join discussion if necessary, but that decision is out of my scope and responsibility.

Comment From: artembilan

Hi @eddumelendez !

I know this is an old story, but I wonder where is your attempt to contribute this new Spring AMQP feature to Spring Boot? Thanks