Remove APIs marked as deprecated for removal

Closes gh-33809
This commit is contained in:
Juergen Hoeller
2024-12-04 13:19:39 +01:00
parent 078d683f47
commit 2b9010c2a2
150 changed files with 141 additions and 5922 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -35,9 +35,10 @@ import org.springframework.util.ResourceUtils;
*
* @author Stephane Nicoll
* @author Sam Brannen
* @author Juergen Hoeller
* @since 6.0
*/
public class FilePatternResourceHintsRegistrar {
public final class FilePatternResourceHintsRegistrar {
private final List<String> classpathLocations;
@@ -46,26 +47,16 @@ public class FilePatternResourceHintsRegistrar {
private final List<String> fileExtensions;
/**
* Create a new instance for the specified file prefixes, classpath locations,
* and file extensions.
* @param filePrefixes the file prefixes
* @param classpathLocations the classpath locations
* @param fileExtensions the file extensions (starting with a dot)
* @deprecated as of 6.0.12 in favor of {@linkplain #forClassPathLocations(String...) the builder}
*/
@Deprecated(since = "6.0.12", forRemoval = true)
public FilePatternResourceHintsRegistrar(List<String> filePrefixes, List<String> classpathLocations,
private FilePatternResourceHintsRegistrar(List<String> filePrefixes, List<String> classpathLocations,
List<String> fileExtensions) {
this.classpathLocations = validateClasspathLocations(classpathLocations);
this.classpathLocations = validateClassPathLocations(classpathLocations);
this.filePrefixes = validateFilePrefixes(filePrefixes);
this.fileExtensions = validateFileExtensions(fileExtensions);
}
@Deprecated(since = "6.0.12", forRemoval = true)
public void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
private void registerHints(ResourceHints hints, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = (classLoader != null ? classLoader : getClass().getClassLoader());
List<String> includes = new ArrayList<>();
for (String location : this.classpathLocations) {
@@ -85,7 +76,7 @@ public class FilePatternResourceHintsRegistrar {
/**
* Configure the registrar with the specified
* {@linkplain Builder#withClasspathLocations(String...) classpath locations}.
* {@linkplain Builder#withClassPathLocations(String...) classpath locations}.
* @param classpathLocations the classpath locations
* @return a {@link Builder} to further configure the registrar
* @since 6.0.12
@@ -97,17 +88,17 @@ public class FilePatternResourceHintsRegistrar {
/**
* Configure the registrar with the specified
* {@linkplain Builder#withClasspathLocations(List) classpath locations}.
* {@linkplain Builder#withClassPathLocations(List) classpath locations}.
* @param classpathLocations the classpath locations
* @return a {@link Builder} to further configure the registrar
* @since 6.0.12
* @see #forClassPathLocations(String...)
*/
public static Builder forClassPathLocations(List<String> classpathLocations) {
return new Builder().withClasspathLocations(classpathLocations);
return new Builder().withClassPathLocations(classpathLocations);
}
private static List<String> validateClasspathLocations(List<String> classpathLocations) {
private static List<String> validateClassPathLocations(List<String> classpathLocations) {
Assert.notEmpty(classpathLocations, "At least one classpath location must be specified");
List<String> parsedLocations = new ArrayList<>();
for (String location : classpathLocations) {
@@ -162,15 +153,20 @@ public class FilePatternResourceHintsRegistrar {
/**
* Consider the specified classpath locations.
* <p>A location can either be a special {@value ResourceUtils#CLASSPATH_URL_PREFIX}
* pseudo location or a standard location, such as {@code com/example/resources}.
* An empty String represents the root of the classpath.
* @param classpathLocations the classpath locations to consider
* @return this builder
* @see #withClasspathLocations(List)
* @deprecated in favor of {@link #withClassPathLocations(String...)}
*/
@Deprecated(since = "7.0", forRemoval = true)
public Builder withClasspathLocations(String... classpathLocations) {
return withClasspathLocations(Arrays.asList(classpathLocations));
return withClassPathLocations(Arrays.asList(classpathLocations));
}
/**
* Consider the specified classpath locations.
* @deprecated in favor of {@link #withClassPathLocations(List)}
*/
@Deprecated(since = "7.0", forRemoval = true)
public Builder withClasspathLocations(List<String> classpathLocations) {
return withClassPathLocations(classpathLocations);
}
/**
@@ -180,10 +176,25 @@ public class FilePatternResourceHintsRegistrar {
* An empty String represents the root of the classpath.
* @param classpathLocations the classpath locations to consider
* @return this builder
* @see #withClasspathLocations(String...)
* @since 7.0
* @see #withClassPathLocations(List)
*/
public Builder withClasspathLocations(List<String> classpathLocations) {
this.classpathLocations.addAll(validateClasspathLocations(classpathLocations));
public Builder withClassPathLocations(String... classpathLocations) {
return withClassPathLocations(Arrays.asList(classpathLocations));
}
/**
* Consider the specified classpath locations.
* <p>A location can either be a special {@value ResourceUtils#CLASSPATH_URL_PREFIX}
* pseudo location or a standard location, such as {@code com/example/resources}.
* An empty String represents the root of the classpath.
* @param classpathLocations the classpath locations to consider
* @return this builder
* @since 7.0
* @see #withClassPathLocations(String...)
*/
public Builder withClassPathLocations(List<String> classpathLocations) {
this.classpathLocations.addAll(validateClassPathLocations(classpathLocations));
return this;
}
@@ -235,7 +246,6 @@ public class FilePatternResourceHintsRegistrar {
return this;
}
private FilePatternResourceHintsRegistrar build() {
return new FilePatternResourceHintsRegistrar(this.filePrefixes,
this.classpathLocations, this.fileExtensions);

View File

@@ -1,58 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core.task;
import java.util.concurrent.Callable;
/**
* Extension of the {@link AsyncTaskExecutor} interface, adding the capability to submit
* tasks for {@code ListenableFutures}.
*
* @author Arjen Poutsma
* @since 4.0
* @deprecated as of 6.0, in favor of
* {@link AsyncTaskExecutor#submitCompletable(Runnable)} and
* {@link AsyncTaskExecutor#submitCompletable(Callable)}
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public interface AsyncListenableTaskExecutor extends AsyncTaskExecutor {
/**
* Submit a {@code Runnable} task for execution, receiving a {@code ListenableFuture}
* representing that task. The Future will return a {@code null} result upon completion.
* @param task the {@code Runnable} to execute (never {@code null})
* @return a {@code ListenableFuture} representing pending completion of the task
* @throws TaskRejectedException if the given task was not accepted
* @deprecated in favor of {@link AsyncTaskExecutor#submitCompletable(Runnable)}
*/
@Deprecated(since = "6.0", forRemoval = true)
org.springframework.util.concurrent.ListenableFuture<?> submitListenable(Runnable task);
/**
* Submit a {@code Callable} task for execution, receiving a {@code ListenableFuture}
* representing that task. The Future will return the Callable's result upon
* completion.
* @param task the {@code Callable} to execute (never {@code null})
* @return a {@code ListenableFuture} representing pending completion of the task
* @throws TaskRejectedException if the given task was not accepted
* @deprecated in favor of {@link AsyncTaskExecutor#submitCompletable(Callable)}
*/
@Deprecated(since = "6.0", forRemoval = true)
<T> org.springframework.util.concurrent.ListenableFuture<T> submitListenable(Callable<T> task);
}

View File

@@ -28,8 +28,6 @@ import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ConcurrencyThrottleSupport;
import org.springframework.util.CustomizableThreadCreator;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
/**
* {@link TaskExecutor} implementation that fires up a new Thread for each task,
@@ -58,9 +56,9 @@ import org.springframework.util.concurrent.ListenableFutureTask;
* @see org.springframework.scheduling.concurrent.SimpleAsyncTaskScheduler
* @see org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor
*/
@SuppressWarnings({"serial", "removal"})
@SuppressWarnings("serial")
public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
implements AsyncListenableTaskExecutor, Serializable, AutoCloseable {
implements AsyncTaskExecutor, Serializable, AutoCloseable {
/**
* Permit any number of concurrent invocations: that is, don't throttle concurrency.
@@ -294,22 +292,6 @@ public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator
return future;
}
@SuppressWarnings("deprecation")
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
ListenableFutureTask<Object> future = new ListenableFutureTask<>(task, null);
execute(future, TIMEOUT_INDEFINITE);
return future;
}
@SuppressWarnings("deprecation")
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
ListenableFutureTask<T> future = new ListenableFutureTask<>(task);
execute(future, TIMEOUT_INDEFINITE);
return future;
}
/**
* Template method for the actual execution of a task.
* <p>The default implementation creates a new Thread and starts it.

View File

@@ -23,13 +23,11 @@ import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.RejectedExecutionException;
import org.springframework.core.task.AsyncListenableTaskExecutor;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.core.task.TaskDecorator;
import org.springframework.core.task.TaskRejectedException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureTask;
/**
* Adapter that takes a JDK {@code java.util.concurrent.Executor} and
@@ -43,8 +41,8 @@ import org.springframework.util.concurrent.ListenableFutureTask;
* @see java.util.concurrent.ExecutorService
* @see java.util.concurrent.Executors
*/
@SuppressWarnings({"deprecation", "removal"})
public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
@SuppressWarnings("deprecation")
public class TaskExecutorAdapter implements AsyncTaskExecutor {
private final Executor concurrentExecutor;
@@ -133,30 +131,6 @@ public class TaskExecutorAdapter implements AsyncListenableTaskExecutor {
}
}
@Override
public ListenableFuture<?> submitListenable(Runnable task) {
try {
ListenableFutureTask<Object> future = new ListenableFutureTask<>(task, null);
doExecute(this.concurrentExecutor, this.taskDecorator, future);
return future;
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException(this.concurrentExecutor, task, ex);
}
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
try {
ListenableFutureTask<T> future = new ListenableFutureTask<>(task);
doExecute(this.concurrentExecutor, this.taskDecorator, future);
return future;
}
catch (RejectedExecutionException ex) {
throw new TaskRejectedException(this.concurrentExecutor, task, ex);
}
}
/**
* Actually execute the given {@code Runnable} (which may be a user-supplied task

View File

@@ -23,7 +23,6 @@ import java.nio.charset.Charset;
import java.util.BitSet;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
@@ -695,48 +694,4 @@ public class MimeType implements Comparable<MimeType>, Serializable {
return map;
}
/**
* Comparator to sort {@link MimeType MimeTypes} in order of specificity.
*
* @param <T> the type of mime types that may be compared by this comparator
* @deprecated As of 6.0, with no direct replacement
*/
@Deprecated(since = "6.0", forRemoval = true)
public static class SpecificityComparator<T extends MimeType> implements Comparator<T> {
@Override
public int compare(T mimeType1, T mimeType2) {
if (mimeType1.isWildcardType() && !mimeType2.isWildcardType()) { // */* < audio/*
return 1;
}
else if (mimeType2.isWildcardType() && !mimeType1.isWildcardType()) { // audio/* > */*
return -1;
}
else if (!mimeType1.getType().equals(mimeType2.getType())) { // audio/basic == text/html
return 0;
}
else { // mediaType1.getType().equals(mediaType2.getType())
if (mimeType1.isWildcardSubtype() && !mimeType2.isWildcardSubtype()) { // audio/* < audio/basic
return 1;
}
else if (mimeType2.isWildcardSubtype() && !mimeType1.isWildcardSubtype()) { // audio/basic > audio/*
return -1;
}
else if (!mimeType1.getSubtype().equals(mimeType2.getSubtype())) { // audio/basic == audio/wave
return 0;
}
else { // mediaType2.getSubtype().equals(mediaType2.getSubtype())
return compareParameters(mimeType1, mimeType2);
}
}
}
protected int compareParameters(T mimeType1, T mimeType2) {
int paramsSize1 = mimeType1.getParameters().size();
int paramsSize2 = mimeType2.getParameters().size();
return Integer.compare(paramsSize2, paramsSize1); // audio/basic;level=1 < audio/basic
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -22,7 +22,6 @@ import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
@@ -51,14 +50,6 @@ public abstract class MimeTypeUtils {
'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U',
'V', 'W', 'X', 'Y', 'Z'};
/**
* Comparator formally used by {@link #sortBySpecificity(List)}.
* @deprecated As of 6.0, with no direct replacement
*/
@SuppressWarnings("removal")
@Deprecated(since = "6.0", forRemoval = true)
public static final Comparator<MimeType> SPECIFICITY_COMPARATOR = new MimeType.SpecificityComparator<>();
/**
* Public constant mime type that includes all media ranges (i.e. "&#42;/&#42;").
*/

View File

@@ -49,24 +49,6 @@ public class PropertyPlaceholderHelper {
this(placeholderPrefix, placeholderSuffix, null, null, true);
}
/**
* Create a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and suffix.
* @param placeholderPrefix the prefix that denotes the start of a placeholder
* @param placeholderSuffix the suffix that denotes the end of a placeholder
* @param valueSeparator the separating character between the placeholder variable
* and the associated default value, if any
* @param ignoreUnresolvablePlaceholders indicates whether unresolvable placeholders should
* be ignored ({@code true}) or cause an exception ({@code false})
* @deprecated as of 6.2, in favor of
* {@link PropertyPlaceholderHelper#PropertyPlaceholderHelper(String, String, String, Character, boolean)}
*/
@Deprecated(since = "6.2", forRemoval = true)
public PropertyPlaceholderHelper(String placeholderPrefix, String placeholderSuffix,
@Nullable String valueSeparator, boolean ignoreUnresolvablePlaceholders) {
this(placeholderPrefix, placeholderSuffix, valueSeparator, null, ignoreUnresolvablePlaceholders);
}
/**
* Create a new {@code PropertyPlaceholderHelper} that uses the supplied prefix and suffix.
* @param placeholderPrefix the prefix that denotes the start of a placeholder

View File

@@ -1,110 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
/**
* Adapts a {@link CompletableFuture} or {@link CompletionStage} into a
* Spring {@link ListenableFuture}.
*
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @since 4.2
* @param <T> the result type returned by this Future's {@code get} method
* @deprecated as of 6.0, with no concrete replacement
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public class CompletableToListenableFutureAdapter<T> implements ListenableFuture<T> {
private final CompletableFuture<T> completableFuture;
private final ListenableFutureCallbackRegistry<T> callbacks = new ListenableFutureCallbackRegistry<>();
/**
* Create a new adapter for the given {@link CompletionStage}.
* @since 4.3.7
*/
public CompletableToListenableFutureAdapter(CompletionStage<T> completionStage) {
this(completionStage.toCompletableFuture());
}
/**
* Create a new adapter for the given {@link CompletableFuture}.
*/
public CompletableToListenableFutureAdapter(CompletableFuture<T> completableFuture) {
this.completableFuture = completableFuture;
this.completableFuture.whenComplete((result, ex) -> {
if (ex != null) {
this.callbacks.failure(ex);
}
else {
this.callbacks.success(result);
}
});
}
@Override
public void addCallback(ListenableFutureCallback<? super T> callback) {
this.callbacks.addCallback(callback);
}
@Override
public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
this.callbacks.addSuccessCallback(successCallback);
this.callbacks.addFailureCallback(failureCallback);
}
@Override
public CompletableFuture<T> completable() {
return this.completableFuture;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
return this.completableFuture.cancel(mayInterruptIfRunning);
}
@Override
public boolean isCancelled() {
return this.completableFuture.isCancelled();
}
@Override
public boolean isDone() {
return this.completableFuture.isDone();
}
@Override
public T get() throws InterruptedException, ExecutionException {
return this.completableFuture.get();
}
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return this.completableFuture.get(timeout, unit);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.function.BiConsumer;
/**
* Failure callback for a {@link ListenableFuture}.
*
* @author Sebastien Deleuze
* @since 4.1
* @deprecated as of 6.0, in favor of
* {@link java.util.concurrent.CompletableFuture#whenComplete(BiConsumer)}
*/
@Deprecated(since = "6.0", forRemoval = true)
@FunctionalInterface
public interface FailureCallback {
/**
* Called when the {@link ListenableFuture} completes with failure.
* <p>Note that Exceptions raised by this method are ignored.
* @param ex the failure
*/
void onFailure(Throwable ex);
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2002-2022 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;
import java.util.function.BiConsumer;
/**
* Extend {@link Future} with the capability to accept completion callbacks.
* If the future has completed when the callback is added, the callback is
* triggered immediately.
*
* <p>Inspired by {@code com.google.common.util.concurrent.ListenableFuture}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Juergen Hoeller
* @since 4.0
* @param <T> the result type returned by this Future's {@code get} method
* @deprecated as of 6.0, in favor of {@link CompletableFuture}
*/
@Deprecated(since = "6.0", forRemoval = true)
public interface ListenableFuture<T> extends Future<T> {
/**
* Register the given {@code ListenableFutureCallback}.
* @param callback the callback to register
* @deprecated as of 6.0, in favor of
* {@link CompletableFuture#whenComplete(BiConsumer)}
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
void addCallback(ListenableFutureCallback<? super T> callback);
/**
* Java 8 lambda-friendly alternative with success and failure callbacks.
* @param successCallback the success callback
* @param failureCallback the failure callback
* @since 4.1
* @deprecated as of 6.0, in favor of
* {@link CompletableFuture#whenComplete(BiConsumer)}
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback);
/**
* Expose this {@link ListenableFuture} as a JDK {@link CompletableFuture}.
* @since 5.0
*/
@SuppressWarnings("NullAway")
default CompletableFuture<T> completable() {
CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
addCallback(completable::complete, completable::completeExceptionally);
return completable;
}
}

View File

@@ -1,86 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.concurrent.ExecutionException;
import org.springframework.lang.Nullable;
/**
* Abstract class that adapts a {@link ListenableFuture} parameterized over S into a
* {@code ListenableFuture} parameterized over T. All methods are delegated to the
* adaptee, where {@link #get()}, {@link #get(long, java.util.concurrent.TimeUnit)},
* and {@link ListenableFutureCallback#onSuccess(Object)} call {@link #adapt(Object)}
* on the adaptee's result.
*
* @author Arjen Poutsma
* @since 4.0
* @param <T> the type of this {@code Future}
* @param <S> the type of the adaptee's {@code Future}
* @deprecated as of 6.0, in favor of
* {@link java.util.concurrent.CompletableFuture}
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public abstract class ListenableFutureAdapter<T, S> extends FutureAdapter<T, S> implements ListenableFuture<T> {
/**
* Construct a new {@code ListenableFutureAdapter} with the given adaptee.
* @param adaptee the future to adapt to
*/
protected ListenableFutureAdapter(ListenableFuture<S> adaptee) {
super(adaptee);
}
@Override
public void addCallback(final ListenableFutureCallback<? super T> callback) {
addCallback(callback, callback);
}
@Override
public void addCallback(final SuccessCallback<? super T> successCallback, final FailureCallback failureCallback) {
ListenableFuture<S> listenableAdaptee = (ListenableFuture<S>) getAdaptee();
listenableAdaptee.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(@Nullable S result) {
T adapted = null;
if (result != null) {
try {
adapted = adaptInternal(result);
}
catch (ExecutionException ex) {
Throwable cause = ex.getCause();
onFailure(cause != null ? cause : ex);
return;
}
catch (Throwable ex) {
onFailure(ex);
return;
}
}
successCallback.onSuccess(adapted);
}
@Override
public void onFailure(Throwable ex) {
failureCallback.onFailure(ex);
}
});
}
}

View File

@@ -1,36 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.function.BiConsumer;
/**
* Callback mechanism for the outcome, success or failure, from a
* {@link ListenableFuture}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @since 4.0
* @param <T> the result type
* @deprecated as of 6.0, in favor of
* {@link java.util.concurrent.CompletableFuture#whenComplete(BiConsumer)}
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public interface ListenableFutureCallback<T> extends SuccessCallback<T>, FailureCallback {
}

View File

@@ -1,157 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.ArrayDeque;
import java.util.Queue;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* Helper class for {@link ListenableFuture} implementations that maintains a queue
* of success and failure callbacks and helps to notify them.
*
* <p>Inspired by {@code com.google.common.util.concurrent.ExecutionList}.
*
* @author Arjen Poutsma
* @author Sebastien Deleuze
* @author Rossen Stoyanchev
* @since 4.0
* @param <T> the callback result type
* @deprecated as of 6.0, with no concrete replacement
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public class ListenableFutureCallbackRegistry<T> {
private final Queue<SuccessCallback<? super T>> successCallbacks = new ArrayDeque<>(1);
private final Queue<FailureCallback> failureCallbacks = new ArrayDeque<>(1);
private State state = State.NEW;
@Nullable
private Object result;
private final Object mutex = new Object();
/**
* Add the given callback to this registry.
* @param callback the callback to add
*/
public void addCallback(ListenableFutureCallback<? super T> callback) {
Assert.notNull(callback, "'callback' must not be null");
synchronized (this.mutex) {
switch (this.state) {
case NEW -> {
this.successCallbacks.add(callback);
this.failureCallbacks.add(callback);
}
case SUCCESS -> notifySuccess(callback);
case FAILURE -> notifyFailure(callback);
}
}
}
@SuppressWarnings("unchecked")
private void notifySuccess(SuccessCallback<? super T> callback) {
try {
callback.onSuccess((T) this.result);
}
catch (Throwable ex) {
// Ignore
}
}
private void notifyFailure(FailureCallback callback) {
Assert.state(this.result instanceof Throwable, "No Throwable result for failure state");
try {
callback.onFailure((Throwable) this.result);
}
catch (Throwable ex) {
// Ignore
}
}
/**
* Add the given success callback to this registry.
* @param callback the success callback to add
* @since 4.1
*/
public void addSuccessCallback(SuccessCallback<? super T> callback) {
Assert.notNull(callback, "'callback' must not be null");
synchronized (this.mutex) {
switch (this.state) {
case NEW -> this.successCallbacks.add(callback);
case SUCCESS -> notifySuccess(callback);
}
}
}
/**
* Add the given failure callback to this registry.
* @param callback the failure callback to add
* @since 4.1
*/
public void addFailureCallback(FailureCallback callback) {
Assert.notNull(callback, "'callback' must not be null");
synchronized (this.mutex) {
switch (this.state) {
case NEW -> this.failureCallbacks.add(callback);
case FAILURE -> notifyFailure(callback);
}
}
}
/**
* Trigger a {@link ListenableFutureCallback#onSuccess(Object)} call on all
* added callbacks with the given result.
* @param result the result to trigger the callbacks with
*/
public void success(@Nullable T result) {
synchronized (this.mutex) {
this.state = State.SUCCESS;
this.result = result;
SuccessCallback<? super T> callback;
while ((callback = this.successCallbacks.poll()) != null) {
notifySuccess(callback);
}
}
}
/**
* Trigger a {@link ListenableFutureCallback#onFailure(Throwable)} call on all
* added callbacks with the given {@code Throwable}.
* @param ex the exception to trigger the callbacks with
*/
public void failure(Throwable ex) {
synchronized (this.mutex) {
this.state = State.FAILURE;
this.result = ex;
FailureCallback callback;
while ((callback = this.failureCallbacks.poll()) != null) {
notifyFailure(callback);
}
}
}
private enum State {NEW, SUCCESS, FAILURE}
}

View File

@@ -1,107 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import org.springframework.lang.Nullable;
/**
* Extension of {@link FutureTask} that implements {@link ListenableFuture}.
*
* @author Arjen Poutsma
* @since 4.0
* @param <T> the result type returned by this Future's {@code get} method
* @deprecated as of 6.0, with no concrete replacement
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public class ListenableFutureTask<T> extends FutureTask<T> implements ListenableFuture<T> {
private final ListenableFutureCallbackRegistry<T> callbacks = new ListenableFutureCallbackRegistry<>();
/**
* Create a new {@code ListenableFutureTask} that will, upon running,
* execute the given {@link Callable}.
* @param callable the callable task
*/
public ListenableFutureTask(Callable<T> callable) {
super(callable);
}
/**
* Create a {@code ListenableFutureTask} that will, upon running,
* execute the given {@link Runnable}, and arrange that {@link #get()}
* will return the given result on successful completion.
* @param runnable the runnable task
* @param result the result to return on successful completion
*/
public ListenableFutureTask(Runnable runnable, @Nullable T result) {
super(runnable, result);
}
@Override
public void addCallback(ListenableFutureCallback<? super T> callback) {
this.callbacks.addCallback(callback);
}
@Override
public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
this.callbacks.addSuccessCallback(successCallback);
this.callbacks.addFailureCallback(failureCallback);
}
@Override
@SuppressWarnings("NullAway")
public CompletableFuture<T> completable() {
CompletableFuture<T> completable = new DelegatingCompletableFuture<>(this);
this.callbacks.addSuccessCallback(completable::complete);
this.callbacks.addFailureCallback(completable::completeExceptionally);
return completable;
}
@Override
protected void done() {
Throwable cause;
try {
T result = get();
this.callbacks.success(result);
return;
}
catch (InterruptedException ex) {
Thread.currentThread().interrupt();
return;
}
catch (ExecutionException ex) {
cause = ex.getCause();
if (cause == null) {
cause = ex;
}
}
catch (Throwable ex) {
cause = ex;
}
this.callbacks.failure(cause);
}
}

View File

@@ -1,40 +0,0 @@
/*
* Copyright 2002-2022 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import reactor.core.publisher.Mono;
/**
* Adapts a {@link Mono} into a {@link ListenableFuture} by obtaining a
* {@code CompletableFuture} from the {@code Mono} via {@link Mono#toFuture()}
* and then adapting it with {@link CompletableToListenableFutureAdapter}.
*
* @author Rossen Stoyanchev
* @author Stephane Maldini
* @since 5.1
* @param <T> the object type
* @deprecated as of 6.0, in favor of {@link Mono#toFuture()}
*/
@Deprecated(since = "6.0")
@SuppressWarnings("removal")
public class MonoToListenableFutureAdapter<T> extends CompletableToListenableFutureAdapter<T> {
public MonoToListenableFutureAdapter(Mono<T> mono) {
super(mono.toFuture());
}
}

View File

@@ -1,192 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
/**
* A {@link ListenableFuture} whose value can be set via {@link #set(Object)}
* or {@link #setException(Throwable)}. It may also get cancelled.
*
* <p>Inspired by {@code com.google.common.util.concurrent.SettableFuture}.
*
* @author Mattias Severson
* @author Rossen Stoyanchev
* @author Juergen Hoeller
* @since 4.1
* @param <T> the result type returned by this Future's {@code get} method
* @deprecated as of 6.0, in favor of {@link CompletableFuture}
*/
@Deprecated(since = "6.0", forRemoval = true)
@SuppressWarnings("removal")
public class SettableListenableFuture<T> implements ListenableFuture<T> {
private static final Callable<Object> DUMMY_CALLABLE = () -> {
throw new IllegalStateException("Should never be called");
};
private final SettableTask<T> settableTask = new SettableTask<>();
/**
* Set the value of this future. This method will return {@code true} if the
* value was set successfully, or {@code false} if the future has already been
* set or cancelled.
* @param value the value that will be set
* @return {@code true} if the value was successfully set, else {@code false}
*/
public boolean set(@Nullable T value) {
return this.settableTask.setResultValue(value);
}
/**
* Set the exception of this future. This method will return {@code true} if the
* exception was set successfully, or {@code false} if the future has already been
* set or cancelled.
* @param exception the value that will be set
* @return {@code true} if the exception was successfully set, else {@code false}
*/
public boolean setException(Throwable exception) {
Assert.notNull(exception, "Exception must not be null");
return this.settableTask.setExceptionResult(exception);
}
@Override
public void addCallback(ListenableFutureCallback<? super T> callback) {
this.settableTask.addCallback(callback);
}
@Override
public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
this.settableTask.addCallback(successCallback, failureCallback);
}
@Override
public CompletableFuture<T> completable() {
return this.settableTask.completable();
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
boolean cancelled = this.settableTask.cancel(mayInterruptIfRunning);
if (cancelled && mayInterruptIfRunning) {
interruptTask();
}
return cancelled;
}
@Override
public boolean isCancelled() {
return this.settableTask.isCancelled();
}
@Override
public boolean isDone() {
return this.settableTask.isDone();
}
/**
* Retrieve the value.
* <p>This method returns the value if it has been set via {@link #set(Object)},
* throws an {@link java.util.concurrent.ExecutionException} if an exception has
* been set via {@link #setException(Throwable)}, or throws a
* {@link java.util.concurrent.CancellationException} if the future has been cancelled.
* @return the value associated with this future
*/
@Nullable
@Override
public T get() throws InterruptedException, ExecutionException {
return this.settableTask.get();
}
/**
* Retrieve the value.
* <p>This method returns the value if it has been set via {@link #set(Object)},
* throws an {@link java.util.concurrent.ExecutionException} if an exception has
* been set via {@link #setException(Throwable)}, or throws a
* {@link java.util.concurrent.CancellationException} if the future has been cancelled.
* @param timeout the maximum time to wait
* @param unit the unit of the timeout argument
* @return the value associated with this future
*/
@Nullable
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
return this.settableTask.get(timeout, unit);
}
/**
* Subclasses can override this method to implement interruption of the future's
* computation. The method is invoked automatically by a successful call to
* {@link #cancel(boolean) cancel(true)}.
* <p>The default implementation is empty.
*/
protected void interruptTask() {
}
private static class SettableTask<T> extends ListenableFutureTask<T> {
@Nullable
private volatile Thread completingThread;
@SuppressWarnings("unchecked")
public SettableTask() {
super((Callable<T>) DUMMY_CALLABLE);
}
public boolean setResultValue(@Nullable T value) {
set(value);
return checkCompletingThread();
}
public boolean setExceptionResult(Throwable exception) {
setException(exception);
return checkCompletingThread();
}
@Override
protected void done() {
if (!isCancelled()) {
// Implicitly invoked by set/setException: store current thread for
// determining whether the given result has actually triggered completion
// (since FutureTask.set/setException unfortunately don't expose that)
this.completingThread = Thread.currentThread();
}
super.done();
}
private boolean checkCompletingThread() {
boolean check = (this.completingThread == Thread.currentThread());
if (check) {
this.completingThread = null; // only first match actually counts
}
return check;
}
}
}

View File

@@ -1,43 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.function.BiConsumer;
import org.springframework.lang.Nullable;
/**
* Success callback for a {@link ListenableFuture}.
*
* @author Sebastien Deleuze
* @since 4.1
* @param <T> the result type
* @deprecated as of 6.0, in favor of
* {@link java.util.concurrent.CompletableFuture#whenComplete(BiConsumer)}
*/
@Deprecated(since = "6.0", forRemoval = true)
@FunctionalInterface
public interface SuccessCallback<T> {
/**
* Called when the {@link ListenableFuture} completes with success.
* <p>Note that Exceptions raised by this method are ignored.
* @param result the result
*/
void onSuccess(@Nullable T result);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2023 the original author or authors.
* Copyright 2002-2024 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.
@@ -78,7 +78,7 @@ class FilePatternResourceHintsRegistrarTests {
@Test
void registerWithMultipleClasspathLocations() {
FilePatternResourceHintsRegistrar.forClassPathLocations("").withClasspathLocations("META-INF")
FilePatternResourceHintsRegistrar.forClassPathLocations("").withClassPathLocations("META-INF")
.withFilePrefixes("test").withFileExtensions(".txt")
.registerHints(this.hints, null);
assertThat(this.hints.resourcePatternHints()).singleElement()
@@ -133,7 +133,7 @@ class FilePatternResourceHintsRegistrarTests {
@Test
void registerWithNonExistingLocationDoesNotRegisterHint() {
FilePatternResourceHintsRegistrar.forClassPathLocations("does-not-exist/")
.withClasspathLocations("another-does-not-exist/")
.withClassPathLocations("another-does-not-exist/")
.withFilePrefixes("test").withFileExtensions(".txt")
.registerHints(this.hints, null);
assertThat(this.hints.resourcePatternHints()).isEmpty();

View File

@@ -1,138 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.io.IOException;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
/**
* @author Arjen Poutsma
* @author Sebastien Deleuze
*/
@SuppressWarnings({"deprecation", "removal"})
class ListenableFutureTaskTests {
@Test
void success() throws Exception {
final String s = "Hello World";
Callable<String> callable = () -> s;
ListenableFutureTask<String> task = new ListenableFutureTask<>(callable);
task.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(String result) {
assertThat(result).isEqualTo(s);
}
@Override
public void onFailure(Throwable ex) {
throw new AssertionError(ex.getMessage(), ex);
}
});
task.run();
assertThat(task.get()).isSameAs(s);
assertThat(task.completable().get()).isSameAs(s);
task.completable().thenAccept(v -> assertThat(v).isSameAs(s));
}
@Test
void failure() {
final String s = "Hello World";
Callable<String> callable = () -> {
throw new IOException(s);
};
ListenableFutureTask<String> task = new ListenableFutureTask<>(callable);
task.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(String result) {
fail("onSuccess not expected");
}
@Override
public void onFailure(Throwable ex) {
assertThat(ex.getMessage()).isEqualTo(s);
}
});
task.run();
assertThatExceptionOfType(ExecutionException.class)
.isThrownBy(task::get)
.havingCause()
.withMessage(s);
assertThatExceptionOfType(ExecutionException.class)
.isThrownBy(task.completable()::get)
.havingCause()
.withMessage(s);
}
@Test
void successWithLambdas() throws Exception {
final String s = "Hello World";
Callable<String> callable = () -> s;
SuccessCallback<String> successCallback = mock();
FailureCallback failureCallback = mock();
ListenableFutureTask<String> task = new ListenableFutureTask<>(callable);
task.addCallback(successCallback, failureCallback);
task.run();
verify(successCallback).onSuccess(s);
verifyNoInteractions(failureCallback);
assertThat(task.get()).isSameAs(s);
assertThat(task.completable().get()).isSameAs(s);
task.completable().thenAccept(v -> assertThat(v).isSameAs(s));
}
@Test
void failureWithLambdas() {
final String s = "Hello World";
IOException ex = new IOException(s);
Callable<String> callable = () -> {
throw ex;
};
SuccessCallback<String> successCallback = mock();
FailureCallback failureCallback = mock();
ListenableFutureTask<String> task = new ListenableFutureTask<>(callable);
task.addCallback(successCallback, failureCallback);
task.run();
verify(failureCallback).onFailure(ex);
verifyNoInteractions(successCallback);
assertThatExceptionOfType(ExecutionException.class)
.isThrownBy(task::get)
.havingCause()
.withMessage(s);
assertThatExceptionOfType(ExecutionException.class)
.isThrownBy(task.completable()::get)
.havingCause()
.withMessage(s);
}
}

View File

@@ -1,74 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.time.Duration;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Mono;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link MonoToListenableFutureAdapter}.
*
* @author Rossen Stoyanchev
*/
@SuppressWarnings({"deprecation", "removal"})
class MonoToListenableFutureAdapterTests {
@Test
void success() {
String expected = "one";
AtomicReference<Object> actual = new AtomicReference<>();
ListenableFuture<String> future = new MonoToListenableFutureAdapter<>(Mono.just(expected));
future.addCallback(actual::set, actual::set);
assertThat(actual.get()).isEqualTo(expected);
}
@Test
@SuppressWarnings("deprecation")
void failure() {
Throwable expected = new IllegalStateException("oops");
AtomicReference<Object> actual = new AtomicReference<>();
ListenableFuture<String> future = new MonoToListenableFutureAdapter<>(Mono.error(expected));
future.addCallback(actual::set, actual::set);
assertThat(actual.get()).isEqualTo(expected);
}
@Test
void cancellation() {
Mono<Long> mono = Mono.delay(Duration.ofSeconds(60));
Future<Long> future = new MonoToListenableFutureAdapter<>(mono);
assertThat(future.cancel(true)).isTrue();
assertThat(future.isCancelled()).isTrue();
}
@Test
void cancellationAfterTerminated() {
Future<Void> future = new MonoToListenableFutureAdapter<>(Mono.empty());
assertThat(future.cancel(true)).as("Should return false if task already completed").isFalse();
assertThat(future.isCancelled()).isFalse();
}
}

View File

@@ -1,414 +0,0 @@
/*
* Copyright 2002-2024 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.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.util.concurrent;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.fail;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
/**
* @author Mattias Severson
* @author Juergen Hoeller
*/
@SuppressWarnings({"deprecation", "removal"})
class SettableListenableFutureTests {
private final SettableListenableFuture<String> settableListenableFuture = new SettableListenableFuture<>();
@Test
void validateInitialValues() {
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isFalse();
}
@Test
void returnsSetValue() throws ExecutionException, InterruptedException {
String string = "hello";
assertThat(settableListenableFuture.set(string)).isTrue();
assertThat(settableListenableFuture.get()).isEqualTo(string);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void returnsSetValueFromCompletable() throws ExecutionException, InterruptedException {
String string = "hello";
assertThat(settableListenableFuture.set(string)).isTrue();
Future<String> completable = settableListenableFuture.completable();
assertThat(completable.get()).isEqualTo(string);
assertThat(completable.isCancelled()).isFalse();
assertThat(completable.isDone()).isTrue();
}
@Test
void setValueUpdatesDoneStatus() {
settableListenableFuture.set("hello");
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void throwsSetExceptionWrappedInExecutionException() {
Throwable exception = new RuntimeException();
assertThat(settableListenableFuture.setException(exception)).isTrue();
assertThatExceptionOfType(ExecutionException.class).isThrownBy(
settableListenableFuture::get)
.withCause(exception);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void throwsSetExceptionWrappedInExecutionExceptionFromCompletable() {
Throwable exception = new RuntimeException();
assertThat(settableListenableFuture.setException(exception)).isTrue();
Future<String> completable = settableListenableFuture.completable();
assertThatExceptionOfType(ExecutionException.class).isThrownBy(
completable::get)
.withCause(exception);
assertThat(completable.isCancelled()).isFalse();
assertThat(completable.isDone()).isTrue();
}
@Test
void throwsSetErrorWrappedInExecutionException() {
Throwable exception = new OutOfMemoryError();
assertThat(settableListenableFuture.setException(exception)).isTrue();
assertThatExceptionOfType(ExecutionException.class).isThrownBy(
settableListenableFuture::get)
.withCause(exception);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void throwsSetErrorWrappedInExecutionExceptionFromCompletable() {
Throwable exception = new OutOfMemoryError();
assertThat(settableListenableFuture.setException(exception)).isTrue();
Future<String> completable = settableListenableFuture.completable();
assertThatExceptionOfType(ExecutionException.class).isThrownBy(
completable::get)
.withCause(exception);
assertThat(completable.isCancelled()).isFalse();
assertThat(completable.isDone()).isTrue();
}
@Test
void setValueTriggersCallback() {
String string = "hello";
final String[] callbackHolder = new String[1];
settableListenableFuture.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(String result) {
callbackHolder[0] = result;
}
@Override
public void onFailure(Throwable ex) {
throw new AssertionError("Expected onSuccess() to be called", ex);
}
});
settableListenableFuture.set(string);
assertThat(callbackHolder[0]).isEqualTo(string);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void setValueTriggersCallbackOnlyOnce() {
String string = "hello";
final String[] callbackHolder = new String[1];
settableListenableFuture.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(String result) {
callbackHolder[0] = result;
}
@Override
public void onFailure(Throwable ex) {
throw new AssertionError("Expected onSuccess() to be called", ex);
}
});
settableListenableFuture.set(string);
assertThat(settableListenableFuture.set("good bye")).isFalse();
assertThat(callbackHolder[0]).isEqualTo(string);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void setExceptionTriggersCallback() {
Throwable exception = new RuntimeException();
final Throwable[] callbackHolder = new Throwable[1];
settableListenableFuture.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(String result) {
fail("Expected onFailure() to be called");
}
@Override
public void onFailure(Throwable ex) {
callbackHolder[0] = ex;
}
});
settableListenableFuture.setException(exception);
assertThat(callbackHolder[0]).isEqualTo(exception);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void setExceptionTriggersCallbackOnlyOnce() {
Throwable exception = new RuntimeException();
final Throwable[] callbackHolder = new Throwable[1];
settableListenableFuture.addCallback(new ListenableFutureCallback<>() {
@Override
public void onSuccess(String result) {
fail("Expected onFailure() to be called");
}
@Override
public void onFailure(Throwable ex) {
callbackHolder[0] = ex;
}
});
settableListenableFuture.setException(exception);
assertThat(settableListenableFuture.setException(new IllegalArgumentException())).isFalse();
assertThat(callbackHolder[0]).isEqualTo(exception);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void nullIsAcceptedAsValueToSet() throws ExecutionException, InterruptedException {
settableListenableFuture.set(null);
assertThat(settableListenableFuture.get()).isNull();
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void getWaitsForCompletion() throws ExecutionException, InterruptedException {
final String string = "hello";
new Thread(() -> {
try {
Thread.sleep(20L);
settableListenableFuture.set(string);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}).start();
String value = settableListenableFuture.get();
assertThat(value).isEqualTo(string);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void getWithTimeoutThrowsTimeoutException() {
assertThatExceptionOfType(TimeoutException.class).isThrownBy(() ->
settableListenableFuture.get(1L, TimeUnit.MILLISECONDS));
}
@Test
void getWithTimeoutWaitsForCompletion() throws ExecutionException, InterruptedException, TimeoutException {
final String string = "hello";
new Thread(() -> {
try {
Thread.sleep(20L);
settableListenableFuture.set(string);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}).start();
String value = settableListenableFuture.get(500L, TimeUnit.MILLISECONDS);
assertThat(value).isEqualTo(string);
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void cancelPreventsValueFromBeingSet() {
assertThat(settableListenableFuture.cancel(true)).isTrue();
assertThat(settableListenableFuture.set("hello")).isFalse();
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void cancelSetsFutureToDone() {
settableListenableFuture.cancel(true);
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void cancelWithMayInterruptIfRunningTrueCallsOverriddenMethod() {
InterruptibleSettableListenableFuture interruptibleFuture = new InterruptibleSettableListenableFuture();
assertThat(interruptibleFuture.cancel(true)).isTrue();
assertThat(interruptibleFuture.calledInterruptTask()).isTrue();
assertThat(interruptibleFuture.isCancelled()).isTrue();
assertThat(interruptibleFuture.isDone()).isTrue();
}
@Test
void cancelWithMayInterruptIfRunningFalseDoesNotCallOverriddenMethod() {
InterruptibleSettableListenableFuture interruptibleFuture = new InterruptibleSettableListenableFuture();
assertThat(interruptibleFuture.cancel(false)).isTrue();
assertThat(interruptibleFuture.calledInterruptTask()).isFalse();
assertThat(interruptibleFuture.isCancelled()).isTrue();
assertThat(interruptibleFuture.isDone()).isTrue();
}
@Test
void setPreventsCancel() {
assertThat(settableListenableFuture.set("hello")).isTrue();
assertThat(settableListenableFuture.cancel(true)).isFalse();
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void cancelPreventsExceptionFromBeingSet() {
assertThat(settableListenableFuture.cancel(true)).isTrue();
assertThat(settableListenableFuture.setException(new RuntimeException())).isFalse();
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void setExceptionPreventsCancel() {
assertThat(settableListenableFuture.setException(new RuntimeException())).isTrue();
assertThat(settableListenableFuture.cancel(true)).isFalse();
assertThat(settableListenableFuture.isCancelled()).isFalse();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void cancelStateThrowsExceptionWhenCallingGet() {
settableListenableFuture.cancel(true);
assertThatExceptionOfType(CancellationException.class).isThrownBy(settableListenableFuture::get);
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
void cancelStateThrowsExceptionWhenCallingGetWithTimeout() {
new Thread(() -> {
try {
Thread.sleep(20L);
settableListenableFuture.cancel(true);
}
catch (InterruptedException ex) {
throw new RuntimeException(ex);
}
}).start();
assertThatExceptionOfType(CancellationException.class).isThrownBy(() ->
settableListenableFuture.get(500L, TimeUnit.MILLISECONDS));
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void cancelDoesNotNotifyCallbacksOnSet() {
ListenableFutureCallback callback = mock();
settableListenableFuture.addCallback(callback);
settableListenableFuture.cancel(true);
verify(callback).onFailure(any(CancellationException.class));
verifyNoMoreInteractions(callback);
settableListenableFuture.set("hello");
verifyNoMoreInteractions(callback);
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
@Test
@SuppressWarnings({"rawtypes", "unchecked"})
public void cancelDoesNotNotifyCallbacksOnSetException() {
ListenableFutureCallback callback = mock();
settableListenableFuture.addCallback(callback);
settableListenableFuture.cancel(true);
verify(callback).onFailure(any(CancellationException.class));
verifyNoMoreInteractions(callback);
settableListenableFuture.setException(new RuntimeException());
verifyNoMoreInteractions(callback);
assertThat(settableListenableFuture.isCancelled()).isTrue();
assertThat(settableListenableFuture.isDone()).isTrue();
}
private static class InterruptibleSettableListenableFuture extends SettableListenableFuture<String> {
private boolean interrupted = false;
@Override
protected void interruptTask() {
interrupted = true;
}
boolean calledInterruptTask() {
return interrupted;
}
}
}