From 71cc51cfbed6c72461f92cfd4551c628628ca021 Mon Sep 17 00:00:00 2001 From: Jerry Lee Date: Sat, 3 Aug 2024 18:52:59 +0800 Subject: [PATCH] refactor: delegate `resetDefaultExecutor()` to `CffuFactoryBuilder`, and avoiding re-wrapping --- .../java/io/foldright/cffu/CffuFactory.java | 6 +-- .../io/foldright/cffu/CffuFactoryBuilder.java | 46 ++++++++++++++++++- .../io/foldright/cffu/CffuFactoryTest.java | 21 ++++++--- .../test/java/io/foldright/cffu/CffuTest.java | 3 +- .../cffu/DefaultExecutorTestUtils.java | 16 +++++++ .../cffu/spi/ExecutorWrapperProviderTest.kt | 5 +- .../foldright/cffu/test/CffuExtensionsTest.kt | 17 ++++--- 7 files changed, 94 insertions(+), 20 deletions(-) create mode 100644 cffu-core/src/test/java/io/foldright/cffu/DefaultExecutorTestUtils.java diff --git a/cffu-core/src/main/java/io/foldright/cffu/CffuFactory.java b/cffu-core/src/main/java/io/foldright/cffu/CffuFactory.java index 5cbe3352..360e1493 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CffuFactory.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CffuFactory.java @@ -63,15 +63,15 @@ public final class CffuFactory { */ @Contract(pure = true) public static CffuFactoryBuilder builder(Executor defaultExecutor) { - return new CffuFactoryBuilder(CompletableFutureUtils.screenExecutor(defaultExecutor)); + return new CffuFactoryBuilder(defaultExecutor); } /** * Returns a new CffuFactory from this CffuFactory that reset the defaultExecutor. */ @Contract(pure = true) - CffuFactory resetDefaultExecutor(Executor defaultExecutor) { - return new CffuFactory(defaultExecutor, forbidObtrudeMethods); + public CffuFactory resetDefaultExecutor(Executor defaultExecutor) { + return CffuFactoryBuilder.resetDefaultExecutor(this, defaultExecutor); } @Contract(pure = true) diff --git a/cffu-core/src/main/java/io/foldright/cffu/CffuFactoryBuilder.java b/cffu-core/src/main/java/io/foldright/cffu/CffuFactoryBuilder.java index 281fe3db..f3f394d7 100644 --- a/cffu-core/src/main/java/io/foldright/cffu/CffuFactoryBuilder.java +++ b/cffu-core/src/main/java/io/foldright/cffu/CffuFactoryBuilder.java @@ -2,6 +2,7 @@ import io.foldright.cffu.spi.ExecutorWrapperProvider; import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.VisibleForTesting; import javax.annotation.concurrent.ThreadSafe; import java.util.List; @@ -28,7 +29,43 @@ public final class CffuFactoryBuilder { private volatile boolean forbidObtrudeMethods = false; CffuFactoryBuilder(Executor defaultExecutor) { - this.defaultExecutor = wrapExecutor(defaultExecutor); + this.defaultExecutor = makeExecutor(defaultExecutor); + } + + private static Executor makeExecutor(Executor executor) { + // check CffuMadeExecutor interface to avoid re-wrapping. + if (executor instanceof CffuMadeExecutor) return executor; + + Executor wrapByProviders = wrapExecutorByProviders(CompletableFutureUtils.screenExecutor(executor)); + return CffuMadeExecutor.wrapMadeInterface(wrapByProviders); + } + + /** + * An interface for avoiding re-wrapping. + */ + @VisibleForTesting + interface CffuMadeExecutor extends Executor { + static CffuMadeExecutor wrapMadeInterface(Executor executor) { + return new CffuMadeExecutor() { + @Override + public Executor unwrap() { + return executor; + } + + @Override + public void execute(Runnable command) { + executor.execute(command); + } + + @Override + public String toString() { + return "CffuMadeExecutor of executor(" + executor + ")"; + } + }; + } + + @VisibleForTesting + Executor unwrap(); } //////////////////////////////////////////////////////////////////////////////// @@ -57,7 +94,12 @@ public CffuFactory build() { return new CffuFactory(defaultExecutor, forbidObtrudeMethods); } - private static Executor wrapExecutor(Executor executor) { + @Contract(pure = true) + static CffuFactory resetDefaultExecutor(CffuFactory fac, Executor defaultExecutor) { + return new CffuFactory(makeExecutor(defaultExecutor), fac.forbidObtrudeMethods()); + } + + private static Executor wrapExecutorByProviders(Executor executor) { for (ExecutorWrapperProvider provider : EXECUTOR_WRAPPER_PROVIDERS) { Supplier msg = () -> provider + "(class: " + provider.getClass().getName() + ") return null"; executor = requireNonNull(provider.wrap(executor), msg); diff --git a/cffu-core/src/test/java/io/foldright/cffu/CffuFactoryTest.java b/cffu-core/src/test/java/io/foldright/cffu/CffuFactoryTest.java index 49630806..ad9fbe15 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CffuFactoryTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CffuFactoryTest.java @@ -19,6 +19,7 @@ import static io.foldright.cffu.CompletableFutureUtils.failedFuture; import static io.foldright.cffu.CompletableFutureUtils.toCompletableFutureArray; +import static io.foldright.cffu.DefaultExecutorTestUtils.unwrapMadeExecutor; import static io.foldright.test_utils.TestUtils.*; import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.concurrent.ForkJoinPool.commonPool; @@ -811,7 +812,7 @@ void test_toCffu() throws Exception { CffuFactory fac = CffuFactory.builder(anotherExecutorService).forbidObtrudeMethods(true).build(); Cffu cffu = fac.toCffu(cffu_in); assertNotSame(cffu_in, cffu); - assertSame(anotherExecutorService, cffu.defaultExecutor()); + assertSame(anotherExecutorService, unwrapMadeExecutor(cffu)); assertSame(fac, cffu.cffuFactory()); assertEquals("obtrude methods is forbidden by cffu", assertThrowsExactly(UnsupportedOperationException.class, () -> cffu.obtrudeValue(44) @@ -913,16 +914,23 @@ void test_cffuListToArray() { @Test void test_getter() { - assertSame(executorService, cffuFactory.defaultExecutor()); + assertEquals("CffuMadeExecutor of executor(" + executorService + ")", + cffuFactory.defaultExecutor().toString()); + + assertSame(executorService, unwrapMadeExecutor(cffuFactory)); assertFalse(cffuFactory.forbidObtrudeMethods()); CffuFactory fac = CffuFactory.builder(anotherExecutorService).forbidObtrudeMethods(true).build(); - assertSame(anotherExecutorService, fac.defaultExecutor()); + assertSame(anotherExecutorService, unwrapMadeExecutor(fac)); assertTrue(fac.forbidObtrudeMethods()); final CffuFactory fac2 = cffuFactory.resetDefaultExecutor(anotherExecutorService); - assertSame(anotherExecutorService, fac2.defaultExecutor()); + assertSame(anotherExecutorService, unwrapMadeExecutor(fac2)); assertEquals(cffuFactory.forbidObtrudeMethods(), fac2.forbidObtrudeMethods()); + + final CffuFactory fac3 = cffuFactory.resetDefaultExecutor(fac2.defaultExecutor()); + assertSame(fac2.defaultExecutor(), fac3.defaultExecutor()); + assertEquals(fac2.forbidObtrudeMethods(), fac3.forbidObtrudeMethods()); } @Test @@ -945,15 +953,16 @@ void test_executorSetting_MayBe_ThreadPerTaskExecutor() throws Exception { CffuFactory fac = CffuFactory.builder(commonPool()).build(); if (USE_COMMON_POOL) { - assertSame(commonPool(), fac.defaultExecutor()); + assertSame(commonPool(), unwrapMadeExecutor(fac)); } else { - String executorClassName = fac.defaultExecutor().getClass().getName(); + String executorClassName = unwrapMadeExecutor(fac).getClass().getName(); assertTrue(executorClassName.endsWith("$ThreadPerTaskExecutor")); } assertEquals(42, fac.supplyAsync(() -> 42).get()); } + // endregion //////////////////////////////////////////////////////////////////////////////// // region# Test helper methods/fields diff --git a/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java b/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java index 1e47ead3..d7550d7c 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java +++ b/cffu-core/src/test/java/io/foldright/cffu/CffuTest.java @@ -18,6 +18,7 @@ import java.util.function.Consumer; import java.util.function.Function; +import static io.foldright.cffu.DefaultExecutorTestUtils.unwrapMadeExecutor; import static io.foldright.test_utils.TestUtils.*; import static java.util.concurrent.CompletableFuture.completedFuture; import static java.util.function.Function.identity; @@ -486,7 +487,7 @@ void test_resetCffuFactory() { Executor executor = Runnable::run; final Cffu f2 = cf.resetDefaultExecutor(executor); - assertSame(executor, f2.defaultExecutor()); + assertSame(executor, unwrapMadeExecutor(f2)); assertEquals(cffuFactory.forbidObtrudeMethods(), f2.cffuFactory().forbidObtrudeMethods()); } diff --git a/cffu-core/src/test/java/io/foldright/cffu/DefaultExecutorTestUtils.java b/cffu-core/src/test/java/io/foldright/cffu/DefaultExecutorTestUtils.java new file mode 100644 index 00000000..4a6142e3 --- /dev/null +++ b/cffu-core/src/test/java/io/foldright/cffu/DefaultExecutorTestUtils.java @@ -0,0 +1,16 @@ +package io.foldright.cffu; + +import java.util.concurrent.Executor; + + +public class DefaultExecutorTestUtils { + public static Executor unwrapMadeExecutor(CffuFactory factory) { + final CffuFactoryBuilder.CffuMadeExecutor executor = (CffuFactoryBuilder.CffuMadeExecutor) factory.defaultExecutor(); + return executor.unwrap(); + } + + public static Executor unwrapMadeExecutor(Cffu cffu) { + final CffuFactoryBuilder.CffuMadeExecutor executor = (CffuFactoryBuilder.CffuMadeExecutor) cffu.defaultExecutor(); + return executor.unwrap(); + } +} diff --git a/cffu-core/src/test/java/io/foldright/cffu/spi/ExecutorWrapperProviderTest.kt b/cffu-core/src/test/java/io/foldright/cffu/spi/ExecutorWrapperProviderTest.kt index 92ca5379..b1e68460 100644 --- a/cffu-core/src/test/java/io/foldright/cffu/spi/ExecutorWrapperProviderTest.kt +++ b/cffu-core/src/test/java/io/foldright/cffu/spi/ExecutorWrapperProviderTest.kt @@ -1,6 +1,7 @@ package io.foldright.cffu.spi import io.foldright.cffu.CffuFactory +import io.foldright.cffu.DefaultExecutorTestUtils.unwrapMadeExecutor import io.foldright.test_utils.testThreadPoolExecutor import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.types.shouldBeSameInstanceAs @@ -10,7 +11,7 @@ class ExecutorWrapperProviderTest : FunSpec({ test("disable TestExecutorWrapper") { val factory = CffuFactory.builder(testThreadPoolExecutor).build() val cffu = factory.runAsync {} - cffu.defaultExecutor().shouldBeSameInstanceAs(testThreadPoolExecutor) + unwrapMadeExecutor(cffu).shouldBeSameInstanceAs(testThreadPoolExecutor) } test("enable TestExecutorWrapper") { @@ -18,7 +19,7 @@ class ExecutorWrapperProviderTest : FunSpec({ val factory = CffuFactory.builder(testThreadPoolExecutor).build() val cffu = factory.runAsync {} - cffu.defaultExecutor().shouldNotBeSameInstanceAs(testThreadPoolExecutor) + unwrapMadeExecutor(cffu).shouldNotBeSameInstanceAs(testThreadPoolExecutor) } beforeTest { diff --git a/cffu-kotlin/src/test/java/io/foldright/cffu/test/CffuExtensionsTest.kt b/cffu-kotlin/src/test/java/io/foldright/cffu/test/CffuExtensionsTest.kt index 9543e9f6..506e2df9 100644 --- a/cffu-kotlin/src/test/java/io/foldright/cffu/test/CffuExtensionsTest.kt +++ b/cffu-kotlin/src/test/java/io/foldright/cffu/test/CffuExtensionsTest.kt @@ -2,6 +2,7 @@ package io.foldright.cffu.test import io.foldright.cffu.Cffu import io.foldright.cffu.CffuFactory +import io.foldright.cffu.DefaultExecutorTestUtils import io.foldright.cffu.NoCfsProvidedException import io.foldright.cffu.kotlin.* import io.foldright.test_utils.testCffuFactory @@ -17,10 +18,7 @@ import io.kotest.matchers.types.shouldBeTypeOf import io.kotest.matchers.types.shouldNotBeSameInstanceAs import io.kotest.matchers.types.shouldNotBeTypeOf import kotlinx.coroutines.future.await -import java.util.concurrent.CompletableFuture -import java.util.concurrent.CompletionStage -import java.util.concurrent.Executors -import java.util.concurrent.TimeUnit +import java.util.concurrent.* const val n = 42 const val anotherN = 4242 @@ -28,6 +26,7 @@ const val s = "43" const val d = 44.0 val rte = RuntimeException("Bang") + class CffuExtensionsTest : FunSpec({ //////////////////////////////////////// // toCffu @@ -36,12 +35,12 @@ class CffuExtensionsTest : FunSpec({ suspend fun checkToCffu(cffu: Cffu, n: Int) { cffu.await() shouldBe n - cffu.defaultExecutor() shouldBeSameInstanceAs testThreadPoolExecutor + cffu.unwrapMadeExecutor() shouldBeSameInstanceAs testThreadPoolExecutor cffu.cffuFactory() shouldBeSameInstanceAs testCffuFactory val fac2 = CffuFactory.builder(testForkJoinPoolExecutor).build() cffu.resetCffuFactory(fac2).let { - it.defaultExecutor() shouldBeSameInstanceAs testForkJoinPoolExecutor + it.unwrapMadeExecutor() shouldBeSameInstanceAs testForkJoinPoolExecutor it.cffuFactory() shouldBeSameInstanceAs fac2 } } @@ -656,3 +655,9 @@ class CffuExtensionsTest : FunSpec({ } } }) + +fun CffuFactory.unwrapMadeExecutor(): Executor = + DefaultExecutorTestUtils.unwrapMadeExecutor(this) + +fun Cffu<*>.unwrapMadeExecutor(): Executor = + DefaultExecutorTestUtils.unwrapMadeExecutor(this)