/** * Apply a prefix to the outbound routing key, which will be prefixed to the original message * routing key (if no explicit routing key was provided in the constructor; ignored otherwise. * Use an empty string ("") for no prefixing. * @param errorRoutingKeyPrefix The prefix (default "error."). * @return this. */ public RepublishMessageRecoverer errorRoutingKeyPrefix(String errorRoutingKeyPrefix) { this.setErrorRoutingKeyPrefix(errorRoutingKeyPrefix); return this; }
private Throwable bigCause(Throwable cause) { if (getStackTraceAsString(cause).length() > this.maxHeaderSize) { return cause; } return bigCause(new RuntimeException(BIG_EXCEPTION_MESSAGE, cause)); }
private String processStackTrace(Throwable cause) { String stackTraceAsString = getStackTraceAsString(cause); if (this.maxStackTraceLength < 0) { int maxStackTraceLen = RabbitUtils .getMaxFrame(((RabbitTemplate) this.errorTemplate).getConnectionFactory()); if (maxStackTraceLen > 0) { maxStackTraceLen -= this.frameMaxHeadroom; this.maxStackTraceLength = maxStackTraceLen; } } if (this.maxStackTraceLength > 0 && stackTraceAsString.length() > this.maxStackTraceLength) { stackTraceAsString = stackTraceAsString.substring(0, this.maxStackTraceLength); this.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); } return stackTraceAsString; }
default UnaryOperator<SimpleMessageListenerContainer> setRetryOpertations(Optional<Integer> maxAttempts, Optional<BackoffOptions> backoffOptions) { StatelessRetryInterceptorBuilder builder = RetryInterceptorBuilder.stateless(); if (maxAttempts.isPresent()) { builder.maxAttempts(maxAttempts.get()); } if (backoffOptions.isPresent()) { BackoffOptions options = backoffOptions.get(); builder.backOffOptions(options.getInitialInterval(), options.getMultiplier(), options.getMaxInterval()); } builder.recoverer(new RejectAndDontRequeueRecoverer()); return setRetryOpertations(builder.build()); } }
@Test public void shouldPublishWithRoutingKeyPrefixedWithErrorWhenExchangeIsNotSet() { recoverer = new RepublishMessageRecoverer(amqpTemplate); recoverer.recover(message, cause); verify(amqpTemplate).send("error.some.key", message); }
@Test public void setDeliveryModeIfNull() { this.message.getMessageProperties().setDeliveryMode(null); this.recoverer = new RepublishMessageRecoverer(amqpTemplate, "error"); this.recoverer.setDeliveryMode(MessageDeliveryMode.NON_PERSISTENT); recoverer.recover(this.message, this.cause); assertEquals(MessageDeliveryMode.NON_PERSISTENT, this.message.getMessageProperties().getDeliveryMode()); }
@Override public RetryOperationsInterceptor getObject() { RetryOperationsInterceptor retryInterceptor = new RetryOperationsInterceptor(); RetryOperations retryTemplate = getRetryOperations(); if (retryTemplate == null) { retryTemplate = new RetryTemplate(); } retryInterceptor.setRetryOperations(retryTemplate); final MessageRecoverer messageRecoverer = getMessageRecoverer(); retryInterceptor.setRecoverer((args, cause) -> { Message message = (Message) args[1]; if (messageRecoverer == null) { logger.warn("Message dropped on recovery: " + message, cause); } else { messageRecoverer.recover(message, cause); } return null; }); return retryInterceptor; }
@Test public void shouldRemapDeliveryMode() { message.getMessageProperties().setDeliveryMode(null); message.getMessageProperties().setReceivedDeliveryMode(MessageDeliveryMode.PERSISTENT); recoverer = new RepublishMessageRecoverer(amqpTemplate, "error") { protected Map<? extends String, ? extends Object> additionalHeaders(Message message, Throwable cause) { message.getMessageProperties().setDeliveryMode(message.getMessageProperties().getReceivedDeliveryMode()); return null; } }; recoverer.recover(message, cause); assertEquals(MessageDeliveryMode.PERSISTENT, message.getMessageProperties().getDeliveryMode()); }
@Bean public RetryOperationsInterceptor rabbitSourceRetryInterceptor() { return RetryInterceptorBuilder.stateless() .maxAttempts(this.properties.getMaxAttempts()) .backOffOptions(this.properties.getInitialRetryInterval(), this.properties.getRetryMultiplier(), this.properties.getMaxRetryInterval()) .recoverer(new RejectAndDontRequeueRecoverer()) .build(); }
@Test public void shouldPublishWithSetErrorRoutingKeyWhenExchangeAndErrorRoutingKeyProvided() { recoverer = new RepublishMessageRecoverer(amqpTemplate, "errorExchange", "errorRoutingKey"); recoverer.recover(message, cause); verify(amqpTemplate).send("errorExchange", "errorRoutingKey", message); }
/** * Apply a prefix to the outbound routing key, which will be prefixed to the original message * routing key (if no explicit routing key was provided in the constructor; ignored otherwise. * Use an empty string ("") for no prefixing. * @param errorRoutingKeyPrefix The prefix (default "error."). * @return this. */ public RepublishMessageRecoverer errorRoutingKeyPrefix(String errorRoutingKeyPrefix) { this.setErrorRoutingKeyPrefix(errorRoutingKeyPrefix); return this; }
private String processStackTrace(Throwable cause) { String stackTraceAsString = getStackTraceAsString(cause); if (this.maxStackTraceLength < 0) { int maxStackTraceLen = RabbitUtils .getMaxFrame(((RabbitTemplate) this.errorTemplate).getConnectionFactory()); if (maxStackTraceLen > 0) { maxStackTraceLen -= this.frameMaxHeadroom; this.maxStackTraceLength = maxStackTraceLen; } } if (this.maxStackTraceLength > 0 && stackTraceAsString.length() > this.maxStackTraceLength) { stackTraceAsString = stackTraceAsString.substring(0, this.maxStackTraceLength); this.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); } return stackTraceAsString; }
@Override public RetryOperationsInterceptor getObject() { RetryOperationsInterceptor retryInterceptor = new RetryOperationsInterceptor(); RetryOperations retryTemplate = getRetryOperations(); if (retryTemplate == null) { retryTemplate = new RetryTemplate(); } retryInterceptor.setRetryOperations(retryTemplate); final MessageRecoverer messageRecoverer = getMessageRecoverer(); retryInterceptor.setRecoverer((args, cause) -> { Message message = (Message) args[1]; if (messageRecoverer == null) { logger.warn("Message dropped on recovery: " + message, cause); } else { messageRecoverer.recover(message, cause); } return null; }); return retryInterceptor; }
@Bean public RetryOperationsInterceptor rabbitSourceRetryInterceptor() { return RetryInterceptorBuilder.stateless() .maxAttempts(this.properties.getMaxAttempts()) .backOffOptions(this.properties.getInitialRetryInterval(), this.properties.getRetryMultiplier(), this.properties.getMaxRetryInterval()) .recoverer(new RejectAndDontRequeueRecoverer()) .build(); }
@Test public void shouldPublishToProvidedExchange() { recoverer = new RepublishMessageRecoverer(amqpTemplate, "error"); recoverer.recover(message, cause); verify(amqpTemplate).send("error", "error.some.key", message); }
@Test public void shouldIncludeTheCauseMessageInTheHeaderOfThePublishedMessage() { recoverer = new RepublishMessageRecoverer(amqpTemplate); recoverer.recover(message, cause); assertEquals(cause.getCause().getMessage(), message.getMessageProperties().getHeaders().get("x-exception-message")); }
@Test public void shouldIncludeTheStacktraceInTheHeaderOfThePublishedMessage() { recoverer = new RepublishMessageRecoverer(amqpTemplate); ByteArrayOutputStream baos = new ByteArrayOutputStream(); cause.printStackTrace(new PrintStream(baos)); final String expectedHeaderValue = baos.toString(); recoverer.recover(message, cause); assertEquals(expectedHeaderValue, message.getMessageProperties().getHeaders().get("x-exception-stacktrace")); }
@Test public void shouldSetTheOriginalMessageExchangeOnInTheHeaders() { message.getMessageProperties().setReceivedExchange("the.original.exchange"); recoverer = new RepublishMessageRecoverer(amqpTemplate, "error"); recoverer.recover(message, cause); assertEquals("the.original.exchange", message.getMessageProperties().getHeaders().get("x-original-exchange")); }