From f5705ff2e5dda71123ac934708471c8b24a4b88f Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Sun, 10 Sep 2023 22:03:44 +0200 Subject: [PATCH 1/3] feat: import the Mutiny workshop examples into the main repository --- pom.xml | 1 + workshop-examples/pom.xml | 36 ++++++ .../src/main/java/_01_basics/_01_Uni.java | 16 +++ .../_01_basics/_02_Uni_UniSubscriber.java | 33 ++++++ .../_01_basics/_03_Uni_From_Supplier.java | 22 ++++ .../_04_Uni_From_Supplier_And_State.java | 20 ++++ .../java/_01_basics/_05_Uni_Deferred.java | 22 ++++ .../java/_01_basics/_06_Uni_From_Emitter.java | 29 +++++ .../_07_Unit_From_Emitter_And_State.java | 21 ++++ .../java/_01_basics/_08_Uni_From_Failure.java | 20 ++++ .../_09_Uni_From_CompletionStage.java | 26 ++++ .../main/java/_01_basics/_10_Uni_Misc.java | 30 +++++ .../main/java/_01_basics/_11_Uni_Delay.java | 32 +++++ .../java/_01_basics/_12_Uni_Disjoint.java | 18 +++ .../src/main/java/_01_basics/_13_Multi.java | 47 ++++++++ .../java/_01_basics/_14_Multi_Subscriber.java | 42 +++++++ .../_01_basics/_15_Multi_From_Emitter.java | 37 ++++++ .../_16_Multi_Control_Subscription.java | 52 ++++++++ .../_01_basics/_17_Multi_By_Repeating.java | 59 ++++++++++ .../_01_basics/_18_Multi_From_Resource.java | 29 +++++ .../_01_basics/_19_Multi_From_Generator.java | 43 +++++++ .../java/_02_groups/_01_Uni_Event_Groups.java | 27 +++++ .../main/java/_02_groups/_02_Uni_Call.java | 33 ++++++ .../java/_02_groups/_03_Uni_Shortcuts.java | 21 ++++ .../_02_groups/_04_Multi_Event_Groups.java | 28 +++++ .../java/_02_groups/_05_Multi_Shortcuts.java | 19 +++ .../_01_Uni_Transform.java | 17 +++ .../_02_Uni_TransformToUni.java | 21 ++++ .../_03_Uni_Shortcuts.java | 22 ++++ .../_04_Uni_Stage.java | 39 ++++++ .../_05_Uni_Combine.java | 30 +++++ .../_06_Multi_Transform.java | 18 +++ .../_07_Multi_TransformToUni.java | 39 ++++++ .../_08_Multi_TransformToMulti.java | 44 +++++++ .../_09_Multi_Shortcuts.java | 44 +++++++ .../_10_Multi_Merge.java | 52 ++++++++ .../_11_Multi_Concatenate.java | 52 ++++++++ .../_12_Multi_Combine.java | 53 +++++++++ .../_13_Multi_Broadcast.java | 38 ++++++ .../_14_Multi_Aggregates.java | 111 ++++++++++++++++++ .../_15_Multi_Buckets.java | 19 +++ .../_16_Multi_Temporal_Buckets.java | 19 +++ .../_17_Multi_Disjoint.java | 19 +++ .../_18_Multi_To_Uni_And_Back.java | 21 ++++ .../_19_Multi_Select.java | 18 +++ .../_01_Uni_Failure_Transform.java | 18 +++ .../_02_Uni_Failure_Recover_With_Item.java | 18 +++ .../_03_Uni_Failure_Recover_With_Uni.java | 18 +++ .../_04_failures/_04_Uni_Failure_Retry.java | 28 +++++ .../_05_Uni_Failure_Retry_Bounded.java | 28 +++++ .../_06_Uni_Failure_Retry_ExpBackoff.java | 31 +++++ .../_07_Multi_Failure_Recover_Completing.java | 32 +++++ .../_08_Multi_Failure_Recover_With_Item.java | 32 +++++ .../_09_Multi_Failure_Recover_With_Multi.java | 32 +++++ .../_04_failures/_10_Multi_Failure_Retry.java | 33 ++++++ ..._11_Multi_Failure_Cancelling_Recovery.java | 23 ++++ .../_12_Multi_Failure_Guarded_Recovery.java | 31 +++++ .../main/java/_05_backpressure/_01_Drop.java | 54 +++++++++ .../java/_05_backpressure/_02_Buffer.java | 54 +++++++++ .../_05_backpressure/_03_Visual_Drop.java | 72 ++++++++++++ .../_05_backpressure/_04_Visual_Buffer.java | 72 ++++++++++++ .../_01_Threading_Subscription.java | 35 ++++++ .../_06_threading/_02_Threading_Emit.java | 35 ++++++ .../_06_threading/_03_Infra_Executor.java | 32 +++++ .../_06_threading/_04_Threading_Blocking.java | 28 +++++ .../_05_Threading_Blocking_Check.java | 35 ++++++ .../_07_misc/_01_Multi_Custom_Operator.java | 45 +++++++ .../src/main/java/_07_misc/_02_Logging.java | 25 ++++ .../src/main/java/_07_misc/_03_Context.java | 27 +++++ 69 files changed, 2277 insertions(+) create mode 100644 workshop-examples/pom.xml create mode 100755 workshop-examples/src/main/java/_01_basics/_01_Uni.java create mode 100755 workshop-examples/src/main/java/_01_basics/_02_Uni_UniSubscriber.java create mode 100755 workshop-examples/src/main/java/_01_basics/_03_Uni_From_Supplier.java create mode 100755 workshop-examples/src/main/java/_01_basics/_04_Uni_From_Supplier_And_State.java create mode 100755 workshop-examples/src/main/java/_01_basics/_05_Uni_Deferred.java create mode 100755 workshop-examples/src/main/java/_01_basics/_06_Uni_From_Emitter.java create mode 100755 workshop-examples/src/main/java/_01_basics/_07_Unit_From_Emitter_And_State.java create mode 100755 workshop-examples/src/main/java/_01_basics/_08_Uni_From_Failure.java create mode 100755 workshop-examples/src/main/java/_01_basics/_09_Uni_From_CompletionStage.java create mode 100755 workshop-examples/src/main/java/_01_basics/_10_Uni_Misc.java create mode 100755 workshop-examples/src/main/java/_01_basics/_11_Uni_Delay.java create mode 100755 workshop-examples/src/main/java/_01_basics/_12_Uni_Disjoint.java create mode 100755 workshop-examples/src/main/java/_01_basics/_13_Multi.java create mode 100755 workshop-examples/src/main/java/_01_basics/_14_Multi_Subscriber.java create mode 100755 workshop-examples/src/main/java/_01_basics/_15_Multi_From_Emitter.java create mode 100755 workshop-examples/src/main/java/_01_basics/_16_Multi_Control_Subscription.java create mode 100755 workshop-examples/src/main/java/_01_basics/_17_Multi_By_Repeating.java create mode 100755 workshop-examples/src/main/java/_01_basics/_18_Multi_From_Resource.java create mode 100755 workshop-examples/src/main/java/_01_basics/_19_Multi_From_Generator.java create mode 100755 workshop-examples/src/main/java/_02_groups/_01_Uni_Event_Groups.java create mode 100755 workshop-examples/src/main/java/_02_groups/_02_Uni_Call.java create mode 100755 workshop-examples/src/main/java/_02_groups/_03_Uni_Shortcuts.java create mode 100755 workshop-examples/src/main/java/_02_groups/_04_Multi_Event_Groups.java create mode 100755 workshop-examples/src/main/java/_02_groups/_05_Multi_Shortcuts.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_01_Uni_Transform.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_02_Uni_TransformToUni.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_03_Uni_Shortcuts.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_04_Uni_Stage.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_05_Uni_Combine.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_06_Multi_Transform.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_07_Multi_TransformToUni.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_08_Multi_TransformToMulti.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_09_Multi_Shortcuts.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_10_Multi_Merge.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_11_Multi_Concatenate.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_12_Multi_Combine.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_13_Multi_Broadcast.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_14_Multi_Aggregates.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_15_Multi_Buckets.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_16_Multi_Temporal_Buckets.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_17_Multi_Disjoint.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java create mode 100755 workshop-examples/src/main/java/_03_composition_transformation/_19_Multi_Select.java create mode 100755 workshop-examples/src/main/java/_04_failures/_01_Uni_Failure_Transform.java create mode 100755 workshop-examples/src/main/java/_04_failures/_02_Uni_Failure_Recover_With_Item.java create mode 100755 workshop-examples/src/main/java/_04_failures/_03_Uni_Failure_Recover_With_Uni.java create mode 100755 workshop-examples/src/main/java/_04_failures/_04_Uni_Failure_Retry.java create mode 100755 workshop-examples/src/main/java/_04_failures/_05_Uni_Failure_Retry_Bounded.java create mode 100755 workshop-examples/src/main/java/_04_failures/_06_Uni_Failure_Retry_ExpBackoff.java create mode 100755 workshop-examples/src/main/java/_04_failures/_07_Multi_Failure_Recover_Completing.java create mode 100755 workshop-examples/src/main/java/_04_failures/_08_Multi_Failure_Recover_With_Item.java create mode 100755 workshop-examples/src/main/java/_04_failures/_09_Multi_Failure_Recover_With_Multi.java create mode 100755 workshop-examples/src/main/java/_04_failures/_10_Multi_Failure_Retry.java create mode 100755 workshop-examples/src/main/java/_04_failures/_11_Multi_Failure_Cancelling_Recovery.java create mode 100755 workshop-examples/src/main/java/_04_failures/_12_Multi_Failure_Guarded_Recovery.java create mode 100755 workshop-examples/src/main/java/_05_backpressure/_01_Drop.java create mode 100755 workshop-examples/src/main/java/_05_backpressure/_02_Buffer.java create mode 100755 workshop-examples/src/main/java/_05_backpressure/_03_Visual_Drop.java create mode 100755 workshop-examples/src/main/java/_05_backpressure/_04_Visual_Buffer.java create mode 100755 workshop-examples/src/main/java/_06_threading/_01_Threading_Subscription.java create mode 100755 workshop-examples/src/main/java/_06_threading/_02_Threading_Emit.java create mode 100755 workshop-examples/src/main/java/_06_threading/_03_Infra_Executor.java create mode 100755 workshop-examples/src/main/java/_06_threading/_04_Threading_Blocking.java create mode 100755 workshop-examples/src/main/java/_06_threading/_05_Threading_Blocking_Check.java create mode 100755 workshop-examples/src/main/java/_07_misc/_01_Multi_Custom_Operator.java create mode 100755 workshop-examples/src/main/java/_07_misc/_02_Logging.java create mode 100755 workshop-examples/src/main/java/_07_misc/_03_Context.java diff --git a/pom.xml b/pom.xml index 5c511a47c..32290243d 100644 --- a/pom.xml +++ b/pom.xml @@ -73,6 +73,7 @@ kotlin bom math + workshop-examples diff --git a/workshop-examples/pom.xml b/workshop-examples/pom.xml new file mode 100644 index 000000000..71be2cc1e --- /dev/null +++ b/workshop-examples/pom.xml @@ -0,0 +1,36 @@ + + + + 4.0.0 + + + io.smallrye.reactive + mutiny-project + 999-SNAPSHOT + + + SmallRye Mutiny - Workshop examples + Workshop examples + mutiny-workshop-examples + + + + io.smallrye.reactive + mutiny + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + true + + + + + + diff --git a/workshop-examples/src/main/java/_01_basics/_01_Uni.java b/workshop-examples/src/main/java/_01_basics/_01_Uni.java new file mode 100755 index 000000000..b30bebd0e --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_01_Uni.java @@ -0,0 +1,16 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import io.smallrye.mutiny.Uni; + +public class _01_Uni { + + public static void main(String[] args) { + System.out.println("⚡️ Hello world"); + + Uni uni = Uni.createFrom().item("Hello, world!"); + + uni.subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_02_Uni_UniSubscriber.java b/workshop-examples/src/main/java/_01_basics/_02_Uni_UniSubscriber.java new file mode 100755 index 000000000..2f408f838 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_02_Uni_UniSubscriber.java @@ -0,0 +1,33 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.subscription.UniSubscriber; +import io.smallrye.mutiny.subscription.UniSubscription; + +public class _02_Uni_UniSubscriber { + + public static void main(String[] args) { + System.out.println("⚡️ Hello world with UniSubscriber"); + + Uni uni = Uni.createFrom().item("Hello, world!"); + + uni.subscribe().withSubscriber(new UniSubscriber() { + @Override + public void onSubscribe(UniSubscription subscription) { + System.out.println("onSubscribe"); + } + + @Override + public void onItem(String item) { + System.out.println("onItem: " + item); + } + + @Override + public void onFailure(Throwable failure) { + System.out.println("onFailure: " + failure.getMessage()); + } + }); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_03_Uni_From_Supplier.java b/workshop-examples/src/main/java/_01_basics/_03_Uni_From_Supplier.java new file mode 100755 index 000000000..5a967fe54 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_03_Uni_From_Supplier.java @@ -0,0 +1,22 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.Random; + +import io.smallrye.mutiny.Uni; + +public class _03_Uni_From_Supplier { + + public static void main(String[] args) { + System.out.println("️⚡️ Uni from supplier"); + + Random random = new Random(); + + Uni uniFromSupplier = Uni.createFrom().item(random::nextInt); + + for (var i = 0; i < 5; i++) { + uniFromSupplier.subscribe().with(System.out::println); + } + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_04_Uni_From_Supplier_And_State.java b/workshop-examples/src/main/java/_01_basics/_04_Uni_From_Supplier_And_State.java new file mode 100755 index 000000000..a72a5e4f7 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_04_Uni_From_Supplier_And_State.java @@ -0,0 +1,20 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.smallrye.mutiny.Uni; + +public class _04_Uni_From_Supplier_And_State { + + public static void main(String[] args) { + System.out.println("️⚡️ Uni from supplier with state"); + + Uni uniFromSupplierAndState = Uni.createFrom().item(AtomicInteger::new, i -> i.addAndGet(10)); + + for (var i = 0; i < 5; i++) { + uniFromSupplierAndState.subscribe().with(System.out::println); + } + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_05_Uni_Deferred.java b/workshop-examples/src/main/java/_01_basics/_05_Uni_Deferred.java new file mode 100755 index 000000000..9b5720f8d --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_05_Uni_Deferred.java @@ -0,0 +1,22 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.atomic.AtomicLong; + +import io.smallrye.mutiny.Uni; + +public class _05_Uni_Deferred { + + public static void main(String[] args) { + System.out.println("️⚡️ Deferred Uni"); + + AtomicLong ids = new AtomicLong(); + + Uni deferredUni = Uni.createFrom().deferred(() -> Uni.createFrom().item(ids::incrementAndGet)); + + for (var i = 0; i < 5; i++) { + deferredUni.subscribe().with(System.out::println); + } + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_06_Uni_From_Emitter.java b/workshop-examples/src/main/java/_01_basics/_06_Uni_From_Emitter.java new file mode 100755 index 000000000..52e706188 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_06_Uni_From_Emitter.java @@ -0,0 +1,29 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ForkJoinPool; + +import io.smallrye.mutiny.Uni; + +public class _06_Uni_From_Emitter { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Uni from emitter"); + + ForkJoinPool forkJoinPool = ForkJoinPool.commonPool(); + CountDownLatch emitterLatch = new CountDownLatch(1); + + Uni uniFromEmitter = Uni.createFrom().emitter(emitter -> { + forkJoinPool.submit(() -> { + emitter.complete("Hello"); + emitterLatch.countDown(); + }); + }); + + uniFromEmitter.subscribe().with(System.out::println); + + emitterLatch.await(); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_07_Unit_From_Emitter_And_State.java b/workshop-examples/src/main/java/_01_basics/_07_Unit_From_Emitter_And_State.java new file mode 100755 index 000000000..6d974866a --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_07_Unit_From_Emitter_And_State.java @@ -0,0 +1,21 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.atomic.AtomicInteger; + +import io.smallrye.mutiny.Uni; + +public class _07_Unit_From_Emitter_And_State { + + public static void main(String[] args) { + System.out.println("⚡️ Uni from emitter and state"); + + Uni uniFromEmitterAndState = Uni.createFrom() + .emitter(AtomicInteger::new, (i, e) -> e.complete(i.addAndGet(10))); + + for (var i = 0; i < 5; i++) { + uniFromEmitterAndState.subscribe().with(System.out::println); + } + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_08_Uni_From_Failure.java b/workshop-examples/src/main/java/_01_basics/_08_Uni_From_Failure.java new file mode 100755 index 000000000..8c8fba909 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_08_Uni_From_Failure.java @@ -0,0 +1,20 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.io.IOException; + +import io.smallrye.mutiny.Uni; + +public class _08_Uni_From_Failure { + + public static void main(String[] args) { + System.out.println("⚡️ Uni from failure"); + + Uni.createFrom().failure(new IOException("Boom")) + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + Uni.createFrom().failure(() -> new IOException("Badaboom")) + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_09_Uni_From_CompletionStage.java b/workshop-examples/src/main/java/_01_basics/_09_Uni_From_CompletionStage.java new file mode 100755 index 000000000..b17f1bb6d --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_09_Uni_From_CompletionStage.java @@ -0,0 +1,26 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; + +import io.smallrye.mutiny.Uni; + +public class _09_Uni_From_CompletionStage { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Uni from CompletionStage"); + + var cs = CompletableFuture + .supplyAsync(() -> "Hello!", delayedExecutor(1, TimeUnit.SECONDS)) + .thenApply(String::toUpperCase); + + Uni.createFrom().completionStage(cs) + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + Thread.sleep(2000); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_10_Uni_Misc.java b/workshop-examples/src/main/java/_01_basics/_10_Uni_Misc.java new file mode 100755 index 000000000..4a57ca8f0 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_10_Uni_Misc.java @@ -0,0 +1,30 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.Optional; + +import io.smallrye.mutiny.Uni; + +public class _10_Uni_Misc { + + public static void main(String[] args) { + System.out.println("⚡️ Misc"); + + Uni.createFrom().nothing() + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + Uni.createFrom().voidItem() + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + Uni.createFrom().nullItem() + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + Uni.createFrom().optional(Optional.of("Hello")) + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + Uni.createFrom().converter(i -> Uni.createFrom().item("[" + i + "]"), 10) + .subscribe().with(System.out::println, failure -> System.out.println(failure.getMessage())); + + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_11_Uni_Delay.java b/workshop-examples/src/main/java/_01_basics/_11_Uni_Delay.java new file mode 100755 index 000000000..1f8a06552 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_11_Uni_Delay.java @@ -0,0 +1,32 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.time.Duration; +import java.util.concurrent.TimeUnit; + +import io.smallrye.mutiny.Uni; + +public class _11_Uni_Delay { + + public static void main(String[] args) { + System.out.println("⚡️ Uni delay"); + + Uni.createFrom().item(666) + .onItem().delayIt().by(Duration.ofSeconds(1)) + .subscribe().with(System.out::println); + + System.out.println("⏰"); + + Uni.createFrom().item(666) + .onItem().delayIt() + .until(n -> Uni.createFrom().completionStage( + supplyAsync( + () -> "Ok", + delayedExecutor(5, TimeUnit.SECONDS)))) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_12_Uni_Disjoint.java b/workshop-examples/src/main/java/_01_basics/_12_Uni_Disjoint.java new file mode 100755 index 000000000..49a9f090e --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_12_Uni_Disjoint.java @@ -0,0 +1,18 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.List; + +import io.smallrye.mutiny.Uni; + +public class _12_Uni_Disjoint { + + public static void main(String[] args) { + System.out.println("⚡️ Uni disjoint"); + + Uni.createFrom().item(List.of(1, 2, 3, 4, 5)) + .onItem().disjoint() + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_13_Multi.java b/workshop-examples/src/main/java/_01_basics/_13_Multi.java new file mode 100755 index 000000000..2a9640d67 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_13_Multi.java @@ -0,0 +1,47 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import io.smallrye.mutiny.Multi; + +public class _13_Multi { + + public static void main(String[] args) { + System.out.println("⚡️ Hello world"); + + // -------------------------------------------------------------------------------------------------- // + + Multi.createFrom().items(1, 2, 3) + .subscribe().with( + subscription -> { + System.out.println("Subscription: " + subscription); + subscription.request(10); + }, + item -> System.out.println("Item: " + item), + failure -> System.out.println("Failure: " + failure.getMessage()), + () -> System.out.println("Completed")); + + // -------------------------------------------------------------------------------------------------- // + + System.out.println("----"); + + Multi.createFrom().range(10, 15) + .subscribe().with(System.out::println); + + var randomNumbers = Stream + .generate(ThreadLocalRandom.current()::nextInt) + .limit(5) + .collect(Collectors.toList()); + + // -------------------------------------------------------------------------------------------------- // + + System.out.println("----"); + + Multi.createFrom().iterable(randomNumbers) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_14_Multi_Subscriber.java b/workshop-examples/src/main/java/_01_basics/_14_Multi_Subscriber.java new file mode 100755 index 000000000..17ee6144f --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_14_Multi_Subscriber.java @@ -0,0 +1,42 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.Flow.Subscriber; +import java.util.concurrent.Flow.Subscription; + +import io.smallrye.mutiny.Multi; + +public class _14_Multi_Subscriber { + + public static void main(String[] args) { + System.out.println("⚡️ Hello world with subscriber"); + + Multi.createFrom().items(1, 2, 3).subscribe().withSubscriber(new Subscriber() { + private Subscription subscription; + + @Override + public void onSubscribe(Subscription subscription) { + System.out.println("onSubscribe()"); + this.subscription = subscription; + this.subscription.request(1); + } + + @Override + public void onNext(Integer integer) { + System.out.println("onNext: " + integer); + this.subscription.request(1); + } + + @Override + public void onError(Throwable t) { + System.out.println("onError: " + t.getMessage()); + } + + @Override + public void onComplete() { + System.out.println("onComplete()"); + } + }); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_15_Multi_From_Emitter.java b/workshop-examples/src/main/java/_01_basics/_15_Multi_From_Emitter.java new file mode 100755 index 000000000..ff8116070 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_15_Multi_From_Emitter.java @@ -0,0 +1,37 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; + +import io.smallrye.mutiny.Multi; + +public class _15_Multi_From_Emitter { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi from emitter"); + + ScheduledExecutorService service = Executors.newScheduledThreadPool(1); + + AtomicReference> ref = new AtomicReference<>(); + AtomicInteger counter = new AtomicInteger(); + CountDownLatch latch = new CountDownLatch(1); + + Multi.createFrom().emitter(emitter -> { + ref.set(service.scheduleAtFixedRate(() -> { + emitter.emit("tick"); + if (counter.getAndIncrement() == 5) { + ref.get().cancel(true); + emitter.complete(); + latch.countDown(); + } + }, 0, 500, TimeUnit.MILLISECONDS)); + }) + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("Done!")); + + latch.await(); + service.shutdown(); + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_16_Multi_Control_Subscription.java b/workshop-examples/src/main/java/_01_basics/_16_Multi_Control_Subscription.java new file mode 100755 index 000000000..24cc70121 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_16_Multi_Control_Subscription.java @@ -0,0 +1,52 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; +import java.util.concurrent.Flow.Subscription; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class _16_Multi_Control_Subscription { + + public static void main(String[] args) { + System.out.println("⚡️ Multi and subscription"); + + Multi.createFrom() + .ticks().every(Duration.of(1, ChronoUnit.SECONDS)) + .subscribe().withSubscriber(new MultiSubscriber() { + + private Subscription subscription; + private int counter = 0; + + @Override + public void onItem(Long tick) { + System.out.println("Tick: " + tick); + if (counter++ == 10) { + subscription.cancel(); + } else { + subscription.request(1); + } + } + + @Override + public void onFailure(Throwable throwable) { + throwable.printStackTrace(); + } + + @Override + public void onCompletion() { + System.out.println("Done!"); + } + + @Override + public void onSubscribe(Subscription subscription) { + this.subscription = subscription; + subscription.request(1); + } + }); + + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_17_Multi_By_Repeating.java b/workshop-examples/src/main/java/_01_basics/_17_Multi_By_Repeating.java new file mode 100755 index 000000000..dac2d5441 --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_17_Multi_By_Repeating.java @@ -0,0 +1,59 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _17_Multi_By_Repeating { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi by repeating"); + + Multi.createBy() + .repeating() + .supplier(Service::fetchValue) + .until(n -> n > 1_000_000L) + .subscribe().with(System.out::println); + + System.out.println("\n----\n"); + + CountDownLatch latch = new CountDownLatch(1); + + Multi.createBy() + .repeating() + .uni(Service::asyncFetchValue) + .atMost(10) + .subscribe().with(System.out::println, Throwable::printStackTrace, latch::countDown); + + latch.await(); + + System.out.println("\n----\n"); + + Multi.createBy() + .repeating() + .completionStage(Service::queryDb) + .whilst(n -> n < 1_000_000L) + .subscribe().with(System.out::println); + } + + static class Service { + + static long fetchValue() { + return ThreadLocalRandom.current().nextLong(1_001_000L); + } + + static Uni asyncFetchValue() { + return Uni.createFrom().completionStage(Service::queryDb); + } + + static CompletionStage queryDb() { + return CompletableFuture.supplyAsync(Service::fetchValue); + } + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_18_Multi_From_Resource.java b/workshop-examples/src/main/java/_01_basics/_18_Multi_From_Resource.java new file mode 100755 index 000000000..19a43aa1b --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_18_Multi_From_Resource.java @@ -0,0 +1,29 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import io.smallrye.mutiny.Multi; + +public class _18_Multi_From_Resource { + + public static void main(String[] args) { + System.out.println("⚡️ Multi from resource"); + + Multi.createFrom() + .resource(MyResource::new, MyResource::stream) + .withFinalizer(MyResource::close) + .subscribe().with(System.out::println); + } + + static class MyResource { + + public Multi stream() { + System.out.println("stream()"); + return Multi.createFrom().range(0, 10); + } + + public void close() { + System.out.println("close()"); + } + } +} diff --git a/workshop-examples/src/main/java/_01_basics/_19_Multi_From_Generator.java b/workshop-examples/src/main/java/_01_basics/_19_Multi_From_Generator.java new file mode 100755 index 000000000..b6fad449f --- /dev/null +++ b/workshop-examples/src/main/java/_01_basics/_19_Multi_From_Generator.java @@ -0,0 +1,43 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _01_basics; + +import java.util.ArrayList; +import java.util.LinkedList; +import java.util.List; +import java.util.Random; + +import io.smallrye.mutiny.Multi; + +public class _19_Multi_From_Generator { + + public static void main(String[] args) { + System.out.println("⚡️ Multi from generator"); + + Multi.createFrom() + .generator(MyState::new, (state, emitter) -> { + List list = state.produceorNull(); + if (list != null) { + emitter.emit(list); + } + return state; + }) + .select().first(10) + .subscribe().with(System.out::println); + } + + private static class MyState { + Random random = new Random(); + LinkedList list = new LinkedList<>(); + + List produceorNull() { + list.add(random.nextInt(10)); + if (list.size() > 3) { + list.remove(0); + return new ArrayList<>(list); + } else { + return null; + } + } + } +} diff --git a/workshop-examples/src/main/java/_02_groups/_01_Uni_Event_Groups.java b/workshop-examples/src/main/java/_02_groups/_01_Uni_Event_Groups.java new file mode 100755 index 000000000..34f1986e1 --- /dev/null +++ b/workshop-examples/src/main/java/_02_groups/_01_Uni_Event_Groups.java @@ -0,0 +1,27 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _02_groups; + +import java.time.Duration; +import java.time.temporal.ChronoUnit; + +import io.smallrye.mutiny.Uni; + +public class _01_Uni_Event_Groups { + + public static void main(String[] args) { + System.out.println("⚡️ Uni inspect events"); + + var result = Uni.createFrom().item("Hello") + .onSubscription().invoke(sub -> System.out.println("onSubscribe " + sub)) + .onCancellation().invoke(() -> System.out.println("onCancellation")) + .onItem().invoke(item -> System.out.println("onItem " + item)) + .onFailure().invoke(failure -> System.out.println("onFailure " + failure)) + .onItemOrFailure().invoke((item, failure) -> System.out.println("onItemOrFailure " + item + ", " + failure)) + .onTermination().invoke((s, t, c) -> System.out.println("onTermination " + s + ", " + t + ", " + c)) + .await().atMost(Duration.of(5, ChronoUnit.SECONDS)); + + System.out.println("📦 uni result = " + result); + System.out.println("⚠️ This was a blocking operation"); + } +} diff --git a/workshop-examples/src/main/java/_02_groups/_02_Uni_Call.java b/workshop-examples/src/main/java/_02_groups/_02_Uni_Call.java new file mode 100755 index 000000000..bfe3deb7a --- /dev/null +++ b/workshop-examples/src/main/java/_02_groups/_02_Uni_Call.java @@ -0,0 +1,33 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _02_groups; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; + +import io.smallrye.mutiny.Uni; + +public class _02_Uni_Call { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Uni call()"); + + CountDownLatch latch = new CountDownLatch(1); + + Uni.createFrom().item(123) + .onItem().call(n -> asyncLog(">>> ", n)) + .onTermination().call(() -> asyncLog("--- ", "")) + .subscribe().with(x -> { + System.out.println(x); + latch.countDown(); + }); + + latch.await(); + } + + static Uni asyncLog(String prefix, Object value) { + var cs = CompletableFuture + .runAsync(() -> System.out.println(Thread.currentThread() + " :: " + prefix + value)); + return Uni.createFrom().completionStage(cs); + } +} diff --git a/workshop-examples/src/main/java/_02_groups/_03_Uni_Shortcuts.java b/workshop-examples/src/main/java/_02_groups/_03_Uni_Shortcuts.java new file mode 100755 index 000000000..a5cd3cc21 --- /dev/null +++ b/workshop-examples/src/main/java/_02_groups/_03_Uni_Shortcuts.java @@ -0,0 +1,21 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _02_groups; + +import io.smallrye.mutiny.Uni; + +public class _03_Uni_Shortcuts { + + public static void main(String[] args) { + System.out.println("⚡️ Uni invoke / call shortcuts"); + + Uni.createFrom().item(123) + .invoke(n -> System.out.println("n = " + n)) + .call(n -> Uni.createFrom() + .voidItem() + .invoke(() -> System.out.println("call(" + n + ")"))) + .eventually(() -> System.out.println("eventually()")) + .subscribe().with(System.out::println); + + } +} diff --git a/workshop-examples/src/main/java/_02_groups/_04_Multi_Event_Groups.java b/workshop-examples/src/main/java/_02_groups/_04_Multi_Event_Groups.java new file mode 100755 index 000000000..1dc3a11c7 --- /dev/null +++ b/workshop-examples/src/main/java/_02_groups/_04_Multi_Event_Groups.java @@ -0,0 +1,28 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _02_groups; + +import java.util.stream.Collectors; + +import io.smallrye.mutiny.Multi; + +public class _04_Multi_Event_Groups { + + public static void main(String[] args) { + System.out.println("⚡️ Multi inspect events"); + + var items = Multi.createFrom().range(1, 6) + .onSubscription().invoke(sub -> System.out.println("onSubscribe " + sub)) + .onRequest().invoke(count -> System.out.println("onRequest " + count)) + .onCancellation().invoke(() -> System.out.println("onCancellation")) + .onItem().invoke(item -> System.out.println("onItem " + item)) + .onFailure().invoke(failure -> System.out.println("onFailure " + failure)) + .onCompletion().invoke(() -> System.out.println("onCompletion")) + .onTermination().invoke((t, c) -> System.out.println("onTermination " + t + ", c")) + .subscribe() + .asStream().collect(Collectors.toList()); + + System.out.println("📦 multi items = " + items); + System.out.println("⚠️ This was a blocking operation"); + } +} diff --git a/workshop-examples/src/main/java/_02_groups/_05_Multi_Shortcuts.java b/workshop-examples/src/main/java/_02_groups/_05_Multi_Shortcuts.java new file mode 100755 index 000000000..51873478d --- /dev/null +++ b/workshop-examples/src/main/java/_02_groups/_05_Multi_Shortcuts.java @@ -0,0 +1,19 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _02_groups; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _05_Multi_Shortcuts { + + public static void main(String[] args) { + System.out.println("⚡️ Multi invoke / call shortcuts"); + + Multi.createFrom().range(1, 10) + .invoke(n -> System.out.println("n = " + n)) + .call(n -> Uni.createFrom() + .voidItem().invoke(() -> System.out.println("call(" + n + ")"))) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_01_Uni_Transform.java b/workshop-examples/src/main/java/_03_composition_transformation/_01_Uni_Transform.java new file mode 100755 index 000000000..1aef9f951 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_01_Uni_Transform.java @@ -0,0 +1,17 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Uni; + +public class _01_Uni_Transform { + + public static void main(String[] args) { + System.out.println("⚡️ Uni transform a value with a function"); + + Uni.createFrom().item(123) + .onItem().transform(n -> n * 100) + .onItem().transform(Object::toString) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_02_Uni_TransformToUni.java b/workshop-examples/src/main/java/_03_composition_transformation/_02_Uni_TransformToUni.java new file mode 100755 index 000000000..cc5286eb4 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_02_Uni_TransformToUni.java @@ -0,0 +1,21 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Uni; + +public class _02_Uni_TransformToUni { + + public static void main(String[] args) { + System.out.println("⚡️ Uni transform a value with a Uni"); + + Uni.createFrom().item(123) + .onItem().transformToUni(n -> increase(n)) + .onItem().transformToUni((n, emitter) -> emitter.complete("[" + n + "]")) + .subscribe().with(System.out::println); + } + + static Uni increase(int n) { + return Uni.createFrom().item(n * 100); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_03_Uni_Shortcuts.java b/workshop-examples/src/main/java/_03_composition_transformation/_03_Uni_Shortcuts.java new file mode 100755 index 000000000..8e1241f6e --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_03_Uni_Shortcuts.java @@ -0,0 +1,22 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Uni; + +public class _03_Uni_Shortcuts { + + public static void main(String[] args) { + System.out.println("⚡️ Uni transform a value with shortcuts"); + + Uni.createFrom().item(123) + .replaceWith(Uni.createFrom().item(456)) + .chain(n -> increase(n)) // or flatMap + .map(n -> "[" + n + "]") + .subscribe().with(System.out::println); + } + + static Uni increase(int n) { + return Uni.createFrom().item(n * 100); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_04_Uni_Stage.java b/workshop-examples/src/main/java/_03_composition_transformation/_04_Uni_Stage.java new file mode 100755 index 000000000..baa2f2243 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_04_Uni_Stage.java @@ -0,0 +1,39 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import java.io.IOException; + +import io.smallrye.mutiny.Uni; + +public class _04_Uni_Stage { + + public static void main(String[] args) { + System.out.println("⚡️ Stages (with Uni)"); + + Uni.createFrom().item(123) + .stage(uni -> processEvents(uni)) + .stage(uni -> handleItem(uni)) + .stage(uni -> handleFailure(uni)) + .subscribe().with(System.out::println, Throwable::printStackTrace); + + } + + private static Uni handleFailure(Uni uni) { + return uni + .onFailure().invoke(failure -> System.out.println("There is a failure: " + failure.getMessage())) + .onFailure(IOException.class).recoverWithItem(-1); + } + + private static Uni handleItem(Uni uni) { + return uni + .onItem().ifNotNull().invoke(() -> System.out.println("The item is not null")) + .onItem().transform(n -> n * 10); + } + + private static Uni processEvents(Uni uni) { + return uni + .onSubscription().invoke(() -> System.out.println("onSubscribe")) + .onItem().invoke(n -> System.out.println("item = " + n)); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_05_Uni_Combine.java b/workshop-examples/src/main/java/_03_composition_transformation/_05_Uni_Combine.java new file mode 100755 index 000000000..f2168ac9c --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_05_Uni_Combine.java @@ -0,0 +1,30 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Uni; + +public class _05_Uni_Combine { + + public static void main(String[] args) { + System.out.println("⚡️ Uni combine"); + + var first = Uni.createFrom().item(1); + var second = Uni.createFrom().item(2); + var third = Uni.createFrom().item(3); + + Uni.combine() + .all().unis(first, second, third) + .asTuple() + .subscribe().with(System.out::println); + + Uni.combine() + .all().unis(first, second, third) + .combinedWith((a, b, c) -> a + b + c) + .subscribe().with(System.out::println); + + Uni.combine() + .any().of(first, second, third) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_06_Multi_Transform.java b/workshop-examples/src/main/java/_03_composition_transformation/_06_Multi_Transform.java new file mode 100755 index 000000000..a14fc5fe1 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_06_Multi_Transform.java @@ -0,0 +1,18 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Multi; + +public class _06_Multi_Transform { + + public static void main(String[] args) { + System.out.println("⚡️ Multi transformations"); + + Multi.createFrom().range(1, 100) + .select().where(n -> n % 2 == 0) + .select().last(5) + .onItem().transform(n -> "[" + n + "]") + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_07_Multi_TransformToUni.java b/workshop-examples/src/main/java/_03_composition_transformation/_07_Multi_TransformToUni.java new file mode 100755 index 000000000..76a7f7c37 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_07_Multi_TransformToUni.java @@ -0,0 +1,39 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _07_Multi_TransformToUni { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi transformations with Uni"); + + CountDownLatch latch = new CountDownLatch(1); + + Multi.createFrom().range(1, 100) + .select().where(n -> n % 2 == 0) + .select().last(5) + .onItem().transformToUniAndMerge(n -> increase(n)) // try transformToUniAndConcatenate + .onItem().transform(n -> "[" + n + "]") + .onCompletion().invoke(latch::countDown) + .subscribe().with(System.out::println); + + latch.await(); + } + + static Uni increase(int n) { + var cs = CompletableFuture.supplyAsync( + () -> n * 100, + delayedExecutor(ThreadLocalRandom.current().nextInt(500), TimeUnit.MILLISECONDS)); + return Uni.createFrom().completionStage(cs); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_08_Multi_TransformToMulti.java b/workshop-examples/src/main/java/_03_composition_transformation/_08_Multi_TransformToMulti.java new file mode 100755 index 000000000..f55db1690 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_08_Multi_TransformToMulti.java @@ -0,0 +1,44 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.runAsync; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import io.smallrye.mutiny.Multi; + +public class _08_Multi_TransformToMulti { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi transformations to Multi"); + + CountDownLatch latch = new CountDownLatch(1); + + Multi.createFrom().range(1, 100) + .select().where(n -> n % 2 == 0) + .select().last(5) + .onItem().transformToMultiAndMerge(n -> query(n)) // try transformToMultiAndConcatenate + .onItem().transform(n -> "[" + n + "]") + .onCompletion().invoke(latch::countDown) + .subscribe().with(System.out::println); + + latch.await(); + } + + static Multi query(int n) { + return Multi.createFrom().emitter(emitter -> { + runAsync( + () -> { + emitter.emit(n); + emitter.emit(n * 10); + emitter.emit(n * 100); + emitter.complete(); + }, + delayedExecutor(ThreadLocalRandom.current().nextInt(500), TimeUnit.MILLISECONDS)); + }); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_09_Multi_Shortcuts.java b/workshop-examples/src/main/java/_03_composition_transformation/_09_Multi_Shortcuts.java new file mode 100755 index 000000000..1450d9632 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_09_Multi_Shortcuts.java @@ -0,0 +1,44 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.runAsync; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; + +import io.smallrye.mutiny.Multi; + +public class _09_Multi_Shortcuts { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi transformations to Multi with shortcuts"); + + CountDownLatch latch = new CountDownLatch(1); + + Multi.createFrom().range(1, 100) + .filter(n -> n % 2 == 0) + .select().last(5) + .flatMap(n -> query(n)) // try concatMap + .map(n -> "[" + n + "]") + .onCompletion().invoke(latch::countDown) + .subscribe().with(System.out::println); + + latch.await(); + } + + static Multi query(int n) { + return Multi.createFrom().emitter(emitter -> { + runAsync( + () -> { + emitter.emit(n); + emitter.emit(n * 10); + emitter.emit(n * 100); + emitter.complete(); + }, + delayedExecutor(ThreadLocalRandom.current().nextInt(500), TimeUnit.MILLISECONDS)); + }); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_10_Multi_Merge.java b/workshop-examples/src/main/java/_03_composition_transformation/_10_Multi_Merge.java new file mode 100755 index 000000000..2c1877698 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_10_Multi_Merge.java @@ -0,0 +1,52 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _10_Multi_Merge { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi merge"); + + var firstGenerator = new Generator(0); + var secondGenerator = new Generator(100); + CountDownLatch latch = new CountDownLatch(1); + + var first = Multi.createBy().repeating() + .uni(firstGenerator::next).atMost(10); + + var second = Multi.createBy().repeating() + .uni(secondGenerator::next).atMost(10); + + Multi.createBy().merging().streams(first, second) + .onTermination().invoke(latch::countDown) + .subscribe().with(System.out::println); + + latch.await(); + } + + private static class Generator { + final AtomicLong aLong; + + Generator(long start) { + aLong = new AtomicLong(start); + } + + Uni next() { + return Uni.createFrom().completionStage( + supplyAsync( + aLong::getAndIncrement, + delayedExecutor(ThreadLocalRandom.current().nextInt(500), TimeUnit.MILLISECONDS))); + } + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_11_Multi_Concatenate.java b/workshop-examples/src/main/java/_03_composition_transformation/_11_Multi_Concatenate.java new file mode 100755 index 000000000..4f9e30c1d --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_11_Multi_Concatenate.java @@ -0,0 +1,52 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _11_Multi_Concatenate { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi concatenation"); + + var firstGenerator = new Generator(0); + var secondGenerator = new Generator(100); + CountDownLatch latch = new CountDownLatch(1); + + var first = Multi.createBy().repeating() + .uni(firstGenerator::next).atMost(10); + + var second = Multi.createBy().repeating() + .uni(secondGenerator::next).atMost(10); + + Multi.createBy().concatenating().streams(first, second) + .onTermination().invoke(latch::countDown) + .subscribe().with(System.out::println); + + latch.await(); + } + + private static class Generator { + final AtomicLong aLong; + + Generator(long start) { + aLong = new AtomicLong(start); + } + + Uni next() { + return Uni.createFrom().completionStage( + supplyAsync( + aLong::getAndIncrement, + delayedExecutor(ThreadLocalRandom.current().nextInt(500), TimeUnit.MILLISECONDS))); + } + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_12_Multi_Combine.java b/workshop-examples/src/main/java/_03_composition_transformation/_12_Multi_Combine.java new file mode 100755 index 000000000..74f9cde14 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_12_Multi_Combine.java @@ -0,0 +1,53 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _12_Multi_Combine { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi combination"); + + var firstGenerator = new Generator(0); + var secondGenerator = new Generator(100); + CountDownLatch latch = new CountDownLatch(1); + + var first = Multi.createBy().repeating() + .uni(firstGenerator::next).atMost(10); + + var second = Multi.createBy().repeating() + .uni(secondGenerator::next).atMost(10); + + Multi.createBy().combining().streams(first, second) + .asTuple() // also try lastItems() + .onTermination().invoke(latch::countDown) + .subscribe().with(System.out::println); + + latch.await(); + } + + private static class Generator { + final AtomicLong aLong; + + Generator(long start) { + aLong = new AtomicLong(start); + } + + Uni next() { + return Uni.createFrom().completionStage( + supplyAsync( + aLong::getAndIncrement, + delayedExecutor(ThreadLocalRandom.current().nextInt(500), TimeUnit.MILLISECONDS))); + } + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_13_Multi_Broadcast.java b/workshop-examples/src/main/java/_03_composition_transformation/_13_Multi_Broadcast.java new file mode 100755 index 000000000..544a97dfb --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_13_Multi_Broadcast.java @@ -0,0 +1,38 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import io.smallrye.mutiny.Multi; + +public class _13_Multi_Broadcast { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi broadcast"); + + var counter = new AtomicInteger(); + var executor = Executors.newCachedThreadPool(); + + var multi = Multi.createBy() + .repeating().supplier(counter::getAndIncrement) + .atMost(10) + .broadcast().toAllSubscribers(); + + executor.submit(() -> multi + .onItem().transform(n -> "🚀 " + n) + .subscribe().with(System.out::println)); + + executor.submit(() -> multi + .onItem().transform(n -> "🧪 " + n) + .subscribe().with(System.out::println)); + + executor.submit(() -> multi + .onItem().transform(n -> "💡 " + n) + .subscribe().with(System.out::println)); + + executor.awaitTermination(10, TimeUnit.SECONDS); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_14_Multi_Aggregates.java b/workshop-examples/src/main/java/_03_composition_transformation/_14_Multi_Aggregates.java new file mode 100755 index 000000000..f66e5a8fb --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_14_Multi_Aggregates.java @@ -0,0 +1,111 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import java.util.Arrays; +import java.util.List; +import java.util.UUID; +import java.util.concurrent.ThreadLocalRandom; +import java.util.stream.Collectors; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _14_Multi_Aggregates { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi aggregates"); + + // ------------------------------------------------------------------ // + + var persons = Multi.createBy().repeating() + .supplier(() -> generate()).atMost(100); + + // ------------------------------------------------------------------ // + + System.out.println(); + + persons + .onItem().scan(() -> 0, (count, next) -> count + 1) + .subscribe().with(count -> System.out.println("We have " + count + " persons")); + + // ------------------------------------------------------------------ // + + System.out.println(); + + persons + .collect().with(Collectors.counting()) + .subscribe().with(count -> System.out.println("We have " + count + " persons")); + + // ------------------------------------------------------------------ // + + System.out.println(); + + persons + .select().where(person -> person.city.equals("Nevers")) + .collect().asList() + .subscribe().with(list -> System.out.println("They live in Nevers: " + list)); + + // ------------------------------------------------------------------ // + + System.out.println(); + + persons + .select().where(person -> person.city.equals("Nevers")) + .collect().in( + StringBuilder::new, + (acc, next) -> acc.append("\n") + .append(" -> ") + .append(next.identifier)) + .subscribe().with(list -> System.out.println("They live in Nevers: " + list)); + + // ------------------------------------------------------------------ // + + System.out.println(); + + var agePerCity = persons + .group().by(person -> person.city) + .onItem().transformToUni(group -> { + String city = group.key(); + Uni avg = group.collect().with(Collectors.averagingInt(person -> person.age)); + return avg.onItem().transform(res -> "Average age in " + city + " is " + res); + }).merge(); + + agePerCity.subscribe().with(System.out::println); + } + + // ------------------------------------------------------------------ // + + static Person generate() { + var rand = ThreadLocalRandom.current(); + return new Person( + UUID.randomUUID().toString(), + rand.nextInt(18, 50), + cities.get(rand.nextInt(cities.size()))); + } + + static List cities = Arrays.asList("Lyon", "Tassin La Demi Lune", "Clermont-Ferrand", "Nevers"); + + // ------------------------------------------------------------------ // + + private static class Person { + final String identifier; + final int age; + final String city; + + Person(String identifier, int age, String city) { + this.identifier = identifier; + this.age = age; + this.city = city; + } + + @Override + public String toString() { + return "Person{" + + "identifier='" + identifier + '\'' + + ", age=" + age + + ", city='" + city + '\'' + + '}'; + } + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_15_Multi_Buckets.java b/workshop-examples/src/main/java/_03_composition_transformation/_15_Multi_Buckets.java new file mode 100755 index 000000000..79d65548d --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_15_Multi_Buckets.java @@ -0,0 +1,19 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import java.time.Duration; + +import io.smallrye.mutiny.Multi; + +public class _15_Multi_Buckets { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi buckets"); + + Multi.createFrom() + .ticks().every(Duration.ofMillis(200)) + .group().intoLists().of(5) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_16_Multi_Temporal_Buckets.java b/workshop-examples/src/main/java/_03_composition_transformation/_16_Multi_Temporal_Buckets.java new file mode 100755 index 000000000..5f7e7d86b --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_16_Multi_Temporal_Buckets.java @@ -0,0 +1,19 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import java.time.Duration; + +import io.smallrye.mutiny.Multi; + +public class _16_Multi_Temporal_Buckets { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi temporal buckets"); + + Multi.createFrom() + .ticks().every(Duration.ofMillis(200)) + .group().intoLists().every(Duration.ofSeconds(2)) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_17_Multi_Disjoint.java b/workshop-examples/src/main/java/_03_composition_transformation/_17_Multi_Disjoint.java new file mode 100755 index 000000000..9c32bb9df --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_17_Multi_Disjoint.java @@ -0,0 +1,19 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Multi; + +public class _17_Multi_Disjoint { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi disjoint items"); + + Multi.createFrom().range(0, 10) + .onItem().transformToMultiAndMerge(n -> Multi.createFrom().items(n, n * 2, n, n * 5, n, n * 10)) + .group().intoLists().of(3) + .onItem().invoke(list -> System.out.println(">>> " + list)) + .onItem().disjoint() + .subscribe().with(System.out::println, Throwable::printStackTrace); + } +} diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java b/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java new file mode 100755 index 000000000..e4c9e8715 --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java @@ -0,0 +1,21 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _18_Multi_To_Uni_And_Back { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi <-> Uni"); + + Multi.createFrom().range(1, 10) + .toUni() + .subscribe().with(System.out::println); + + Uni.createFrom().item(123) + .toMulti() + .subscribe().with(System.out::println); + } +} \ No newline at end of file diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_19_Multi_Select.java b/workshop-examples/src/main/java/_03_composition_transformation/_19_Multi_Select.java new file mode 100755 index 000000000..d83890f1b --- /dev/null +++ b/workshop-examples/src/main/java/_03_composition_transformation/_19_Multi_Select.java @@ -0,0 +1,18 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _03_composition_transformation; + +import io.smallrye.mutiny.Multi; + +public class _19_Multi_Select { + + public static void main(String[] args) throws InterruptedException { + System.out.println("⚡️ Multi select"); + + Multi.createFrom().range(1, 100) + .skip().first(10) + .select().where(n -> n % 2 == 0) + .select().last(10) + .subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_01_Uni_Failure_Transform.java b/workshop-examples/src/main/java/_04_failures/_01_Uni_Failure_Transform.java new file mode 100755 index 000000000..d6e0c0c50 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_01_Uni_Failure_Transform.java @@ -0,0 +1,18 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; + +import io.smallrye.mutiny.Uni; + +public class _01_Uni_Failure_Transform { + + public static void main(String[] args) { + System.out.println("⚡️ Uni failure transformation"); + + Uni.createFrom().failure(new IOException("Boom")) + .onFailure(IOException.class).transform(RuntimeException::new) + .subscribe().with(System.out::println, Throwable::printStackTrace); + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_02_Uni_Failure_Recover_With_Item.java b/workshop-examples/src/main/java/_04_failures/_02_Uni_Failure_Recover_With_Item.java new file mode 100755 index 000000000..379a0dca0 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_02_Uni_Failure_Recover_With_Item.java @@ -0,0 +1,18 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; + +import io.smallrye.mutiny.Uni; + +public class _02_Uni_Failure_Recover_With_Item { + + public static void main(String[] args) { + System.out.println("⚡️ Uni failure recover with item"); + + Uni.createFrom().failure(new IOException("Boom")) + .onFailure(IOException.class).recoverWithItem(Throwable::getMessage) + .subscribe().with(System.out::println, Throwable::printStackTrace); + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_03_Uni_Failure_Recover_With_Uni.java b/workshop-examples/src/main/java/_04_failures/_03_Uni_Failure_Recover_With_Uni.java new file mode 100755 index 000000000..2825f7ca1 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_03_Uni_Failure_Recover_With_Uni.java @@ -0,0 +1,18 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; + +import io.smallrye.mutiny.Uni; + +public class _03_Uni_Failure_Recover_With_Uni { + + public static void main(String[] args) { + System.out.println("⚡️ Uni failure recover with Uni"); + + Uni.createFrom().failure(new IOException("Boom")) + .onFailure(IOException.class).recoverWithUni(t -> Uni.createFrom().item("N/A -> " + t.getMessage())) + .subscribe().with(System.out::println, Throwable::printStackTrace); + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_04_Uni_Failure_Retry.java b/workshop-examples/src/main/java/_04_failures/_04_Uni_Failure_Retry.java new file mode 100755 index 000000000..772df6a0a --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_04_Uni_Failure_Retry.java @@ -0,0 +1,28 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.subscription.UniEmitter; + +public class _04_Uni_Failure_Retry { + + public static void main(String[] args) { + System.out.println("⚡️ Uni failure retry"); + + Uni.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().invoke(() -> System.out.println("Failed")) + .onFailure().retry().indefinitely() + .subscribe().with(System.out::println); + } + + private static void generate(UniEmitter emitter) { + if (ThreadLocalRandom.current().nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.complete("Ok"); + } else { + emitter.fail(new RuntimeException("Boom")); + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_05_Uni_Failure_Retry_Bounded.java b/workshop-examples/src/main/java/_04_failures/_05_Uni_Failure_Retry_Bounded.java new file mode 100755 index 000000000..9b0342330 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_05_Uni_Failure_Retry_Bounded.java @@ -0,0 +1,28 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.subscription.UniEmitter; + +public class _05_Uni_Failure_Retry_Bounded { + + public static void main(String[] args) { + System.out.println("⚡️ Uni failure retry"); + + Uni.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().invoke(() -> System.out.println("Failed")) + .onFailure().retry().atMost(10) + .subscribe().with(System.out::println, Throwable::printStackTrace); + } + + private static void generate(UniEmitter emitter) { + if (ThreadLocalRandom.current().nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.complete("Ok"); + } else { + emitter.fail(new RuntimeException("Boom")); + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_06_Uni_Failure_Retry_ExpBackoff.java b/workshop-examples/src/main/java/_04_failures/_06_Uni_Failure_Retry_ExpBackoff.java new file mode 100755 index 000000000..6eabbc1bd --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_06_Uni_Failure_Retry_ExpBackoff.java @@ -0,0 +1,31 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.time.Duration; +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.subscription.UniEmitter; + +public class _06_Uni_Failure_Retry_ExpBackoff { + + public static void main(String[] args) { + System.out.println("⚡️ Uni failure retry"); + + long start = System.currentTimeMillis(); + + Uni.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().invoke(() -> System.out.println("Failed after " + (System.currentTimeMillis() - start) + "ms")) + .onFailure().retry().withBackOff(Duration.ofMillis(100), Duration.ofMillis(1000)).expireIn(10_000) + .subscribe().with(System.out::println, Throwable::printStackTrace); + } + + private static void generate(UniEmitter emitter) { + if (ThreadLocalRandom.current().nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.complete("Ok"); + } else { + emitter.fail(new RuntimeException("Boom")); + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_07_Multi_Failure_Recover_Completing.java b/workshop-examples/src/main/java/_04_failures/_07_Multi_Failure_Recover_Completing.java new file mode 100755 index 000000000..b4655a609 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_07_Multi_Failure_Recover_Completing.java @@ -0,0 +1,32 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiEmitter; + +public class _07_Multi_Failure_Recover_Completing { + + public static void main(String[] args) { + System.out.println("⚡️ Multi failure recover by completing"); + + Multi.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().recoverWithCompletion() + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("✅")); + } + + private static void generate(MultiEmitter emitter) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + while (true) { + if (random.nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.fail(new IOException("Boom")); + return; + } else { + emitter.emit(random.nextInt(0, 100)); + } + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_08_Multi_Failure_Recover_With_Item.java b/workshop-examples/src/main/java/_04_failures/_08_Multi_Failure_Recover_With_Item.java new file mode 100755 index 000000000..15335b3b5 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_08_Multi_Failure_Recover_With_Item.java @@ -0,0 +1,32 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiEmitter; + +public class _08_Multi_Failure_Recover_With_Item { + + public static void main(String[] args) { + System.out.println("⚡️ Multi failure recover with item"); + + Multi.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().recoverWithItem(() -> 666) + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("✅")); + } + + private static void generate(MultiEmitter emitter) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + while (true) { + if (random.nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.fail(new IOException("Boom")); + return; + } else { + emitter.emit(random.nextInt(0, 100)); + } + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_09_Multi_Failure_Recover_With_Multi.java b/workshop-examples/src/main/java/_04_failures/_09_Multi_Failure_Recover_With_Multi.java new file mode 100755 index 000000000..9c0b529bd --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_09_Multi_Failure_Recover_With_Multi.java @@ -0,0 +1,32 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiEmitter; + +public class _09_Multi_Failure_Recover_With_Multi { + + public static void main(String[] args) { + System.out.println("⚡️ Multi failure recover with Multi"); + + Multi.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().recoverWithMulti(() -> Multi.createFrom().items(666, 999)) + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("✅")); + } + + private static void generate(MultiEmitter emitter) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + while (true) { + if (random.nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.fail(new IOException("Boom")); + return; + } else { + emitter.emit(random.nextInt(0, 100)); + } + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_10_Multi_Failure_Retry.java b/workshop-examples/src/main/java/_04_failures/_10_Multi_Failure_Retry.java new file mode 100755 index 000000000..ad7bd3fa9 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_10_Multi_Failure_Retry.java @@ -0,0 +1,33 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import java.io.IOException; +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.MultiEmitter; + +public class _10_Multi_Failure_Retry { + + public static void main(String[] args) { + System.out.println("⚡️ Multi failure retry"); + + Multi.createFrom().emitter(emitter -> generate(emitter)) + .onFailure().invoke(() -> System.out.println("💥")) + .onFailure().retry().atMost(5) + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("✅")); + } + + private static void generate(MultiEmitter emitter) { + ThreadLocalRandom random = ThreadLocalRandom.current(); + while (true) { + if (random.nextDouble(0.0d, 1.0d) < 0.05d) { + emitter.fail(new IOException("Boom")); + return; + } else { + emitter.emit(random.nextInt(0, 100)); + } + } + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_11_Multi_Failure_Cancelling_Recovery.java b/workshop-examples/src/main/java/_04_failures/_11_Multi_Failure_Cancelling_Recovery.java new file mode 100755 index 000000000..d2b4e951b --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_11_Multi_Failure_Cancelling_Recovery.java @@ -0,0 +1,23 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import io.smallrye.mutiny.Multi; + +public class _11_Multi_Failure_Cancelling_Recovery { + + public static void main(String[] args) { + System.out.println("⚡️ Multi failure that cancels after recovery"); + + Multi.createFrom().range(0, 10) + .onItem().invoke(n -> { + if (n == 6) { + throw new RuntimeException("Bada Boom"); + } else { + System.out.println(n + " 👍"); + } + }) + .onFailure().recoverWithItem(6) + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("✅")); + } +} diff --git a/workshop-examples/src/main/java/_04_failures/_12_Multi_Failure_Guarded_Recovery.java b/workshop-examples/src/main/java/_04_failures/_12_Multi_Failure_Guarded_Recovery.java new file mode 100755 index 000000000..8c9d2d182 --- /dev/null +++ b/workshop-examples/src/main/java/_04_failures/_12_Multi_Failure_Guarded_Recovery.java @@ -0,0 +1,31 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _04_failures; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _12_Multi_Failure_Guarded_Recovery { + + public static void main(String[] args) { + System.out.println("⚡️ Multi failure that does not cancel after recovery"); + + Multi.createFrom().range(0, 10) + .onItem().transformToUniAndConcatenate(i -> safeGuardedOperation(i)) + .onFailure().recoverWithItem(6) + .subscribe().with(System.out::println, Throwable::printStackTrace, () -> System.out.println("✅")); + } + + private static Uni safeGuardedOperation(Integer i) { + return Uni + .createFrom().item(i) + .onItem().invoke(n -> { + if (n == 6) { + throw new RuntimeException("Bada Boom"); + } else { + System.out.println(n + " 👍"); + } + }) + .onFailure().recoverWithItem(i); + } +} diff --git a/workshop-examples/src/main/java/_05_backpressure/_01_Drop.java b/workshop-examples/src/main/java/_05_backpressure/_01_Drop.java new file mode 100755 index 000000000..1477fb038 --- /dev/null +++ b/workshop-examples/src/main/java/_05_backpressure/_01_Drop.java @@ -0,0 +1,54 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _05_backpressure; + +import java.util.concurrent.Flow.Subscription; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.BackPressureStrategy; +import io.smallrye.mutiny.subscription.MultiEmitter; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class _01_Drop { + + public static void main(String[] args) { + System.out.println("⚡️ Back-pressure: drop"); + + Multi.createFrom().emitter(emitter -> emitTooFast(emitter), BackPressureStrategy.ERROR) + .onOverflow().invoke(s -> System.out.print("🚨 ")).drop() // Comment out for some fun + .subscribe().withSubscriber(new MultiSubscriber() { + @Override + public void onSubscribe(Subscription s) { + s.request(5); + } + + @Override + public void onItem(Object s) { + System.out.print(s + " "); + } + + @Override + public void onFailure(Throwable throwable) { + System.out.println("\n✋ " + throwable.getMessage()); + } + + @Override + public void onCompletion() { + System.out.println("\n✅"); + } + }); + } + + private static void emitTooFast(MultiEmitter emitter) { + new Thread(() -> { + while (true) { + emitter.emit("📦"); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + } +} diff --git a/workshop-examples/src/main/java/_05_backpressure/_02_Buffer.java b/workshop-examples/src/main/java/_05_backpressure/_02_Buffer.java new file mode 100755 index 000000000..7bff73b3f --- /dev/null +++ b/workshop-examples/src/main/java/_05_backpressure/_02_Buffer.java @@ -0,0 +1,54 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _05_backpressure; + +import java.util.concurrent.Flow.Subscription; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.BackPressureStrategy; +import io.smallrye.mutiny.subscription.MultiEmitter; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class _02_Buffer { + + public static void main(String[] args) { + System.out.println("⚡️ Back-pressure: buffer"); + + Multi.createFrom().emitter(emitter -> emitTooFast(emitter), BackPressureStrategy.ERROR) + .onOverflow().buffer(32) + .subscribe().withSubscriber(new MultiSubscriber() { + @Override + public void onSubscribe(Subscription s) { + s.request(5); + } + + @Override + public void onItem(Object s) { + System.out.print(s + " "); + } + + @Override + public void onFailure(Throwable throwable) { + System.out.println("\n✋ " + throwable.getMessage()); + } + + @Override + public void onCompletion() { + System.out.println("\n✅"); + } + }); + } + + private static void emitTooFast(MultiEmitter emitter) { + new Thread(() -> { + while (true) { + emitter.emit("📦"); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + } +} diff --git a/workshop-examples/src/main/java/_05_backpressure/_03_Visual_Drop.java b/workshop-examples/src/main/java/_05_backpressure/_03_Visual_Drop.java new file mode 100755 index 000000000..8c0269e33 --- /dev/null +++ b/workshop-examples/src/main/java/_05_backpressure/_03_Visual_Drop.java @@ -0,0 +1,72 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _05_backpressure; + +import java.util.concurrent.Flow.Subscription; +import java.util.function.Consumer; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.BackPressureStrategy; +import io.smallrye.mutiny.subscription.MultiEmitter; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class _03_Visual_Drop { + + public static void main(String[] args) { + System.out.println("⚡️ Back-pressure: drops visualised"); + + Multi.createFrom().emitter(emitter -> emitTooFast(emitter), BackPressureStrategy.ERROR) + .onItem().invoke((Consumer) System.out::println) + .onOverflow().dropPreviousItems() // also compare with .drop() + .subscribe().withSubscriber(new MultiSubscriber() { + @Override + public void onSubscribe(Subscription s) { + s.request(5); + periodicallyRequest(s); + } + + private void periodicallyRequest(Subscription s) { + new Thread(() -> { + while (true) { + try { + Thread.sleep(5_000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(" 🤷 request"); + s.request(2); + } + }).start(); + } + + @Override + public void onItem(Object s) { + System.out.println(" ➡️ " + s); + } + + @Override + public void onFailure(Throwable throwable) { + System.out.println("✋ " + throwable.getMessage()); + } + + @Override + public void onCompletion() { + System.out.println("✅"); + } + }); + } + + private static void emitTooFast(MultiEmitter emitter) { + new Thread(() -> { + long n = 0; + while (true) { + emitter.emit("📦 " + ++n); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + } +} diff --git a/workshop-examples/src/main/java/_05_backpressure/_04_Visual_Buffer.java b/workshop-examples/src/main/java/_05_backpressure/_04_Visual_Buffer.java new file mode 100755 index 000000000..f9c85bf7c --- /dev/null +++ b/workshop-examples/src/main/java/_05_backpressure/_04_Visual_Buffer.java @@ -0,0 +1,72 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _05_backpressure; + +import java.util.concurrent.Flow.Subscription; +import java.util.function.Consumer; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.subscription.BackPressureStrategy; +import io.smallrye.mutiny.subscription.MultiEmitter; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class _04_Visual_Buffer { + + public static void main(String[] args) { + System.out.println("⚡️ Back-pressure: buffering visualised"); + + Multi.createFrom().emitter(emitter -> emitTooFast(emitter), BackPressureStrategy.ERROR) + .onItem().invoke((Consumer) System.out::println) + .onOverflow().buffer(64) + .subscribe().withSubscriber(new MultiSubscriber() { + @Override + public void onSubscribe(Subscription s) { + s.request(5); + periodicallyRequest(s); + } + + private void periodicallyRequest(Subscription s) { + new Thread(() -> { + while (true) { + try { + Thread.sleep(5_000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + System.out.println(" 🤷 request"); + s.request(2); + } + }).start(); + } + + @Override + public void onItem(Object s) { + System.out.println(" ➡️ " + s); + } + + @Override + public void onFailure(Throwable throwable) { + System.out.println("✋ " + throwable.getMessage()); + } + + @Override + public void onCompletion() { + System.out.println("✅"); + } + }); + } + + private static void emitTooFast(MultiEmitter emitter) { + new Thread(() -> { + long n = 0; + while (true) { + emitter.emit("📦 " + ++n); + try { + Thread.sleep(250); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }).start(); + } +} diff --git a/workshop-examples/src/main/java/_06_threading/_01_Threading_Subscription.java b/workshop-examples/src/main/java/_06_threading/_01_Threading_Subscription.java new file mode 100755 index 000000000..4e1be4807 --- /dev/null +++ b/workshop-examples/src/main/java/_06_threading/_01_Threading_Subscription.java @@ -0,0 +1,35 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _06_threading; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _01_Threading_Subscription { + + public static void main(String[] args) { + System.out.println("⚡️ runSubscriptionOn (do not block the subscriber)"); + + var service = Executors.newFixedThreadPool(4); + + Multi.createBy().repeating().uni(() -> generate()).indefinitely() + .runSubscriptionOn(service) + .subscribe().with(item -> System.out.println(Thread.currentThread().getName() + " :: " + item)); + } + + static final AtomicInteger counter = new AtomicInteger(); + + static Uni generate() { + return Uni.createFrom().completionStage( + supplyAsync(counter::getAndIncrement, + delayedExecutor(ThreadLocalRandom.current().nextInt(1000), TimeUnit.MILLISECONDS))); + } +} diff --git a/workshop-examples/src/main/java/_06_threading/_02_Threading_Emit.java b/workshop-examples/src/main/java/_06_threading/_02_Threading_Emit.java new file mode 100755 index 000000000..b8ca9510b --- /dev/null +++ b/workshop-examples/src/main/java/_06_threading/_02_Threading_Emit.java @@ -0,0 +1,35 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _06_threading; + +import static java.util.concurrent.CompletableFuture.delayedExecutor; +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadLocalRandom; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; + +public class _02_Threading_Emit { + + public static void main(String[] args) { + System.out.println("⚡️ emitOn (dispatch blocking event processing)"); + + var service = Executors.newFixedThreadPool(4); + + Multi.createBy().repeating().uni(() -> generate()).indefinitely() + .emitOn(service) + .subscribe().with(item -> System.out.println(Thread.currentThread().getName() + " :: " + item)); + } + + static final AtomicInteger counter = new AtomicInteger(); + + static Uni generate() { + return Uni.createFrom().completionStage( + supplyAsync(counter::getAndIncrement, + delayedExecutor(ThreadLocalRandom.current().nextInt(1000), TimeUnit.MILLISECONDS))); + } +} diff --git a/workshop-examples/src/main/java/_06_threading/_03_Infra_Executor.java b/workshop-examples/src/main/java/_06_threading/_03_Infra_Executor.java new file mode 100755 index 000000000..c7e24b3f0 --- /dev/null +++ b/workshop-examples/src/main/java/_06_threading/_03_Infra_Executor.java @@ -0,0 +1,32 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _06_threading; + +import static java.util.concurrent.CompletableFuture.supplyAsync; + +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.infrastructure.Infrastructure; + +public class _03_Infra_Executor { + + public static void main(String[] args) { + System.out.println("⚡️ emitOn (dispatch blocking event processing to the Mutiny default worker pool)"); + + var service = Executors.newFixedThreadPool(4); + + Multi.createBy().repeating().uni(() -> generate()).indefinitely() + .emitOn(service) + .subscribe().with(item -> System.out.println(Thread.currentThread().getName() + " :: " + item)); + } + + static final AtomicInteger counter = new AtomicInteger(); + + static Uni generate() { + return Uni.createFrom().completionStage( + supplyAsync(counter::getAndIncrement, Infrastructure.getDefaultWorkerPool())); + } +} diff --git a/workshop-examples/src/main/java/_06_threading/_04_Threading_Blocking.java b/workshop-examples/src/main/java/_06_threading/_04_Threading_Blocking.java new file mode 100755 index 000000000..c713c78bc --- /dev/null +++ b/workshop-examples/src/main/java/_06_threading/_04_Threading_Blocking.java @@ -0,0 +1,28 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _06_threading; + +import java.util.stream.Collectors; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.BlockingIterable; + +public class _04_Threading_Blocking { + + public static void main(String[] args) { + System.out.println("⚡️ blocking"); + + BlockingIterable iterable = Multi.createFrom().range(0, 10) + .subscribe().asIterable(); + + var list = iterable.stream().collect(Collectors.toList()); + + System.out.println(list); + + Integer someInt = Uni.createFrom().item(123) + .await().indefinitely(); + + System.out.println(someInt); + } +} diff --git a/workshop-examples/src/main/java/_06_threading/_05_Threading_Blocking_Check.java b/workshop-examples/src/main/java/_06_threading/_05_Threading_Blocking_Check.java new file mode 100755 index 000000000..6b340a55d --- /dev/null +++ b/workshop-examples/src/main/java/_06_threading/_05_Threading_Blocking_Check.java @@ -0,0 +1,35 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _06_threading; + +import java.util.stream.Collectors; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.Uni; +import io.smallrye.mutiny.helpers.BlockingIterable; +import io.smallrye.mutiny.infrastructure.Infrastructure; + +public class _05_Threading_Blocking_Check { + + public static void main(String[] args) { + System.out.println("⚡️ blocking"); + + Infrastructure.setCanCallerThreadBeBlockedSupplier(() -> !Thread.currentThread().getName().contains("yolo")); + + new Thread(() -> { + BlockingIterable iterable = Multi.createFrom().range(0, 10) + .subscribe().asIterable(); + + var list = iterable.stream().collect(Collectors.toList()); + + System.out.println(list); + }, "yolo-1").start(); + + new Thread(() -> { + Integer someInt = Uni.createFrom().item(123) + .await().indefinitely(); + + System.out.println(someInt); + }, "yolo-2").start(); + } +} diff --git a/workshop-examples/src/main/java/_07_misc/_01_Multi_Custom_Operator.java b/workshop-examples/src/main/java/_07_misc/_01_Multi_Custom_Operator.java new file mode 100755 index 000000000..5fd5efc0f --- /dev/null +++ b/workshop-examples/src/main/java/_07_misc/_01_Multi_Custom_Operator.java @@ -0,0 +1,45 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _07_misc; + +import java.util.concurrent.ThreadLocalRandom; + +import io.smallrye.mutiny.Multi; +import io.smallrye.mutiny.operators.multi.AbstractMultiOperator; +import io.smallrye.mutiny.operators.multi.MultiOperatorProcessor; +import io.smallrye.mutiny.subscription.MultiSubscriber; + +public class _01_Multi_Custom_Operator { + + public static void main(String[] args) { + System.out.println("⚡️ Custom operator, randomly drop items"); + + Multi.createFrom().range(1, 20) + .plug(RandomDrop::new) + .subscribe().with(System.out::println); + } + + static class RandomDrop extends AbstractMultiOperator { + public RandomDrop(Multi upstream) { + super(upstream); + } + + @Override + public void subscribe(MultiSubscriber downstream) { + upstream.subscribe().withSubscriber(new DropProcessor(downstream)); + } + + private class DropProcessor extends MultiOperatorProcessor { + DropProcessor(MultiSubscriber downstream) { + super(downstream); + } + + @Override + public void onItem(T item) { + if (ThreadLocalRandom.current().nextBoolean()) { + super.onItem(item); + } + } + } + } +} diff --git a/workshop-examples/src/main/java/_07_misc/_02_Logging.java b/workshop-examples/src/main/java/_07_misc/_02_Logging.java new file mode 100755 index 000000000..be49e4650 --- /dev/null +++ b/workshop-examples/src/main/java/_07_misc/_02_Logging.java @@ -0,0 +1,25 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _07_misc; + +import io.smallrye.mutiny.Multi; + +public class _02_Logging { + + public static void main(String[] args) { + System.out.println("⚡️ Logging"); + + Multi multi = Multi.createFrom().range(1, 3) + .log("source") + .onItem().transform(i -> ">>> " + i) + .log("transformed"); + + System.out.println(); + System.out.println("🚀 First subscriber"); + multi.subscribe().with(System.out::println); + + System.out.println(); + System.out.println("🚀 Second subscriber"); + multi.subscribe().with(System.out::println); + } +} diff --git a/workshop-examples/src/main/java/_07_misc/_03_Context.java b/workshop-examples/src/main/java/_07_misc/_03_Context.java new file mode 100755 index 000000000..8ad80250a --- /dev/null +++ b/workshop-examples/src/main/java/_07_misc/_03_Context.java @@ -0,0 +1,27 @@ +///usr/bin/env jbang "$0" "$@" ; exit $? +//DEPS io.smallrye.reactive:mutiny:2.4.0 +package _07_misc; + +import java.util.List; + +import io.smallrye.mutiny.Context; +import io.smallrye.mutiny.Multi; + +public class _03_Context { + + public static void main(String[] args) { + System.out.println("⚡️ Using subscription-bound contexts"); + + Context context = Context.of("foo", 123, "bar", "abc-123-def"); + + List list = Multi.createFrom().range(1, 10) + .withContext((multi, ctx) -> multi.onItem().invoke(n -> ctx.put("n", n))) + .select().where(n -> n % 2 == 0) + .withContext( + (multi, ctx) -> multi.onItem().transform(n -> n + "::" + ctx.get("n") + " @foo -> " + ctx.get("foo"))) + .collect().asList() + .awaitUsing(context).indefinitely(); + + System.out.println(list); + } +} From b4eb5adad12bb8eec14a3f4097b6a0626d64d815 Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Sun, 10 Sep 2023 23:04:29 +0200 Subject: [PATCH 2/3] feat: script to update the workshop Mutiny target version --- .build/update-workshop-target-version.sh | 3 ++ pom.xml | 6 ++++ workshop-examples/pom.xml | 30 +++++++++++++++++++ .../_18_Multi_To_Uni_And_Back.java | 2 +- 4 files changed, 40 insertions(+), 1 deletion(-) create mode 100755 .build/update-workshop-target-version.sh diff --git a/.build/update-workshop-target-version.sh b/.build/update-workshop-target-version.sh new file mode 100755 index 000000000..70c1903cd --- /dev/null +++ b/.build/update-workshop-target-version.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +./mvnw -Pupdate-workshop-examples -f workshop-examples compile -DworkshopVersion=$1 +find workshop-examples -name '*.java' | xargs chmod +x \ No newline at end of file diff --git a/pom.xml b/pom.xml index 32290243d..496f571f7 100644 --- a/pom.xml +++ b/pom.xml @@ -116,6 +116,7 @@ 1.7.20 1.2.3 + 1.1.0 @@ -369,6 +370,11 @@ + + io.github.floverfelt + find-and-replace-maven-plugin + ${find-and-replace-maven-plugin.version} + diff --git a/workshop-examples/pom.xml b/workshop-examples/pom.xml index 71be2cc1e..982a86a40 100644 --- a/workshop-examples/pom.xml +++ b/workshop-examples/pom.xml @@ -33,4 +33,34 @@ + + + update-workshop-examples + + + + io.github.floverfelt + find-and-replace-maven-plugin + + + exec + process-sources + + find-and-replace + + + file-contents + //DEPS io.smallrye.reactive:mutiny:.* + //DEPS io.smallrye.reactive:mutiny:${workshopVersion} + true + .java + + + + + + + + + diff --git a/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java b/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java index e4c9e8715..9b7999f30 100755 --- a/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java +++ b/workshop-examples/src/main/java/_03_composition_transformation/_18_Multi_To_Uni_And_Back.java @@ -18,4 +18,4 @@ public static void main(String[] args) throws InterruptedException { .toMulti() .subscribe().with(System.out::println); } -} \ No newline at end of file +} From 51a222632c7794387d27d39ee4e4a56122a68f2d Mon Sep 17 00:00:00 2001 From: Julien Ponge Date: Mon, 11 Sep 2023 15:25:25 +0200 Subject: [PATCH 3/3] docs: reference the Mutiny workshop examples --- README.md | 6 ++++ .../docs/tutorials/mutiny-workshop.md | 17 ++++++++++ .../tutorials/running-workshop-sample.png | Bin 0 -> 106311 bytes documentation/mkdocs.yml | 1 + workshop-examples/README.md | 29 ++++++++++++++++++ 5 files changed, 53 insertions(+) create mode 100644 documentation/docs/tutorials/mutiny-workshop.md create mode 100644 documentation/docs/tutorials/running-workshop-sample.png create mode 100644 workshop-examples/README.md diff --git a/README.md b/README.md index a297036e1..34b869486 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,12 @@ Mutiny is based on the [Reactive Streams protocol](https://www.reactive-streams. In addition, Mutiny offers converters to interact with other popular libraries and [Kotlin](https://kotlinlang.org/). +## 👓 Mutiny workshop examples + +You can learn about Mutiny from the [documentation and website](https://smallrye.io/smallrye-mutiny). + +This repository also contains the [Mutiny workshop examples](workshop-examples) that cover the common concerns through self-contained executable [JBang](https://www.jbang.dev/) scripts. + ## 📦 Build instructions Mutiny is built with Apache Maven, so all you need is: diff --git a/documentation/docs/tutorials/mutiny-workshop.md b/documentation/docs/tutorials/mutiny-workshop.md new file mode 100644 index 000000000..e8c007cc3 --- /dev/null +++ b/documentation/docs/tutorials/mutiny-workshop.md @@ -0,0 +1,17 @@ +--- +tags: +- tutorial +- beginner +--- + +# Go further with the Mutiny workshop! + +One great option to teach yourself Mutiny is to go through the [Mutiny workshop examples](https://github.com/smallrye/smallrye-mutiny/tree/main/workshop-examples). + +These self-contained [JBang](https://jbang.dev/) scripts cover the main parts of the Mutiny APIs. + +It's a fun and easy way to discover Mutiny! + +Check out [https://github.com/smallrye/smallrye-mutiny/tree/main/workshop-examples](https://github.com/smallrye/smallrye-mutiny/tree/main/workshop-examples) to learn more. + +![Running a workshop sample](running-workshop-sample.png){ width="400" } diff --git a/documentation/docs/tutorials/running-workshop-sample.png b/documentation/docs/tutorials/running-workshop-sample.png new file mode 100644 index 0000000000000000000000000000000000000000..6eec153821b033657643aa43519c9e10a7ae8839 GIT binary patch literal 106311 zcmeEtg;yL;@Fy0627(8d;2sIzYVfg$ji z3JS`K2?`R*+F2W!S{Qj3c)_Uo%?Z73WfSY+{V zSpFVu40O$Js_lVgp%hMh2f<&2E3bQey{qUj4t}JfjXLVwF1ar++#d59KiO|BB)c7h zWxB*rV{B)ugP{e(k;1Nagv*)kBFK|xf_=z@P|57w6zeHzXfOdA;Jd#*J0j!TIAkGj zoaViMHXqx9&jEl%vj;QX%4>snzXorocF`9j0u$n}<|ve<>8*g!f<-`Hdu5ka+8u9~ zR@m)^&MF3L5u6H6SZv=IV*$oo#+NSFJrIbE-Gclr_(tF@SPDWgBpe*s3DL0k?3?*u zqJ(`WCa=-{r&Ob@OtQpO?%;TPIW)UqS*qb}>0923(>4kIR*Uu^#F9OZm|&>eG`%IO zZK>cG1IJ)Dp=8ENuG%WNboupW_uY*X_gX>glLG~SGFhV68;CMa`iWlvgH@?xb zn+uh6J`@~sx##?vSZSHKVG2FBwp4Bl0?`C?+q8sN(HNvf-`6MuZ1|}zPibuRG-EzU zH?t?=yvq-Ntpk?@j^hso9o>-vOXjUfXmMD0?Bo0+ouT~|N;D!=yI2oG_(!FS;1!m) zXnwpzC}6uQ4Z6bRU}*eL`-H{KoIW(d)}Qr-@PW{_;K2~LC)#;fq}Ue;5a|LUH%P~t zsxAym?y8%Q&*=oZZA>(MLd95f-lBn9-lU9iSOVdMfo~6ONdREiUD>NFFv@IcA@>M_ z>)*In-+hfZNwV=9m2P0tW*0qEh$G?WnnY6ABtGxeTw#@+Kt=lg_?1)f`=u?(ioY8l zFAV_M^~#?{1HB$V9iCD1%<4yw9{3}2O@N#3``hPG{Iu_dQI^AX+P@_pEv}J?4`2s_QlD;VP>2}cjdUw1%O$%aN&|FvmkM`1u zD=%j|4Lyd=T0Z-ddjA^iiTv*VKFWQYSO%Fz88IG8r<|BluE^au_>oR-TB&a-}xlE`ypIlvZ)z_@P zw7Rcn5wHAQZP3ynj5|neUM+xKuk7uD(eu#*U-OW_2nd+K;4umfBAa)=wG~YI2!;|W zL<0K}JI3b|39(S1q~I2@VV~V6ysCGFB3DE~pB4IE=SmMuq3eRteh}6`-+~51 zqPL0xmKBn^=SL+~35L1<$SwAC)M!(HTSOeexOobIo+gV@$* zPDFw9PZ4}1Sg5$Ltx#|D(1=nQLSzLf$Sp{4iPL=C3jIzZKfd2XwG3IZq0f73AEFQv z7Q7$g7EB&u*-hTfgA(%Tquu-4SbZ@~d7?u9nuMA#2VaNyN&Y)NWs=m-i+y?<4pqT` zAWi8dF&^nw@uT-%c_wltl&x{9a2X#0s{E_s$3Heo?q<5@nJHKnK25$iqjO|+M66A4 z4Rp=86Oob{5*QMt)s`w?m9UJM*RDM`i<;yq+ArWLsGjOA7?{K?wpQUS^b~zWj1Z~* z`f6<4G@z!Z#%$hnUU2?3VpCu>w5VS>qe8Yow)A?@cL}M`yjn5|`!u6UqsA@pp@9@` zbu?NW+Byss^FZZ*X}wYnb&q)mOO->_Lx@A+d9GS|6X)G%2MY%Q#~aRbhq|4#X%~Us zodgHgFQOR|%i_F($D^cs^Lus<_KpJ%&ii$HS9`lVE<3q9Qq#qwTzNBPQM*5P>@UtQ z6L*eh+6tddgZq)EkvpOT1kHjp@y?cX3;3gb+A}A0qdE}%WC9VoWV--ehJlxXNZ~dl zud{*KVj?)*#ZN7sAmHwAwAf{}s&U528QV*_$K4NH}lSb9k# z3}Y2Vdi80OUsR=qrCFq-(k{Pbf7xcA;cCORVgY#ERIP3YdfA^k^gGY(*^MamvWz=M znrH8uv%O|Z*9iFO=$zt~bT4@geXW8Yi4PHlh+ogWn@+;L?Z)cjd3kmf1DtYobklG# z2CBF&>+gM7PYO%g_JHB(;9>HJymP;}Jfpo>JibrNh}B77mToJ1TSo##QWptavtAv! zTs~OFZ-?oFnME7LH^J{iEP->!{q(LIX&z&;zsh*C%BfNt`WuvlFVd?vICvp>_TH;LNsCg_o zvEvHk^|{v(Z}3{o9D1=v7)cGRB5U!uTn++kMb^)H@o>sH(z%}apovIW42-{HTG7;p z{!HxeFBv@8taK@Mp}*mOh<~v1Y13`YWE$W#cTrZU^STN>I7(|zJ4?eS(rA#rp<*L; zHaNO(o^3vKdAu#Wb%X1B_3l-WFlQD(?i^|YHkQ051h0p5?JA@dRYmF`UP!D??Cq|q zso+<-htJlZ=lVTAb5p@l6j9tUP#TW=^v}l%$7(S&STzdv3Jls*EV9CT*x4duCs%h~M?Tp+qJKo#-@|`PjGLxk$f6`pLZ@^5 zM{Yy64(++6?fVZ`iwgv`ZM$ISe24t|e(^y&LE`(-O_Yu;mmHUeh^B~^K_p0QEO#vU zoNMl%?&Z$2(R(ieG za}(RD+tO**yj~Z+#{pfK$(Z}}E?PzArxqL+-?oo~)LYc|>D+Xz8rJM{D=me7o)-J4 z$Y{YgI$d#Y-{s;kvdvqOEjN3Z50+m2eE6y5e0JR2q~UOV-lBF5do4j=!cF02@=&pd zbkBQL<5o-HhIUr4X}h-WCIghw2E_9`jx!#=J5+PYS2K1@)R-ANsyLz^5#>Dw5_z6B z=5NRx@>1)}w*YQbMs;XgvR%2f#k4Qmv@gD&3|-aZTc_Tw-Ql-V1FvsAc3*cPaq)cM zO}~%3D?C5Xq0gZ=Z=LtDe89Lo^z`EQYD=PCrSg2mpgLAU1Ey{L+B*Rn3@?48AUl4W zSC~J}SosyiGc5H;aOm1AKH3MbsAC^5wtBGHZ|L}KnXOw`U~48|?{C2-7d5zV#x(^C_)WY#f+q+z^FiF z7%(VsY_M0L5;*9`1CH}|Sp=K{4Dwe!1Q=M5DHznBHqxN;%O@K2d+GE03>gy$_8RmL z1@r@ELj2Vl5;_y|uQC)L=o%Qmf}off=&WF1XJ}|;Z({B6E?!#}Q~_)AQOzC<41@IL z2QDW6{sc7sjH#lkgQ}Dyr-8L4o!%#FeM35+rOnGYU;rQ|sAy^EphpO_w6L=01acGq zYQYIAzf{u`6aH%AV9rgfDkV!OXl-Xm$V$gR$3V;jM@UEru=`}hDK8}Qr#t8$H?fI> zgAFG=y|c43oih`iwVg3NBL@cuJ;Mk34czX$ni93ew{13Ob2 z2UBY+!k2ON^sOBoxQU5hZuIx(H=Tw+(|_({W&dYdpb65yT%l*AW1#V{bp8vY?zxPzKH?$M9wglbN zf#;v~`qTN}7yonw(7#OmU#$4e=U=rTM)SY{=>J|c9ylAtpJkwR#5EO?Q3RbqQugwJ zAOsyKexF~;KY522A<@9V_`t-3_!WWR2VY)!WAxnjW|*@du{XVc6`!EUCyxvz2L&0> zHj6bIF$)ftrJtFk8Vm>XQH+gv(PcwURO@>2X*=2ey8d{n)%N-*^%{@6j*PeZ{>a_g zC>^%HZ&3N;wt#?ugXrulaAdH5eJK#31PBqt1zEDg312At>${Q!=I>*`(*XBZ#jh*5 zzF_|Rg#|`vC?HY)=ZlN#&0sV5T7~~#b$pqM$leST=u_bWzF;uke}BCVAdsOWZ&#@P zRR;zW2M4On%}SB`_#f(QB*8k{83{E1L$r@PKUim`q9PLcduRG6^C=6q#;%ebm@{B= zfomdE0r}6ApM*h;+xAEZ?av>Z2zGCJpz>YXiafcRT1~C^D?>gwIF zkc^4sZl;R`dwSW;AMuu*P@Uk_JoZ~PgJ!nCr1-;!+w-{8R0BIo{~rmo5|x#eH$#kH z{S^KzT_(y&FvDz{7w=`;*YcFWUDo&S{js&2-f`co*sRv`28aqJ-F1nLAOl_XL$P=i z`K%XkM9~ex=;7E^Xb@&STJ2E2$Z*kL<2_D1#27K}`~Z(^ZtysqEL32A=(smsZuMZb zwas=}XgHm0l)y|q|L$k`vu-@qx|yHL`G`R~3#Z%TZt32Dl8UORk3~hsV!1WKs>Lw+ z;%TfG^;24z|Dl_Qb!b#nKBwc}cuelVr|8{@{IR}7?YMjAH}3?jTW{;A6io zi;Cy_0YgcQu_^T?a%&HW4TZdibJe|8Hw|#;CM)gyll9iTO=mx-iBJw=I$!;IRDH(9 z+l@QM~}3IUR~*c<_Aw5l5NL8{zRs z|4>XpHIpGU;AG$-dG+;E&Mz((DN)}&M(yS{eP~t#QU`!Z|IlL9BZe2HTXp)5uUNg z85Xv8w@I5jB&cJmR3(b%;e^C&?o51_l%6fI_4W$q&gS9u8Sq9BDeNoP$i5$@`SbY* zZ#FD}*ZZ!L`)!?kLGRWZePWM`Rp74IVbi5zt~fev3%7oQqNg?wi{&&8VVk#`0M9%qH-_Maq45Y}QbW92FGG}Xqqw&oc| zG>I}N#+eHB13H%@2u1v|d_a2{-zdsS2!pKY)8jRS>qF(+JtFSQ3#N;m?10{UnT)K< z{TcHa*Xtt^a%3~|?%_uJZIhvtsrf?fma=TS%^&>+1~)j?%^^VTuKrl^on^0?i6@Wy zLZec3`HIOBHHEP3ngdl*grr1m+!I?fCo02jLj{LV%)+8V=af<3aJthHAoZ=nveU^g z2e_7Br{AtcU>}s5{fdp_cm#131&-^q0Hg76|YLnIUVEj zdn50?ty*0&$HD+So(~OJ#mibpIBu02V&u<*NsM2#T~A-RWE{-b_Ljn)n`x$PRBcl4 z#7LN><~=>R8SH2*%*>t=}Tc@KGT7Fu%oE?v3^{$70kElH_%PDkms>%+f73GFD zL$i2Sbk2eo!g2c8c^$EL;gZ7Y0Ud#R8-w;`?~+s^QKB*%KR-8jta=0>HIP87Bx9

X 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

9#rwHT;c>Jjv`|( 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