title: How to configure Spring AMQP to not requeue when AmqpRejectAndDontRequeueException is thrown from the listener?
body: <p>I currently have a Spring Boot Application configured to use spring-boot-starter-amqp 2.1.5.RELEASE. I have configured it for retries in the yaml:</p>
  
  <pre><code>  rabbitmq:
      listener:
        simple:
          retry:
            enabled: true # retrys enabled
            max-attempts: 3 # total number number attempts (includes the original one)
            multiplier: 1.5 # multiple of initial interval for subsequent retries
            initial-interval: 1000 # first interval between attempts
  </code></pre>
  
  <p>In my listener under an certain scenario I throw a AmqpRejectAndDontRequeueException but this does not prevent the re-queueing.</p>
  
  <p>How can I configure a configuration bean inline with the automatic spring configuration to stop further requeue of a message if this exception is thrown?</p>
  
  <p>Queue A should attempt the processing on the queueA listener 3 times and the logs support this.
  Queue B should attempt only once and stop when the AmqpRejectAndDontRequeueException is thrown.</p>
  
  <p>SpringBoot Application class:</p>
  
  <pre><code>import java.time.LocalDateTime;
  import java.time.temporal.ChronoUnit;
  import java.util.Arrays;
  import java.util.List;
  
  import javax.annotation.PostConstruct;
  
  import org.apache.commons.lang3.ObjectUtils;
  import org.springframework.amqp.AmqpRejectAndDontRequeueException;
  import org.springframework.amqp.core.AmqpAdmin;
  import org.springframework.amqp.core.Binding;
  import org.springframework.amqp.core.BindingBuilder;
  import org.springframework.amqp.core.DirectExchange;
  import org.springframework.amqp.core.Message;
  import org.springframework.amqp.core.Queue;
  import org.springframework.amqp.core.QueueBuilder;
  import org.springframework.amqp.rabbit.annotation.RabbitListener;
  import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
  import org.springframework.amqp.rabbit.connection.ConnectionFactory;
  import org.springframework.amqp.rabbit.core.RabbitAdmin;
  import org.springframework.amqp.rabbit.core.RabbitTemplate;
  import org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
  import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
  import org.springframework.beans.factory.annotation.Autowired;
  import org.springframework.boot.SpringApplication;
  import org.springframework.boot.autoconfigure.SpringBootApplication;
  import org.springframework.context.ConfigurableApplicationContext;
  import org.springframework.context.annotation.Bean;
  import org.springframework.messaging.handler.annotation.Header;
  import org.springframework.messaging.handler.annotation.Payload;
  
  import com.fasterxml.jackson.databind.ObjectMapper;
  import com.fasterxml.jackson.datatype.jsonorg.JsonOrgModule;
  
  import lombok.extern.slf4j.Slf4j;
  
  @SpringBootApplication
  @Slf4j
  public class AmqpApplication
  {
  
      protected static final String X_ATTEMPTS_HEADER = "x-attempts";
      protected static final String X_LAST_ATTEMPT_DATE_HEADER = "x-last-attempt-date";
  
      public static void main(String[] args) throws InterruptedException
      {
          ConfigurableApplicationContext context = SpringApplication.run(AmqpApplication.class, args);
          context.close();
          System.exit(0);
      }
  
      private String host = "localhost";
  
      private Integer port = 5672;
  
      private String vhost = "/";
  
      private String username = "guest";
  
      private String password = "guest";
  
      private String exchangeName = "common-exchange";
  
      @Autowired
      private RabbitTemplate rabbitTemplate;
  
      /**
       * Configures the connection factory using the configured values
       * 
       * @return Connection factory to use to connect to rabbitmq and send events
       **/
      private ConnectionFactory connectionFactory()
      {
          CachingConnectionFactory factory = new CachingConnectionFactory(host, port);
  
          factory.setRequestedHeartBeat(30);
          factory.setConnectionTimeout(30000);
          factory.setChannelCacheSize(10);
  
          factory.setVirtualHost(vhost);
          factory.setUsername(username);
          factory.setPassword(password);
          return factory;
      }
  
      @Bean
      public Queue queueA()
      {
          return QueueBuilder.durable("a").withArgument("x-dead-letter-exchange", "a")
                  .withArgument("x-dead-letter-routing-key", "a-dead-letter").build();
      }
  
      @Bean
      public Queue queueB()
      {
          return QueueBuilder.durable("b").withArgument("x-dead-letter-exchange", "b")
                  .withArgument("x-dead-letter-routing-key", "b-dead-letter").build();
      }
  
      @Bean
      Queue DeadLetterQueueA()
      {
          return QueueBuilder.durable("a-dead-letter").build();
      }
  
      @Bean
      Queue DeadLetterQueueB()
      {
          return QueueBuilder.durable("b-dead-letter").build();
      }
  
      /**
       * Required for executing adminstration functions against an AMQP Broker
       */
      @Bean
      public AmqpAdmin amqpAdmin(RabbitListenerEndpointRegistry registry)
      {
          //@// @formatter:off
  
          RabbitAdmin admin = new RabbitAdmin(connectionFactory());
          admin.declareQueue(queueA());
          admin.declareQueue(queueB());
          registry.start();
          return admin;
      }
  
      /**
       * The following is a complete declaration of an exchange, a queue and a
       * exchange-queue binding
       */
      @Bean
      public DirectExchange directExchange()
      {
          return new DirectExchange(exchangeName, true, false);
      }
  
      @Bean
      public List&lt;Binding&gt; exchangeBinding()
      {
          // Important part is the routing key -- this is just an example
          return Arrays.asList(
                  BindingBuilder.bind(queueA()).to(directExchange()).with("a"),
                  BindingBuilder.bind(DeadLetterQueueA()).to(directExchange())
                          .with("a"),
                          BindingBuilder.bind(queueB()).to(directExchange()).with("b"),
                          BindingBuilder.bind(DeadLetterQueueB()).to(directExchange())
                                  .with("b"));
      }
  
  
  
      @Bean
      public RabbitTemplate rabbitTemplate()
      {
          // Add the object mapper to the converter
          ObjectMapper objectMapper = new ObjectMapper();
          objectMapper.registerModule(new JsonOrgModule());
  
          // Add the object mapper to the converter
          RabbitTemplate template = new RabbitTemplate(connectionFactory());
          template.setMessageConverter(new Jackson2JsonMessageConverter(objectMapper));
  
          template.setExchange(exchangeName);
  
          return template;
      }
  
      @PostConstruct
      public void sendMessages() throws InterruptedException
      {
          rabbitTemplate.convertAndSend(exchangeName, "a", new BeanObject().setName("a"));
          rabbitTemplate.convertAndSend(exchangeName, "b", new BeanObject().setName("b"));
      }
  
     @RabbitListener(queues = "a")
      public void aListener(@Payload BeanObject payload, Message message,
              @Header(required = false, name = X_ATTEMPTS_HEADER, defaultValue = "0") Integer attempts)
      {
          beforeProcessing(payload,message,attempts);
          throw new RuntimeException();
      }
  
      @RabbitListener(queues = "b")
      public void bListener(@Payload BeanObject payload, Message message,
              @Header(required = false, name = X_ATTEMPTS_HEADER, defaultValue = "0") Integer attempts)
      {
          beforeProcessing(payload,message,attempts);
          throw new AmqpRejectAndDontRequeueException("");
      }
  
      private void beforeProcessing(BeanObject payload, Message message,
              @Header(required = false, name = X_ATTEMPTS_HEADER, defaultValue = "0") Integer attemptNo)
      {
  
          //// @formatter:off
          attemptNo++;// Increment
  
          message.getMessageProperties().getHeaders().put(X_ATTEMPTS_HEADER, attemptNo);//update attempts header
       // @formatter:on
          log.info(
                  "bean: {}, attemptNo: {}",
                  payload, attemptNo);
      }
  
  }
  </code></pre>
  
  <p>MessageConverter Class:</p>
  
  <pre><code>
  import java.io.IOException;
  
  import org.springframework.amqp.core.Message;
  import org.springframework.amqp.core.MessageProperties;
  import org.springframework.amqp.support.converter.MessageConversionException;
  import org.springframework.stereotype.Component;
  
  import com.fasterxml.jackson.databind.DeserializationFeature;
  import com.fasterxml.jackson.databind.ObjectMapper;
  
  @Component
  public class MessageConverter implements org.springframework.amqp.support.converter.MessageConverter
  {
      private final ObjectMapper objectMapper = new ObjectMapper()
              .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
  
      @Override
      public Message toMessage(Object object, MessageProperties messageProperties) throws MessageConversionException
      {
  
          return null;
      }
  
      @Override
      public Object fromMessage(Message message) throws MessageConversionException
      {
          if (message.getMessageProperties() == null || message.getMessageProperties().getHeaders() == null
                  || !message.getMessageProperties().getHeaders().containsKey("__TypeId__"))
          {
              throw new MessageConversionException(
                      "No header exists in the message for [__TypeId__]. This is required to hint the conversion type.");
          }
          String typeId = message.getMessageProperties().getHeaders().get("__TypeId__").toString();
          try
          {
              return objectMapper.readValue(message.getBody(), Class.forName(typeId));
          }
          catch (ClassNotFoundException | IOException e)
          {
              throw new MessageConversionException(
                      String.format("Unable to convert message payload to type [%s]", typeId));
          }
      }
  
  }
  </code></pre>
  
  <p>Lombok Bean class:</p>
  
  <pre><code>package com.amqp;
  
  import lombok.Data;
  import lombok.experimental.Accessors;
  
  @Data
  @Accessors(chain = true)
  public class BeanObject
  {
      private String name;
  }
  
  </code></pre>
  
  <p>POM:</p>
  
  <pre><code>&lt;?xml version="1.0" encoding="UTF-8"?&gt;
  &lt;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 http://maven.apache.org/xsd/maven-4.0.0.xsd"&gt;
      &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
      &lt;parent&gt;
          &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
          &lt;artifactId&gt;spring-boot-starter-parent&lt;/artifactId&gt;
          &lt;version&gt;2.0.1.RELEASE&lt;/version&gt;
          &lt;relativePath/&gt; &lt;!-- lookup parent from repository --&gt;
      &lt;/parent&gt;
      &lt;groupId&gt;com.amqp&lt;/groupId&gt;
      &lt;artifactId&gt;amqp&lt;/artifactId&gt;
      &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
      &lt;name&gt;amqp&lt;/name&gt;
      &lt;description&gt;Demo project for Spring Boot&lt;/description&gt;
  
      &lt;properties&gt;
          &lt;java.version&gt;1.8&lt;/java.version&gt;
      &lt;/properties&gt;
  
      &lt;dependencies&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-starter-amqp&lt;/artifactId&gt;
          &lt;/dependency&gt;
  
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-devtools&lt;/artifactId&gt;
              &lt;optional&gt;true&lt;/optional&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.projectlombok&lt;/groupId&gt;
              &lt;artifactId&gt;lombok&lt;/artifactId&gt;
              &lt;optional&gt;true&lt;/optional&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.apache.commons&lt;/groupId&gt;
              &lt;artifactId&gt;commons-lang3&lt;/artifactId&gt;
          &lt;/dependency&gt;
  
          &lt;dependency&gt;
              &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
              &lt;artifactId&gt;jackson-core&lt;/artifactId&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;com.fasterxml.jackson.core&lt;/groupId&gt;
              &lt;artifactId&gt;jackson-databind&lt;/artifactId&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;com.fasterxml.jackson.datatype&lt;/groupId&gt;
              &lt;artifactId&gt;jackson-datatype-jdk8&lt;/artifactId&gt;
          &lt;/dependency&gt;
          &lt;!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-jsr310 --&gt;
  &lt;dependency&gt;
      &lt;groupId&gt;com.fasterxml.jackson.datatype&lt;/groupId&gt;
      &lt;artifactId&gt;jackson-datatype-jsr310&lt;/artifactId&gt;
  &lt;/dependency&gt;
          &lt;!-- https://mvnrepository.com/artifact/com.fasterxml/jackson-module-json-org --&gt;
  &lt;!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.datatype/jackson-datatype-json-org --&gt;
  &lt;dependency&gt;
      &lt;groupId&gt;com.fasterxml.jackson.datatype&lt;/groupId&gt;
      &lt;artifactId&gt;jackson-datatype-json-org&lt;/artifactId&gt;
  &lt;/dependency&gt;
  
          &lt;dependency&gt;
              &lt;groupId&gt;javax.interceptor&lt;/groupId&gt;
              &lt;artifactId&gt;javax.interceptor-api&lt;/artifactId&gt;
              &lt;version&gt;1.2&lt;/version&gt;
          &lt;/dependency&gt;
          &lt;dependency&gt;
              &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
              &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
              &lt;scope&gt;test&lt;/scope&gt;
          &lt;/dependency&gt;
      &lt;/dependencies&gt;
  
      &lt;build&gt;
          &lt;plugins&gt;
              &lt;plugin&gt;
                  &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
                  &lt;artifactId&gt;spring-boot-maven-plugin&lt;/artifactId&gt;
              &lt;/plugin&gt;
          &lt;/plugins&gt;
      &lt;/build&gt;
  
  &lt;/project&gt;
  </code></pre>
  
  <p>Logs:</p>
  
  <pre><code>bean: BeanObject(name=a), attemptNo: 1
  bean: BeanObject(name=b), attemptNo: 1
  
  bean: BeanObject(name=a), attemptNo: 2
  bean: BeanObject(name=b), attemptNo: 2
  bean: BeanObject(name=b), attemptNo: 3
  bean: BeanObject(name=a), attemptNo: 3
  </code></pre>