This is an automated email from the ASF dual-hosted git repository. daim pushed a commit to branch OAK-12132 in repository https://gitbox.apache.org/repos/asf/jackrabbit-oak.git
commit bf7710c71942e22b1c9e918087fb2ad140036c5a Author: rishabhdaim <[email protected]> AuthorDate: Fri Mar 6 17:57:41 2026 +0530 OAK-12132 : remove toCompletableFuture from FutureConverter --- .../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<>();
