GH-9985: Fix NPE in ReqHandlerRetryAdvice with MetricsRetryListener
Fixes: #9985
Issue link: https://github.com/spring-projects/spring-integration/issues/9985
When `RequestHandlerRetryAdvice` is supplied with a `RetryTemplate` and `MetricsRetryListener`,
the `NullPointerException` is thrown from the Micrometer `Timer`, where `name` tag cannot be `null`.
In fact, the `RetryCallback` implementation in the `RequestHandlerRetryAdvice` does not provide any `label`.
* Fix `RequestHandlerRetryAdvice` to produce label based on the `componentName` or just class name of the
AOP caller as fallback
(cherry picked from commit 30355f243c)
This commit is contained in:
committed by
Spring Builds
parent
5399924675
commit
86292584f9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.springframework.integration.handler.advice;
|
||||
|
||||
import org.springframework.integration.handler.AbstractReplyProducingMessageHandler;
|
||||
import org.springframework.integration.support.ErrorMessageUtils;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessagingException;
|
||||
@@ -78,7 +79,7 @@ public class RequestHandlerRetryAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
@Override
|
||||
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
|
||||
IntegrationRetryCallback retryCallback = new IntegrationRetryCallback(message, callback);
|
||||
IntegrationRetryCallback retryCallback = new IntegrationRetryCallback(message, callback, target);
|
||||
RetryState retryState = this.retryStateGenerator.determineRetryState(message);
|
||||
try {
|
||||
return this.retryTemplate.execute(retryCallback, this.recoveryCallback, retryState);
|
||||
@@ -113,7 +114,7 @@ public class RequestHandlerRetryAdvice extends AbstractRequestHandlerAdvice {
|
||||
|
||||
}
|
||||
|
||||
private record IntegrationRetryCallback(Message<?> messageToTry, ExecutionCallback callback)
|
||||
private record IntegrationRetryCallback(Message<?> messageToTry, ExecutionCallback callback, Object target)
|
||||
implements RetryCallback<Object, Exception> {
|
||||
|
||||
@Override
|
||||
@@ -121,6 +122,13 @@ public class RequestHandlerRetryAdvice extends AbstractRequestHandlerAdvice {
|
||||
return this.callback.cloneAndExecute();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLabel() {
|
||||
return this.target instanceof AbstractReplyProducingMessageHandler.RequestHandler requestHandler
|
||||
? requestHandler.getAdvisedHandler().getComponentName()
|
||||
: this.target.getClass().getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2024 the original author or authors.
|
||||
* Copyright 2002-2025 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,9 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import io.micrometer.core.instrument.MeterRegistry;
|
||||
import io.micrometer.core.instrument.Timer;
|
||||
import io.micrometer.core.instrument.simple.SimpleMeterRegistry;
|
||||
import org.aopalliance.aop.Advice;
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
@@ -58,6 +61,7 @@ import org.springframework.messaging.support.GenericMessage;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.DefaultRetryState;
|
||||
import org.springframework.retry.support.MetricsRetryListener;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.scheduling.TaskScheduler;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
@@ -993,6 +997,46 @@ public class AdvisedMessageHandlerTests {
|
||||
assertThat(((ErrorMessage) error).getOriginalMessage().getPayload()).isEqualTo("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void retryAdviceWithMetricsListener() {
|
||||
AbstractReplyProducingMessageHandler handler = new AbstractReplyProducingMessageHandler() {
|
||||
|
||||
@Override
|
||||
protected Object handleRequestMessage(Message<?> requestMessage) {
|
||||
throw new RuntimeException("intentional");
|
||||
}
|
||||
};
|
||||
|
||||
MeterRegistry meterRegistry = new SimpleMeterRegistry();
|
||||
|
||||
RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.registerListener(new MetricsRetryListener(meterRegistry));
|
||||
advice.setRetryTemplate(retryTemplate);
|
||||
advice.setBeanFactory(mock(BeanFactory.class));
|
||||
advice.afterPropertiesSet();
|
||||
|
||||
List<Advice> adviceChain = new ArrayList<>();
|
||||
adviceChain.add(advice);
|
||||
handler.setAdviceChain(adviceChain);
|
||||
handler.setBeanName("testEndpoint");
|
||||
handler.setBeanFactory(mock(BeanFactory.class));
|
||||
handler.afterPropertiesSet();
|
||||
|
||||
Message<String> message = new GenericMessage<>("Hello, world!");
|
||||
assertThatExceptionOfType(MessagingException.class)
|
||||
.isThrownBy(() -> handler.handleMessage(message))
|
||||
.withRootCauseInstanceOf(RuntimeException.class)
|
||||
.withStackTraceContaining("intentional");
|
||||
|
||||
Timer retryTimer = meterRegistry.find(MetricsRetryListener.TIMER_NAME)
|
||||
.tag("name", "testEndpoint")
|
||||
.tag("retry.count", "3")
|
||||
.timer();
|
||||
|
||||
assertThat(retryTimer.count()).isEqualTo(1);
|
||||
}
|
||||
|
||||
private interface Bar {
|
||||
|
||||
Object handleRequestMessage(Message<?> message) throws Throwable;
|
||||
|
||||
Reference in New Issue
Block a user