Skip to content

Commit

Permalink
GH-9617: @SuppressWarnings("removal") for ListenableFuture
Browse files Browse the repository at this point in the history
Fixes: #9617
Issue link: #9617

The `ListenableFuture` is marked `forRemoval` in Spring Framework.
So, fix the code base to use `@SuppressWarnings("removal")`.
Also, add a warning into logs that `ListenableFuture` support will be removed in `7.0`.

* Fix JavaDocs where `ListenableFuture` is mentioned in favor of `CompletableFuture`
  • Loading branch information
artembilan committed Oct 30, 2024
1 parent f423280 commit b50c402
Show file tree
Hide file tree
Showing 7 changed files with 22 additions and 32 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ ext {
springKafkaVersion = '3.3.0-SNAPSHOT'
springRetryVersion = '2.0.10'
springSecurityVersion = '6.4.0-SNAPSHOT'
springVersion = '6.2.0-RC2'
springVersion = '6.2.0-SNAPSHOT'
springWsVersion = '4.0.11'
testcontainersVersion = '1.20.3'
tomcatVersion = '10.1.31'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,9 +363,9 @@ public void setShouldTrack(boolean shouldTrack) {

/**
* Set the executor for use when the gateway method returns
* {@link java.util.concurrent.Future} or {@link org.springframework.util.concurrent.ListenableFuture}.
* {@link Future} or {@link CompletableFuture}.
* Set it to null to disable the async processing, and any
* {@link java.util.concurrent.Future} return types must be returned by the downstream flow.
* {@link Future} return types must be returned by the downstream flow.
* @param executor The executor.
*/
public void setAsyncExecutor(@Nullable Executor executor) {
Expand Down Expand Up @@ -522,7 +522,7 @@ public T getObject() {

@Override
@Nullable
@SuppressWarnings("deprecation")
@SuppressWarnings("removal")
public Object invoke(final MethodInvocation invocation) throws Throwable { // NOSONAR
Method method = invocation.getMethod();
Class<?> returnType;
Expand All @@ -542,6 +542,9 @@ else if (CompletableFuture.class.equals(returnType)) { // exact
return CompletableFuture.supplyAsync(invoker, this.asyncExecutor);
}
else if (org.springframework.util.concurrent.ListenableFuture.class.equals(returnType)) {
logger.warn("The 'org.springframework.util.concurrent.ListenableFuture' is deprecated for removal." +
"The 'CompletableFuture' is recommended to be used instead." +
"The 'ListenableFuture' support will be removed in Spring Integration 7.0.");
return ((org.springframework.core.task.AsyncListenableTaskExecutor) this.asyncExecutor)
.submitListenable(invoker::get);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
* @author Marius Bogoevici
* @author Ngoc Nhan
*
* since 4.1
* @since 4.1
*/
public abstract class AbstractMessageProducingHandler extends AbstractMessageHandler
implements MessageProducer, HeaderPropagationAware {
Expand Down Expand Up @@ -321,7 +321,7 @@ else if (reply instanceof AbstractIntegrationMessageBuilder<?>) {
return replyChannel;
}

@SuppressWarnings("deprecation")
@SuppressWarnings("removal")
private void doProduceOutput(Message<?> requestMessage, MessageHeaders requestHeaders, Object reply,
@Nullable Object replyChannelArg) {

Expand Down Expand Up @@ -361,7 +361,7 @@ private void doProduceOutput(Message<?> requestMessage, MessageHeaders requestHe
sendOutput(createOutputMessage(reply, requestHeaders), replyChannel, false);
}

private static Publisher<?> toPublisherReply(Object reply, @Nullable ReactiveAdapter reactiveAdapter) {
private Publisher<?> toPublisherReply(Object reply, @Nullable ReactiveAdapter reactiveAdapter) {
if (reactiveAdapter != null) {
return reactiveAdapter.toPublisher(reply);
}
Expand All @@ -371,7 +371,7 @@ private static Publisher<?> toPublisherReply(Object reply, @Nullable ReactiveAda
}

@SuppressWarnings("try")
private static CompletableFuture<?> toFutureReply(Object reply, @Nullable ReactiveAdapter reactiveAdapter) {
private CompletableFuture<?> toFutureReply(Object reply, @Nullable ReactiveAdapter reactiveAdapter) {
if (reactiveAdapter != null) {
Mono<?> reactiveReply;
Publisher<?> publisher = reactiveAdapter.toPublisher(reply);
Expand Down Expand Up @@ -419,12 +419,15 @@ via whenComplete() callback. So, when value is set into the Future, it is availa
}
}

@SuppressWarnings("deprecation")
private static CompletableFuture<?> toCompletableFuture(Object reply) {
if (reply instanceof CompletableFuture<?>) {
return (CompletableFuture<?>) reply;
@SuppressWarnings("removal")
private CompletableFuture<?> toCompletableFuture(Object reply) {
if (reply instanceof CompletableFuture<?> completableFuture) {
return completableFuture;
}
else {
logger.warn("The 'org.springframework.util.concurrent.ListenableFuture' is deprecated for removal." +
"The 'CompletableFuture' is recommended to be used instead." +
"The 'ListenableFuture' support will be removed in Spring Integration 7.0.");
return ((org.springframework.util.concurrent.ListenableFuture<?>) reply).completable();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -444,14 +444,6 @@ public void testExecs() throws Exception {
assertThat(latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(result2.get().getName()).startsWith("exec-");

org.springframework.util.concurrent.ListenableFuture<Thread> result3 =
this.execGateway.test3(Thread.currentThread());
final CountDownLatch latch1 = new CountDownLatch(1);
result3.addCallback(data -> latch1.countDown(), ex -> {
});
assertThat(latch1.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(result3.get().getName()).startsWith("exec-");

/*
@IntegrationComponentScan(useDefaultFilters = false,
includeFilters = @ComponentScan.Filter(TestMessagingGateway.class))
Expand Down Expand Up @@ -770,10 +762,6 @@ public interface ExecGateway {
@Gateway(requestChannel = "gatewayThreadChannel")
CompletableFuture<Thread> test2(Thread caller);

@Gateway(requestChannel = "gatewayThreadChannel")
@SuppressWarnings("deprecation")
org.springframework.util.concurrent.ListenableFuture<Thread> test3(Thread caller);

}

@MessagingGateway(name = "noExecutorGateway", asyncExecutor = AnnotationConstants.NULL)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,18 +50,18 @@
* <p>
* An RSocket operation is determined by the configured {@link RSocketInteractionModel} or respective SpEL
* expression to be evaluated at runtime against the request message.
* By default the {@link RSocketInteractionModel#requestResponse} operation is used.
* By default, the {@link RSocketInteractionModel#requestResponse} operation is used.
* <p>
* For a {@link Publisher}-based requests, it must be present in the request message {@code payload}.
* The flattening via upstream {@link org.springframework.integration.channel.FluxMessageChannel} will work, too,
* but this way we will lose a scope of particular request and every {@link Publisher} event
* will be send in its own plain request.
* will be sent in its own plain request.
* <p>
* If reply is a {@link reactor.core.publisher.Flux}, it is wrapped to the {@link Mono} to retain a request scope.
* The downstream flow is responsible to obtain this {@link reactor.core.publisher.Flux} from a message payload
* and subscribe to it by itself. The {@link Mono} reply from this component is subscribed from the downstream
* {@link org.springframework.integration.channel.FluxMessageChannel} or it is adapted to the
* {@link org.springframework.util.concurrent.ListenableFuture} otherwise.
* {@link java.util.concurrent.CompletableFuture} otherwise.
*
* @author Artem Bilan
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,10 @@
import org.springframework.scheduling.TaskScheduler;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.concurrent.ListenableFuture;

/**
* Base {@link StompSessionManager} implementation to manage a single {@link StompSession}
* over its {@link ListenableFuture} from the target implementation of this class.
* over its {@link CompletableFuture} from the target implementation of this class.
* <p>
* The connection to the {@link StompSession} is made during {@link #start()}.
* <p>
Expand Down
3 changes: 0 additions & 3 deletions src/reference/antora/modules/ROOT/pages/gateway.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -618,9 +618,6 @@ There are two modes of operation when returning this type:
* When the async executor is explicitly set to `null` and the return type is `CompletableFuture` or the return type is a subclass of `CompletableFuture`, the flow is invoked on the caller's thread.
In this scenario, the downstream flow is expected to return a `CompletableFuture` of the appropriate type.

NOTE: The `org.springframework.util.concurrent.ListenableFuture` has been deprecated starting with Spring Framework `6.0`.
It is recommended now to migrate to the `CompletableFuture` which provides similar processing functionality.

[[usage-scenarios]]
==== Usage Scenarios

Expand Down

0 comments on commit b50c402

Please sign in to comment.