Skip to content

Commit

Permalink
feat: rename CompletableFuture extension methods of Backport CF ins…
Browse files Browse the repository at this point in the history
…tance methods, remove prefix `cffu` 🍩
  • Loading branch information
oldratlee committed Apr 24, 2024
1 parent 398e080 commit 9f1856f
Show file tree
Hide file tree
Showing 7 changed files with 88 additions and 72 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ jobs:
test:
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#choosing-github-hosted-runners
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
name: CI by multiply java versions

steps:
Expand All @@ -26,7 +26,6 @@ jobs:
21
22
distribution: zulu
# only first java setup need enable cache
cache: maven

- name: Run integration test
Expand Down
18 changes: 0 additions & 18 deletions .github/workflows/fast_ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,24 +23,6 @@ jobs:
with:
java-version: 21
distribution: microsoft
# only first java setup need enable cache
cache: maven
- name: Build and test with Java 21
run: ./mvnw -V --no-transfer-progress clean install

- name: Setup Java 8
uses: actions/setup-java@v4
with:
java-version: 8
distribution: zulu
- name: Test with Java 8
run: ./mvnw -V --no-transfer-progress surefire:test

- name: 'Remove self maven install files(OS: *nix)'
run: rm -rf $HOME/.m2/repository/io/foldright/cffu*
# https://docs.github.com/en/actions/learn-github-actions/expressions
# https://docs.github.com/en/actions/learn-github-actions/variables#detecting-the-operating-system
if: runner.os != 'Windows'
- name: 'Remove self maven install files(OS: Windows)'
run: Remove-Item -Recurse -Force $home/.m2/repository/io/foldright/cffu*
if: runner.os == 'Windows'
10 changes: 3 additions & 7 deletions cffu-core/src/main/java/io/foldright/cffu/CffuFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
* About factory methods conventions of {@link CffuFactory}:
* <ul>
* <li>factory methods return {@link Cffu} instead of {@link CompletableFuture}.
* <li>new methods, aka. no equivalent method in {@link CompletableFuture}, prefix method name with {@code cffu}.
* <li>only provide varargs methods for multiply Cffu/CF input arguments;
* if you have {@code List} input, use static util methods {@link #cffuListToArray(List)}
* or {@link #completableFutureListToArray(List)} to convert it to array first.
Expand Down Expand Up @@ -293,11 +292,11 @@ public final <T> Cffu<T>[] asCffuArray(CompletionStage<T>... stages) {
}

////////////////////////////////////////////////////////////////////////////////
//# allOf / anyOf methods, without return result type enhancement
//# allOf / anyOf methods
// - allOf*, return Cffu<Void>, equivalent to same name methods:
// - CompletableFuture.allOf()
// - CompletableFutureUtils.allOfFastFail()
// - anyOf*, return Cffu<Object>, equivalent to same name methods:
// - anyOf*, equivalent to same name methods:
// - CompletableFuture.anyOf()
// - CompletableFutureUtils.anyOfSuccess()
////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -567,10 +566,7 @@ public Executor delayedExecutor(long delay, TimeUnit unit, Executor executor) {
}

////////////////////////////////////////////////////////////////////////////////
//# New type-safe allOf/anyOf Factory Methods
// method name prefix with `cffu`
//
// - allResultsOf
//# New allResultsOf* Factory Methods
////////////////////////////////////////////////////////////////////////////////

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
@file:OptIn(ExperimentalTime::class)

package io.foldright.showcases

import io.foldright.test_utils.*
Expand All @@ -18,7 +16,6 @@ import java.lang.System.currentTimeMillis
import java.lang.Thread.currentThread
import java.util.concurrent.*
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.time.ExperimentalTime

class CompletableFutureUsageShowcaseTest : FunSpec({
val n = 42
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("EXTENSION_SHADOWED_BY_MEMBER")

package io.foldright.cffu.kotlin

import io.foldright.cffu.CffuState
Expand All @@ -10,6 +12,8 @@ import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionStage
import java.util.concurrent.Executor
import java.util.concurrent.TimeUnit
import java.util.function.Function
import java.util.function.Supplier


////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -383,8 +387,6 @@ fun <T1, T2, T3, T4, T5> CompletableFuture<T1>.combineFastFail(
////////////////////////////////////////////////////////////////////////////////
//# Backport CF instance methods
// compatibility for low Java version
//
// all methods name prefix with `cffu`
////////////////////////////////////////////////////////////////////////////////

//# Error Handling methods of CompletionStage
Expand All @@ -399,7 +401,7 @@ fun <T1, T2, T3, T4, T5> CompletableFuture<T1>.combineFastFail(
* if given CompletionStage completed exceptionally
* @return the new CompletionStage
*/
fun <T> CompletableFuture<T>.cffuExceptionallyAsync(fn: (Throwable) -> T): CompletableFuture<T> =
fun <T> CompletableFuture<T>.exceptionallyAsync(fn: Function<Throwable, out T>): CompletableFuture<T> =
CompletableFutureUtils.exceptionallyAsync(this, fn)

/**
Expand All @@ -412,20 +414,22 @@ fun <T> CompletableFuture<T>.cffuExceptionallyAsync(fn: (Throwable) -> T): Compl
* @param executor the executor to use for asynchronous execution
* @return the new CompletionStage
*/
fun <T> CompletableFuture<T>.cffuExceptionallyAsync(fn: (Throwable) -> T, executor: Executor): CompletableFuture<T> =
fun <T> CompletableFuture<T>.exceptionallyAsync(
fn: Function<Throwable, out T>, executor: Executor
): CompletableFuture<T> =
CompletableFutureUtils.exceptionallyAsync(this, fn, executor)

//# Timeout Control methods

/**
* Exceptionally completes this CompletableFuture with a [TimeoutException][java.util.concurrent.TimeoutException]
* Exceptionally completes this CompletableFuture with a TimeoutException
* if not otherwise completed before the given timeout.
*
* @param timeout how long to wait before completing exceptionally with a TimeoutException, in units of `unit`
* @param unit a `TimeUnit` determining how to interpret the `timeout` parameter
* @return this CompletableFuture
*/
fun <T> CompletableFuture<T>.cffuOrTimeout(timeout: Long, unit: TimeUnit): CompletableFuture<T> =
fun <T> CompletableFuture<T>.orTimeout(timeout: Long, unit: TimeUnit): CompletableFuture<T> =
CompletableFutureUtils.orTimeout(this, timeout, unit)

/**
Expand All @@ -436,7 +440,7 @@ fun <T> CompletableFuture<T>.cffuOrTimeout(timeout: Long, unit: TimeUnit): Compl
* @param unit a `TimeUnit` determining how to interpret the `timeout` parameter
* @return given CompletableFuture
*/
fun <T> CompletableFuture<T>.cffuCompleteOnTimeout(value: T, timeout: Long, unit: TimeUnit): CompletableFuture<T> =
fun <T> CompletableFuture<T>.completeOnTimeout(value: T, timeout: Long, unit: TimeUnit): CompletableFuture<T> =
CompletableFutureUtils.completeOnTimeout(this, value, timeout, unit)

//# Advanced methods of CompletionStage
Expand All @@ -449,7 +453,9 @@ fun <T> CompletableFuture<T>.cffuCompleteOnTimeout(value: T, timeout: Long, unit
* CompletionStage if given CompletionStage completed exceptionally
* @return the new CompletionStage
*/
fun <T> CompletableFuture<T>.cffuExceptionallyCompose(fn: (Throwable) -> CompletionStage<T>): CompletableFuture<T> =
fun <T> CompletableFuture<T>.exceptionallyCompose(
fn: Function<Throwable, out CompletionStage<T>>
): CompletableFuture<T> =
CompletableFutureUtils.exceptionallyCompose(this, fn)

/**
Expand All @@ -461,8 +467,8 @@ fun <T> CompletableFuture<T>.cffuExceptionallyCompose(fn: (Throwable) -> Complet
* CompletionStage if given CompletionStage completed exceptionally
* @return the new CompletionStage
*/
fun <T> CompletableFuture<T>.cffuExceptionallyComposeAsync(
fn: (Throwable) -> CompletionStage<T>
fun <T> CompletableFuture<T>.exceptionallyComposeAsync(
fn: Function<Throwable, out CompletionStage<T>>
): CompletableFuture<T> =
CompletableFutureUtils.exceptionallyComposeAsync(this, fn)

Expand All @@ -475,8 +481,8 @@ fun <T> CompletableFuture<T>.cffuExceptionallyComposeAsync(
* @param executor the executor to use for asynchronous execution
* @return the new CompletionStage
*/
fun <T> CompletableFuture<T>.cffuExceptionallyComposeAsync(
fn: (Throwable) -> CompletionStage<T>, executor: Executor
fun <T> CompletableFuture<T>.exceptionallyComposeAsync(
fn: Function<Throwable, out CompletionStage<T>>, executor: Executor
): CompletableFuture<T> =
CompletableFutureUtils.exceptionallyComposeAsync(this, fn, executor)

Expand All @@ -501,9 +507,8 @@ fun <T> CompletableFuture<T>.cffuExceptionallyComposeAsync(
* ```
*
* <b><i>CAUTION:<br></i></b>
* if the wait timed out, this method throws an (unchecked) [CompletionException][java.util.concurrent.CompletionException]
* with the [TimeoutException][java.util.concurrent.TimeoutException] as its cause;
* NOT throws a (checked) [TimeoutException][java.util.concurrent.TimeoutException] like [CompletableFuture.get].
* if the wait timed out, this method throws an (unchecked) CompletionException with the TimeoutException as its cause;
* NOT throws a (checked) TimeoutException like [CompletableFuture.get].
*
* @param timeout the maximum time to wait
* @param unit the time unit of the timeout argument
Expand All @@ -530,7 +535,7 @@ fun <T> CompletableFuture<T>.join(timeout: Long, unit: TimeUnit): T =
* ```
*/
@Suppress("UNCHECKED_CAST")
fun <T> CompletableFuture<T>.cffuResultNow(): T =
fun <T> CompletableFuture<T>.resultNow(): T =
CompletableFutureUtils.resultNow(this) as T

/**
Expand All @@ -543,7 +548,7 @@ fun <T> CompletableFuture<T>.cffuResultNow(): T =
* or the task was cancelled
* @see CompletableFuture#resultNow()
*/
fun <T> CompletableFuture<T>.cffuExceptionNow(): Throwable =
fun <T> CompletableFuture<T>.exceptionNow(): Throwable =
CompletableFutureUtils.exceptionNow(this)

/**
Expand All @@ -566,7 +571,7 @@ fun <T> CompletableFuture<T>.cffuState(): CffuState =
* @param supplier a function returning the value to be used to complete given CompletableFuture
* @return given CompletableFuture
*/
fun <T> CompletableFuture<T>.cffuCompleteAsync(supplier: () -> T): CompletableFuture<T> =
fun <T> CompletableFuture<T>.completeAsync(supplier: Supplier<out T>): CompletableFuture<T> =
CompletableFutureUtils.completeAsync(this, supplier)

/**
Expand All @@ -577,7 +582,7 @@ fun <T> CompletableFuture<T>.cffuCompleteAsync(supplier: () -> T): CompletableFu
* @param executor the executor to use for asynchronous execution
* @return given CompletableFuture
*/
fun <T> CompletableFuture<T>.cffuCompleteAsync(supplier: () -> T, executor: Executor): CompletableFuture<T> =
fun <T> CompletableFuture<T>.completeAsync(supplier: Supplier<out T>, executor: Executor): CompletableFuture<T> =
CompletableFutureUtils.completeAsync(this, supplier, executor)

//# Re-Config methods
Expand All @@ -594,7 +599,7 @@ fun <T> CompletableFuture<T>.cffuCompleteAsync(supplier: () -> T, executor: Exec
*
* @return the new CompletionStage
*/
fun <T> CompletableFuture<T>.cffuMinimalCompletionStage(): CompletionStage<T> =
fun <T> CompletableFuture<T>.minimalCompletionStage(): CompletionStage<T> =
CompletableFutureUtils.minimalCompletionStage(this)

/**
Expand All @@ -606,7 +611,7 @@ fun <T> CompletableFuture<T>.cffuMinimalCompletionStage(): CompletionStage<T> =
*
* @return the new CompletableFuture
*/
fun <T> CompletableFuture<T>.cffuCopy(): CompletableFuture<T> =
fun <T> CompletableFuture<T>.copy(): CompletableFuture<T> =
CompletableFutureUtils.copy(this)

/**
Expand All @@ -615,5 +620,5 @@ fun <T> CompletableFuture<T>.cffuCopy(): CompletableFuture<T> =
* @param <T> the type of the value
* @return a new CompletableFuture
*/
fun <T, U> CompletableFuture<T>.cffuNewIncompleteFuture(): CompletableFuture<U> =
fun <T, U> CompletableFuture<T>.newIncompleteFuture(): CompletableFuture<U> =
CompletableFutureUtils.newIncompleteFuture(this)
Original file line number Diff line number Diff line change
Expand Up @@ -264,61 +264,59 @@ class CompletableFutureExtensionsTest : FunSpec({
////////////////////////////////////////////////////////////////////////////////
//# Backport CF instance methods
// compatibility for low Java version
//
// all methods name prefix with `cffu`
////////////////////////////////////////////////////////////////////////////////

test("exceptionallyAsync") {
val cf = CompletableFutureUtils.failedFuture<Int>(rte)
cf.cffuExceptionallyAsync { n }.get() shouldBe n
cf.cffuExceptionallyAsync({ n }, testThreadPoolExecutor).get() shouldBe n
cf.exceptionallyAsync { n }.get() shouldBe n
cf.exceptionallyAsync({ n }, testThreadPoolExecutor).get() shouldBe n
}

test(" test_timeout") {
test("test_timeout") {
var cf = CompletableFuture<Int>()
shouldThrow<ExecutionException> {
cf.cffuOrTimeout(1, TimeUnit.MILLISECONDS).get()
cf.orTimeout(1, TimeUnit.MILLISECONDS).get()
}.cause.shouldBeTypeOf<TimeoutException>()

cf = CompletableFuture()
cf.cffuCompleteOnTimeout(n, 1, TimeUnit.MILLISECONDS).get() shouldBe n
cf.completeOnTimeout(n, 1, TimeUnit.MILLISECONDS).get() shouldBe n
}

test("exceptionallyCompose") {
val cf = CompletableFutureUtils.failedFuture<Int>(rte)
cf.cffuExceptionallyCompose { CompletableFuture.completedFuture(n) }.get() shouldBe n
cf.cffuExceptionallyComposeAsync { CompletableFuture.completedFuture(n) }.get() shouldBe n
cf.cffuExceptionallyComposeAsync({ CompletableFuture.completedFuture(n) }, testThreadPoolExecutor)
cf.exceptionallyCompose { CompletableFuture.completedFuture(n) }.get() shouldBe n
cf.exceptionallyComposeAsync { CompletableFuture.completedFuture(n) }.get() shouldBe n
cf.exceptionallyComposeAsync({ CompletableFuture.completedFuture(n) }, testThreadPoolExecutor)
.get() shouldBe n
}

test("read") {
test("read methods") {
val cf = CompletableFuture.completedFuture(n)
val ff = CompletableFutureUtils.failedFuture<Int>(rte)

cf.join(1, TimeUnit.MILLISECONDS) shouldBe n
cf.cffuResultNow() shouldBe n
ff.cffuExceptionNow() shouldBeSameInstanceAs rte
cf.resultNow() shouldBe n
ff.exceptionNow() shouldBeSameInstanceAs rte

cf.cffuState() shouldBe CffuState.SUCCESS
ff.cffuState() shouldBe CffuState.FAILED
}

test("write") {
test("write methods") {
val cf = CompletableFuture<Int>()
cf.cffuCompleteAsync { n }.get() shouldBe n
cf.cffuCompleteAsync({ n }, testThreadPoolExecutor).get() shouldBe n
cf.completeAsync { n }.get() shouldBe n
cf.completeAsync({ n }, testThreadPoolExecutor).get() shouldBe n
}

test("re_config") {
CompletableFuture.completedFuture(n).cffuMinimalCompletionStage()
CompletableFuture.completedFuture(n).minimalCompletionStage()
.toCompletableFuture().get() shouldBe n

val cf = CompletableFuture<Int>()
cf.cffuCopy().complete(n)
cf.copy().complete(n)
cf.isDone.shouldBeFalse()

val incomplete = cf.cffuNewIncompleteFuture<Int, Any>()
val incomplete: CompletableFuture<Int> = cf.newIncompleteFuture()
incomplete.isDone.shouldBeFalse()
incomplete.complete(n)
cf.isDone.shouldBeFalse()
Expand Down
41 changes: 40 additions & 1 deletion scripts/integration_test
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,44 @@ for jdk_version in "${JDK_VERSIONS[@]}"; do

kt_version_var_name="KT_VERSION_FOR_JAVA${jdk_version}"
kt_version_opt="${!kt_version_var_name:+-Dkotlin.version=${!kt_version_var_name}}"
mvu::mvn_cmd ${CI_MORE_BEGIN_OPTS:-} dependency:properties surefire:test ${CI_MORE_END_OPTS:-} $kt_version_opt

pl=
if ((jdk_version < default_build_jdk_version)); then
pl='-pl cffu-core,cffu-ttl-executor-wrapper'
fi

mvu::mvn_cmd ${pl:-} ${CI_MORE_BEGIN_OPTS:-} dependency:properties surefire:test ${CI_MORE_END_OPTS:-} $kt_version_opt
done

# test the kotlin extension methods works
#
# CAUTION: MUST re-compile the client sources(test codes) by Java 8
# Extensions are resolved statically.
# If a class has a member function, and an extension function is defined
# which has the same receiver type, the same name, and is applicable to given arguments,
# the member always wins
# https://kotlinlang.org/docs/extensions.html#extensions-are-resolved-statically

jvu::switch_to_jdk 8

cd cffu-kotlin

# recompile cffu-kotlin and run test
rm -rf target/*test*
cu::head_line_echo "recompile and test cffu-kotlin with Java 8: $JAVA_HOME"
mvu::mvn_cmd -P'!gen-git-properties' -Dmaven.main.skip -Denforcer.skip ${CI_MORE_BEGIN_OPTS:-} dependency:properties test-compile surefire:test ${CI_MORE_END_OPTS:-}

for jdk_version in "${JDK_VERSIONS[@]}"; do
# skip default jdk, already tested above
((jdk_version == 8)) && continue

jvu::switch_to_jdk "$jdk_version"

# just test without build
cu::head_line_echo "test cffu-kotlin with Java $jdk_version: $JAVA_HOME"

kt_version_var_name="KT_VERSION_FOR_JAVA${jdk_version}"
kt_version_opt="${!kt_version_var_name:+-Dkotlin.version=${!kt_version_var_name}}"

mvu::mvn_cmd -Dmaven.main.skip -Denforcer.skip ${CI_MORE_BEGIN_OPTS:-} dependency:properties surefire:test ${CI_MORE_END_OPTS:-} $kt_version_opt
done

0 comments on commit 9f1856f

Please sign in to comment.