This is an automated email from the ASF dual-hosted git repository.
daim pushed a commit to branch trunk
in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
The following commit(s) were added to refs/heads/trunk by this push:
new 20e64a2a32 OAK-12132 : remove toCompletableFuture from FutureConverter
(#2788)
20e64a2a32 is described below
commit 20e64a2a32d3b48ec04a45603f6de06009ee0a43
Author: Rishabh Kumar <[email protected]>
AuthorDate: Fri Mar 6 20:10:33 2026 +0530
OAK-12132 : remove toCompletableFuture from FutureConverter (#2788)
---
.../internal/concurrent/FutureConverter.java | 86 +---------
.../commons/internal/concurrent/package-info.java | 2 +-
.../internal/concurrent/FutureConverterTest.java | 189 ---------------------
3 files changed, 3 insertions(+), 274 deletions(-)
diff --git
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverter.java
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverter.java
index 9f9bab4ac7..a97bb39dfb 100644
---
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverter.java
+++
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverter.java
@@ -21,16 +21,15 @@ package
org.apache.jackrabbit.oak.commons.internal.concurrent;
import org.apache.jackrabbit.guava.common.util.concurrent.ListenableFuture;
import org.jetbrains.annotations.NotNull;
-import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
-import java.util.stream.Collectors;
/**
- * Utility to convert {@link
org.apache.jackrabbit.guava.common.util.concurrent.ListenableFuture} to {@link
java.util.concurrent.CompletableFuture}
+ * Utility to convert between {@link
org.apache.jackrabbit.guava.common.util.concurrent.ListenableFuture}
+ * and {@link java.util.concurrent.CompletableFuture}.
*/
// TODO: remove this class once we remove all Guava Concurent Packages
public class FutureConverter {
@@ -38,71 +37,6 @@ public class FutureConverter {
// no instances for you
}
- private static final Executor DIRECT_EXECUTOR = Runnable::run;
-
-
- public static <T> List<CompletableFuture<T>> toCompletableFuture(final
List<? extends ListenableFuture<T>> listenableFutures) {
- return listenableFutures.stream()
- .map(FutureConverter::toCompletableFuture)
- .collect(Collectors.toList());
- }
-
- /**
- * Converts a {@link
org.apache.jackrabbit.guava.common.util.concurrent.ListenableFuture}
- * to a {@link java.util.concurrent.CompletableFuture}.
- * <p>
- * The returned CompletableFuture will be completed when the
ListenableFuture completes,
- * either with its result or with an exception if the ListenableFuture
fails.
- *
- * @param listenableFuture the ListenableFuture to convert
- * @param <T> the result type
- * @return a CompletableFuture representing the same computation
- */
- public static <T> CompletableFuture<T> toCompletableFuture(final
ListenableFuture<T> listenableFuture) {
- CompletableFuture<T> completable = new CompletableFuture<>() {
- @Override
- public boolean cancel(boolean mayInterruptIfRunning) {
- // Cancel the Guava ListenableFuture
- boolean canceled =
listenableFuture.cancel(mayInterruptIfRunning);
- // Also cancel this CompletableFuture
- super.cancel(mayInterruptIfRunning);
- return canceled;
- }
-
- @Override
- public T get() throws InterruptedException, ExecutionException {
- try {
- return super.get();
- } catch (InterruptedException e) {
- // Ensure interrupt status is preserved
- Thread.currentThread().interrupt();
- throw e;
- }
- }
-
- @Override
- public T get(long timeout, TimeUnit unit) throws
InterruptedException, ExecutionException, TimeoutException {
- try {
- return super.get(timeout, unit);
- } catch (InterruptedException e) {
- // Ensure interrupt status is preserved
- Thread.currentThread().interrupt();
- throw e;
- }
- }
- };
-
- // Check if the ListenableFuture is already done to avoid unnecessary
async overhead
- if (listenableFuture.isDone()) {
- handleConversion(listenableFuture, completable);
- } else {
- // Future is not done yet, add listener for completion
- listenableFuture.addListener(() ->
handleConversion(listenableFuture, completable), DIRECT_EXECUTOR);
- }
-
- return completable;
- }
-
/**
* Converts a Java {@link CompletableFuture} to a Guava {@link
ListenableFuture}.
* <p>
@@ -160,20 +94,4 @@ public class FutureConverter {
};
}
- // helper methods
-
- private static <T> void handleConversion(final ListenableFuture<T>
listenableFuture, final CompletableFuture<T> completable) {
- try {
- if (listenableFuture.isCancelled()) {
- completable.cancel(false);
- } else {
- completable.complete(listenableFuture.get());
- }
- } catch (InterruptedException ex) {
- Thread.currentThread().interrupt();
- completable.completeExceptionally(ex);
- } catch (Exception ex) {
- completable.completeExceptionally(ex.getCause() != null ?
ex.getCause() : ex);
- }
- }
}
diff --git
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/package-info.java
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/package-info.java
index 75cc10fd69..64007c2781 100644
---
a/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/package-info.java
+++
b/oak-commons/src/main/java/org/apache/jackrabbit/oak/commons/internal/concurrent/package-info.java
@@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-@Version("1.3.0")
+@Version("2.0.0")
@Internal(since = "1.0.0")
package org.apache.jackrabbit.oak.commons.internal.concurrent;
diff --git
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverterTest.java
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverterTest.java
index f638b95b7d..9f3c131685 100644
---
a/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverterTest.java
+++
b/oak-commons/src/test/java/org/apache/jackrabbit/oak/commons/internal/concurrent/FutureConverterTest.java
@@ -19,12 +19,9 @@
package org.apache.jackrabbit.oak.commons.internal.concurrent;
import org.apache.jackrabbit.guava.common.util.concurrent.ListenableFuture;
-import org.apache.jackrabbit.guava.common.util.concurrent.SettableFuture;
import org.junit.Assert;
import org.junit.Test;
-import java.util.Arrays;
-import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
@@ -37,192 +34,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
*/
public class FutureConverterTest {
- @Test
- public void testSuccessfulCompletion() throws Exception {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- listenable.set("success");
-
- // Should complete with the same value
- Assert.assertEquals("success", completable.get(1, TimeUnit.SECONDS));
- Assert.assertTrue(completable.isDone());
- Assert.assertFalse(completable.isCompletedExceptionally());
- }
-
- @Test
- public void testExceptionalCompletion() {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- listenable.setException(new RuntimeException("fail!"));
-
- // Should complete exceptionally
- ExecutionException ex = Assert.assertThrows(ExecutionException.class,
() -> completable.get(1, TimeUnit.SECONDS));
- Assert.assertTrue(ex.getCause() instanceof RuntimeException);
- Assert.assertEquals("fail!", ex.getCause().getMessage());
- Assert.assertTrue(completable.isCompletedExceptionally());
- }
-
- @Test
- public void testCancellation() {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- listenable.cancel(true);
-
- // Should complete exceptionally with CancellationException
- Assert.assertThrows(CancellationException.class, () ->
completable.get(1, TimeUnit.SECONDS));
- Assert.assertTrue(completable.isCompletedExceptionally());
- }
-
- @Test
- public void toCompletableFutureCancelFromListenable() {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- listenable.cancel(true);
-
- try {
- completable.get(1, TimeUnit.SECONDS);
- Assert.fail("Expected CancellationException");
- } catch (CancellationException e) {
- // expected
- } catch (Exception e) {
- Assert.fail("Unexpected exception: " + e);
- }
-
- Assert.assertTrue(completable.isCancelled());
- }
-
- @Test
- public void toCompletableFutureCancelFromCompletable() {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- boolean cancelled = completable.cancel(true);
-
- Assert.assertTrue(cancelled);
- Assert.assertTrue(completable.isCancelled());
- Assert.assertTrue(listenable.isCancelled());
- }
-
- @Test
- public void toCompletableFutureTestGetRestoresInterruptStatus() throws
Exception {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- final AtomicBoolean interruptedInCatch = new AtomicBoolean(false);
- final AtomicBoolean caughtInterruptedException = new
AtomicBoolean(false);
- final CountDownLatch threadStarted = new CountDownLatch(1);
-
- Thread testThread = new Thread(() -> {
- threadStarted.countDown();
- try {
- completable.get(); // This will block, we interrupt
- Assert.fail("Expected InterruptedException");
- } catch (InterruptedException e) {
- // Expected interrupt
- caughtInterruptedException.set(true);
- interruptedInCatch.set(Thread.currentThread().isInterrupted());
- } catch (Exception e) {
- Assert.fail("Unexpected exception: " + e);
- }
- });
-
- testThread.start();
- // Wait for thread to start and then interrupt
- threadStarted.await();
- Thread.sleep(50); // Small delay to ensure get() is called
- testThread.interrupt();
-
- testThread.join();
-
- Assert.assertTrue("Should have caught InterruptedException",
caughtInterruptedException.get());
- Assert.assertTrue("Thread should be interrupted when catching
InterruptedException", interruptedInCatch.get());
- }
-
- @Test
- public void toCompletableFutureTestGetTimeoutRestoresInterruptStatus()
throws Exception {
- SettableFuture<String> listenable = SettableFuture.create();
- CompletableFuture<String> completable =
FutureConverter.toCompletableFuture(listenable);
-
- final AtomicBoolean interruptedInCatch = new AtomicBoolean(false);
- final AtomicBoolean caughtInterruptedException = new
AtomicBoolean(false);
- final CountDownLatch threadStarted = new CountDownLatch(1);
-
- Thread testThread = new Thread(() -> {
- threadStarted.countDown();
- try {
- completable.get(10, TimeUnit.SECONDS); // Will block and get
interrupted
- Assert.fail("Expected InterruptedException");
- } catch (InterruptedException e) {
- // Expected interrupt
- caughtInterruptedException.set(true);
- interruptedInCatch.set(Thread.currentThread().isInterrupted());
- } catch (Exception e) {
- Assert.fail("Unexpected exception: " + e);
- }
- });
-
- testThread.start();
- // Wait for thread to start and then interrupt
- threadStarted.await();
- Thread.sleep(50); // Small delay to ensure get() is called
- testThread.interrupt();
-
- testThread.join();
-
- Assert.assertTrue("Should have caught InterruptedException",
caughtInterruptedException.get());
- Assert.assertTrue("Thread should be interrupted when catching
InterruptedException", interruptedInCatch.get());
- }
-
- @Test
- public void testConvertListSuccessful() throws Exception {
- SettableFuture<String> f1 = SettableFuture.create();
- SettableFuture<String> f2 = SettableFuture.create();
-
- List<ListenableFuture<String>> listenableList = Arrays.asList(f1, f2);
- List<CompletableFuture<String>> completableList =
FutureConverter.toCompletableFuture(listenableList);
-
- // Complete Guava futures
- f1.set("first");
- f2.set("second");
-
- // Assert CompletableFuture results
- Assert.assertEquals("first", completableList.get(0).get());
- Assert.assertEquals("second", completableList.get(1).get());
- }
-
- @Test
- public void testConvertListPartialFailure() {
- SettableFuture<String> f1 = SettableFuture.create();
- SettableFuture<String> f2 = SettableFuture.create();
-
- List<ListenableFuture<String>> listenableList = Arrays.asList(f1, f2);
- List<CompletableFuture<String>> completableList =
FutureConverter.toCompletableFuture(listenableList);
-
- f1.set("ok");
- f2.setException(new RuntimeException("fail"));
-
- // Verify first future succeeds
- try {
- Assert.assertEquals("ok", completableList.get(0).get());
- } catch (Exception e) {
- Assert.fail("First future should succeed");
- }
-
- // Verify second future completes exceptionally
- Assert.assertThrows(ExecutionException.class, () ->
completableList.get(1).get());
- }
-
- @Test
- public void testConvertListEmpty() {
- List<ListenableFuture<String>> emptyList = List.of();
- List<CompletableFuture<String>> completableList =
FutureConverter.toCompletableFuture(emptyList);
- Assert.assertTrue(completableList.isEmpty());
- }
-
@Test
public void toListenableFutureSuccessfulCompletion() throws Exception {
CompletableFuture<String> completable = new CompletableFuture<>();