mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-09-05 22:17:09 +00:00
deps: Refactor dependencies (#3224)
* remove spring dep move junit, logging, mockito under dep mgmt * upgrade anti-corruption-layer deps * async method invocation * balking, bloc * bridge to bytecode * caching * callback - cqrs * component - health check * hexagonal - metadata mapping * rest of the patterns * remove checkstyle, take spotless into use
This commit is contained in:
@@ -27,7 +27,6 @@ package com.iluwatar.promise;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -36,26 +35,28 @@ import lombok.extern.slf4j.Slf4j;
|
||||
* The Promise object is used for asynchronous computations. A Promise represents an operation that
|
||||
* hasn't completed yet, but is expected in the future.
|
||||
*
|
||||
* <p>A Promise represents a proxy for a value not necessarily known when the promise is created.
|
||||
* It allows you to associate dependent promises to an asynchronous action's eventual success value
|
||||
* or failure reason. This lets asynchronous methods return values like synchronous methods: instead
|
||||
* of the final value, the asynchronous method returns a promise of having a value at some point in
|
||||
* the future.
|
||||
* <p>A Promise represents a proxy for a value not necessarily known when the promise is created. It
|
||||
* allows you to associate dependent promises to an asynchronous action's eventual success value or
|
||||
* failure reason. This lets asynchronous methods return values like synchronous methods: instead of
|
||||
* the final value, the asynchronous method returns a promise of having a value at some point in the
|
||||
* future.
|
||||
*
|
||||
* <p>Promises provide a few advantages over callback objects:
|
||||
*
|
||||
* <ul>
|
||||
* <li> Functional composition and error handling
|
||||
* <li> Prevents callback hell and provides callback aggregation
|
||||
* <li>Functional composition and error handling
|
||||
* <li>Prevents callback hell and provides callback aggregation
|
||||
* </ul>
|
||||
*
|
||||
* <p>In this application the usage of promise is demonstrated with two examples:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Count Lines: In this example a file is downloaded and its line count is calculated.
|
||||
* The calculated line count is then consumed and printed on console.
|
||||
* <li>Lowest Character Frequency: In this example a file is downloaded and its lowest frequency
|
||||
* character is found and printed on console. This happens via a chain of promises, we start with
|
||||
* a file download promise, then a promise of character frequency, then a promise of lowest
|
||||
* frequency character which is finally consumed and result is printed on console.
|
||||
* <li>Count Lines: In this example a file is downloaded and its line count is calculated. The
|
||||
* calculated line count is then consumed and printed on console.
|
||||
* <li>Lowest Character Frequency: In this example a file is downloaded and its lowest frequency
|
||||
* character is found and printed on console. This happens via a chain of promises, we start
|
||||
* with a file download promise, then a promise of character frequency, then a promise of
|
||||
* lowest frequency character which is finally consumed and result is printed on console.
|
||||
* </ul>
|
||||
*
|
||||
* @see CompletableFuture
|
||||
@@ -99,12 +100,12 @@ public class App {
|
||||
* consume the result in a Consumer<Character>
|
||||
*/
|
||||
private void calculateLowestFrequencyChar() {
|
||||
lowestFrequencyChar().thenAccept(
|
||||
charFrequency -> {
|
||||
LOGGER.info("Char with lowest frequency is: {}", charFrequency);
|
||||
taskCompleted();
|
||||
}
|
||||
);
|
||||
lowestFrequencyChar()
|
||||
.thenAccept(
|
||||
charFrequency -> {
|
||||
LOGGER.info("Char with lowest frequency is: {}", charFrequency);
|
||||
taskCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -112,12 +113,12 @@ public class App {
|
||||
* in a Consumer<Integer>
|
||||
*/
|
||||
private void calculateLineCount() {
|
||||
countLines().thenAccept(
|
||||
count -> {
|
||||
LOGGER.info("Line count is: {}", count);
|
||||
taskCompleted();
|
||||
}
|
||||
);
|
||||
countLines()
|
||||
.thenAccept(
|
||||
count -> {
|
||||
LOGGER.info("Line count is: {}", count);
|
||||
taskCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -150,14 +151,12 @@ public class App {
|
||||
*/
|
||||
private Promise<String> download(String urlString) {
|
||||
return new Promise<String>()
|
||||
.fulfillInAsync(
|
||||
() -> Utility.downloadFile(urlString), executor)
|
||||
.fulfillInAsync(() -> Utility.downloadFile(urlString), executor)
|
||||
.onError(
|
||||
throwable -> {
|
||||
LOGGER.error("An error occurred: ", throwable);
|
||||
taskCompleted();
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
private void stop() throws InterruptedException {
|
||||
|
||||
@@ -44,9 +44,7 @@ public class Promise<T> extends PromiseSupport<T> {
|
||||
private Runnable fulfillmentAction;
|
||||
private Consumer<? super Throwable> exceptionHandler;
|
||||
|
||||
/**
|
||||
* Creates a promise that will be fulfilled in the future.
|
||||
*/
|
||||
/** Creates a promise that will be fulfilled in the future. */
|
||||
public Promise() {
|
||||
// Empty constructor
|
||||
}
|
||||
@@ -66,7 +64,7 @@ public class Promise<T> extends PromiseSupport<T> {
|
||||
* Fulfills the promise with exception due to error in execution.
|
||||
*
|
||||
* @param exception the exception will be wrapped in {@link ExecutionException} when accessing the
|
||||
* value using {@link #get()}.
|
||||
* value using {@link #get()}.
|
||||
*/
|
||||
@Override
|
||||
public void fulfillExceptionally(Exception exception) {
|
||||
@@ -93,18 +91,19 @@ public class Promise<T> extends PromiseSupport<T> {
|
||||
* Executes the task using the executor in other thread and fulfills the promise returned once the
|
||||
* task completes either successfully or with an exception.
|
||||
*
|
||||
* @param task the task that will provide the value to fulfill the promise.
|
||||
* @param task the task that will provide the value to fulfill the promise.
|
||||
* @param executor the executor in which the task should be run.
|
||||
* @return a promise that represents the result of running the task provided.
|
||||
*/
|
||||
public Promise<T> fulfillInAsync(final Callable<T> task, Executor executor) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
fulfill(task.call());
|
||||
} catch (Exception ex) {
|
||||
fulfillExceptionally(ex);
|
||||
}
|
||||
});
|
||||
executor.execute(
|
||||
() -> {
|
||||
try {
|
||||
fulfill(task.call());
|
||||
} catch (Exception ex) {
|
||||
fulfillExceptionally(ex);
|
||||
}
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -125,7 +124,7 @@ public class Promise<T> extends PromiseSupport<T> {
|
||||
* Set the exception handler on this promise.
|
||||
*
|
||||
* @param exceptionHandler a consumer that will handle the exception occurred while fulfilling the
|
||||
* promise.
|
||||
* promise.
|
||||
* @return this
|
||||
*/
|
||||
public Promise<T> onError(Consumer<? super Throwable> exceptionHandler) {
|
||||
@@ -198,4 +197,4 @@ public class Promise<T> extends PromiseSupport<T> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,9 +39,7 @@ import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* Utility to perform various operations.
|
||||
*/
|
||||
/** Utility to perform various operations. */
|
||||
@Slf4j
|
||||
public class Utility {
|
||||
|
||||
@@ -53,7 +51,8 @@ public class Utility {
|
||||
*/
|
||||
public static Map<Character, Long> characterFrequency(String fileLocation) {
|
||||
try (var bufferedReader = new BufferedReader(new FileReader(fileLocation))) {
|
||||
return bufferedReader.lines()
|
||||
return bufferedReader
|
||||
.lines()
|
||||
.flatMapToInt(String::chars)
|
||||
.mapToObj(x -> (char) x)
|
||||
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
|
||||
@@ -69,9 +68,7 @@ public class Utility {
|
||||
* @return the character, {@code Optional.empty()} otherwise.
|
||||
*/
|
||||
public static Character lowestFrequencyChar(Map<Character, Long> charFrequency) {
|
||||
return charFrequency
|
||||
.entrySet()
|
||||
.stream()
|
||||
return charFrequency.entrySet().stream()
|
||||
.min(Comparator.comparingLong(Entry::getValue))
|
||||
.map(Entry::getKey)
|
||||
.orElseThrow();
|
||||
@@ -101,7 +98,7 @@ public class Utility {
|
||||
var url = new URL(urlString);
|
||||
var file = File.createTempFile("promise_pattern", null);
|
||||
try (var bufferedReader = new BufferedReader(new InputStreamReader(url.openStream()));
|
||||
var writer = new FileWriter(file)) {
|
||||
var writer = new FileWriter(file)) {
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
writer.write(line);
|
||||
|
||||
@@ -28,9 +28,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Application test.
|
||||
*/
|
||||
/** Application test. */
|
||||
class AppTest {
|
||||
|
||||
@Test
|
||||
|
||||
@@ -41,9 +41,7 @@ import java.util.function.Consumer;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/**
|
||||
* Tests Promise class.
|
||||
*/
|
||||
/** Tests Promise class. */
|
||||
class PromiseTest {
|
||||
|
||||
private Executor executor;
|
||||
@@ -66,17 +64,18 @@ class PromiseTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void promiseIsFulfilledWithAnExceptionIfTaskThrowsAnException()
|
||||
throws InterruptedException {
|
||||
void promiseIsFulfilledWithAnExceptionIfTaskThrowsAnException() throws InterruptedException {
|
||||
testWaitingForeverForPromiseToBeFulfilled();
|
||||
testWaitingSomeTimeForPromiseToBeFulfilled();
|
||||
}
|
||||
|
||||
private void testWaitingForeverForPromiseToBeFulfilled() throws InterruptedException {
|
||||
var promise = new Promise<Integer>();
|
||||
promise.fulfillInAsync(() -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
}, executor);
|
||||
promise.fulfillInAsync(
|
||||
() -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
},
|
||||
executor);
|
||||
|
||||
try {
|
||||
promise.get();
|
||||
@@ -97,9 +96,11 @@ class PromiseTest {
|
||||
|
||||
private void testWaitingSomeTimeForPromiseToBeFulfilled() throws InterruptedException {
|
||||
var promise = new Promise<Integer>();
|
||||
promise.fulfillInAsync(() -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
}, executor);
|
||||
promise.fulfillInAsync(
|
||||
() -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
},
|
||||
executor);
|
||||
|
||||
try {
|
||||
promise.get(1000, TimeUnit.SECONDS);
|
||||
@@ -116,15 +117,15 @@ class PromiseTest {
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isCancelled());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
void dependentPromiseIsFulfilledAfterTheConsumerConsumesTheResultOfThisPromise()
|
||||
throws InterruptedException, ExecutionException {
|
||||
var dependentPromise = promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenAccept(value -> assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value));
|
||||
var dependentPromise =
|
||||
promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenAccept(value -> assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value));
|
||||
|
||||
dependentPromise.get();
|
||||
assertTrue(dependentPromise.isDone());
|
||||
@@ -134,16 +135,19 @@ class PromiseTest {
|
||||
@Test
|
||||
void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnException()
|
||||
throws InterruptedException {
|
||||
var dependentPromise = promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenAccept(value -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
});
|
||||
var dependentPromise =
|
||||
promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenAccept(
|
||||
value -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
});
|
||||
|
||||
try {
|
||||
dependentPromise.get();
|
||||
fail("Fetching dependent promise should result in exception "
|
||||
+ "if the action threw an exception");
|
||||
fail(
|
||||
"Fetching dependent promise should result in exception "
|
||||
+ "if the action threw an exception");
|
||||
} catch (ExecutionException ex) {
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isCancelled());
|
||||
@@ -151,8 +155,9 @@ class PromiseTest {
|
||||
|
||||
try {
|
||||
dependentPromise.get(1000, TimeUnit.SECONDS);
|
||||
fail("Fetching dependent promise should result in exception "
|
||||
+ "if the action threw an exception");
|
||||
fail(
|
||||
"Fetching dependent promise should result in exception "
|
||||
+ "if the action threw an exception");
|
||||
} catch (ExecutionException ex) {
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isCancelled());
|
||||
@@ -162,13 +167,14 @@ class PromiseTest {
|
||||
@Test
|
||||
void dependentPromiseIsFulfilledAfterTheFunctionTransformsTheResultOfThisPromise()
|
||||
throws InterruptedException, ExecutionException {
|
||||
var dependentPromise = promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenApply(value -> {
|
||||
assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value);
|
||||
return String.valueOf(value);
|
||||
});
|
||||
|
||||
var dependentPromise =
|
||||
promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenApply(
|
||||
value -> {
|
||||
assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value);
|
||||
return String.valueOf(value);
|
||||
});
|
||||
|
||||
assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.get());
|
||||
assertTrue(dependentPromise.isDone());
|
||||
@@ -178,16 +184,19 @@ class PromiseTest {
|
||||
@Test
|
||||
void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsException()
|
||||
throws InterruptedException {
|
||||
var dependentPromise = promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenApply(value -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
});
|
||||
var dependentPromise =
|
||||
promise
|
||||
.fulfillInAsync(new NumberCrunchingTask(), executor)
|
||||
.thenApply(
|
||||
value -> {
|
||||
throw new RuntimeException("Barf!");
|
||||
});
|
||||
|
||||
try {
|
||||
dependentPromise.get();
|
||||
fail("Fetching dependent promise should result in exception "
|
||||
+ "if the function threw an exception");
|
||||
fail(
|
||||
"Fetching dependent promise should result in exception "
|
||||
+ "if the function threw an exception");
|
||||
} catch (ExecutionException ex) {
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isCancelled());
|
||||
@@ -195,8 +204,9 @@ class PromiseTest {
|
||||
|
||||
try {
|
||||
dependentPromise.get(1000, TimeUnit.SECONDS);
|
||||
fail("Fetching dependent promise should result in exception "
|
||||
+ "if the function threw an exception");
|
||||
fail(
|
||||
"Fetching dependent promise should result in exception "
|
||||
+ "if the function threw an exception");
|
||||
} catch (ExecutionException ex) {
|
||||
assertTrue(promise.isDone());
|
||||
assertFalse(promise.isCancelled());
|
||||
|
||||
Reference in New Issue
Block a user