use of org.springframework.amqp.AmqpRejectAndDontRequeueException in project spring-amqp by spring-projects.
the class BlockingQueueConsumerTests method testDontRequeueNested.
@Test
public void testDontRequeueNested() throws Exception {
Exception ex = new RuntimeException(new RuntimeException(new AmqpRejectAndDontRequeueException("fail")));
testRequeueOrNotDefaultYes(ex, false);
}
use of org.springframework.amqp.AmqpRejectAndDontRequeueException in project spring-amqp by spring-projects.
the class ErrorHandlerTests method testMessagingException.
@Test
public void testMessagingException() {
Throwable cause = new MessageHandlingException(null, "test", new MessageHandlingException(null, "test", new ClassCastException()));
try {
doTest(cause);
fail("Expected exception");
} catch (AmqpRejectAndDontRequeueException e) {
// noop
}
}
use of org.springframework.amqp.AmqpRejectAndDontRequeueException in project spring-amqp by spring-projects.
the class ExternalTxManagerTests method testMessageListenerRollbackGuts.
/**
* Verifies that the channel is rolled back after an exception.
*/
private void testMessageListenerRollbackGuts(boolean expectRequeue, int propagation) throws Exception {
ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class);
Connection mockConnection = mock(Connection.class);
final Channel channel = mock(Channel.class);
given(channel.isOpen()).willReturn(true);
final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(mockConnectionFactory);
cachingConnectionFactory.setExecutor(mock(ExecutorService.class));
given(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).willReturn(mockConnection);
given(mockConnection.isOpen()).willReturn(true);
final AtomicReference<Exception> tooManyChannels = new AtomicReference<Exception>();
willAnswer(ensureOneChannelAnswer(channel, tooManyChannels)).given(mockConnection).createChannel();
willAnswer(invocation -> channel).given(mockConnection).createChannel();
final AtomicReference<Consumer> consumer = new AtomicReference<Consumer>();
final CountDownLatch consumerLatch = new CountDownLatch(1);
willAnswer(invocation -> {
consumer.set(invocation.getArgument(6));
consumerLatch.countDown();
return "consumerTag";
}).given(channel).basicConsume(anyString(), anyBoolean(), anyString(), anyBoolean(), anyBoolean(), anyMap(), any(Consumer.class));
final CountDownLatch rollbackLatch = new CountDownLatch(1);
willAnswer(invocation -> {
rollbackLatch.countDown();
return null;
}).given(channel).txRollback();
final CountDownLatch rejectLatch = new CountDownLatch(1);
willAnswer(invocation -> {
rejectLatch.countDown();
return null;
}).given(channel).basicReject(anyLong(), anyBoolean());
willAnswer(invocation -> {
rejectLatch.countDown();
return null;
}).given(channel).basicNack(anyLong(), anyBoolean(), anyBoolean());
final CountDownLatch latch = new CountDownLatch(1);
AbstractMessageListenerContainer container = createContainer(cachingConnectionFactory);
container.setTransactionAttribute(new DefaultTransactionAttribute(propagation));
container.setMessageListener(message -> {
latch.countDown();
throw expectRequeue ? new RuntimeException("force rollback") : new AmqpRejectAndDontRequeueException("force rollback");
});
container.setQueueNames("queue");
container.setChannelTransacted(true);
container.setShutdownTimeout(100);
container.setTransactionManager(new DummyTxManager());
container.afterPropertiesSet();
container.start();
assertThat(consumerLatch.await(10, TimeUnit.SECONDS)).isTrue();
consumer.get().handleDelivery("qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] { 0 });
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
Exception e = tooManyChannels.get();
if (e != null) {
throw e;
}
verify(mockConnection, times(1)).createChannel();
assertThat(rejectLatch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(rollbackLatch.await(10, TimeUnit.SECONDS)).isTrue();
if (propagation != TransactionDefinition.PROPAGATION_NEVER) {
verify(channel).basicReject(anyLong(), eq(expectRequeue));
} else {
verify(channel).basicNack(anyLong(), eq(Boolean.TRUE), eq(expectRequeue));
}
container.stop();
}
use of org.springframework.amqp.AmqpRejectAndDontRequeueException in project spring-amqp by spring-projects.
the class LocallyTransactedTests method testMessageListener.
/**
* Verifies that an up-stack transactional RabbitTemplate uses the listener's
* channel (MessageListener).
*/
@Test
public void testMessageListener() throws Exception {
ConnectionFactory mockConnectionFactory = mock(ConnectionFactory.class);
Connection mockConnection = mock(Connection.class);
final Channel onlyChannel = mock(Channel.class);
given(onlyChannel.isOpen()).willReturn(true);
final CachingConnectionFactory cachingConnectionFactory = new CachingConnectionFactory(mockConnectionFactory);
cachingConnectionFactory.setExecutor(mock(ExecutorService.class));
given(mockConnectionFactory.newConnection(any(ExecutorService.class), anyString())).willReturn(mockConnection);
given(mockConnection.isOpen()).willReturn(true);
final AtomicReference<Exception> tooManyChannels = new AtomicReference<Exception>();
AtomicBoolean done = new AtomicBoolean();
willAnswer(invocation -> {
if (!done.getAndSet(true)) {
return onlyChannel;
}
tooManyChannels.set(new Exception("More than one channel requested"));
Channel channel = mock(Channel.class);
given(channel.isOpen()).willReturn(true);
return channel;
}).given(mockConnection).createChannel();
final AtomicReference<Consumer> consumer = new AtomicReference<Consumer>();
final CountDownLatch consumerLatch = new CountDownLatch(1);
willAnswer(invocation -> {
consumer.set(invocation.getArgument(6));
consumerLatch.countDown();
return "consumerTag";
}).given(onlyChannel).basicConsume(anyString(), anyBoolean(), anyString(), anyBoolean(), anyBoolean(), anyMap(), any(Consumer.class));
final AtomicReference<CountDownLatch> commitLatch = new AtomicReference<>(new CountDownLatch(1));
willAnswer(invocation -> {
commitLatch.get().countDown();
return null;
}).given(onlyChannel).txCommit();
final AtomicReference<CountDownLatch> rollbackLatch = new AtomicReference<>(new CountDownLatch(1));
willAnswer(invocation -> {
rollbackLatch.get().countDown();
return null;
}).given(onlyChannel).txRollback();
willAnswer(invocation -> {
return null;
}).given(onlyChannel).basicAck(anyLong(), anyBoolean());
final CountDownLatch latch = new CountDownLatch(1);
AbstractMessageListenerContainer container = createContainer(cachingConnectionFactory);
container.setMessageListener(message -> {
RabbitTemplate rabbitTemplate = new RabbitTemplate(cachingConnectionFactory);
rabbitTemplate.setChannelTransacted(true);
// should use same channel as container
rabbitTemplate.convertAndSend("foo", "bar", "baz");
latch.countDown();
});
container.setQueueNames("queue");
container.setChannelTransacted(true);
container.setShutdownTimeout(100);
container.afterPropertiesSet();
container.start();
assertThat(consumerLatch.await(10, TimeUnit.SECONDS)).isTrue();
consumer.get().handleDelivery("qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] { 0 });
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
Exception e = tooManyChannels.get();
if (e != null) {
throw e;
}
verify(mockConnection, times(1)).createChannel();
assertThat(commitLatch.get().await(10, TimeUnit.SECONDS)).isTrue();
verify(onlyChannel).txCommit();
verify(onlyChannel).basicPublish(Mockito.anyString(), Mockito.anyString(), Mockito.anyBoolean(), Mockito.any(BasicProperties.class), Mockito.any(byte[].class));
DirectFieldAccessor dfa = new DirectFieldAccessor(cachingConnectionFactory);
List<?> channels = (List<?>) dfa.getPropertyValue("cachedChannelsTransactional");
assertThat(channels).hasSize(0);
container.setMessageListener(m -> {
throw new RuntimeException();
});
commitLatch.set(new CountDownLatch(1));
consumer.get().handleDelivery("qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] { 0 });
assertThat(commitLatch.get().await(10, TimeUnit.SECONDS)).isTrue();
assertThat(rollbackLatch.get().await(10, TimeUnit.SECONDS)).isTrue();
verify(onlyChannel).basicNack(anyLong(), anyBoolean(), anyBoolean());
verify(onlyChannel, times(1)).txRollback();
// ImmediateAck tests
container.setMessageListener(m -> {
throw new AmqpRejectAndDontRequeueException("foo", new ImmediateAcknowledgeAmqpException("bar"));
});
commitLatch.set(new CountDownLatch(1));
rollbackLatch.set(new CountDownLatch(1));
consumer.get().handleDelivery("qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] { 0 });
assertThat(rollbackLatch.get().await(10, TimeUnit.SECONDS)).isTrue();
assertThat(commitLatch.get().await(10, TimeUnit.SECONDS)).isTrue();
verify(onlyChannel, times(2)).basicNack(anyLong(), anyBoolean(), anyBoolean());
verify(onlyChannel, times(2)).txRollback();
container.setAfterReceivePostProcessors(m -> null);
container.setMessageListener(m -> {
// NOSONAR
});
commitLatch.set(new CountDownLatch(1));
consumer.get().handleDelivery("qux", new Envelope(1, false, "foo", "bar"), new BasicProperties(), new byte[] { 0 });
assertThat(commitLatch.get().await(10, TimeUnit.SECONDS)).isTrue();
verify(onlyChannel, times(2)).basicAck(anyLong(), anyBoolean());
verify(onlyChannel, times(4)).txCommit();
container.stop();
}
use of org.springframework.amqp.AmqpRejectAndDontRequeueException in project spring-cloud-stream by spring-cloud.
the class RabbitMessageChannelBinder method getErrorMessageHandler.
@Override
protected MessageHandler getErrorMessageHandler(ConsumerDestination destination, String group, final ExtendedConsumerProperties<RabbitConsumerProperties> properties) {
if (properties.getExtension().isRepublishToDlq()) {
return new MessageHandler() {
private static final long ACK_TIMEOUT = 10_000;
private final RabbitTemplate template = new RabbitTemplate(RabbitMessageChannelBinder.this.connectionFactory);
private final ConfirmType confirmType;
{
this.template.setUsePublisherConnection(true);
this.template.setChannelTransacted(properties.getExtension().isTransacted());
this.template.setMandatory(RabbitMessageChannelBinder.this.connectionFactory.isPublisherReturns());
if (RabbitMessageChannelBinder.this.connectionFactory.isSimplePublisherConfirms()) {
this.confirmType = ConfirmType.SIMPLE;
} else if (RabbitMessageChannelBinder.this.connectionFactory.isPublisherConfirms()) {
this.confirmType = ConfirmType.CORRELATED;
} else {
this.confirmType = ConfirmType.NONE;
}
}
private final String exchange = deadLetterExchangeName(properties.getExtension());
private final String routingKey = properties.getExtension().getDeadLetterRoutingKey();
private final int frameMaxHeadroom = properties.getExtension().getFrameMaxHeadroom();
private int maxStackTraceLength = -1;
private Boolean dlxPresent;
@Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
Message amqpMessage = StaticMessageHeaderAccessor.getSourceData(message);
if (!(message instanceof ErrorMessage)) {
logger.error("Expected an ErrorMessage, not a " + message.getClass().toString() + " for: " + message);
} else if (amqpMessage == null) {
logger.error("No raw message header in " + message);
} else {
if (!checkDlx()) {
return;
}
Throwable cause = (Throwable) message.getPayload();
if (!shouldRepublish(cause)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping republish of: " + message);
}
return;
}
MessageProperties messageProperties = amqpMessage.getMessageProperties();
Map<String, Object> headers = messageProperties.getHeaders();
String stackTraceAsString = getStackTraceAsString(cause);
if (this.maxStackTraceLength < 0) {
int rabbitMaxStackTraceLength = RabbitUtils.getMaxFrame(this.template.getConnectionFactory());
if (rabbitMaxStackTraceLength > 0) {
// maxStackTraceLength -= this.frameMaxHeadroom;
this.maxStackTraceLength = rabbitMaxStackTraceLength - this.frameMaxHeadroom;
}
}
if (this.maxStackTraceLength > 0 && stackTraceAsString.length() > this.maxStackTraceLength) {
stackTraceAsString = stackTraceAsString.substring(0, this.maxStackTraceLength);
logger.warn("Stack trace in republished message header truncated due to frame_max limitations; " + "consider increasing frame_max on the broker or reduce the stack trace depth", cause);
}
headers.put(RepublishMessageRecoverer.X_EXCEPTION_STACKTRACE, stackTraceAsString);
headers.put(RepublishMessageRecoverer.X_EXCEPTION_MESSAGE, cause.getCause() != null ? cause.getCause().getMessage() : cause.getMessage());
headers.put(RepublishMessageRecoverer.X_ORIGINAL_EXCHANGE, messageProperties.getReceivedExchange());
headers.put(RepublishMessageRecoverer.X_ORIGINAL_ROUTING_KEY, messageProperties.getReceivedRoutingKey());
if (properties.getExtension().getRepublishDeliveyMode() != null) {
messageProperties.setDeliveryMode(properties.getExtension().getRepublishDeliveyMode());
}
doSend(this.exchange, this.routingKey != null ? this.routingKey : messageProperties.getConsumerQueue(), amqpMessage);
if (properties.getExtension().getAcknowledgeMode().equals(AcknowledgeMode.MANUAL)) {
org.springframework.messaging.Message<?> original = ((ErrorMessage) message).getOriginalMessage();
if (original != null) {
// If we are using manual acks, ack the original message.
try {
original.getHeaders().get(AmqpHeaders.CHANNEL, Channel.class).basicAck(original.getHeaders().get(AmqpHeaders.DELIVERY_TAG, Long.class), false);
} catch (IOException e) {
logger.debug("Failed to ack original message", e);
}
}
}
}
}
private void doSend(String exchange, String routingKey, Message amqpMessage) {
if (ConfirmType.SIMPLE.equals(this.confirmType)) {
this.template.invoke(temp -> {
temp.send(exchange, routingKey, amqpMessage);
if (!temp.waitForConfirms(ACK_TIMEOUT)) {
throw new AmqpRejectAndDontRequeueException("Negative ack for DLQ message received");
}
return null;
});
} else if (ConfirmType.CORRELATED.equals(this.confirmType)) {
CorrelationData corr = new CorrelationData();
this.template.send(exchange, routingKey, amqpMessage, corr);
try {
Confirm confirm = corr.getFuture().get(ACK_TIMEOUT, TimeUnit.MILLISECONDS);
if (!confirm.isAck()) {
throw new AmqpRejectAndDontRequeueException("Negative ack for DLQ message received");
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new AmqpRejectAndDontRequeueException(e);
} catch (ExecutionException e) {
throw new AmqpRejectAndDontRequeueException(e.getCause());
} catch (TimeoutException e) {
throw new AmqpRejectAndDontRequeueException(e);
}
if (corr.getReturned() != null) {
RabbitMessageChannelBinder.this.logger.error("DLQ message was returned: " + amqpMessage);
throw new AmqpRejectAndDontRequeueException("DLQ message was returned");
}
} else {
this.template.send(exchange, routingKey, amqpMessage);
}
}
private boolean checkDlx() {
if (this.dlxPresent == null) {
if (properties.getExtension().isAutoBindDlq()) {
this.dlxPresent = Boolean.TRUE;
} else {
this.dlxPresent = this.template.execute(channel -> {
String dlx = deadLetterExchangeName(properties.getExtension());
try {
channel.exchangeDeclarePassive(dlx);
return Boolean.TRUE;
} catch (IOException e) {
logger.warn("'republishToDlq' is true, but the '" + dlx + "' dead letter exchange is not present; disabling 'republishToDlq'");
return Boolean.FALSE;
}
});
}
}
return this.dlxPresent;
}
/**
* Traverse the cause tree, stopping at AmqpRejectAndDontRequeueException
* or ImmediateAcknowledgeAmqpException.
* @param throwable the throwable.
* @return true if neither found or AmqpRejectAndDontRequeueException is
* found first.
*/
private boolean shouldRepublish(Throwable throwable) {
Throwable cause = throwable;
while (cause != null && !(cause instanceof AmqpRejectAndDontRequeueException) && !(cause instanceof ImmediateAcknowledgeAmqpException)) {
cause = cause.getCause();
}
return !(cause instanceof ImmediateAcknowledgeAmqpException);
}
};
} else if (properties.getMaxAttempts() > 1) {
return new MessageHandler() {
private final RejectAndDontRequeueRecoverer recoverer = new RejectAndDontRequeueRecoverer();
@Override
public void handleMessage(org.springframework.messaging.Message<?> message) throws MessagingException {
Message amqpMessage = StaticMessageHeaderAccessor.getSourceData(message);
/*
* NOTE: The following IF and subsequent ELSE IF should never happen
* under normal interaction and it should always go to the last ELSE
* However, given that this is a handler subscribing to the public
* channel and that we can't control what type of Message may be sent
* to that channel (user decides to send a Message manually) the
* 'IF/ELSE IF' provides a safety net to handle any message properly.
*/
if (!(message instanceof ErrorMessage)) {
logger.error("Expected an ErrorMessage, not a " + message.getClass().toString() + " for: " + message);
throw new ListenerExecutionFailedException("Unexpected error message " + message, new AmqpRejectAndDontRequeueException(""), (Message[]) null);
} else if (amqpMessage == null) {
logger.error("No raw message header in " + message);
throw new ListenerExecutionFailedException("Unexpected error message " + message, new AmqpRejectAndDontRequeueException(""), amqpMessage);
} else {
this.recoverer.recover(amqpMessage, (Throwable) message.getPayload());
}
}
};
} else {
return super.getErrorMessageHandler(destination, group, properties);
}
}
Aggregations