diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java index 7b630f8da247..9afaa3fc33fa 100644 --- a/callback/src/main/java/com/iluwatar/callback/App.java +++ b/callback/src/main/java/com/iluwatar/callback/App.java @@ -39,6 +39,11 @@ private App() {} /** Program entry point. */ public static void main(final String[] args) { var task = new SimpleTask(); - task.executeWith(() -> LOGGER.info("I'm done now.")); + + LOGGER.info("=== Synchronous callback ==="); + task.executeWith(() -> LOGGER.info("Sync callback executed.")); + + LOGGER.info("=== Asynchronous callback ==="); + task.executeAsyncWith(() -> LOGGER.info("Async callback executed.")).join(); } } diff --git a/callback/src/main/java/com/iluwatar/callback/Callback.java b/callback/src/main/java/com/iluwatar/callback/Callback.java index 7b75b5c71077..67b2880e1681 100644 --- a/callback/src/main/java/com/iluwatar/callback/Callback.java +++ b/callback/src/main/java/com/iluwatar/callback/Callback.java @@ -25,6 +25,7 @@ package com.iluwatar.callback; /** Callback interface. */ +@FunctionalInterface public interface Callback { void call(); diff --git a/callback/src/main/java/com/iluwatar/callback/Task.java b/callback/src/main/java/com/iluwatar/callback/Task.java index d69697454dff..d58d55540f52 100644 --- a/callback/src/main/java/com/iluwatar/callback/Task.java +++ b/callback/src/main/java/com/iluwatar/callback/Task.java @@ -25,15 +25,30 @@ package com.iluwatar.callback; import java.util.Optional; +import java.util.concurrent.CompletableFuture; -/** Template-method class for callback hook execution. */ +/** + * Template-method class for callback hook execution. + * + *

Provides both synchronous and asynchronous execution with callback support. + */ public abstract class Task { - /** Execute with callback. */ + /** Execute the task and call the callback method synchronously upon completion. */ final void executeWith(Callback callback) { execute(); Optional.ofNullable(callback).ifPresent(Callback::call); } + /** Execute the task and asynchronously call the callback method upon completion. */ + final CompletableFuture executeAsyncWith(Callback callback) { + return CompletableFuture.runAsync( + () -> { + execute(); + Optional.ofNullable(callback).ifPresent(Callback::call); + }); + } + + /** Actual work to be implemented by subclasses. */ public abstract void execute(); } diff --git a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java index 99939d491f4e..64780babec84 100644 --- a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java +++ b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java @@ -26,6 +26,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.jupiter.api.Test; /** @@ -39,19 +42,44 @@ class CallbackTest { private Integer callingCount = 0; @Test - void test() { - Callback callback = () -> callingCount++; - + void testSynchronousCallback() { + var counter = new AtomicInteger(); + Callback callback = counter::incrementAndGet; var task = new SimpleTask(); - assertEquals(Integer.valueOf(0), callingCount, "Initial calling count of 0"); - + assertEquals(0, counter.get(), "Initial count should be 0"); task.executeWith(callback); + assertEquals(1, counter.get(), "Callback should be called once"); + task.executeWith(callback); + assertEquals(2, counter.get(), "Callback should be called twice"); + } - assertEquals(Integer.valueOf(1), callingCount, "Callback called once"); + @Test + void testAsynchronousCallback() { + var task = new SimpleTask(); - task.executeWith(callback); + var counter1 = new AtomicInteger(); + final CompletableFuture future1 = new CompletableFuture<>(); + Callback callback1 = + () -> { + counter1.incrementAndGet(); + future1.complete(null); + }; + var f1 = task.executeAsyncWith(callback1); + future1.orTimeout(1, TimeUnit.SECONDS).join(); + f1.join(); + assertEquals(1, counter1.get(), "Async callback should increment once"); - assertEquals(Integer.valueOf(2), callingCount, "Callback called twice"); + var counter2 = new AtomicInteger(); + final CompletableFuture future2 = new CompletableFuture<>(); + Callback callback2 = + () -> { + counter2.incrementAndGet(); + future2.complete(null); + }; + var f2 = task.executeAsyncWith(callback2); + future2.orTimeout(1, TimeUnit.SECONDS).join(); + f2.join(); + assertEquals(1, counter2.get(), "Async callback should increment once again"); } }