Skip to content

Commit a86c211

Browse files
committed
feat: display synchronous and asynchronous callbacks
1 parent 5f8069b commit a86c211

File tree

4 files changed

+59
-30
lines changed

4 files changed

+59
-30
lines changed

callback/src/main/java/com/iluwatar/callback/App.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,13 @@ public final class App {
3737
private App() {}
3838

3939
/** Program entry point. */
40-
public static void main(final String[] args) throws InterruptedException {
40+
public static void main(final String[] args) {
4141
var task = new SimpleTask();
42-
task.executeWith(() -> LOGGER.info("I'm done now."));
43-
Thread.sleep(3000);
42+
43+
LOGGER.info("=== Synchronous callback ===");
44+
task.executeWith(() -> LOGGER.info("Sync callback executed."));
45+
46+
LOGGER.info("=== Asynchronous callback ===");
47+
task.executeAsyncWith(() -> LOGGER.info("Async callback executed.")).join();
4448
}
4549
}

callback/src/main/java/com/iluwatar/callback/Callback.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
package com.iluwatar.callback;
2626

2727
/** Callback interface. */
28+
@FunctionalInterface
2829
public interface Callback {
2930

3031
void call();

callback/src/main/java/com/iluwatar/callback/Task.java

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,21 +24,31 @@
2424
*/
2525
package com.iluwatar.callback;
2626

27+
import java.util.Optional;
2728
import java.util.concurrent.CompletableFuture;
2829

29-
/** Template-method class for callback hook execution. */
30+
/**
31+
* Template-method class for callback hook execution.
32+
*
33+
* <p>Provides both synchronous and asynchronous execution with callback support.
34+
*/
3035
public abstract class Task {
3136

32-
/** Execute the task and asynchronously call the callback method upon completion. */
37+
/** Execute the task and call the callback method synchronously upon completion. */
3338
final void executeWith(Callback callback) {
34-
CompletableFuture.runAsync(
39+
execute();
40+
Optional.ofNullable(callback).ifPresent(Callback::call);
41+
}
42+
43+
/** Execute the task and asynchronously call the callback method upon completion. */
44+
final CompletableFuture<Void> executeAsyncWith(Callback callback) {
45+
return CompletableFuture.runAsync(
3546
() -> {
3647
execute();
37-
if (callback != null) {
38-
callback.call();
39-
}
48+
Optional.ofNullable(callback).ifPresent(Callback::call);
4049
});
4150
}
4251

52+
/** Actual work to be implemented by subclasses. */
4353
public abstract void execute();
4454
}

callback/src/test/java/com/iluwatar/callback/CallbackTest.java

Lines changed: 35 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@
2626

2727
import static org.junit.jupiter.api.Assertions.assertEquals;
2828

29-
import java.util.concurrent.CountDownLatch;
29+
import java.util.concurrent.CompletableFuture;
3030
import java.util.concurrent.TimeUnit;
31+
import java.util.concurrent.atomic.AtomicInteger;
3132
import org.junit.jupiter.api.Test;
3233

3334
/**
@@ -41,31 +42,44 @@ class CallbackTest {
4142
private Integer callingCount = 0;
4243

4344
@Test
44-
void test() throws InterruptedException {
45-
CountDownLatch latch = new CountDownLatch(1);
46-
47-
CountDownLatch finalLatch = latch;
48-
Callback callback =
49-
() -> {
50-
callingCount++;
51-
finalLatch.countDown();
52-
};
53-
45+
void testSynchronousCallback() {
46+
var counter = new AtomicInteger();
47+
Callback callback = counter::incrementAndGet;
5448
var task = new SimpleTask();
5549

50+
assertEquals(0, counter.get(), "Initial count should be 0");
5651
task.executeWith(callback);
57-
58-
latch.await(5, TimeUnit.SECONDS);
59-
60-
assertEquals(Integer.valueOf(1), callingCount, "Callback called once");
61-
62-
callingCount = 0;
63-
latch = new CountDownLatch(1);
64-
52+
assertEquals(1, counter.get(), "Callback should be called once");
6553
task.executeWith(callback);
54+
assertEquals(2, counter.get(), "Callback should be called twice");
55+
}
56+
57+
@Test
58+
void testAsynchronousCallback() {
59+
var task = new SimpleTask();
6660

67-
latch.await(5, TimeUnit.SECONDS);
61+
var counter1 = new AtomicInteger();
62+
final CompletableFuture<Void> future1 = new CompletableFuture<>();
63+
Callback callback1 =
64+
() -> {
65+
counter1.incrementAndGet();
66+
future1.complete(null);
67+
};
68+
var f1 = task.executeAsyncWith(callback1);
69+
future1.orTimeout(1, TimeUnit.SECONDS).join();
70+
f1.join();
71+
assertEquals(1, counter1.get(), "Async callback should increment once");
6872

69-
assertEquals(Integer.valueOf(1), callingCount, "Callback called once again");
73+
var counter2 = new AtomicInteger();
74+
final CompletableFuture<Void> future2 = new CompletableFuture<>();
75+
Callback callback2 =
76+
() -> {
77+
counter2.incrementAndGet();
78+
future2.complete(null);
79+
};
80+
var f2 = task.executeAsyncWith(callback2);
81+
future2.orTimeout(1, TimeUnit.SECONDS).join();
82+
f2.join();
83+
assertEquals(1, counter2.get(), "Async callback should increment once again");
7084
}
7185
}

0 commit comments

Comments
 (0)