From ea7503414ef384fe2b09759d58f30cac89f1de26 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 20 Jul 2016 19:58:20 +0530 Subject: [PATCH 001/145] Work on #403, added basic implementation of promise pattern --- promise/pom.xml | 47 +++++ .../main/java/com/iluwatar/promise/App.java | 24 +++ .../promise/ListenableAsyncResult.java | 12 ++ .../promise/PromiseAsyncExecutor.java | 7 + .../iluwatar/promise/ThreadAsyncExecutor.java | 173 ++++++++++++++++++ 5 files changed, 263 insertions(+) create mode 100644 promise/pom.xml create mode 100644 promise/src/main/java/com/iluwatar/promise/App.java create mode 100644 promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java create mode 100644 promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java create mode 100644 promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java diff --git a/promise/pom.xml b/promise/pom.xml new file mode 100644 index 000000000..ca12515ee --- /dev/null +++ b/promise/pom.xml @@ -0,0 +1,47 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + promise + + + junit + junit + test + + + org.mockito + mockito-core + test + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java new file mode 100644 index 000000000..dc22c307a --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -0,0 +1,24 @@ +package com.iluwatar.promise; + +public class App { + + /** + * Program entry point + * @param args arguments + */ + public static void main(String[] args) { + ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); + executor.execute(() -> { + Thread.sleep(1000); + return 10; + }).then(value -> {System.out.println("Consumed the value: " + value);}) + .then(nullVal -> {System.out.println("Post consuming value");}); + + + executor.execute(() -> { + Thread.sleep(1000); + return "10"; + }).then(value -> {return 10 + Integer.parseInt(value);}) + .then(intValue -> {System.out.println("Consumed int value: " + intValue);}); + } +} diff --git a/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java b/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java new file mode 100644 index 000000000..a68154e17 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java @@ -0,0 +1,12 @@ +package com.iluwatar.promise; + +import java.util.function.Consumer; +import java.util.function.Function; + +import com.iluwatar.async.method.invocation.AsyncResult; + +public interface ListenableAsyncResult extends AsyncResult { + ListenableAsyncResult then(Consumer action); + ListenableAsyncResult then(Function func); + ListenableAsyncResult error(Consumer action); +} diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java new file mode 100644 index 000000000..eb43b0546 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java @@ -0,0 +1,7 @@ +package com.iluwatar.promise; + +import java.util.concurrent.Callable; + +public interface PromiseAsyncExecutor { + ListenableAsyncResult execute(Callable task); +} diff --git a/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java new file mode 100644 index 000000000..aa057d676 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java @@ -0,0 +1,173 @@ +package com.iluwatar.promise; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Function; + +public class ThreadAsyncExecutor implements PromiseAsyncExecutor { + + /** Index for thread naming */ + private final AtomicInteger idx = new AtomicInteger(0); + + @Override + public ListenableAsyncResult execute(Callable task) { + Promise promise = new Promise<>(); + new Thread(() -> { + try { + promise.setValue(task.call()); + promise.postComplete(); + } catch (Exception ex) { + promise.setException(ex); + } + } , "executor-" + idx.incrementAndGet()).start(); + return promise; + } + + // TODO there is scope of extending the completable future from async method invocation project. Do that. + private class Promise implements ListenableAsyncResult { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + volatile int state = RUNNING; + T value; + Exception exception; + Runnable fulfilmentAction; + + public Promise() { + this.lock = new Object(); + } + + void postComplete() { + fulfilmentAction.run(); + } + + /** + * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param value + * value of the evaluated task + */ + public void setValue(T value) { + this.value = value; + this.state = COMPLETED; + synchronized (lock) { + lock.notifyAll(); + } + } + + /** + * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for + * completion. + * + * @param exception + * exception of the failed task + */ + public void setException(Exception exception) { + this.exception = exception; + this.state = FAILED; + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean isCompleted() { + return state > RUNNING; + } + + @Override + public T getValue() throws ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new IllegalStateException("Execution not completed yet"); + } + } + + @Override + public void await() throws InterruptedException { + synchronized (lock) { + if (!isCompleted()) { + lock.wait(); + } + } + } + + @Override + public ListenableAsyncResult then(Consumer action) { + Promise dest = new Promise<>(); + fulfilmentAction = new ConsumeAction(this, dest, action); + return dest; + } + + @Override + public ListenableAsyncResult then(Function func) { + Promise dest = new Promise<>(); + fulfilmentAction = new FunctionAction(this, dest, func); + return dest; + } + + @Override + public ListenableAsyncResult error(Consumer action) { + return null; + } + + private class ConsumeAction implements Runnable { + + private Promise current; + private Promise dest; + private Consumer action; + + public ConsumeAction(Promise current, Promise dest, Consumer action) { + this.current = current; + this.dest = dest; + this.action = action; + } + + @Override + public void run() { + try { + action.accept(current.getValue()); + dest.setValue(null); + } catch (ExecutionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + dest.postComplete(); + } + } + + private class FunctionAction implements Runnable { + + private Promise current; + private Promise dest; + private Function func; + + public FunctionAction(Promise current, Promise dest, Function func) { + this.current = current; + this.dest = dest; + this.func = func; + } + + @Override + public void run() { + try { + V result = func.apply(current.getValue()); + dest.setValue(result); + } catch (ExecutionException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + dest.postComplete(); + } + } + } + } \ No newline at end of file From 102341443d33b7cca89f4e37b7c2e490fe780584 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 21 Jul 2016 19:13:42 +0530 Subject: [PATCH 002/145] Work on #403, added application class and test cases. --- pom.xml | 1 + promise/pom.xml | 5 + .../main/java/com/iluwatar/promise/App.java | 30 ++- .../promise/ListenableAsyncResult.java | 12 -- .../java/com/iluwatar/promise/Promise.java | 143 +++++++++++++++ .../promise/PromiseAsyncExecutor.java | 7 - .../iluwatar/promise/ThreadAsyncExecutor.java | 173 ------------------ .../java/com/iluwatar/promise/AppTest.java | 15 ++ .../com/iluwatar/promise/PromiseTest.java | 128 +++++++++++++ 9 files changed, 314 insertions(+), 200 deletions(-) delete mode 100644 promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java create mode 100644 promise/src/main/java/com/iluwatar/promise/Promise.java delete mode 100644 promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java delete mode 100644 promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java create mode 100644 promise/src/test/java/com/iluwatar/promise/AppTest.java create mode 100644 promise/src/test/java/com/iluwatar/promise/PromiseTest.java diff --git a/pom.xml b/pom.xml index 6f3e0d698..45d574e73 100644 --- a/pom.xml +++ b/pom.xml @@ -128,6 +128,7 @@ hexagonal abstract-document aggregator-microservices + promise page-object diff --git a/promise/pom.xml b/promise/pom.xml index ca12515ee..f5727b951 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -43,5 +43,10 @@ mockito-core test + + com.iluwatar + async-method-invocation + 1.13.0-SNAPSHOT + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index dc22c307a..5817e68da 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -1,24 +1,38 @@ package com.iluwatar.promise; +import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; + +/** + * + * Application that uses promise pattern. + */ public class App { /** * Program entry point * @param args arguments + * @throws InterruptedException if main thread is interruped. */ - public static void main(String[] args) { + public static void main(String[] args) throws InterruptedException { ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); - executor.execute(() -> { + + Promise consumedPromise = new Promise<>(); + consumedPromise.fulfillInAsync(() -> { Thread.sleep(1000); return 10; - }).then(value -> {System.out.println("Consumed the value: " + value);}) - .then(nullVal -> {System.out.println("Post consuming value");}); + }, executor).then(value -> { + System.out.println("Consumed int value: " + value); + }); - - executor.execute(() -> { + Promise transformedPromise = new Promise<>(); + transformedPromise.fulfillInAsync(() -> { Thread.sleep(1000); return "10"; - }).then(value -> {return 10 + Integer.parseInt(value);}) - .then(intValue -> {System.out.println("Consumed int value: " + intValue);}); + }, executor).then(value -> { return Integer.parseInt(value); }).then(value -> { + System.out.println(value); + }); + + consumedPromise.await(); + transformedPromise.await(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java b/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java deleted file mode 100644 index a68154e17..000000000 --- a/promise/src/main/java/com/iluwatar/promise/ListenableAsyncResult.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.iluwatar.promise; - -import java.util.function.Consumer; -import java.util.function.Function; - -import com.iluwatar.async.method.invocation.AsyncResult; - -public interface ListenableAsyncResult extends AsyncResult { - ListenableAsyncResult then(Consumer action); - ListenableAsyncResult then(Function func); - ListenableAsyncResult error(Consumer action); -} diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java new file mode 100644 index 000000000..0bc4accbb --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -0,0 +1,143 @@ +package com.iluwatar.promise; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.function.Function; + +import com.iluwatar.async.method.invocation.AsyncExecutor; +import com.iluwatar.async.method.invocation.internal.CompletableResult; + +/** + * Implements the promise pattern. + * @param type of result. + */ +public class Promise extends CompletableResult { + + private Runnable fulfillmentAction; + + /** + * Creates a promise that will be fulfilled in future. + */ + public Promise() { + super(null); + } + + /** + * Fulfills the promise with the provided value. + * @param value the fulfilled value that can be accessed using {@link #getValue()}. + */ + @Override + public void setValue(T value) { + super.setValue(value); + postComplete(); + } + + /** + * 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 #getValue()}. + */ + @Override + public void setException(Exception exception) { + super.setException(exception); + postComplete(); + } + + void postComplete() { + if (fulfillmentAction == null) { + return; + } + fulfillmentAction.run(); + } + + /** + * 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 executor the executor in which the task should be run. + * @return a promise that represents the result of running the task provided. + */ + public Promise fulfillInAsync(final Callable task, AsyncExecutor executor) { + executor.startProcess(new Callable() { + + @Override + public Void call() throws Exception { + setValue(task.call()); + return null; + } + }); + return this; + } + + /** + * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with + * result of this promise as argument to the action provided. + * @param action action to be executed. + * @return a new promise. + */ + public Promise then(Consumer action) { + Promise dest = new Promise<>(); + fulfillmentAction = new ConsumeAction(this, dest, action); + return dest; + } + + /** + * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with + * result of this promise as argument to the function provided. + * @param func function to be executed. + * @return a new promise. + */ + public Promise then(Function func) { + Promise dest = new Promise<>(); + fulfillmentAction = new FunctionAction(this, dest, func); + return dest; + } + + private class ConsumeAction implements Runnable { + + private Promise current; + private Promise dest; + private Consumer action; + + public ConsumeAction(Promise current, Promise dest, Consumer action) { + this.current = current; + this.dest = dest; + this.action = action; + } + + @Override + public void run() { + try { + action.accept(current.getValue()); + dest.setValue(null); + } catch (Throwable e) { + dest.setException((Exception) e.getCause()); + } + } + } + + private class FunctionAction implements Runnable { + + private Promise current; + private Promise dest; + private Function func; + + public FunctionAction(Promise current, Promise dest, Function func) { + this.current = current; + this.dest = dest; + this.func = func; + } + + @Override + public void run() { + try { + V result = func.apply(current.getValue()); + dest.setValue(result); + } catch (Throwable e) { + dest.setException((Exception) e.getCause()); + } + } + } +} diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java deleted file mode 100644 index eb43b0546..000000000 --- a/promise/src/main/java/com/iluwatar/promise/PromiseAsyncExecutor.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.iluwatar.promise; - -import java.util.concurrent.Callable; - -public interface PromiseAsyncExecutor { - ListenableAsyncResult execute(Callable task); -} diff --git a/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java b/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java deleted file mode 100644 index aa057d676..000000000 --- a/promise/src/main/java/com/iluwatar/promise/ThreadAsyncExecutor.java +++ /dev/null @@ -1,173 +0,0 @@ -package com.iluwatar.promise; - -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.function.Function; - -public class ThreadAsyncExecutor implements PromiseAsyncExecutor { - - /** Index for thread naming */ - private final AtomicInteger idx = new AtomicInteger(0); - - @Override - public ListenableAsyncResult execute(Callable task) { - Promise promise = new Promise<>(); - new Thread(() -> { - try { - promise.setValue(task.call()); - promise.postComplete(); - } catch (Exception ex) { - promise.setException(ex); - } - } , "executor-" + idx.incrementAndGet()).start(); - return promise; - } - - // TODO there is scope of extending the completable future from async method invocation project. Do that. - private class Promise implements ListenableAsyncResult { - - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; - - final Object lock; - volatile int state = RUNNING; - T value; - Exception exception; - Runnable fulfilmentAction; - - public Promise() { - this.lock = new Object(); - } - - void postComplete() { - fulfilmentAction.run(); - } - - /** - * Sets the value from successful execution and executes callback if available. Notifies any thread waiting for - * completion. - * - * @param value - * value of the evaluated task - */ - public void setValue(T value) { - this.value = value; - this.state = COMPLETED; - synchronized (lock) { - lock.notifyAll(); - } - } - - /** - * Sets the exception from failed execution and executes callback if available. Notifies any thread waiting for - * completion. - * - * @param exception - * exception of the failed task - */ - public void setException(Exception exception) { - this.exception = exception; - this.state = FAILED; - synchronized (lock) { - lock.notifyAll(); - } - } - - @Override - public boolean isCompleted() { - return state > RUNNING; - } - - @Override - public T getValue() throws ExecutionException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - throw new IllegalStateException("Execution not completed yet"); - } - } - - @Override - public void await() throws InterruptedException { - synchronized (lock) { - if (!isCompleted()) { - lock.wait(); - } - } - } - - @Override - public ListenableAsyncResult then(Consumer action) { - Promise dest = new Promise<>(); - fulfilmentAction = new ConsumeAction(this, dest, action); - return dest; - } - - @Override - public ListenableAsyncResult then(Function func) { - Promise dest = new Promise<>(); - fulfilmentAction = new FunctionAction(this, dest, func); - return dest; - } - - @Override - public ListenableAsyncResult error(Consumer action) { - return null; - } - - private class ConsumeAction implements Runnable { - - private Promise current; - private Promise dest; - private Consumer action; - - public ConsumeAction(Promise current, Promise dest, Consumer action) { - this.current = current; - this.dest = dest; - this.action = action; - } - - @Override - public void run() { - try { - action.accept(current.getValue()); - dest.setValue(null); - } catch (ExecutionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - dest.postComplete(); - } - } - - private class FunctionAction implements Runnable { - - private Promise current; - private Promise dest; - private Function func; - - public FunctionAction(Promise current, Promise dest, Function func) { - this.current = current; - this.dest = dest; - this.func = func; - } - - @Override - public void run() { - try { - V result = func.apply(current.getValue()); - dest.setValue(result); - } catch (ExecutionException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - dest.postComplete(); - } - } - } - } \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/AppTest.java b/promise/src/test/java/com/iluwatar/promise/AppTest.java new file mode 100644 index 000000000..b59187cb1 --- /dev/null +++ b/promise/src/test/java/com/iluwatar/promise/AppTest.java @@ -0,0 +1,15 @@ +package com.iluwatar.promise; + +import org.junit.Test; + +/** + * + * Application test. + */ +public class AppTest { + + @Test + public void testApp() throws InterruptedException { + App.main(null); + } +} diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java new file mode 100644 index 000000000..9c28be1b3 --- /dev/null +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -0,0 +1,128 @@ +package com.iluwatar.promise; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.function.Consumer; +import java.util.function.Function; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; + +/** + * Tests Promise class. + */ +public class PromiseTest { + + private ThreadAsyncExecutor executor; + private Promise promise; + @Rule public ExpectedException exception = ExpectedException.none(); + + @Before + public void setUp() { + executor = new ThreadAsyncExecutor(); + promise = new Promise<>(); + } + + @Test + public void promiseIsFulfilledWithTheResultantValueOfExecutingTheTask() + throws InterruptedException, ExecutionException { + promise.fulfillInAsync(new NumberCrunchingTask(), executor); + + // await fulfillment + promise.await(); + + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.getValue()); + } + + @Test + public void dependentPromiseIsFulfilledAfterTheConsumerConsumesTheResultOfThisPromise() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(value -> { + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); + }); + + + // await fulfillment + dependentPromise.await(); + } + + @Test + public void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnException() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(new Consumer() { + + @Override + public void accept(Integer t) { + throw new RuntimeException("Barf!"); + } + }); + + + // await fulfillment + dependentPromise.await(); + + exception.expect(ExecutionException.class); + + dependentPromise.getValue(); + } + + @Test + public void dependentPromiseIsFulfilledAfterTheFunctionTransformsTheResultOfThisPromise() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(value -> { + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); + return String.valueOf(value); + }); + + + // await fulfillment + dependentPromise.await(); + + assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.getValue()); + } + + @Test + public void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsException() + throws InterruptedException, ExecutionException { + Promise dependentPromise = promise + .fulfillInAsync(new NumberCrunchingTask(), executor) + .then(new Function() { + + @Override + public String apply(Integer t) { + throw new RuntimeException("Barf!"); + } + }); + + // await fulfillment + dependentPromise.await(); + + exception.expect(ExecutionException.class); + + dependentPromise.getValue(); + } + + private static class NumberCrunchingTask implements Callable { + + private static final Integer CRUNCHED_NUMBER = Integer.MAX_VALUE; + + @Override + public Integer call() throws Exception { + // Do number crunching + Thread.sleep(1000); + return CRUNCHED_NUMBER; + } + } +} From 2b945ca27f991d03a5ffd58d4faa809d1aa8df23 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 22 Jul 2016 16:47:52 +0530 Subject: [PATCH 003/145] Work on #403, removed dependency on async method invocation module, added more tests --- promise/pom.xml | 5 - .../main/java/com/iluwatar/promise/App.java | 26 ++- .../java/com/iluwatar/promise/Promise.java | 169 ++++++++++++++---- .../java/com/iluwatar/promise/AppTest.java | 4 +- .../com/iluwatar/promise/PromiseTest.java | 147 ++++++++++++--- 5 files changed, 275 insertions(+), 76 deletions(-) diff --git a/promise/pom.xml b/promise/pom.xml index f5727b951..ca12515ee 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -43,10 +43,5 @@ mockito-core test - - com.iluwatar - async-method-invocation - 1.13.0-SNAPSHOT - diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 5817e68da..f9e089f3d 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -1,6 +1,9 @@ package com.iluwatar.promise; -import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; /** * @@ -12,10 +15,19 @@ public class App { * Program entry point * @param args arguments * @throws InterruptedException if main thread is interruped. + * @throws ExecutionException */ - public static void main(String[] args) throws InterruptedException { - ThreadAsyncExecutor executor = new ThreadAsyncExecutor(); - + public static void main(String[] args) throws InterruptedException, ExecutionException { + ExecutorService executor = Executors.newSingleThreadExecutor(); + try { + promiseUsage(executor); + } finally { + executor.shutdownNow(); + } + } + + private static void promiseUsage(Executor executor) + throws InterruptedException, ExecutionException { Promise consumedPromise = new Promise<>(); consumedPromise.fulfillInAsync(() -> { Thread.sleep(1000); @@ -29,10 +41,10 @@ public class App { Thread.sleep(1000); return "10"; }, executor).then(value -> { return Integer.parseInt(value); }).then(value -> { - System.out.println(value); + System.out.println("Consumed transformed int value: " + value); }); - consumedPromise.await(); - transformedPromise.await(); + consumedPromise.get(); + transformedPromise.get(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 0bc4accbb..991c2a05c 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -2,17 +2,18 @@ package com.iluwatar.promise; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; -import com.iluwatar.async.method.invocation.AsyncExecutor; -import com.iluwatar.async.method.invocation.internal.CompletableResult; - /** * Implements the promise pattern. * @param type of result. */ -public class Promise extends CompletableResult { +public class Promise extends PromiseSupport { private Runnable fulfillmentAction; @@ -20,31 +21,30 @@ public class Promise extends CompletableResult { * Creates a promise that will be fulfilled in future. */ public Promise() { - super(null); } /** * Fulfills the promise with the provided value. - * @param value the fulfilled value that can be accessed using {@link #getValue()}. + * @param value the fulfilled value that can be accessed using {@link #get()}. */ @Override - public void setValue(T value) { - super.setValue(value); - postComplete(); + public void fulfill(T value) { + super.fulfill(value); + postFulfillment(); } /** * 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 #getValue()}. + * when accessing the value using {@link #get()}. */ @Override - public void setException(Exception exception) { - super.setException(exception); - postComplete(); + public void fulfillExceptionally(Exception exception) { + super.fulfillExceptionally(exception); + postFulfillment(); } - void postComplete() { + void postFulfillment() { if (fulfillmentAction == null) { return; } @@ -59,13 +59,12 @@ public class Promise extends CompletableResult { * @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 fulfillInAsync(final Callable task, AsyncExecutor executor) { - executor.startProcess(new Callable() { - - @Override - public Void call() throws Exception { - setValue(task.call()); - return null; + public Promise fulfillInAsync(final Callable task, Executor executor) { + executor.execute(() -> { + try { + fulfill(task.call()); + } catch (Exception e) { + fulfillExceptionally(e); } }); return this; @@ -91,18 +90,22 @@ public class Promise extends CompletableResult { */ public Promise then(Function func) { Promise dest = new Promise<>(); - fulfillmentAction = new FunctionAction(this, dest, func); + fulfillmentAction = new TransformAction(this, dest, func); return dest; } + /** + * A consume action provides the action, the value from source promise and fulfills the + * destination promise. + */ private class ConsumeAction implements Runnable { - private Promise current; + private Promise src; private Promise dest; private Consumer action; - public ConsumeAction(Promise current, Promise dest, Consumer action) { - this.current = current; + ConsumeAction(Promise src, Promise dest, Consumer action) { + this.src = src; this.dest = dest; this.action = action; } @@ -110,22 +113,26 @@ public class Promise extends CompletableResult { @Override public void run() { try { - action.accept(current.getValue()); - dest.setValue(null); + action.accept(src.get()); + dest.fulfill(null); } catch (Throwable e) { - dest.setException((Exception) e.getCause()); + dest.fulfillExceptionally((Exception) e.getCause()); } } } - private class FunctionAction implements Runnable { + /** + * A function action provides transformation function, value from source promise and fulfills the + * destination promise with the transformed value. + */ + private class TransformAction implements Runnable { - private Promise current; + private Promise src; private Promise dest; private Function func; - public FunctionAction(Promise current, Promise dest, Function func) { - this.current = current; + TransformAction(Promise src, Promise dest, Function func) { + this.src = src; this.dest = dest; this.func = func; } @@ -133,11 +140,103 @@ public class Promise extends CompletableResult { @Override public void run() { try { - V result = func.apply(current.getValue()); - dest.setValue(result); + V result = func.apply(src.get()); + dest.fulfill(result); } catch (Throwable e) { - dest.setException((Exception) e.getCause()); + dest.fulfillExceptionally((Exception) e.getCause()); } } } } + + +/** + * A really simplified implementation of future that allows completing it successfully with a value + * or exceptionally with an exception. + */ +class PromiseSupport implements Future { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + + volatile int state = RUNNING; + T value; + Exception exception; + + PromiseSupport() { + this.lock = new Object(); + } + + void fulfill(T value) { + this.value = value; + this.state = COMPLETED; + synchronized (lock) { + lock.notifyAll(); + } + } + + void fulfillExceptionally(Exception exception) { + this.exception = exception; + this.state = FAILED; + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return state > RUNNING; + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(); + if (state == COMPLETED) { + return value; + } else { + throw new ExecutionException(exception); + } + } + } + } + + @Override + public T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(unit.toMillis(timeout)); + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new TimeoutException(); + } + } + } + } +} \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/AppTest.java b/promise/src/test/java/com/iluwatar/promise/AppTest.java index b59187cb1..b2628127c 100644 --- a/promise/src/test/java/com/iluwatar/promise/AppTest.java +++ b/promise/src/test/java/com/iluwatar/promise/AppTest.java @@ -1,5 +1,7 @@ package com.iluwatar.promise; +import java.util.concurrent.ExecutionException; + import org.junit.Test; /** @@ -9,7 +11,7 @@ import org.junit.Test; public class AppTest { @Test - public void testApp() throws InterruptedException { + public void testApp() throws InterruptedException, ExecutionException { App.main(null); } } diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index 9c28be1b3..c64b82d06 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -1,9 +1,16 @@ package com.iluwatar.promise; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; @@ -12,20 +19,18 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; -import com.iluwatar.async.method.invocation.ThreadAsyncExecutor; - /** * Tests Promise class. */ public class PromiseTest { - private ThreadAsyncExecutor executor; + private Executor executor; private Promise promise; @Rule public ExpectedException exception = ExpectedException.none(); @Before public void setUp() { - executor = new ThreadAsyncExecutor(); + executor = Executors.newSingleThreadExecutor(); promise = new Promise<>(); } @@ -34,10 +39,70 @@ public class PromiseTest { throws InterruptedException, ExecutionException { promise.fulfillInAsync(new NumberCrunchingTask(), executor); - // await fulfillment - promise.await(); + assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.get()); + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + @Test + public void promiseIsFulfilledWithAnExceptionIfTaskThrowsAnException() + throws InterruptedException, ExecutionException, TimeoutException { + testWaitingForeverForPromiseToBeFulfilled(); + testWaitingSomeTimeForPromiseToBeFulfilled(); + } - assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, promise.getValue()); + private void testWaitingForeverForPromiseToBeFulfilled() throws InterruptedException, TimeoutException { + Promise promise = new Promise<>(); + promise.fulfillInAsync(new Callable() { + + @Override + public Integer call() throws Exception { + throw new RuntimeException("Barf!"); + }}, executor); + + try { + promise.get(); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + promise.get(1000, TimeUnit.SECONDS); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + } + + private void testWaitingSomeTimeForPromiseToBeFulfilled() + throws InterruptedException, TimeoutException { + Promise promise = new Promise<>(); + promise.fulfillInAsync(new Callable() { + + @Override + public Integer call() throws Exception { + throw new RuntimeException("Barf!"); + }}, executor); + + try { + promise.get(1000, TimeUnit.SECONDS); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + promise.get(); + fail("Fetching promise should result in exception if the task threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + } @Test @@ -50,13 +115,14 @@ public class PromiseTest { }); - // await fulfillment - dependentPromise.await(); + dependentPromise.get(); + assertTrue(dependentPromise.isDone()); + assertFalse(dependentPromise.isCancelled()); } @Test public void dependentPromiseIsFulfilledWithAnExceptionIfConsumerThrowsAnException() - throws InterruptedException, ExecutionException { + throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) .then(new Consumer() { @@ -67,13 +133,21 @@ public class PromiseTest { } }); - - // await fulfillment - dependentPromise.await(); - - exception.expect(ExecutionException.class); - - dependentPromise.getValue(); + try { + dependentPromise.get(); + fail("Fetching dependent promise should result in exception if the action threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + dependentPromise.get(1000, TimeUnit.SECONDS); + fail("Fetching dependent promise should result in exception if the action threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } } @Test @@ -87,15 +161,14 @@ public class PromiseTest { }); - // await fulfillment - dependentPromise.await(); - - assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.getValue()); + assertEquals(String.valueOf(NumberCrunchingTask.CRUNCHED_NUMBER), dependentPromise.get()); + assertTrue(dependentPromise.isDone()); + assertFalse(dependentPromise.isCancelled()); } @Test public void dependentPromiseIsFulfilledWithAnExceptionIfTheFunctionThrowsException() - throws InterruptedException, ExecutionException { + throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) .then(new Function() { @@ -106,12 +179,30 @@ public class PromiseTest { } }); - // await fulfillment - dependentPromise.await(); - - exception.expect(ExecutionException.class); - - dependentPromise.getValue(); + try { + dependentPromise.get(); + fail("Fetching dependent promise should result in exception if the function threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + + try { + dependentPromise.get(1000, TimeUnit.SECONDS); + fail("Fetching dependent promise should result in exception if the function threw an exception"); + } catch (ExecutionException ex) { + assertTrue(promise.isDone()); + assertFalse(promise.isCancelled()); + } + } + + @Test + public void fetchingAnAlreadyFulfilledPromiseReturnsTheFulfilledValueImmediately() + throws InterruptedException, ExecutionException, TimeoutException { + Promise promise = new Promise<>(); + promise.fulfill(NumberCrunchingTask.CRUNCHED_NUMBER); + + promise.get(1000, TimeUnit.SECONDS); } private static class NumberCrunchingTask implements Callable { From eb560f5f54b6a928e978b81549d7369f957364a0 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 22 Jul 2016 16:53:01 +0530 Subject: [PATCH 004/145] Work on #403, removed checkstyle violations --- .../main/java/com/iluwatar/promise/App.java | 2 +- .../java/com/iluwatar/promise/Promise.java | 95 ------------------- .../com/iluwatar/promise/PromiseTest.java | 6 +- 3 files changed, 5 insertions(+), 98 deletions(-) diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index f9e089f3d..3390f2a23 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -15,7 +15,7 @@ public class App { * Program entry point * @param args arguments * @throws InterruptedException if main thread is interruped. - * @throws ExecutionException + * @throws ExecutionException if an execution error occurs. */ public static void main(String[] args) throws InterruptedException, ExecutionException { ExecutorService executor = Executors.newSingleThreadExecutor(); diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 991c2a05c..03977c541 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -3,9 +3,6 @@ package com.iluwatar.promise; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.function.Consumer; import java.util.function.Function; @@ -147,96 +144,4 @@ public class Promise extends PromiseSupport { } } } -} - - -/** - * A really simplified implementation of future that allows completing it successfully with a value - * or exceptionally with an exception. - */ -class PromiseSupport implements Future { - - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; - - final Object lock; - - volatile int state = RUNNING; - T value; - Exception exception; - - PromiseSupport() { - this.lock = new Object(); - } - - void fulfill(T value) { - this.value = value; - this.state = COMPLETED; - synchronized (lock) { - lock.notifyAll(); - } - } - - void fulfillExceptionally(Exception exception) { - this.exception = exception; - this.state = FAILED; - synchronized (lock) { - lock.notifyAll(); - } - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return state > RUNNING; - } - - @Override - public T get() throws InterruptedException, ExecutionException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - synchronized (lock) { - lock.wait(); - if (state == COMPLETED) { - return value; - } else { - throw new ExecutionException(exception); - } - } - } - } - - @Override - public T get(long timeout, TimeUnit unit) - throws InterruptedException, ExecutionException, TimeoutException { - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - synchronized (lock) { - lock.wait(unit.toMillis(timeout)); - if (state == COMPLETED) { - return value; - } else if (state == FAILED) { - throw new ExecutionException(exception); - } else { - throw new TimeoutException(); - } - } - } - } } \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index c64b82d06..842558589 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -58,7 +58,8 @@ public class PromiseTest { @Override public Integer call() throws Exception { throw new RuntimeException("Barf!"); - }}, executor); + } + }, executor); try { promise.get(); @@ -85,7 +86,8 @@ public class PromiseTest { @Override public Integer call() throws Exception { throw new RuntimeException("Barf!"); - }}, executor); + } + }, executor); try { promise.get(1000, TimeUnit.SECONDS); From 09ba5ca656dd17ac5c2d518452ad5f89df388e57 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 22 Jul 2016 18:34:05 +0530 Subject: [PATCH 005/145] Work on #403, added class diagrams and javadocs --- promise/etc/promise.png | Bin 0 -> 59544 bytes promise/etc/promise.ucls | 115 +++++++++++++++++ .../main/java/com/iluwatar/promise/App.java | 44 ++++++- .../java/com/iluwatar/promise/Promise.java | 25 +++- .../com/iluwatar/promise/PromiseSupport.java | 119 ++++++++++++++++++ .../java/com/iluwatar/promise/AppTest.java | 22 ++++ .../com/iluwatar/promise/PromiseTest.java | 22 ++++ 7 files changed, 344 insertions(+), 3 deletions(-) create mode 100644 promise/etc/promise.png create mode 100644 promise/etc/promise.ucls create mode 100644 promise/src/main/java/com/iluwatar/promise/PromiseSupport.java diff --git a/promise/etc/promise.png b/promise/etc/promise.png new file mode 100644 index 0000000000000000000000000000000000000000..1a0f671080915d26268f2ce16df59c430a14926e GIT binary patch literal 59544 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 literal 0 HcmV?d00001 diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls new file mode 100644 index 000000000..fe3c6c2e0 --- /dev/null +++ b/promise/etc/promise.ucls @@ -0,0 +1,115 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 3390f2a23..6ce7de994 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -1,5 +1,28 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.promise; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; @@ -7,14 +30,31 @@ import java.util.concurrent.Executors; /** * - * Application that uses promise pattern. + *

The Promise object is used for asynchronous computations. A Promise represents an operation that + * hasn't completed yet, but is expected in the future. + * + *

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. + * + *

Promises provide a few advantages over callback objects: + *

    + *
  • Functional composition and error handling + *
  • Prevents callback hell and provides callback aggregation + *
+ * + * @see CompletableFuture */ public class App { + private App() { + } + /** * Program entry point * @param args arguments - * @throws InterruptedException if main thread is interruped. + * @throws InterruptedException if main thread is interrupted. * @throws ExecutionException if an execution error occurs. */ public static void main(String[] args) throws InterruptedException, ExecutionException { diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 03977c541..fe7dc6f9f 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.promise; import java.util.concurrent.Callable; @@ -8,6 +30,7 @@ import java.util.function.Function; /** * Implements the promise pattern. + * * @param type of result. */ public class Promise extends PromiseSupport { @@ -41,7 +64,7 @@ public class Promise extends PromiseSupport { postFulfillment(); } - void postFulfillment() { + private void postFulfillment() { if (fulfillmentAction == null) { return; } diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java new file mode 100644 index 000000000..dde2ca452 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java @@ -0,0 +1,119 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.promise; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +/** + * A really simplified implementation of future that allows completing it successfully with a value + * or exceptionally with an exception. + */ +class PromiseSupport implements Future { + + static final int RUNNING = 1; + static final int FAILED = 2; + static final int COMPLETED = 3; + + final Object lock; + + volatile int state = RUNNING; + T value; + Exception exception; + + PromiseSupport() { + this.lock = new Object(); + } + + void fulfill(T value) { + this.value = value; + this.state = COMPLETED; + synchronized (lock) { + lock.notifyAll(); + } + } + + void fulfillExceptionally(Exception exception) { + this.exception = exception; + this.state = FAILED; + synchronized (lock) { + lock.notifyAll(); + } + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return state > RUNNING; + } + + @Override + public T get() throws InterruptedException, ExecutionException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(); + if (state == COMPLETED) { + return value; + } else { + throw new ExecutionException(exception); + } + } + } + } + + @Override + public T get(long timeout, TimeUnit unit) + throws InterruptedException, ExecutionException, TimeoutException { + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + synchronized (lock) { + lock.wait(unit.toMillis(timeout)); + if (state == COMPLETED) { + return value; + } else if (state == FAILED) { + throw new ExecutionException(exception); + } else { + throw new TimeoutException(); + } + } + } + } +} \ No newline at end of file diff --git a/promise/src/test/java/com/iluwatar/promise/AppTest.java b/promise/src/test/java/com/iluwatar/promise/AppTest.java index b2628127c..1d1cb061d 100644 --- a/promise/src/test/java/com/iluwatar/promise/AppTest.java +++ b/promise/src/test/java/com/iluwatar/promise/AppTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.promise; import java.util.concurrent.ExecutionException; diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index 842558589..de0ecb6d7 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.promise; import static org.junit.Assert.assertEquals; From 40ac5525421fa2932224048fce76aded3fe3f332 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sun, 24 Jul 2016 01:45:49 +0530 Subject: [PATCH 006/145] Work on #403, added README --- promise/README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 promise/README.md diff --git a/promise/README.md b/promise/README.md new file mode 100644 index 000000000..069a8dbfe --- /dev/null +++ b/promise/README.md @@ -0,0 +1,45 @@ +--- +layout: pattern +title: Promise +folder: promise +permalink: /patterns/promise/ +categories: Structural +tags: + - Java + - Concurrency + - Difficulty-Intermediate +--- + +## Also known as +CompletableFuture + +## Intent +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. Promises are a way to write async code that still appears as though it is executing +in a synchronous way. + +![alt text](./etc/promise.png "Promise") + +## Applicability +Promise pattern is applicable in concurrent programming when some work needs to be done asynchronously +and: + +* code maintainablity and readability suffers due to callback hell. +* you need to compose promises and need better error handling for asynchronous tasks. +* you want to use functional style of programming. + + +## Real world examples + +* [java.util.concurrent.CompletableFuture](https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html) +* [Guava ListenableFuture](https://github.com/google/guava/wiki/ListenableFutureExplained) + +## Related Patterns + * Async Method Invocation + * Callback + +## Credits + +* [You are missing the point to Promises](https://gist.github.com/domenic/3889970) +* [Functional style callbacks using CompleteableFuture](https://www.infoq.com/articles/Functional-Style-Callbacks-Using-CompletableFuture) From 5fcef89384835575a5ff02429fc3da3ac5a78ace Mon Sep 17 00:00:00 2001 From: Kamil Pietruszka Date: Tue, 26 Jul 2016 20:33:43 +0200 Subject: [PATCH 007/145] #459 added known aliast for monostate pattern --- monostate/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/monostate/README.md b/monostate/README.md index 415d13f0e..3576dc659 100644 --- a/monostate/README.md +++ b/monostate/README.md @@ -9,6 +9,9 @@ tags: - Difficulty-Beginner --- +## Also known as +Borg + ## Intent Enforces a behaviour like sharing the same state amongst all instances. From 94c3a2caf3cccba03197a53fb6c3e03190036d8f Mon Sep 17 00:00:00 2001 From: Sumit Yadav Date: Wed, 3 Aug 2016 15:02:46 +0530 Subject: [PATCH 008/145] removed extra "is" from the javadoc of InitializingOnDemandHolderIdiom class --- .../com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 4aa6afe12..0c450c60c 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -26,7 +26,7 @@ package com.iluwatar.singleton; * The Initialize-on-demand-holder idiom is a secure way of creating a lazy initialized singleton * object in Java. *

- * The technique is is as lazy as possible and works in all known versions of Java. It takes advantage + * The technique is as lazy as possible and works in all known versions of Java. It takes advantage * of language guarantees about class initialization, and will therefore work correctly in all * Java-compliant compilers and virtual machines. *

From 76970633b8a654e05d4341e96ee639bc3bcde969 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Thu, 4 Aug 2016 18:10:50 +0530 Subject: [PATCH 009/145] Work on #403, incorporate review changes --- promise/README.md | 5 +- promise/etc/promise.ucls | 46 ++++++------ .../main/java/com/iluwatar/promise/App.java | 73 +++++++++++++++---- .../java/com/iluwatar/promise/Promise.java | 16 ++-- .../com/iluwatar/promise/PromiseSupport.java | 14 ++-- 5 files changed, 100 insertions(+), 54 deletions(-) diff --git a/promise/README.md b/promise/README.md index 069a8dbfe..638bb3ef5 100644 --- a/promise/README.md +++ b/promise/README.md @@ -3,10 +3,11 @@ layout: pattern title: Promise folder: promise permalink: /patterns/promise/ -categories: Structural +categories: Concurrency tags: - Java - - Concurrency + - Functional + - Reactive - Difficulty-Intermediate --- diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls index fe3c6c2e0..cdfb6ed7f 100644 --- a/promise/etc/promise.ucls +++ b/promise/etc/promise.ucls @@ -74,38 +74,38 @@ - - - - - - - - - + - - - - - - - - - - - - - + + + + + - + + + + + + + + + + + + + + + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 6ce7de994..3a1ecfa01 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -22,6 +22,15 @@ */ package com.iluwatar.promise; +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.URL; +import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -44,6 +53,8 @@ import java.util.concurrent.Executors; *

  • Prevents callback hell and provides callback aggregation * * + *

    + * * @see CompletableFuture */ public class App { @@ -68,23 +79,57 @@ public class App { private static void promiseUsage(Executor executor) throws InterruptedException, ExecutionException { - Promise consumedPromise = new Promise<>(); - consumedPromise.fulfillInAsync(() -> { - Thread.sleep(1000); - return 10; - }, executor).then(value -> { - System.out.println("Consumed int value: " + value); + String urlString = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; + Promise lineCountPromise = new Promise().fulfillInAsync(() -> { + return downloadFile(urlString); + }, executor).then(fileLocation -> { + return countLines(fileLocation); }); - Promise transformedPromise = new Promise<>(); - transformedPromise.fulfillInAsync(() -> { - Thread.sleep(1000); - return "10"; - }, executor).then(value -> { return Integer.parseInt(value); }).then(value -> { - System.out.println("Consumed transformed int value: " + value); + Promise> charFrequencyPromise = new Promise().fulfillInAsync(() -> { + return String.valueOf(downloadFile(urlString)); + }, executor).then(fileLocation -> { + return characterFrequency(fileLocation); }); - consumedPromise.get(); - transformedPromise.get(); + lineCountPromise.get(); + System.out.println("Line count is: " + lineCountPromise.get()); + charFrequencyPromise.get(); + System.out.println("Char frequency is: " + charFrequencyPromise.get()); + } + + private static Map characterFrequency(String fileLocation) { + // TODO Auto-generated method stub + return null; + } + + private static Integer countLines(String fileLocation) { + int lineCount = 0; + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader);) { + for (String line; (line = bufferedReader.readLine()) != null; ) { + lineCount++; + } + } catch (IOException ex) { + ex.printStackTrace(); + } + return lineCount; + } + + private static String downloadFile(String urlString) throws InterruptedException, IOException { + URL url = new URL(urlString); + File file = File.createTempFile("promise_pattern", null); + try (Reader reader = new InputStreamReader(url.openStream()); + BufferedReader bufferedReader = new BufferedReader(reader); + FileWriter writer = new FileWriter(file)) { + for (String line; (line = bufferedReader.readLine()) != null; ) { + writer.write(line); + writer.write("\n"); + } + } catch (IOException ex) { + ex.printStackTrace(); + } + System.out.println("File downloaded at: " + file.getAbsolutePath()); + return file.getAbsolutePath(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index fe7dc6f9f..7d8a97e84 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -120,11 +120,11 @@ public class Promise extends PromiseSupport { */ private class ConsumeAction implements Runnable { - private Promise src; - private Promise dest; - private Consumer action; + private final Promise src; + private final Promise dest; + private final Consumer action; - ConsumeAction(Promise src, Promise dest, Consumer action) { + private ConsumeAction(Promise src, Promise dest, Consumer action) { this.src = src; this.dest = dest; this.action = action; @@ -147,11 +147,11 @@ public class Promise extends PromiseSupport { */ private class TransformAction implements Runnable { - private Promise src; - private Promise dest; - private Function func; + private final Promise src; + private final Promise dest; + private final Function func; - TransformAction(Promise src, Promise dest, Function func) { + private TransformAction(Promise src, Promise dest, Function func) { this.src = src; this.dest = dest; this.func = func; diff --git a/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java index dde2ca452..048586e23 100644 --- a/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java +++ b/promise/src/main/java/com/iluwatar/promise/PromiseSupport.java @@ -33,15 +33,15 @@ import java.util.concurrent.TimeoutException; */ class PromiseSupport implements Future { - static final int RUNNING = 1; - static final int FAILED = 2; - static final int COMPLETED = 3; + private static final int RUNNING = 1; + private static final int FAILED = 2; + private static final int COMPLETED = 3; - final Object lock; + private final Object lock; - volatile int state = RUNNING; - T value; - Exception exception; + private volatile int state = RUNNING; + private T value; + private Exception exception; PromiseSupport() { this.lock = new Object(); From d484e7f731fe36832b4dd3ee690c1180bcc6117b Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Fri, 5 Aug 2016 14:38:25 +0530 Subject: [PATCH 010/145] Documented singleton double check idiom, explaining the dynamics that happen on each step for better understanding. Did this due to a PR #475 --- .../iluwatar/singleton/ThreadSafeDoubleCheckLocking.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java index 50203609c..a8fd9648a 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeDoubleCheckLocking.java @@ -53,11 +53,20 @@ public final class ThreadSafeDoubleCheckLocking { public static ThreadSafeDoubleCheckLocking getInstance() { // local variable increases performance by 25 percent // Joshua Bloch "Effective Java, Second Edition", p. 283-284 + ThreadSafeDoubleCheckLocking result = instance; + // Check if singleton instance is initialized. If it is initialized then we can return the instance. if (result == null) { + // It is not initialized but we cannot be sure because some other thread might have initialized it + // in the meanwhile. So to make sure we need to lock on an object to get mutual exclusion. synchronized (ThreadSafeDoubleCheckLocking.class) { + // Again assign the instance to local variable to check if it was initialized by some other thread + // while current thread was blocked to enter the locked zone. If it was initialized then we can + // return the previously created instance just like the previous null check. result = instance; if (result == null) { + // The instance is still not initialized so we can safely (no other thread can enter this zone) + // create an instance and make it our singleton instance. instance = result = new ThreadSafeDoubleCheckLocking(); } } From ffbc5f2f295784b30073b9b339518c590b95397a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 13 Aug 2016 17:08:57 +0300 Subject: [PATCH 011/145] Reorganize LotteryNumbers for easier inclusion in the blog --- .../hexagonal/domain/LotteryNumbers.java | 84 +++++++++---------- 1 file changed, 42 insertions(+), 42 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java index 6f54e743b..69d654657 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -79,6 +79,47 @@ public class LotteryNumbers { return Collections.unmodifiableSet(numbers); } + /** + * Generates 4 unique random numbers between 1-20 into numbers set. + */ + private void generateRandomNumbers() { + numbers.clear(); + RandomNumberGenerator generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER); + while (numbers.size() < NUM_NUMBERS) { + int num = generator.nextInt(); + if (!numbers.contains(num)) { + numbers.add(num); + } + } + } + + /** + * + * Helper class for generating random numbers. + * + */ + private static class RandomNumberGenerator { + + private PrimitiveIterator.OfInt randomIterator; + + /** + * Initialize a new random number generator that generates random numbers in the range [min, max] + * + * @param min the min value (inclusive) + * @param max the max value (inclusive) + */ + public RandomNumberGenerator(int min, int max) { + randomIterator = new Random().ints(min, max + 1).iterator(); + } + + /** + * @return a random number in the range (min, max) + */ + public int nextInt() { + return randomIterator.nextInt(); + } + } + @Override public int hashCode() { final int prime = 31; @@ -107,46 +148,5 @@ public class LotteryNumbers { return false; } return true; - } - - /** - * Generates 4 unique random numbers between 1-20 into numbers set. - */ - private void generateRandomNumbers() { - numbers.clear(); - RandomNumberGenerator generator = new RandomNumberGenerator(MIN_NUMBER, MAX_NUMBER); - while (numbers.size() < NUM_NUMBERS) { - int num = generator.nextInt(); - if (!numbers.contains(num)) { - numbers.add(num); - } - } - } - - /** - * - * Helper class for generating random numbers. - * - */ - private static class RandomNumberGenerator { - - private PrimitiveIterator.OfInt randomIterator; - - /** - * Initialize a new random number generator that generates random numbers in the range [min, max] - * - * @param min the min value (inclusive) - * @param max the max value (inclusive) - */ - public RandomNumberGenerator(int min, int max) { - randomIterator = new Random().ints(min, max + 1).iterator(); - } - - /** - * @return a random number in the range (min, max) - */ - public int nextInt() { - return randomIterator.nextInt(); - } - } + } } From 7c2f5da9260357437a884d2117ae5d292d8eb800 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 13 Aug 2016 18:28:53 +0300 Subject: [PATCH 012/145] Add final keyword --- hexagonal/src/main/java/com/iluwatar/hexagonal/App.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 3ae55b706..0f7239a89 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -69,7 +69,7 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; */ public class App { - private static List allPlayerDetails; + private static final List allPlayerDetails; static { allPlayerDetails = new ArrayList<>(); From 0b36a3153d6b4aef7604b26259ca03fa89a3b4a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 14 Aug 2016 22:42:59 +0300 Subject: [PATCH 013/145] Fix checkstyle error --- .../main/java/com/iluwatar/hexagonal/App.java | 90 +++++++++---------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 0f7239a89..92ebf3572 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -69,53 +69,53 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; */ public class App { - private static final List allPlayerDetails; + private static final List PLAYERS; static { - allPlayerDetails = new ArrayList<>(); - allPlayerDetails.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); - allPlayerDetails.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); - allPlayerDetails.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); - allPlayerDetails.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); - allPlayerDetails.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); - allPlayerDetails.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); - allPlayerDetails.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); - allPlayerDetails.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); - allPlayerDetails.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); - allPlayerDetails.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); - allPlayerDetails.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); - allPlayerDetails.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); - allPlayerDetails.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); - allPlayerDetails.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); - allPlayerDetails.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); - allPlayerDetails.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); - allPlayerDetails.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); - allPlayerDetails.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); - allPlayerDetails.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); - allPlayerDetails.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); - allPlayerDetails.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); - allPlayerDetails.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); - allPlayerDetails.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); - allPlayerDetails.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); - allPlayerDetails.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); - allPlayerDetails.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); - allPlayerDetails.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); - allPlayerDetails.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); - allPlayerDetails.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); - allPlayerDetails.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); - allPlayerDetails.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); - allPlayerDetails.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); - allPlayerDetails.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); - allPlayerDetails.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); - allPlayerDetails.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); - allPlayerDetails.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); - allPlayerDetails.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); - allPlayerDetails.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + PLAYERS = new ArrayList<>(); + PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); WireTransfersImpl wireTransfers = new WireTransfersImpl(); Random random = new Random(); - for (int i = 0; i < allPlayerDetails.size(); i++) { - wireTransfers.setFunds(allPlayerDetails.get(i).getBankAccount(), + for (int i = 0; i < PLAYERS.size(); i++) { + wireTransfers.setFunds(PLAYERS.get(i).getBankAccount(), random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); } } @@ -145,7 +145,7 @@ public class App { private static PlayerDetails getRandomPlayerDetails() { Random random = new Random(); - int idx = random.nextInt(allPlayerDetails.size()); - return allPlayerDetails.get(idx); + int idx = random.nextInt(PLAYERS.size()); + return PLAYERS.get(idx); } } From b16d7fc970538c0f8194a5dc7db567d0e8b16546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 14 Aug 2016 23:00:27 +0300 Subject: [PATCH 014/145] Configure Travis notification email --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index deb436cd2..bcbad6827 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,4 +15,8 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh +notifications: + email: + - iluwatar@gmail.com + sudo: false # route the build to the container-based infrastructure for a faster build From 7e77216919c0c5cef6e67e477c2ee93ee16f25f8 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Mon, 15 Aug 2016 11:53:39 +0200 Subject: [PATCH 015/145] remove link, resolves #479 to avoid confusion, the link is removed. For more information on why this is done please look at the referenced issue --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 811d6a17a..1789398ec 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,7 @@ are familiar with the patterns. # Getting started Before you dive into the material, you should be familiar with various -[Programming/Software Design Principles](http://webpro.github.io/programming-principles/). +Programming/Software Design Principles. All designs should be as simple as possible. You should start with KISS, YAGNI, and Do The Simplest Thing That Could Possibly Work principles. Complexity and From 56100927a9537831e6d73d20d3a43840f1fc2d9b Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Mon, 15 Aug 2016 12:33:24 +0200 Subject: [PATCH 016/145] Addendum #481 Change page-index for faq.md --- faq.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/faq.md b/faq.md index 69f7b795e..2280b42f5 100644 --- a/faq.md +++ b/faq.md @@ -3,7 +3,7 @@ layout: page title: FAQ permalink: /faq/ icon: fa-question -page-index: 1 +page-index: 5 --- ### Q1: What is the difference between State and Strategy patterns? {#Q1} From c79df708b14115176721aa8dbda1ccd97f88da37 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 13:17:53 +0530 Subject: [PATCH 017/145] #211 added real world examples from Java api for creational patterns --- abstract-factory/README.md | 4 +++- builder/README.md | 3 +++ factory-method/README.md | 12 ++++++++---- singleton/README.md | 3 +++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/abstract-factory/README.md b/abstract-factory/README.md index 485599b98..fd669551d 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -4,7 +4,7 @@ title: Abstract Factory folder: abstract-factory permalink: /patterns/abstract-factory/ categories: Creational -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -30,6 +30,8 @@ Use the Abstract Factory pattern when ## Real world examples * [javax.xml.parsers.DocumentBuilderFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/parsers/DocumentBuilderFactory.html) +* [javax.xml.transform.TransformerFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/transform/TransformerFactory.html#newInstance--) +* [javax.xml.xpath.XPathFactory](http://docs.oracle.com/javase/8/docs/api/javax/xml/xpath/XPathFactory.html#newInstance--) ## Credits diff --git a/builder/README.md b/builder/README.md index 5d1f3d24d..9f374d94d 100644 --- a/builder/README.md +++ b/builder/README.md @@ -26,6 +26,9 @@ Use the Builder pattern when ## Real world examples * [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html) +* [java.nio.ByteBuffer](http://docs.oracle.com/javase/8/docs/api/java/nio/ByteBuffer.html#put-byte-) as well as similar buffers such as FloatBuffer, IntBuffer and so on. +* [java.lang.StringBuffer](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuffer.html#append-boolean-) +* All implementations of [java.lang.Appendable](http://docs.oracle.com/javase/8/docs/api/java/lang/Appendable.html) * [Apache Camel builders](https://github.com/apache/camel/tree/0e195428ee04531be27a0b659005e3aa8d159d23/camel-core/src/main/java/org/apache/camel/builder) ## Credits diff --git a/factory-method/README.md b/factory-method/README.md index 05549cf4f..c379e9609 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -4,7 +4,7 @@ title: Factory Method folder: factory-method permalink: /patterns/factory-method/ categories: Creational -tags: +tags: - Java - Difficulty-Beginner - Gang Of Four @@ -29,9 +29,13 @@ Use the Factory Method pattern when ## Known uses -* java.util.Calendar -* java.util.ResourceBundle -* java.text.NumberFormat#getInstance() +* [java.util.Calendar](http://docs.oracle.com/javase/8/docs/api/java/util/Calendar.html#getInstance--) +* [java.util.ResourceBundle](http://docs.oracle.com/javase/8/docs/api/java/util/ResourceBundle.html#getBundle-java.lang.String-) +* [java.text.NumberFormat](http://docs.oracle.com/javase/8/docs/api/java/text/NumberFormat.html#getInstance--) +* [java.nio.charset.Charset](http://docs.oracle.com/javase/8/docs/api/java/nio/charset/Charset.html#forName-java.lang.String-) +* [java.net.URLStreamHandlerFactory](http://docs.oracle.com/javase/8/docs/api/java/net/URLStreamHandlerFactory.html#createURLStreamHandler-java.lang.String-) +* [java.util.EnumSet](https://docs.oracle.com/javase/8/docs/api/java/util/EnumSet.html#of-E-) +* [javax.xml.bind.JAXBContext](https://docs.oracle.com/javase/8/docs/api/javax/xml/bind/JAXBContext.html#createMarshaller--) ## Credits diff --git a/singleton/README.md b/singleton/README.md index 2a481f5c8..f821752ba 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -31,6 +31,9 @@ Use the Singleton pattern when ## Real world examples * [java.lang.Runtime#getRuntime()](http://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html#getRuntime%28%29) +* [java.awt.Desktop#getDesktop()](http://docs.oracle.com/javase/8/docs/api/java/awt/Desktop.html#getDesktop--) +* [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) + ## Credits From eb75773891360ff8f872fa4aea0fd3acb2f6c034 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 19:44:48 +0530 Subject: [PATCH 018/145] Added FAQ on Memento pattern Difference between java Serialization and Memento pattern added --- faq.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/faq.md b/faq.md index 2280b42f5..01a7e7b72 100644 --- a/faq.md +++ b/faq.md @@ -65,3 +65,11 @@ Flyweight. ### Q7: What are the differences between FluentInterface and Builder patterns? {#Q7} Fluent interfaces are sometimes confused with the Builder pattern, because they share method chaining and a fluent usage. However, fluent interfaces are not primarily used to create shared (mutable) objects, but to configure complex objects without having to respecify the target object on every property change. + +### Q8: What is the difference between java.io.Serialization and Memento pattern? {#Q8} + +Memento is typically used to implement rollback/save-point support. Example we might want to mark the state of an object at a point in time, do some work and then decide to rollback to the previous state. + +On the other hand serialization may be used as a tool to save the state of an object into byte[] and preserving the contents in memory or disk. When someone invokes the memento to revert object's previous state then we can deserialize the information stored and recreate previous state. + +So Memento is a pattern and serialization is a tool that can be used to implement this pattern. Other ways to implement the pattern can be to clone the contents of the object and keep track of those clones. From a0c77c32b58dec68b75a65ed8de4e0eff966ae15 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 20:49:28 +0530 Subject: [PATCH 019/145] #211 added further examples for structural and behavioral patterns --- adapter/README.md | 4 ++++ chain/README.md | 1 + command/README.md | 3 ++- decorator/README.md | 8 ++++++++ flyweight/README.md | 4 ++-- interpreter/README.md | 9 ++++++++- iterator/README.md | 3 ++- mediator/README.md | 10 +++++++++- observer/README.md | 4 +++- state/README.md | 5 +++++ visitor/README.md | 3 +++ 11 files changed, 47 insertions(+), 7 deletions(-) diff --git a/adapter/README.md b/adapter/README.md index ea3baa7fa..6f517127f 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -30,6 +30,10 @@ Use the Adapter pattern when ## Real world examples * [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29) +* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-) +* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-) +* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-) + ## Credits diff --git a/chain/README.md b/chain/README.md index ef18f6f64..0bf73fb92 100644 --- a/chain/README.md +++ b/chain/README.md @@ -28,6 +28,7 @@ Use Chain of Responsibility when * [java.util.logging.Logger#log()](http://docs.oracle.com/javase/8/docs/api/java/util/logging/Logger.html#log%28java.util.logging.Level,%20java.lang.String%29) * [Apache Commons Chain](https://commons.apache.org/proper/commons-chain/index.html) +* [javax.servlet.Filter#doFilter()](http://docs.oracle.com/javaee/7/api/javax/servlet/Filter.html#doFilter-javax.servlet.ServletRequest-javax.servlet.ServletResponse-javax.servlet.FilterChain-) ## Credits diff --git a/command/README.md b/command/README.md index a5478394c..987ec6a34 100644 --- a/command/README.md +++ b/command/README.md @@ -4,7 +4,7 @@ title: Command folder: command permalink: /patterns/command/ categories: Behavioral -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -40,6 +40,7 @@ Use the Command pattern when you want to * [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html) * [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki) +* [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html) ## Credits diff --git a/decorator/README.md b/decorator/README.md index 63795114c..9acf417ad 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -27,6 +27,14 @@ Use Decorator * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing +## Real World examples + * [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html), + [java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) and [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html) + * [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-) + * [java.util.Collections#unmodifiableXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableCollection-java.util.Collection-) + * [java.util.Collections#checkedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#checkedCollection-java.util.Collection-java.lang.Class-) + + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/flyweight/README.md b/flyweight/README.md index a98dced8e..5bb8eb380 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -4,7 +4,7 @@ title: Flyweight folder: flyweight permalink: /patterns/flyweight/ categories: Structural -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -30,7 +30,7 @@ true ## Real world examples -* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) +* [java.lang.Integer#valueOf(int)](http://docs.oracle.com/javase/8/docs/api/java/lang/Integer.html#valueOf%28int%29) and similarly for Byte, Character and other wrapped types. ## Credits diff --git a/interpreter/README.md b/interpreter/README.md index 87c1c47f7..4fcc5e6ff 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -4,7 +4,7 @@ title: Interpreter folder: interpreter permalink: /patterns/interpreter/ categories: Behavioral -tags: +tags: - Java - Gang Of Four - Difficulty-Intermediate @@ -25,6 +25,13 @@ trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable +## Real World Applications +* [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) +* [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) +* All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) +* [javax.el.ELResolver](http://docs.oracle.com/javaee/7/api/javax/el/ELResolver.html) + + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/iterator/README.md b/iterator/README.md index d6be7758d..723e7f03c 100644 --- a/iterator/README.md +++ b/iterator/README.md @@ -4,7 +4,7 @@ title: Iterator folder: iterator permalink: /patterns/iterator/ categories: Behavioral -tags: +tags: - Java - Difficulty-Beginner - Gang Of Four @@ -29,6 +29,7 @@ Use the Iterator pattern ## Real world examples * [java.util.Iterator](http://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html) +* [java.util.Enumeration](http://docs.oracle.com/javase/8/docs/api/java/util/Enumeration.html) ## Credits diff --git a/mediator/README.md b/mediator/README.md index c7a0478c8..5a784bbcb 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -24,6 +24,14 @@ Use the Mediator pattern when * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing +## Real World Applications + +* All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) +* [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-) +* submit() and invokeXXX() methods of [java.util.concurrent.ExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ExecutorService.html) +* scheduleXXX() methods of [java.util.concurrent.ScheduledExecutorService](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html) +* [java.lang.reflect.Method#invoke()](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Method.html#invoke-java.lang.Object-java.lang.Object...-) + ## Credits -* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) +* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/observer/README.md b/observer/README.md index 6fbe3cdab..e25359f02 100644 --- a/observer/README.md +++ b/observer/README.md @@ -4,7 +4,7 @@ title: Observer folder: observer permalink: /patterns/observer/ categories: Behavioral -tags: +tags: - Java - Difficulty-Beginner - Gang Of Four @@ -35,6 +35,8 @@ Use the Observer pattern in any of the following situations ## Real world examples * [java.util.Observer](http://docs.oracle.com/javase/8/docs/api/java/util/Observer.html) +* [java.util.EventListener](http://docs.oracle.com/javase/8/docs/api/java/util/EventListener.html) +* [javax.servlet.http.HttpSessionBindingListener](http://docs.oracle.com/javaee/7/api/javax/servlet/http/HttpSessionBindingListener.html) ## Credits diff --git a/state/README.md b/state/README.md index f5cb189fd..948c62a08 100644 --- a/state/README.md +++ b/state/README.md @@ -25,6 +25,11 @@ Use the State pattern in either of the following cases * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. +## Real world applications + +* [javax.faces.lifecycle.Lifecycle#execute()](http://docs.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html#execute-javax.faces.context.FacesContext-) controlled by [FacesServlet](http://docs.oracle.com/javaee/7/api/javax/faces/webapp/FacesServlet.html), the behavior is dependent on current phase of lifecycle. +* [JDiameter - Diameter State Machine](https://github.com/npathai/jdiameter/blob/master/core/jdiameter/api/src/main/java/org/jdiameter/api/app/State.java) + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) diff --git a/visitor/README.md b/visitor/README.md index c1e24a624..bda789a18 100644 --- a/visitor/README.md +++ b/visitor/README.md @@ -27,6 +27,9 @@ Use the Visitor pattern when ## Real world examples * [Apache Wicket](https://github.com/apache/wicket) component tree, see [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java) +* [javax.lang.model.element.AnnotationValue](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValue.html) and [AnnotationValueVisitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/AnnotationValueVisitor.html) +* [javax.lang.model.element.Element](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/Element.html) and [Element Visitor](http://docs.oracle.com/javase/8/docs/api/javax/lang/model/element/ElementVisitor.html) +* [java.nio.file.FileVisitor](http://docs.oracle.com/javase/8/docs/api/java/nio/file/FileVisitor.html) ## Credits From 28647cdf482e878bb28ae535ae9c23cc1992f8a5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Sat, 20 Aug 2016 20:57:48 +0530 Subject: [PATCH 020/145] #211, consistent use of real world examples section in all readme files. --- decorator/README.md | 4 ++-- interpreter/README.md | 2 +- mediator/README.md | 2 +- state/README.md | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/decorator/README.md b/decorator/README.md index 9acf417ad..08f869645 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -27,13 +27,13 @@ Use Decorator * for responsibilities that can be withdrawn * when extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing -## Real World examples +## Real world examples * [java.io.InputStream](http://docs.oracle.com/javase/8/docs/api/java/io/InputStream.html), [java.io.OutputStream](http://docs.oracle.com/javase/8/docs/api/java/io/OutputStream.html), [java.io.Reader](http://docs.oracle.com/javase/8/docs/api/java/io/Reader.html) and [java.io.Writer](http://docs.oracle.com/javase/8/docs/api/java/io/Writer.html) * [java.util.Collections#synchronizedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#synchronizedCollection-java.util.Collection-) * [java.util.Collections#unmodifiableXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#unmodifiableCollection-java.util.Collection-) * [java.util.Collections#checkedXXX()](http://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#checkedCollection-java.util.Collection-java.lang.Class-) - + ## Credits diff --git a/interpreter/README.md b/interpreter/README.md index 4fcc5e6ff..be6517962 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -25,7 +25,7 @@ trees. The Interpreter pattern works best when * the grammar is simple. For complex grammars, the class hierarchy for the grammar becomes large and unmanageable. Tools such as parser generators are a better alternative in such cases. They can interpret expressions without building abstract syntax trees, which can save space and possibly time * efficiency is not a critical concern. The most efficient interpreters are usually not implemented by interpreting parse trees directly but by first translating them into another form. For example, regular expressions are often transformed into state machines. But even then, the translator can be implemented by the Interpreter pattern, so the pattern is still applicable -## Real World Applications +## Real world examples * [java.util.Pattern](http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html) * [java.text.Normalizer](http://docs.oracle.com/javase/8/docs/api/java/text/Normalizer.html) * All subclasses of [java.text.Format](http://docs.oracle.com/javase/8/docs/api/java/text/Format.html) diff --git a/mediator/README.md b/mediator/README.md index 5a784bbcb..3452082ef 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -24,7 +24,7 @@ Use the Mediator pattern when * reusing an object is difficult because it refers to and communicates with many other objects * a behavior that's distributed between several classes should be customizable without a lot of subclassing -## Real World Applications +## Real world examples * All scheduleXXX() methods of [java.util.Timer](http://docs.oracle.com/javase/8/docs/api/java/util/Timer.html) * [java.util.concurrent.Executor#execute()](http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/Executor.html#execute-java.lang.Runnable-) diff --git a/state/README.md b/state/README.md index 948c62a08..549afa61f 100644 --- a/state/README.md +++ b/state/README.md @@ -25,7 +25,7 @@ Use the State pattern in either of the following cases * an object's behavior depends on its state, and it must change its behavior at run-time depending on that state * operations have large, multipart conditional statements that depend on the object's state. This state is usually represented by one or more enumerated constants. Often, several operations will contain this same conditional structure. The State pattern puts each branch of the conditional in a separate class. This lets you treat the object's state as an object in its own right that can vary independently from other objects. -## Real world applications +## Real world examples * [javax.faces.lifecycle.Lifecycle#execute()](http://docs.oracle.com/javaee/7/api/javax/faces/lifecycle/Lifecycle.html#execute-javax.faces.context.FacesContext-) controlled by [FacesServlet](http://docs.oracle.com/javaee/7/api/javax/faces/webapp/FacesServlet.html), the behavior is dependent on current phase of lifecycle. * [JDiameter - Diameter State Machine](https://github.com/npathai/jdiameter/blob/master/core/jdiameter/api/src/main/java/org/jdiameter/api/app/State.java) From 95cf9fe367c7c7c58e80f9b35ef5db0ca00c63e5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 22 Aug 2016 18:43:29 +0530 Subject: [PATCH 021/145] Work on #403, made example readable and moved methods into utility --- promise/etc/promise.png | Bin 59544 -> 55725 bytes promise/etc/promise.ucls | 30 ++--- .../main/java/com/iluwatar/promise/App.java | 127 ++++++++++-------- .../java/com/iluwatar/promise/Utility.java | 91 +++++++++++++ 4 files changed, 172 insertions(+), 76 deletions(-) create mode 100644 promise/src/main/java/com/iluwatar/promise/Utility.java diff --git a/promise/etc/promise.png b/promise/etc/promise.png index 1a0f671080915d26268f2ce16df59c430a14926e..0aef198acb4e9abdb4cf5b34f61869b46e870c8a 100644 GIT binary patch literal 55725 zcmcG$bzGI(wm1F&A_58qp`;?pqE)0*q>=7c1nEv`ML<}DNGaVV-Q5U+bhjYg-SL}i z;oiE>Iq&=4-{;hGE`XIWSHq@mi2crl;n;;O!^RKSEFWnwH{$<5(u|j45&ygcqct5RY$|&~5Sy(p7l>wNFVGq&Yc*2DH|0ku0BbalYPP*J<)+6jLiV zQi`PG%*}o1uvU3Z3^y$A_w`~}uwq@0EtUs!cjyZ!@6qd*Lk>%drF_pjH)o}yd&f)V zhO)KR^f(Vot<+o&7oNA$gJ(8GMLuaJ8DF+0y=sxMXWZmEy&6SQp*c!H+~hkiugb#0 zz}#G0OG~Iwu9!)0Q#q#Aoq1_-*T?!QZtT5#29lDqJUsq5IIn`J)MWA^S=wg%l(aN+ zYCQTGv{oe|BElDCJqU2gB_kfxc7K0ncXlek$mnG{R_04?M#FQN22N1g_1xFD0GC9B zV4{O6W?EHs(b2*Grx5M%m{sVRtXu8J-WqZWTPR{qnrOUgk8VB<-ehmbHkl4^; zTH0U&YE(o0?K0Q1{d9#bmAa$jt+wFZ2F~{wop*)%x7Q}-YTXUfiEmymH-FJIl>a&y zU4_NQrcn1}Wo3fXg)4}|$gs*~Z?)^|EOrtahGEWoI?LdxDLkM2-pzVkk6_crz52z% zLUslQ7QAPzEsEksPat&ty}uqS_|eR#hj$sZ81&-=Dcxk>U&h=hC}g&^Et>C2I4U&4 zNZWdU%{{F?PnYS@BLcYHhnsu`^hSxj_EQZ_>QEBhm!u?78=A7RrQ zDDua!$w{Ug*N;nIdarq@DQIb3yZWQ9JH3G5g0$Q2mGk+!$w$tc)z%@E*uZWQ-nP&~ ze6L8o)bd9FufNQ_hZZjS^V=C2(sp)7R8od%9(lK0!xDrlx7t|C3kxwcsx>Kc-R+?$ zt|1@n*uFj$b;!>z>c+K6S}u{vc6A7!S4=)w%jsC%ly>7pIxd3*Z<@t&dc>&z!r7@~ zNCS8Z$i@w^oMVdYFL`gqgoQojbA#K%;cy9|uiA#6*<^BTjPPbEa&nT1SCf(!#>(w< z2gG{13EIM$Un~#G$cGJ1lB3NNY8f*#^&~a6v=m;vSWiSGDIIm4vQf($@&3BHU(iv} zd=jfqRr%j2JMpR&33OkA`^YLACsF*lE^Nu`8^(jFyEn(|94}w-!X@RX85@hHulxlesy#tm7Dh{(UkX8d9>6jrJ~|ey7=;X0Lu32Si)1!ZN0r2YR2$S^U`Ej zuq+ewVGrHJxWq)>Q<<=VclPiQojMBKUmTui^#-eP3OViWxbbu1U=>KKe2N4y{{))( zANVBW1f68eE*`!MznxuIvw#kNg(n@Q8cen(UwzvRHI?p}Bm^D6*xXDtk8aJh<8O(} z-VMm-w=s;fO`_t$M`R%yY~;%Dxf~n(=p=dh5Vkp3l#zcha@va3m*z3-!8D%|Gi$rg zDbY5Nv{IB|yi@B=UU;ZaPrt)yw<@otRZFlo#yOm+M!0(=23+p;c?!eAjwP%O6RT z{4LxjI);?IhbMBj+d(s7D%c_OvE7lqJ#{FLDI4VMaTOGnQUkm1^i`ia5cb`raKgat z*9jAmcxFv$%Vu>yfCj`)VZq~<+OK-jkB=L!-MwhH)UVr{YR8~C+O!hGf_cgO^-Ktv zMwxZ{w}bUCEgZY${VCsDIL0d(DmU~u=3;P7hVdjxQ%BySGiW&%-ax^Fkh7kU0}(B2 zunm~3@pm{W4t9xSSG|Ovk~yYHlTSh%dhLngVY~U3E5lE`4>q?K{Nybxa^ncRNnv+o zlguUtuHTq*Jw13O78%89XKDU`(~(6=>n!u(OCi}gxfB*@kFuSm0Zy8h0NMM*o_>BZ zF+ABkue+_s3S7^4Mp?V$yyoXs!o#zB?xVhWA|b(o3UlMA|21Z*9q&lVeMhF*UGgJN zglLxM57erk!+TdvW0qDbG}zp*@(X&yJ~6tURr=5@6I3+dWIS>{v@e?L?d4OYqnAiI zEVB{UvY$j33+i2&I6GC+)NH8tqVi~ZRvzDuCq!We^}b%}PgT-fxJEGR2jZap zd_R1HxJg;c1KN`6{G+Q^xcXF}ljq0x18HM}?qlw*N$Dh&aV)(?)z?X69I>+B zf2O+Jon!^(pTlldU`LAQo33a97#+oo^@EKjZPV|prol4ghQ9EKp4^{s5d3wShYKn0 z1y)!5Gz%sd`)a|uT>N`ovZtk-++W-NoYU6IZdTUw%67Ys^el;^Pqa`wHm8|@?ALQgd)TTc>S=%+}F@}ATor}F)Tvk@;bAV3qJ38sy zPS;(FQ~bYauyOP9Y`ov^z%M=$+LG-YNsRlHAxN`9!d_28kI(0M}vhlG#q4C?yj+pcMj%LMRQ~*Pd@c?acKv!@hYa1di)94(b%nF zHVyAM=#l=cmRS#HzO)bMcQz;+6Z`1TuASX|sq z79oy%{$*vu?O-!)34VBXq!rgZUh)Pp>-6Wde%sAu4{U;)$xcE$zP^nM3r|#3;tKRl zOt!Z=yaf8iuww-Ush9Z^8ldnGKcr5GBdX^rvrNq_Eo(q3FbG7AkBArxr4O{4lhiI( ztr&conBV*+l}T4Zb(*HYc4zTr#7#u+oSuekcdXA&8?5Gv5) zXaL7_gkY?A%?MQ8zK8EKGSj{l6|oo^S3MF7`EXTA6(4VU7ZfI*xa6N=VqheEplZ%* zFE$I4iZ*7pNiWRLZv^|_+Gv`!P2tX#RWzH7`NkAaU^A>+WtwjaC3tss_SM|V$!Up2 zf@I{r@;@JE__3Sm;MLG*vflo0&m{g>o+Id;YgHpJbrLnyA`-Ue`+0u+GCC7$DJdrgcP6=vD(C;{Rhg!n^ z`=1gFQMT773qWp8-JFZ*d->J(_;5)>y)}Pj?v~i|*rnZ-uFSmpxmy=eVUX5yL_QJL zMbkH4js&HPn5avzGboG4N@P4CEGVxA8Re}g*54)z+GT!P4>-sOYI_O7M?rx}e9M5G z?ZarNd0l<|`0#M^pfV>ryIX%~a3VYV=c9c#4z^Dc(Z)N|qM6P23rd_ySxvMJ)5Y-_ za=Bg9N2*<8%IyO32-MVG>giz*xAa(%WEv6;e#E_iiY6|8vcCS=^~%l~z53>jp=w|E z#C>9N-Y}ZIHS)E=4ME*2XefAYgviv`!w=bvUgvtbH#N0I{K%*~l^;$)>I0X9(aX$u zkz-MFX;i8Phv2t#hj_JcR=@wKwAeE zb_x7_rQ=>kc6RJD7KiO}5T*vSoSZjQ6Y=!5U=nOB8>2 zt`U9|%zPPi1e(ZU3n+lf=B0gF{cE`O@}onasU`|d^89LM8oh9(Jm1;pwZ7wau&%aU zx$Qxdo?jTuDTP?uK{KtDplSofXxjWD&bKy=+UsgBUVMy*ASL8ed8Io*C?|)Okf7Jt z*!F_HwssZtBJkpzllN;{p-*HzhP`k|Zy#%iJo?9j3_r9qwidKD)?L<0q6sBPlPQss z4<;{{Y7CnJjbSv2OY`XXII?lXY>Z}a*X4}&Y#}o# z$>8Nn>(k?+eykVE5%i^N#l~@iBh^Z35k0W&H@k1%WQ`OHi;qs`BO)2PiA<{njrh`5 zR+Y^n6CgHZD^p=fTGit_JNPYFvAMZcA3qZ0P^M>c&wurM1R_jd1KDmGu)8s^M}DpH z38Lw?9PIldsHo`4!XgAF7ajczY}fDRL*)`;o7fnzqPV!0Gc)~s{R)GE&{C5UDcin% z8*OUps;xEN)cJw{1}wTLSfAEUg7Ogqhq7mCb7s5%R5lC>4QQ#&svH*z)s*MUONV6c z`b}^A96mic8m)8;_Ip>S53qss(Q4Oe}-~bMp<~^XCR> zYN|Kg=(4Y&;KfEE+dL_3lwJ$*b>A^Kyu++HLo@-4UhHx#C{pNLKn5;) zZDlXn?!~0X;l1Xkt*@vE6E`3ts)V7EvFCYqPl8goSO58g zu(ByMadHsc+TVCK3DH#9PE@_~zJXq_KVp`|SbcmQz4KR!dl<=4_~QGgK>4`a_F%K! zz3#BLs-mU!gg!yIT1~Mi+}21>j~|XFL@nEMe~P~Q;_X%=0f6_oA`|ajdN&4+;HkX3 z<{d*BDfbXT&gJH^)7O6I>EY2K2O(aD!*=Scdd~;sw$_Oqb80AnHh& zsLZ6%*8uwYl@TRi1BM0$Ajsvq`ba-Ul=HK>`DFX~ZflfUl8y9#&sf0HKhJ^1PdVm0 zxj{+Ua)0+hZrZdLLCqq&S@ ztG5@0jiq>nEJO4QOx@Fic`S{QUSe&$a%rX&eQGiz!?=*|IUmaEfR?zBpF>KXW#*gO z98b?1bn2R*U_%UFQeaN2U-hx&eo|Ojp%ZrW#Tw1GoO@+L9tnzn+b5LEdxAK~7gSJrqv_|qHA39e5WXvZ_1!%1|5UGbkkn@WEm7UT=` z`XJCRI~~O)!&h%^m7ib4lAiX><+vpDQSSOvk!3ROy{obo7Cs3-^>J|>ThboCvfZ#; zkd<;^Cm=W+-H{JO%=!j$)|t*z!{i;xrO1Py`6vBKlmg_wu~_sxR`#;m-T^TYp9UXW z`lN7hsDidd=I}#w=I<0*aQyHYHnvi3z>9dF?i+*o_^a;oXsGJ8%kO0l?W0S7|KX1> zYzMNxTcxXf|J$FUsoWIhb@mU~;g2=T3aUXz0$xD=@@3C^l>596@>^WB@FU8D8oq&* zkp#hh%LyN9rPoE7;0gIC&hzUbzBE=W0@XcatR^YSXTrqXaEd1V`n6qDOtt1?YSC^i zF>4Fr?~{;4Vag0tHf>IHL?f9l0=H1gCQwniH9UCp^QU<{yo~EOvGOjb2c5Lp+a<1N z_C4dFPPeb#R90Q|^6E%z{P^)P6_phTd<=~IZ$X*STxm&PK}`el?=uOTG=6vh`M@r~ zq5WvJ-q+;+cEM_=_n6dKK)&-|W6Dz^f z%gfJTH8MhpsSb<1o&XP*E^%6q4L`hztOPN3EC3L$HZ)RgcWgiVnd$ZHtix!Q{ucx+ z<*{o8@*|7qc%>s~hooEQyVgQOG6{G;*kcoy?oYy=>FIjpQKrXr!L_sYZvzmI@4{cf zF6bqzZhK*sJz}Ro=REObR}Hi4h`4HL&AolA5J1)kuz7$v?1K`$t1EwMYBneI$MZdA zqqo!}?3`*j1YihnQ1K++2I#l}2w6cTI}y`WpSn6qR#r|^rGYF`5)Ny(=9~H>UsJ-^ zL1Bm-H6aFR%RWcsvzy48?^7P>&Rm{`jg2B`7v5jenta-Poi8f# zdnptiO-AW*dxL<*a-&{X*Q2a_B>!l6n?@PJzeNf9jQ4+4#2mMW8ujdc#MdRY>0+KF z3L4_!#+KVQMzIG8!$CKQd$UuC%}o=4@>g%>tP4k(82iFTxEn-rXfaev(51RUR}`Nn3{*Nwm|ZU{=uP2%PcEX3?WcZhE+gae;)R466%3g;X-QGR zAe>(NH967~$84?NZ(%0MVBVAQz9%D}tG*`aH-)m@%2{4V!l%;uqXR~T{V#tCKt-L;(r|TtL$w#Ud0zCvt(lm5=;j|Ex!+k>6(?34CnYy~D$!?OwI#48dFWHbQfcYX&@jmAT~-~hf5lqUUsD+&(Fa#6n<^#9mNfJLQw^@=^i;$E3IJ;=r+CO}rSpe%68hTh@Kl_YKP{DKr0N%d0m_F*Z0mwC1 z2p9J#Q!OD?RwGLHRq~hZ?I(me&R3BVzYhur^BB-4e^G4Hv$=7D z6f3qh%+bieKz1rI{tG81s~tKaA2xQ-=;(5ZNwx+joAo>t=ng8!R|wYWGq~?+s|hMo zu)Tl<$N8L3Ytrk3Vpik;Z2RhUS{oZj0}%iX$T<9vEByT?J#|k-`q{Ud^o}yKMc(t= ze1flvw_twa@NIQWB5)fpsr2-16E3tS?ED}zNIZ+|I9!_7AFFsRu#o02{QIH*?B5y& zWc{rzav!qvC+C@tb-VgXk79ITTqprxUBb3VQ}*whyJt^L{|``U|8+u6h7TGmT*_u# zpXPagx<2`M_ugiQ-00Yf*A?RZuf#+E^iIMGSzOHU@wv>)t5>?a1?s;EXFFK2{Y(oA z;L+~wPTRI?%A5mC6wcqjTZ@bLls1}Gk4fBhIxZmqO%ExOr;!3!GC+I*r>Pj=7Zns- z;6{^qolhmM6YOtiT#g^O-Fzx2T6%_u-AzYfH#8%nPU2GL*i0=BEZ1@#tSP=u`rXme zV|3p0>KRnBkQH>IzxSXe5Nz6}rnW^zUzwSQ=qy##h(9AK_q2MjmsLD})B3Zi@Z@N( z)NwD>9MZ^}M#HiSzFl9h*R3ro8Xgo>8W51+?mjgAO%a5=7?%JxO7P0(-L2E31ZU?c zK&pWVKtt0o2()eX;Get(u;VHB7o2mN*WkSXrA@OuJKgXn)k+j|b`}Jj)p+^9_9KHE zV2yw>7|{R_BexyZOR@fHZ}pZn)ty|PzLFpU={>PNntKDUE7fwL`xJ-)di!Uk^+6A( zYjn=aZ!HgT5nSzv{FXpMef`D?C}NUiSdtR$e-$wz&cBNoul+kZn6U*hV6y_7<`#gs z=h0oxPIy$AwHuxfd+%RLLYLmC&55$gevjq%8nk|NMw%5R0n85egJxqyjbC5AN*{zvdjV55YKLjC=jv-HA>@=XD7dLl!>4eUNZ5n;HO1XJKKbEN-526wX{5 zd=ME$uM7vH>wSY9PR{=o;&}1)p3%b4mabxM5etW%DSZD@BN}W*l7LU};$5J?_?5Em zba}pP@{ey>9=egA&x`0k&-Df%&Di>q5Nli`Rl9Yhl;)EJN(s!^W$q|-4&QSEe-u=( zL^qfs3NN6S>gv!X&wm|*rF<8jls&%s=%TbKZ>2*eC@+b8_b<5~%sdc}%!uTvlo1w& z&xi!pHX|hkAkC4Ne^k}ddIMo~uKqP5q#u^2@;S|ACc9_z-noFH4r_0yjM=9$WUD*r z1Q90I$||M(HP4lw)?*qbMhf3qhBFrBjdV^G|5!-z^Xm$tDgFyZHospgBA=%bgoqo8aX!_~^!O2-csYpr30sy6b@@3ac+z{b_QU++53Qw9W{o z?HrC4!H%suun7IkbX_drx>)>m@%U8j(s4M*xufNF*2ng8fRz5s6a_G2m&(xy!Ow$JR@e#g8bXH zm(n}qMZA^<#BJA1eG+lW)9E=X8g6GJvKL-Y{}w3J zDp_nM{qEv!yZujmvW8Bj|3>v=`#}r~KJjy*al8%13W41f>Qyy0WkBx)nTPiGcj3{| z(Mn9ZxEG+_OQ1q_JLGWOU5+rTdcDw{6wU2O<4E_{ua%xZf3BmW^K#3kGd-O~7Z(HS zMMwT(aVUQ-Njxm|V|cjO3;CgX6vzgwR46vt(#neJ*{7PtWz7o9CNyXffwJ*Q$Q&kK zZlRZjl5VC~OhRr%fPsXhg{N@I&@Wz0PEMvV;(}rokkQ<8!;y$*C8q3?8o7Y{GU=SZM@^YNrZ)X8GRysF@0uAFJo^p7h zlR`HD2O8!*4!8*MWdcGKvv>@Jul9Uo6cj@EZcFci3siz>fl`^(QP81AFkwJQ26^x! z9?_@-6$|q5Lfo-`olF{%<^rn$D)gj{g<|ay&jtOGoEctG0=?G;^1?*pXn+66932nB z!v$ji{)dN!7>i5=X1U+@ENp(&LWT~dBE)I`&FfBL!5|nJ;=NVt9e+PemK&7$WB+S&rUKWCXeHexJNud1VX*v<~2&KFOt zCV!{8dUJWKe6uTo$eSLNv$D?tkV-GB+1oj919ohMFSCq5n@1nxS~b)wuT+6n>=hY_ ztJiLQo%|x$;E9t{FmkykkYH>{GXYe;> z5y%-x6h+>rWM<~(=L-gqje*@vBx=Z}O}F)xjt(8cTFuIfb7<56WY+qBKmx&@f&`^) zU<6T7)k#RctLh`-3Yw|M2a^T{mB&RrA|^(}J7{y_|1NaX{lRx7lu_8vVJ^bV*2oCA z%%xI58$(etYWRWEJ|D-00n6=qhhcW?*z2qn0K&1bz{&(7I5DTdJTvJJBh7(k*(x`V zkJc2ft0CeEf`%Kcc-~p>8b|*}iJLr7$tf%MIInki11(v3o)JJR;IG=kAJ{I#fW)i@ zAM8S4p<&>rr)f&(7_L8W8!2v8f$%5H2eR09O(hqZKusRl2-KDf?*SuEmoIzG4Ef^t zZ)syY+Y4Hn9^|US{;6%NiklSp7jau0pXiv&ac&7f$6mj_2E=u5Xn~&VaGz~=VQcZj zofA@$??C!Au)j@EvG1wnQaT2v{Pu%bM4u0*6C+vKI=hZ|+t|z>+~BT1+~?Jb5m||E z2|4`d(R7|e5CJAnn&rmN=??iI)^|D%VkP_vJ$Vg~LG!$z&hWo~Bj$~=y;Rh#C zboBFydB9`B(U&H;fQbNRAbA|br4lkAB8`G@%elx*?5CSgRpQEC6x=PO+I+ zkdTk%Y$G*0BrG&1aCH33NEFQnf;}rYr zhfs|b5aZc&;B1dS<}BWQ$>Mf*f}%tckF5^@8rTd} zm|fAl)qcz_8;y#p>LupkKrjnLPLYw{fuxQm7myziv=0(>Tokx+p?DOJ!PvGRbHu_t zVYcg9p#=dgb`eHfJ^X@R zJq#o3Y%azu`HAT`iOWpO8_}Q(qZ;<1%yS(vVmrbChNnB(tAI>8zi_W0#t&TGvs|}F zHqu}5rSn7D?;OxB%=3;Oc0IEA*w}(8zbQGl#;!IUo?pY$r?8Ku4`RPri>@5IBkvX9 zS(o~O*4S=>^!)AlG+?J0o(MgUd>ge(IvBlNkxvh}^)ydTI3K%hn)x5C?XM^JBxA=H zuMIfPevc?l{lt9i99KZ706zv;a8(ql>DdVoy#_FH+* z{~{ES`WKKQA>=Khd6YPH(i($z##=WNs>)BfSwF@sx=J!={S2QGH!4l}!F%#JOz2ZM zQL~AuVR|d#vP0!|9hHts7tu>)xY^nHv|n<5lKzwgL66QGN(C#i&*?H4-PU0vL(Y%e zjCw|AFtmS)eO#dGOVK?bYGUhA&6?{@lGvL73ZB|R0inaZ@^w0+!y~DO54#qI3tM~B z`T?6CHaLiLAMX`4oQr*D;T5Fazp@r;? z&)BG`;c`>*+|F6HVzXRN;+H;A+8rJ|@pqa)4?x6kJf4%{5m1da`DdEN-J>?B-NN{f zd}Id!Uq{r)W{(txn(5@u!S#Lqq|;285;9rIU~zr{f`dPII0GjlK4KzCd;38(HR4;u zM)|@wQbz#1oYC>4HXHyCf*j^aMJ#$iRz~4v>g0ef@6=Jy-_p|g^=os3KuYZMkmjl1 zLU4bE@w%n0d+X?~XsLz>{14KVtx;J)$iZj-l!IsGKw{Jnka}>&-+FM+teG00<`~Jz zX>8z(l37~vM6&mYI^Cpm^D+ArP1dmM^iybPwr!FEZmfn?h!Au2pfF6)gWJ+s~sebuaZsIR_A?c_x!jLx;{3Bv1B z)Fvja#n^aGOmPxa|A^>OUdg`ylfVxMOSDRI?lLmIC8)2f$jrDbCriMfviZi{{qwq7 z6178e^2IzTJnH1mk>XyxSI=AbF=2nFh(%B!?yMXUnQQ_GsNHQJy0L$`SkD8tHLJ620M zlX0b1*{!91`GU|rCqKKjRegp}WB1QIdh%0VLS#MVhe*g%FIk{oyHACIka80k8Rm_R zE*BYl>zGyJU~Z^a^Sq-8mh+h48smp{c5F@;zeM$}PQGy6H690|UBGoXBL#_<7oT!7 zJt80kvrQXIv0|<6<5?&CnbS`8Foay}~Nx(PQhfq@+^-p@XfN+x1i)ICO3?k-*41qPb;Ce`Shu{fXf!d5R(M%pAcm z#f<0LO+-aiN^DoIt8%@5N}H`um0aTybLo z?J4t8^kp@38!~>FX?RNqB;-DknfugD2@TKVI5x(9bcCP=R>Xq}oEC{KEMcKMH1+7s}BU=R98ARye) z-QDdp=|RYIK&fQ!RY%SWQv${mORzX6h3}o_j#j)*bll4V@{HsCpSnO!T(RLPRBWmN z(z?OY)-6XrPtW>B-b=Fp{y(=@Oiw$BR3~|0r&v@!q*>~xZ)?Ed2~2HCFVcZv@fDmpqZ)*NqLs^^qyWFT)p7GZ%F0zVrh$43;EO ztk-PrpI?2E<;*&w&*GOXuwc1t3e-F;LciiT!g<^X@%)Rci|a2lYLm)Ok#%npi-D zKeGqW?Gml*X0wdkgM3_|hcqy(l3dxYqkc-e4A$bE)7dNbk|vumY4o;OQ0J-j2fY7M zTQJGSeC}V7yPa@B6dv5I>>I)3%(b+;(bTGMu{Itef40@J2K%*pcrGg>?snf>kGZ^e;T3Jv(@kn)=2P<};noIG0$M8RZ=6xpbH;&2 z?WjlcO2CU!H)yw%Yff-$p|woLgP_#N5$)o(^ITMIo!#I-2IlJewl@iwB+W`qgX1Im zeE?Wg9B+yjhX| zq1?z|VPb5ccu}_<`Fdw>O*-cP)D2aW+2~mqb|)sQ%>i2)u%g)ZGi*YRKrL5Lq9^6_ z((3VYe`YJlw$oU#0BnE!IFilAu>NQ*Ao(j$s=w0pu+^`^mmE_rwOi8{6&(haC*U2p z!^vQ`b~^nn*(jBo8ar*HIgq&yCHUSf04=Cnhlh#8jgOl)SWmg#SXio`-ewy-DI7Q* zOOq2OB#hXaE51%-8_q;vTVY21p zvy})|ul(JAT6Jv8wLNf0s;kfN01LjzWs}Z4Bk1jS#^$03WU167YO0IyGqKX*ET7}A zCALYCps1(h6-MiblQ?B6Y?K5rbqr27;}_``Cp(X^L>nFk@}n}HgKnvGIk@@Zv}54@ zlf91Jg*_9E;d#UsS0^th6NTxaf(6|U3hgjV>FAUE7D**yO&T;U$;;bpV1{fbo!&q# z@bkaA!OZ&wAii3u2l6wm@9OB#p%0kth6r7*p=5O0uzh(&(Qcz%s&vw8qnR_5FRJG& z<=h<2Or-=R&3`L$P?b3)&q+r}IUrqrq{mXh3N)fIvj6@yRyn$sOyK0)9Gyj9jH_b6 zEvC(-Y&#j}y;ZimcTK<$X^h~dS0{%%$?ZV4dbsPn(j#RB zc+O(8fKa>S_vBsUr9gW9l&~Z-hI<~*Ki$KFkLa<_i8k5uR z>+EdSb1L>eI^z#MybPdaH+r28B)??c3|(3=n%xaS$!;*}Oo_D;zML+TPY8YsrO&E< zO8+M@oVq1N9Wh96pWZnYc-QFSmBqbznKn?>$DedQi^~-9z4>fmWw9?lBLiqzk>WE; zGHmk#%*Hm5uM9$f78IM%R(d(1qM7tDP0Jd{RooynOxhVdqPZiQs?dCk`g)zB1QiS% zM}Xgrf||bjW|_?rKZ=Yt#0_#KaB>e_uC&iJ7(@t6CJc`b<|3$7WD5Y|^5Wpr6!VUr z@Mtk3*!7>h+zNxL!5)D~;ps^>NbwFJ6@lk*er~N=X{<^^axMko=KAa@$qrE2f|~@h zCc3(xKIWN&;hKM3KhDv=|M+*W*EfVo2c*0p=F=ooYO0*I5+tgQEz85g>VY&~ze7H^ zkU4p4etY33XnWIBo39$A{OnF^{;#xo^92rT<1$K0BP#j#pM8Eb>v~2j0C^}lMC{Q3CKuaXHz4JRK5Z@|ZO~2lSf-g$yUBegHn3@SO8r3(Xq? zZaSt{Y}<=FSQ7Upp+g`YP$4&P#Kw&gmHAjGZ5~X(XTbH;3+F1&JOEGdqSEq=hkSuA z?U(dVVVlK*ZjksU`*oG6(C$ z;kLj^T8J9|q>YbDVZuqzxLnVO^(MoOaqJfLRmh~?=G zk`zQ3->?upURZqGx(vd}h0k#$`>XR5;h0MT%*U5U!8~amo&wH*?pa&o1)>(!sDiYA zbCXcISq}NvQ4sPO(EJkraY8_83LMdsY$6NNY;HUb@=wp6|J;?5F*`j@1H0;o;_x`k z&61S^Y|!#RzWke{08QXt)HPtdcsv2dfx>CQRXF3Ma!&}!4ups@feI_&Fp#t1%Y%2U)(CotN3>o)6a^Z}mxv+= z8{4QXLBjPIAp~&M7KQuP75~`Ux&gLMKIk8X6qz0ZPlb#vrQv6e)5>C8(_x{yd6_=_$-jb`;2t;_Uagx6G-*0DRkW>M3m<`+2l2Mt#lme$sQHGVE^TCjc8Q%vykh*bFXk=uv zc(t`ymE{a{J_voRyzP&yg!m2Q`9;95#vcM$_M;>o+DYkPF}WsWm*o4* zaoY_+Ton+rec1q_i*)v93V)%QKQg`ot7H0Xu-#1Mm|@V_Gm~67$ZaZ}t^ zazhDj_x~V3&<#-0&@ARO1fT!rTJRba6cnf(FRinlKUE>*ux~zGV89}O09r=C4!a7d({)U12=1o$Pi~auhYR|(x0)_+cWv#DIWRDT@{a>2d_&iUbKDjO^Bt+qmejoW7 z1y}hVBpmiG2{ z1k~t2@d;c_PyX^IexxbzI#=+&1_6&Wjy51?Jp@_7-!MS2?ux?;BYor8{)(IaGZ4Ji zIU_P6<-QdhV36Ph8=Wvr$L)C+aFzv@g1nv)wOusZMsD-{bFJ-fyVBcxMdzA)2DiNL zEm@2JxfT-c@PS9vs{+&-G}Z)WFOIHFRMWX9Vikg;S@`xFX>@O1ioLz6$fO&9N?DKS z3%m=Uf7@FB7mS)uZt|Fl?jT7*0afTf$l0iA)#$I@l=eB?8Tbf5syOpo1pV6=o6f0y zAaS@&mM*}O_P2u9CicB8(13au{{S7p7?3@>NQ;X*55H3dXU^zT{nom9(-$Buv&~g- zPR#xWL9>3>xeh;`c<*XAVyk&70-6ec{{*0!qy7j~0C`_L#lswAKNRM4&Y8}`3(Td? z-XqpAFu|yRPC23TsfZZT7hQcGU>;r{GOT6;I(F5x0#g^){tUoHlupmcm_0hG4rWy6?Rks%hce%M94PZOH)I%piSwx>13?Om zPESvVxzRY{-KISc{l83{ix;KJx32w>i~6txrMS5(o0~;|YHZpaas3Y^Jpu- zn#z)TJz_BR1r$!xdPi=^VJa#?H8jm4ytQxy3Y2s}iU1_3ysC;{1?4e-5M{ejLw`8{ zfGa)y=kicK>Ki_gkrk1H5EP0w|FPa+`3qYb1-%#GUv+eK^K|Ag&knhd zE0a7>nm38A@x5{9TwA$}9MAtC3)Y4tq;R!2mcIJswMEoh{bFm)@Sf)9@vDgSbj}N2 zTL%zTFb;xN6av0!LWX4+gJfK+5S;f|TBOvkJL1b1lb=1b;Q$)K!jRr5JlehYED{#m z2t@172Qy(Xw`letJ=3fvze#|rf^z~AvCwM~K7_DN!2TZ|Vj14zJ{-%->nl)0THhat z1%|U+GYe#aR5b6R!XA{&jWhg}MgG;AU=N)D;tTMNdpkA^Fy$T4y8dE-Ih~N&D5~<& z!35zwfX2zPC2**Tmu@w92LLHHBK1Z{XZ3Y=7XT;Exs$=e9KzQkzO_&esH~#Q&ni9m zoxcoFUe=v)PchzCTp)Noq}$%xTk}uV@!zgh4`BCP<96JwuwM9>hk=@T8A)V!qv|&` z=XT+D<%Z7K=T=}59i3^Qm<-{@(sxGI4ZyF8L0P`PjH51JQcy}47Xx;sDGQ*J zDgnM-{f*s~O%$|4S#VU?1Gd4aF*`82%=S6=Gvg6i+B&7G;4$Dkvceb%-+SeQxdS#D z^b##s`diG4QGcHk1PXsw)0*~?Pn2|$JYElx< zN)RH8r`9J3Gk^U$3<_OQZFs}^>g^-RawszU>ye$ee$p}NQ3ea$3l_K+Dj?HnSxJcS z*)mp-0!@i{Z6IKn`?uMJ{~^bsw#l_q12Vt99!K~`b>K4t2WH{P-t)`{VgkUX1m2xs zpnIlW>rc@3-P#>!mQj7U-cja-Qmi$2qY`;WL_`%9%1B7;0#qm1+k>26dv6}J3E=bx z5>jDD`$0L$q=)(NFp%-PTXR9i&r0zr0Hf{;4A}A}pj4{_5`pH}m7C`QXn*M&hu-PF zarF*9W*5d?aF9d?Np!IC)X%y5_gVZRr_z3T^LY~WK=r*+GlbBL7Th_nbN*kh(M^P7 zd}FYjqXvop0mC{dq=Tzr&bvU@KETt5@<*aQSOTrRNQ@yWa*du-s7}En&xR79##-dh z&ui5+c?E^Z_VThnzayWly=(L5&Or99t);uxKm5bmq3T=iP}uyZCdM2}+645E=If*;kaHt;i>A@u)YNp#@^U%dr~mf@Na&eC z9yj^R`*rNP+m1%(V`TR88*@MLU4un@Zy%y7MCwoY1a+@nz{l@>|MrOzy10OPt)~9l zw=>#segUQ2MjsdrK?7F!RJi8juu=VRCBYjKz9!X5l8+`{y;4ys`i`^Z8nZVv_x&@I zUi)hFW`yM{GA@|GXvt}&S`*t%K+Th&Hs`1oy5>CEo}au$GTJs)X(J%Om`J!PaEnO_ z9RkKD5d0#F^+LC@kQs1-s#fzvW5k+Au(Zdxl-;5vy!r#(4ec(fLPw;E)&9C=%}f+0 zRp8gQ%s$}seiDA?sY6aCDjH`rTj1+eb;*G&ECyQRzDh@o3m-Zf;hZYBG#$Q8&bIM4 zfa|Yz#_86PV!l-V=24CY+^!OOl*2ib@EY|OcQE-xxzglmJoxX}Qj6H$F-)So?yd8Y z2DMZ4h|tSa#%$o#$;>l43fW7sF|Mbka00)(cirvQG|kUka~B&mJ;c8ZNJ)`^KTnYd$%@66ijEQm=A9BBPf@lhsr_ph(#T0 zqfQQKx0SKorjDXm%iz0rA}1Q3P-9OHZDt_EBux6hdzdj@H>RsAfs2c)GcdG4f75Lf zOB52D638B++z`kN7N7`xCMyUn7Qn2hS#}aD(q^LHN+Cx_tvETFGt9k_nAM++ow*YO zoPLHCEXI1#Rb+W>4W+2uwR*6pXT}}3A9HARzN>g_R294_)HZPhF6{rK?5)G9>b7{{O^4DU2m;a)5+a~TsB|k0A}J*$ zDUvD(NOy-Q(g;Y0G}7H5B`GPXNZhfNH_kctd!Fz9tL(MrnrqH6$N0rqk)+oj55Jwj z!KZnkUFD>(_5u;98g}Q-9kXsBf0lpVSF3K8NL=FpK65%5WBLWg%QGKkeWWjYd2F`< zG^mRb|6{WFN}TI?l^k^iP&V<|ObJ@gbq{6{Nkg>yRb2dCldshYahvM{|I}1|VWGiV zzJgfZ3FEt;JJbCu3U}Ij^d`aI`{piMeHMe%{-a-W>+rsksutEapnnB!b55$77&NS~ z`EgiD;uX$DEW91TNLq5A*k7DiMw0pR&qq=R4=bRP7@2o4!JZ-}IhzLb)cZUrnVcBA4z{+b_9^dJ z$j<(QAJLnqbLs{zTWOb)XRV7}ZD|Zi#qtOXxu~e9g~i3!y13!HXNxbq1f@84&F}t7 zOT(2bMi;Iorn0l#yAWdn;e$Bo8>2C*@`RuDA!8iF*}@1!TaHAN8_f|UBFx8-!lzlA zF3F+i;7FAs=$V@v@=FwU^-f8VTI$Zn)+fSA_WZQrjq`?pCX&zk!Rc_G5p`g9f`Fn! zc0?$Sm{n>~5ie{)Ua3J@8AjqZMoN_4PgHZSY!VVrzsGmZF=6Y6#pwUqcY*yRky^}G zrk;0^iD?W2L+;i2mHJ^!Os88<=v8vMzQf{fwqLUI%APDW?ap19s(HDr%5oWAo8<1G zQuvL3_IOgU=aXgfJtwPSz3wJ@`n`ds&mr0cbn!kl)#C^;(;k}HwKeQHZ(Us-Gd_Dk z084+z7Ap{eB4f+hbmYk93}p{l6w2KWuRdE5+1O$^O#AXz|#!Lzxgtn3eJh5s4T)QBzkRE;d(rwoKgScD4*uiM(!d;$Esy5WZq#0m5670KY+{ zk57#HXX;NXx8=4<9*>}V(Fh-wCg|i;IV)WzW_-5NpBy4GzP!Gk{Z?)ZJ$0Y0a#67; z^`D_IIHw;P<0}cpMyiKKJ`DBt>R}^;XPx}yp6k5X{pr)jTgknh@8+i`zb>{UJ$z_| zNP^u-KpQ<->6j}K&{ALj1rDkwb{46kIOZouW#Sn(apDkz7yHfl6cYH!sz~2u|GgIE z?`(GjZ`9nhP&3fp{)CQR&?;xUAX{H*6%xAJofOgC9jx(zhA&uoXE8`lDa2%DqWH-t zkK=+ruSff?MM->qbp!|NUrP&0W5X%beF|oD@5l9FZ{D21(YMter9vB^Jo$3j$nZ{? zWgPp>ewhyR)Mzy@hmK0nsonl-KYf^sgl);m8YKC3W=71}Go0h<*>$nS(hX6S&#?&z z#4JY`pL-=G^4Gcy}0E~;|)-q|Mkv$ZWg6>D?$v9x9X6&(<& zY|M!3QoH3$;a30gm)hp$d>3J97X|%C%;+M5U-SPJhcFR9o}5dUbEUqd#~&Nsk?79wXM4jc z;E0lqN=RUb_`pki76yhfdy9$~WYek6WxNa2Ew@EQB}Y*<5z7csa<#YPz9i-{jeTo< zu77R3flN{Q*{FH(Q&sRe3_i?>i_vfm&(&cbcSI8Jf=NfsJ zJ`vsh=Cad5-s@9b4EAjX~VPHtI6?1(;$(5gm9;n*g&oE9PQdnIOWZk?xn zT~@XT#R}?8{l{p(bDFH7MQ*WM&R4Oh+Zvm^5L{lDMoHg2UF?i+^o>Egzd3ospU>Jl zPvn@892&bNHDU5Hc>??ynKS z{+PKoI#l8#dCj-6q`KP2zyReiHa79r&FhXF0saB!^gi9f@4P@g8Ptr9;gWK5FWB}1 zGb(rq2m+qiW_F-@c+f2M*bqiTd-WMb%+%@zY?R&4v|CYgnpK8ApY`_B%fn8l5bLYv0 z_Ifujx!J4o=*m}9F6iZcMq$Ne;Fty0S-+{xZGUZ?-zt$3a+Jq1((%!N8P(*@^o~9^I81IvkV~H8yzK3in@{n^Q^d?q8&W zzG30Iox0<_y$K16Sn=p6cijRc>e|I-)YOyf^)HE8GBB%Uq*XE`S);(tS)wFIWfS_- zyQcl56y#^M8e< z!2hS{xnycMiRsMaaYR*WAo5i*Z2Wg95AnhC-$3 zJLuQ_#ES~s+uN00SBS01BGhvnZEacE*odp>7{htA&WlOZF5Y1dcmz>gJiX5h{))|t zjO0X_J99LhE{ui&Rhf6}0c4hpP^pB)#`-D2-Yp8OOL7oNU?d~*3U6DCM?^A86kh4a zo{3P5RYQTisb5bfJQQ23&DdT6Ase1HXyIsRNRRsC-N8!@6v(WI+`}0mfzuJBt6N=& zximM|(A73SRWmS@ef&191oHX#aR1s`sn5Yy5~sJbW1&#GANlenS^|5Jl6Uh}{H*P? z1QCT8!2wk!_SKUM(QOj@h{Fk+&mpvWj~Gp68*qlv{>1Zy?_IPa9Dvuo4Z2vPT2YfC zT084YyH6PsmY4ShQmKG*t;EE{4V?7yDq)P-Q0Y*M5_-k6Hjw?I9LE;x+FR&wHSMI> z*qD0O_^Ro(p@Sz1v>MNNg^#zpv$M79pjFI@`CZy+;{m?hKy}4epL0S_aRluV+R6p* z^lI}VSI5KMrV;GDV0`0g0Z1Q5lDIFEK8ED!d6$s0lLR6F3!eizNqygU>grO^$a-Nh zl-*b5Y-+$WBNNe}OhG^pBkW3!v*WTU92BDT%g^uO19KmqqU$KEGIn!BEFn@d{lvn1 z0}gGJ8f#W&uqvx-4=t#;6|)>B3XK3WX~r}XyoSI?FnOf#nVzt~pz(s|>2cEwjK07v zkt17SGo@Isj^}8-5Y$1Lb_z)dc2z~m;o3(|Z7Y0DWsUCl$RlCp&ynEY6dcSoUFj&V z;gMD>6Y;9jPJD5lNz{|amYiqWJqD|iY_YKcQclQ35CeC8qt!?m?!uCJ$MkY!vVwh^ zF$>!l&tYdHLN>YFFy$^4irE6*`?aZ>MsS((6Thv+Md11+@^hv^YCJPz-6Nv&3xf0I zC&*(&za?-M0xUW6j9o95a_*zB)E1SDsaXUm2UNFy+-u*)@4StB+2nrcVBmiPLAJpp z;-RWN{?uBp!L3|Pz=ph~$(vU@k5$r|8NnnYhiQ80iG8?qIAJh3DK5sPYqzrI{n__# z6cc%=PeJazHXemPM}2GbbZx8z)99-1_ztWpB59?NWD$2w_2J#EtvX)G#fMrNp8s&pr$fPh0Gw`fSD2 ziG|7jx#gAP^49qF!V~CCZpp6UKbuc&zdg)$8T-ME2sHZ+?rH;2b3Q*Qj-1j?7o8v@4fjTO%7V&r&fjwF!J|iyeuOML zN<~EzYM3!Uk+|lM1A)d>0QSWl;)6C%Mtkw4jIJ1w{9bv(mpuGbu#=H-$cSI2GHsll z3=o&}`&XQpI>narfB1lreUHh(G z3z;kODY~`~%R~{*-TqfwrEk>-avKtIJsWG~zjihv^Wm~M)YhpouQ|b1Jlta{D&u$E166cQHv6Nr#$o<1y&JBz^$ z8JM3=KS}Ps@c-+%evu~qDh~g9qx4^>Ex$?sRPL}d&G}=`>M5z7+wL_(^6o1Q+V5uj zu`pb|YHV*?pq1q%{{oRJ#^a(e6(;i}TRz9SZcnNyj4ax)dMf4h@9 zZXfRMnu&^r+;p^nFD8&-`$e837Z)(^FM!H+*G1tS0!S)RS)3czmBzY%0i``zm!qNGRCd16Cj($M^A)kKaikHQ?x6YL9vWB~tO@k?p$Y zOnrSXLoJUFpXyViDk|c6`PNp%%LTLNG_elbN+uC#TDN`ebP1ix6G7)lh2vIfNeJPTaM} zgdr6APww9zR1#fZYVSl-rg$6?AtiM4Q{dCJwDPAV%buZ}3TG>yCGGJ>Y1ozWJN5xq z$i=VT&v3W2dhD^T_~xF$^T!7JLTlqch;+o=o~D|JZ<$03a$4Q;NMn#v$KFBFG8_uN zv_o|AH6+6E+aT`wwN_=jwNaGHZH-U#U>w2CzN=q{cyzm-cq0e{6D1PE>!Y8%!leLi zY0}wzU9!`|5jkvQ<4hu#t(p7YpbW&lgK@hk?-kx^mz|_YkMKcHVxqU}f#Oa(2fDJ! zyUB8%tcNwGV`z2@MrSX?m9T0%8Cfvi&i+Ibz|L1TYJxn$m@Wy>E0I;-pf_`Ws)X0+ z;j5b@2npSGy?B6|K(bD|mkn zT=~H6Ib`{z(y>7EqX}N=Ft%3_dPmIXCp+GN;Oe;*(x5^aM5y&v(s5n|A}Dt4LGmjr zpY4KhZf>jI8J`>Wk{+k-pH7?wuv0~k3J9vKN4F#_ODwe#|2_2tP~GfSGEB4z=4;wm z^9WpBCFqq57v}FTcXt~%vaNOb7|J;XhzG3vHLH~0p-H`ejMDTi<|iRu;|MlEQ_b4F zHRsle?JEs_&l#{R+q8a~K01ax!E(48vJ4JP)safah|?oe4X1ti-0@HDH1v>Ypl3^e zc$mb7s%!~gMasHnI=s6wfDtBoQogynB9$R=fh#$a-Lv9_KO#QrdXny{l&s01q5)a$ z(+7P7Uq9;#D0+vwVn={rlw?=6lFI_P*`mJv{tTi$kAXE&`&)k2V zQ)K6_i%Z#tov-O=U1)`fOyH@uTTsR4oPh+&;QM-6hUIvq<>5O6RAr$(=hF(F%qo+) z0^ObWw8N)Y6fc}$@kFS)Xy)jt_~NYw>17#xhEC?$n0VWF6&`)Cs(H4jU-PV|6!z9k zefnD|U%Ia0ye!}j8efUetN>y-Q%l5fI1>tFGlS@$4Qf66_di*PR#^f}#BM^yIwi9Hb|em8F7iPb<2;jW{-1 zRA{{Hz8C#<*7`w7#&B3%7&!AfeUTiRNtJpYeWfKLstKmm!zeelvAckp8D_k+-4K4iasa*m^qF8Ru(-vFUjyIhwb5tvT{&FpdWr>IR zl4YK#GRJ0-(NJ}@x{{}!qn^`z)h8FZ(yo_1ZSYopqdpA$f?^Gn@?j`(3Kh5 z9(s&H&)J`AG`$i5hUE(973mh+84`H{B?_4NU6zt4ITrnq6BORIZ+*6kbzV>HT&)cJ z`dR|s@!k+W|7QXEw2@@tfRmp+<7k+yJ_rl$F)3OrV|zEimj`pT&>o!29ZaF;NEGAX z6S(#Rc2?_roN2ah&9l(5cA8iGzf01yi3m4N|2&7=#aTM`pV=4{tAKz;mMS3l{S0w0 zkYSr2!ZvH1W&U4JOxK&Co`ZyGrRX0Nra;Ct_`17C?`Wgy%5ba7+qjRmv*=g1_t%X8 zTU`s}dJ+iLz}EJIVpbDK?ljS@U-@8cm69EQF8*te zXy4tftW53+QTxK@yG%Y`*zj)Yc}O<4Hi^H1(oz&_h`L|0&+8{u7=XXHNV=uE_n?CjCI z^{73e60kfQb{g@k+N6z&MT?8?0Y`WqvozOCGVS4tORkjiP}ow_232|BuXN{$d5L!A z=79AhY7I+%M@?d8L{tIgC^*KzHPl@G1g$P+eWLad$P#`lS?k^zjD32Jsd0E`9v&f6 zW6yqWKSY185KxyrI$8iNTc+T2)vfB^L)itokAnv*wL0%9q085nFkoA;@V0^RbpjCi zVKQ_pho*Ei_wLmn|6=EQI4Og$s61QhoSA`v=POqRx*uJ8z$4-~I_L4D!21Gsp*}r6 za^iR6uYZib8bnq@Vf^^7X}d2gFR#i<>aa1;y%($TWZdE#w~b=KcbU5CriQT=xB~b>XvfgKmYn!BZ&4ClS?39KaBu}Xk1kB{&FIt?!EQvdTdd^ zPiO$3CLewUVF6!7tiDm-!h*6x495Ja>L-JZ$vE!n<@)&_v$zp*38? zZiwfsRElF`V@qS-B>t}pXz)$Mp3`?88w=@rl9ih)4@_%?(#q?zxCF@E*=0q-5UCE+8Sf;@4Mp!#DcqqCJI<*CmjyEk`nivq3IK|7(Pr0S{=dFPFCma1XQ3*-O2 z?+1UFP2wtS#&drEbJvR+(E2>-N3QHy*o&0sA32LTE$OOk@HNXOV1Et&_kR=5ciU#f zRBP~z*`g|`g*u*|>zKcTkc}?;9FL zaIp}w9HcUpXyeh4l?c9TXXoL8Z(ZEh%5SopP2q%R<>V~6`=C&V#7n&ef-EQv$Q5NM ztKhV-CQBXooFQC>earEQIruU!UptUSu>BS7cu^GM`K_|wKHgB1_;L4kB9e%pyGO0> zCA*HNu{NfLB9=j|d0#jyW_5xMXgyGAUw1G|O-b?1$mmFu;Nh_Af5+{7Fib;p)pehP z_$rV}s4IVP#GgL{08J+aO03Fx36jxUPnDtf(_e_=Y)jB8KzD%?mk7ih7rbwR`~V$n zbmAozr(%d-H=VEg=2=Zm78dh4w?_)xE*|9u7K)yLTJwc&>iiGz0=sK!B7n7E(>Ys7 zyjdNPmDaQFW*=m;Q0x9UJp8oX&JMiTwPLMHV!y2Ys6JWQoqT@G8xTY1CGqeD#B_il zN1`Ik8eeg}fMz(rYx(`$sH*ljuG~h_UFRwLUs_R7jDBzOm!HvpE6Mex!k(Dt&Ez1oM*)}1_p)HdV&1I^B^F10Mx-C+e8b_NDCPj*+YReur| z!WI-vluS5}hB5wIh9lbbM<27?*LY2^eBxuz@X7bj?ogri_qW*NJPi=LYicSb#`p)3 zM56`)yuo86RuJ}P5y8i0W1y+&b+X}&cr9FNx$=&72UIc>rB=apfs~DK#wPxuvUJEU zT=?YXLXFnj)YJ<455w*iM2>ktZ_+n{>VvuH$ad;d8*BO$M7(HuySknu#C_P(qd$Ff`cnrSg4R~VRQ3&$-(9Y*JqV( zTlF{9Yf7yZ@bEWYp(86g#U$-N&u7w}-BZXAwx(`aR?S9F%`GS>NKvi#LMr9v1S+cl z*7%IW5`bHh;=6O?fb!RCwt85uvsvgOf+nNBMclZxu76+q_WftKiBUyzRN+n7&oWc^#E-GEc|VsXapNgOIc0 z{^p0Z$+7Dk90&~7s}DJ!IbIGhyeQonspgB$J0_xV{3`B#mg-tPM&;`i-PAs_-4zwn zsYLTH(;4FDGL%=*SmA})R_=5q_&`18`D_0zIvpjf_?ghGJHEj}V49l!|W0hX#G8=0y$)X#R2Z2&RtSK^xUOsWP@K zRZ?ko0xK6+tZ0Nx;{}+s1%0$>#au4v`wS!*Y(Z@;Q@O2Tv{sa-tF?{5RqE8K!@ojU z&|+guEmo1heLg?PALmQaqx@yhYg+y9pEt<^FZIamn}EEAd4 zAY)ng`It7C)J(F|rdlFz3{+p~Yq&C~gqw;~>^vS`20|9ekW_22K1(WoLdNt5P-ZlF zeKVBBQOs+kgG&e`dI@n#T4P}lp^sMexBwqK{N?I)^qu+3*ZeDCoR7W<1WM-&z4G?X zRAoy_pH@)2XJgc!Xg(EbHcM(j!c}h%Q!&qN7CPPg=6RZi^n}K}KXuUWQ4$@NO$XD8 zerlKQZ15rJ#8N&V3b50`Mv~;y&8cRfzZ`Z<3>FZ(M?diZE9!ZAUTb>L1{;JBQ1m%2 zj7)MmwnwnoFUqf;h#)6FUI0hN^Y;d=+;$md?VoMzkoXW<`XTL4E(SAv4=ZgGLVI^C z4+sbMpadanUOqupp1t*8D#4ql8fV(E^TGY_uBq}jwU3jga$dPm8FIeTJYPoP1scXW z9k;((t;s1L9%FZv+IoNC1`=Ex90QxQqGZb(rtt}AoP8_Z$Q#xqU=m-GxP%R^Tr0Gl zD^b=$PMx*DcI%%v#Y}B*DX$j5IY(l912ARk>)9y<6z9c!sIKZ>?6CXfwBCET_q|%8 zlrxo|Z;*EM*>Z2uYs1#gK^yKXQL;XF3_ivvj@fnd31Dfb4J7>`67Z0(jnIUf=@t9& zuSRd4!yLrFZj!5Y?Jq zk|8viM%ew38>aKi^faR-HwqGQ)h@G6JN;H`^jZC~J!UGVz|rvm2^{`~PBYMzpMF$; zpFbC{?r5%%Uwb3;FL{i+?N$X4@$q&m?H?+3k_l|C!HN=9iv%2jf5eVTn&av>v8Tfp z_Nv*bv9Zbq@@AQ*ugM~a25M{VzP2#_b8AyQy@T@dEeIj>@R%{o5x69lj81hnfxK1EsK$YScYu+ob>^z+~CBh5?K_D7s$~# zuC%qhaRF=rWXPAFN&c4a;L|?r#&1d)JS4~w`0;>PXty(tEs&gbUdIM+{tLAU6V*|d zPut+P%LxK3=e{z1Z;bzO$tAi?%uu*5RKGXy+`p-)%p8zO@cv*kIu^hIHtc7NUT`q= zSeeK167pS<&UZcR{XKe?6w8gtF|{m*#-;ZPZPgMl)5(qnicLG-OzYd2n<}Xkk^G)7 z)oet2L-mC!jf-NS{nkNfU?k&H_P>SKjd@;~w;@*86`l99 zx@E(A?wvo%q8^%IwLW_p8ux%JHglS^Rq*McN8GeicV+7QCf`0Pj8ac{sqjZn6o`(4 z6K1hT$YIyi$-eX)P43Pex9S?*FaizYje8uRJ>C$QKKjW!21-?g&@n0%m!!wZ+{2oi zKE7D{8(&!{$pr<6`9bnL{YWo3F}eCzzK!tC>GO#X;4aXCf3?L!-u_H;%EHoYZ#T zAB#>B8aO=AF#P^)=p}eqc~DaU)=ya)0&wx?N=CwS=9zn?^fHs(@NzBD~k-Ojjsq$@JUUrrl%D^$5XLw^$Pg-k)gjXKcAB?{ze1eA(v&| zas^{s+glZt&qcO?lmmy{;P#Y&iKkWO@?{Z@mijkW&mqj91GRoZB9ap~XL|-8h}N^d zJ9jWOuq-7~O0h;t7Z3+@w&bhvA5$*;*rt`WqUI7m`gP2Y3*Ng)&5GTJhQwiEM8w4X z#SgIc@9he0Fuj3z$XkIsG5)p+&%4nQ3k&P9=*?tRwMV-DqXyp_z^2`QDnuH z=Z-OeLF5C4Mmj1RD37S#zYAPP>tR`_-OAeLiDG!Mu`a?< zoAjn#!n6BRgLX>wEaXI4DUv@52rj>{{1V^Z{Tg_==Rgm};ipQ4d|F=|{A+en^7JSWTE6@F`a7E z%GVR+NHjehHJqv9`>izpyyaNwVD5{~yzynm#u~Le%#4^0ev0IEgQ+}RChK8U)yKilr4_vh)9?XNw^{T(e% zetc#8H0Xr_ExHfC%6;#B{aL1^qwN}25){q1C8|q8I5A@hgxq(!lIu1yGxeR+`*&-0 zJnUa8Y0Jn^c38*FFkhBg^}W6zgo{ONYYHdfcmMnwU}X4m=r7?g?4USUjx83~lg#C> zDRjjwE_8$&C@U%~POSS{)$>I8m@Nv8J1)h*x5&PX^?jFDV10yn-+Z}!D6H97` z%0~cl=VMTt%mXwmdsNCFzB}E_rMB zzOd=M7}8=A0DCH^`NxlYOWg|vh|tKShFh*qR=)TjhBEKf+l0bf>@5t0zd zQh9v1YXF)MDt@;@pnKMWU5>A%{x@UqyKX&_zgudRz)+-;F;X#6KK6=-=grRwY0C;OH8hJ8Bz?} z|INi0SO6Cv{a)*%B&2F&uQgIU4p(4XR#dEjWL=f*i^DU-Au#j%;o-cc?-;8#&r{kP8nG0)S7o^X3D#*SDO~vu#!5kS`%lQ0!A5d5AcsaHVQleD)BAQBw)1-SkCEDk{VOP}y@bzE&?3QuP>wZiVtlL8 zW*+EM><Gk%Jj2#(q4Q865jf+pY3cbTm~J(64(dw*)&t^w{BA zz>63P9&&TAH+Ni`Jber>(Oi<-Wc8*5v?nT*@r!4p;h;rU6a+6kP9Nspm zKlkDB4q_KFH`t03`MMo1v0{ROXH`SWJn>Vh_!(bl3D_t~zJRiLF!SCqkbPj9ylp%U z&94$$srhrAfi>Tq?n89ymR+417AedsshYKDZS(HivjkBbI)@&_&|WLXqVOWB4Npge<)phQZ8i z@&aSUNH2-kw|2_nFNt0#>{72jkx<5y`73!7y;Vw?&o-7r$vn9`&CARC&XrC!JONuw z=3O@PzWzP(=q599M7!I*TN_oJ&6NRP2R(}59)XW5x?5^vCaA3O{6aITY*7{^<9DpW z#EX)UKV)OiQ(v8#Nxh%dbhXt;96jn-Ps&dhJD{3*V2xA-ZoCdBpbZuHlm2+ArLu82-SpfMKV@htQk%w5q93cxL)o zg%I3>9{TnPR4BcztusBMo)^B~^~JO(xzQ2bZ1p7``k+jNP-{Kslddpqp09De@bOw| z_0zQrW|_4yPrhQ(*`6F>Zqb(wH}x0YdH+6Gw~}vvwlO^5g7XUzqL=iSyS{(T0ciB@ zuIL@G9EKZTwLmZuXKfoUw@r!btq6_I@#qMpc!YyHJWi$+7{3m5b&V zc#acK9wOSYA5`4$t@z0PXMEuG?^_x0{rRIfh5Pbuh^V5Z6ag;w)`rw4@kzTF=&B5_dh=xCKsM^!*LpKxszAqf(nwiznZ|4d*3*pt?H%i^Qn*&=m)F zJJ=sbjqQub5B*CTY$S3WxD__jwMZMt21;{TOKF`U2mSV7mNRl~@`9LmF%46UJ}j3h zPdyq4j7CUH!k>54r>d$-N=*Da0D;MX_ri4qu|H!G2!?t2{vLt& zunJ>12a{6=Ls%2(pFc=WyOwJF^Pf4Thw#XO6ukdgpfbk4OSQj8;Lq`&w+&;OVk9J% zk@x?yEzQXDdyxLQ8O!HcwLCxx-u7S+0q)t>-|#qu%D8}w2!C;6h?>TfNNx6?*GRuA zgHRp0TIT#Q?o#4cVSGIy&YO2b+oJgY8F%ywB72gyrT5y`+Eqfrq|2@IvI+nGJ&LN0 z%6Fa@^7)DXdiG9~-v0hcNO{Z$S}3i7IsM~GRgK5_fD+6>_pCGj-vigtRs`Ba@T6Qa9vX zRI(hvpGiwAiW>ll=6}26nn55@h)V^TjVDMd^z@uPJqZ{`Yg|$0kOtPJr9Rl%sG7hy zy@Q&f%=(g%5JsS3Z5kS$miE71121Z-yFe4(H2;m$uh-+6*EKq4lQA8Bv-& zta37g%3z+`Sel?Tpsp^MMbQ}PW54voM?4vNAQE0OYwiDMBZ#X@Aa0tU&h>JBTOZG@ zP4t%lUzWjv7YGxHf%A$=rU{a!GXa5_<_jnbpmNd@yn5B`=eA%j2(b-#@bMkB>Uw&< z0{(wqO!V~V3pwXSDM`%DsTxEgE`_6$)9!b3f~v~WUbDsd1YM|QL!Ks&mgjl^*RT-^B2zt~5kVSI zd+I?Cu8{`mxNu&6@Gy#c!p`{luh|BJa1yys@Z|gCNP&_`m95`T>B8al;>v$}+=R)+ z%B&TH!KN4{U`-7OwC(Ql1o#p+ri6VcLYKk&3C_4Nw|{L)HT2Z5s{8J|z8RO~W!a@= zxuoyZnMUCP21j!)bqoCXvgtZy*6RcPEjz;((R-Cd#wKBf@Ye%4970owzOlP)@pXM2 z+zKt$EEuuhlBG{Ij;lW*%AezFh(Ua8nV0Y@9F)E{&#xWOfxEGnQ)lt@Lz<%be-4$f zM~8p{%(EH&>!<$}TOgf_e)F#l{a?Y19EDZu)$a*Eokk=>XAGw+L_*jT%U9qsM;yNI zIbSzz6v%}1ot~;3p2jWXh08PV97Ud&yz!)-0!IV~drr%PC)K^Yz6lwHTTQ zf$4C*E(ywy`8JL}-drVZno@@2Sl9{RBuX1$D$>=$SI{9P>Ky%uBxc$)@!v+?Nb2FL zrM4PeW_p=J@o1ZP3&F?qXbtd<@)9pa+LZlC`mI5*dtPW??f?piOtW?1Fy0&A@5^E+I!4~%d$%dXpEb2aZ0wP2)!QW& zL*V(cx3)e6){TRh5NVEWpC?{^%kt}=$nn+gD@ta5qA~zczIB_mDf*n7Z->|d- zM@4#i1svH~s+9(ffOu5aQXBx3c&4ln1OGn+_T+`$Vu`RIgIAclEW9Y0L&yK&X3O~i zIumZ{i_=HI+B0MX)2!|+2^?)TihSiU4nU4MtUKd>Fmy31oBnr~QOQtPW5mGq+6r3u z!wMV}pjECzaOsT_h=VMPzCiyr2$dNKe}H*{uH(fECW_|;l@y8Q8W+aZ=LVebfAT=d{Pz=Pd zdSFmGfjq+=JLeJH7>_3+ehu>Y&^L$vWbT^)cuWh|MY{Jp@S4 z>gv_5{0Hz^FlZM4su`j%ij3AG@H^(_U$xx8PzihD*6%$LP&#^y*I+h|qGS zl&c&(8hSpi{vou@K}nc9v+7@}wV+sNXLFX|F}COMy_cQV{rP`Y9r+h&g%6RI6v7|C zTVnZE4ws|%lq@jGF@qqx!O1rlOg#nUM&TfY!lKPJin9TL{^BAyt20$HAfCmU-r%#k zXG|3P2BjvA;+(oGs&c^3`j_D#n!Iw>5&D}4hQ1dRr=PoL7g zy4sZbt>9axr(Lr?s>$);P1{ti0CCQG?DnCN$jCyNwL_mlC0Mq<{d=QL=S&PlD!0#t zfgt13&FWIn>AZ3SmUV~o_k*$6X=E!cZWX_jF~^Wu!H~iJk$WFbI6RG@Q^3&0Z0isU zu{_6Mp}s-Lhni~k2D~{mLOA}qGE2;t>;EyySw8tTi2VnH&y6vSo35}g^7Mr7N#%p( z9V8&l%{dYu3A>eg>5fgAP(N%X9Ge*ZeFX&SZ@m+XkfDg zl;G7okiQ8P_ls5-a-)0^hx>@b9(O z(Ew|0IA7Wx${pao7EGK-!Fw> zZx5QGaBB0JWPHC@Q(ZltdN`+ydA&{F{d#!}l%NHdgFUvl$#Hjheyoe1cICW74sO%! ztZhx*oG8};-LsdEd0H&Tcr|u!isgq#-yI;A81|yP*qe6`mVTRYRVnrV+v%3p?aOiZ z_j7$yS{sAtU1SVP`}IxQqVt@{12j{#&=fL)w(!FLzZcFVJHGvWLCw6mF#FbC!Iy|v zo01efX)&)kbm1`l=XUoFNiH5L@S$XuRG+L9DF0j(zhKOmtPg+dccI48*k2p}Evci3 zG3CV}kw2Qnw|%K(q-10UL@C%YQBnRAj7*#p2P}NMqW4NpPd#OuPIF&Vs&Re`$~#rO z_34US)#HJTPHYCu?}udHj}NXZ9qR(RoS^3g>tTK3zy=-JdV4!j$T={M66^~%iF70JNA2a+)lVSUT<%-Bt&eRGCeRMqh_6%_=CH z^BxLcV_~^0yHe%H_}pnEo$>7i-7ne(Oi$_@6%l_$SS+7@FGgXQ-o zsIEV2n_-i*sYbtrc(jRMm<#w2^g%E&F{DO{2hvt9>x=54$gllT2AFJUTO$rNQEt|! zQpbYD)~SW`>1$hPxtve^kf=>8+E^}@$9~zj_NPfkRC~+idZXi+=j#tsp<9y+I7gF7 z-1Rr+8MemLH$SHA?uOZDolm580snar?+=2wEiIk zGxPq=4g^m1S`XdZz8rvyZkIT`o8zwKMm;9R8{oU_nEo5fkIU6-Qgh5_P%@PPUtnP+!9UlKxm;n@ih z-MFUdi?GFp7H47-ZmW6Ev1@m_1CdgO_K}jDIe@@2?{igeQNN;B<;ul_GoX~(>tV%x z1II}(^MhX=y+<}8ZF?O7qDC-!ePtV>CvxCC2APpM?f$bh&|Owe9i{_ww_G^ej(x; zJ3Wv1W$>)N=EH^}TYMfi9zZ0`079NX;v-(Pvrg^xf`e$Z(CAbC5vmsSQ01PY=p^~| z>|b2jP#_}n(?idipPy-M`juYh@>|PiYiy1I)ZmAFQ)&caWA2t~mO+wl&7pz%|1CL| zM=X31YioH41@6&y2fi1J_t&pd>FGgBPFRU{PEO({1v!t;1)1O%;r>g)+((7qavww| z=ue)^f=BiF6qt(&Db90KIQDAxHa*+j62BF^U@M-jrx?id12?rM?wXp}JF{Pqv{>5z z`ah+;WkA$j*F8K#D<$3C4H6Pk(x7xJT?$A`$Iw#JEilp`f`F8OG}57fv~+`X$N!+$ zb=~ni&->+_Z+sYL=KRjtXRo#QTKnuS8ho7Nsqp%Z$lp15UkZ#wQ3l->sBpCEqZR0?LAGK6HDT$*^nZ&j1BR}U6tI!fGmX<2Jj~xq(?cr@|+KlxGrO8yXVgXAwB9&Ow<`~Ng;TcE8QCputHY) zNY1WK&d!C%psL3Nz|SEQx6k8zKGcWV4{TD}DU04ys}EiIPrpHm#ajaE-7vGJ8*OwW zhD);t3jRkEcH7?a&J4OB1i4-F9D~^#FcV}Zrav? z2Zjm9o1SsnH)Q3yzzz*4Of}1{F1An&ew8&S9d;XMe%{Bs23zko!o6t&Lbtl_snLz7_&NDt zuabW*M?bkW?xed*ic!%@eA`$(d_$oyB_>xC9yE*=s39vGQhK?cb?YJ19Wj45f*?wZ z;FXwi#3MO74V9jB{T-Im-!^v+-glNZCMxmXj{?4o4HHiNFnD1Ubh)0Ne-3<)MWLUs z`T1QOD4-x4vuq*byMsRSTxoQf?^4==L#^8Kfiy_JJIv8^{h~pf)Aa&QoqsS3-ZwJh zSOaE3cZV0-ll;Jd;Q=q z2;O|(_~Dn?ztkqlQm;K=_dam=5$2m~lzB<2JCsTYrdqd=>$Lu6ns~&3*Kxem<2&|d zP0;t?8_7O;Ey}U|{MJ2d8qZnl(d%JQvjuu)c%=XW9Ke+5yCa~TE0T?v10Z)!(9z3u z>l@HX5Q#Lc4Z>{+usT=@Q^Pca&<+plAi?_zGj8((TVpr7Jw?!#Iu%RvCe_`@pP$lI zkIF{$;j^*Qj9a!t=EkTPw(XqII)nf>x8mcWhVt_Q4mby>dh0(7fdl-eNZA#qvTC9U5N4hfUlB~cH^i%HDN z`kHQkjD!#5hF}D^)8<$?AVdQT1-)VK>1mPft=VwoP!ZNda5fUgu5m3Wr-^EiRx`^?vZ#%pcBzR3qdn}_!-T^m;e zbkOqK%!(nZSb+WcVnUaw2JON1K4trwj%1ODkO!a0s7iIzOCiN_M}(}kp$T_Yef@C9 zfZz!;qmsQ6$;FJp_6FoFp2ujfuiemIB0@S+a#b6OM^_wpfW6}ODqH(+GccXQCaFhB z*f25eEunn&5_TeV=VWb{<=vhP8091dj^&-I`7 zJyTUrG(?LqDFYp+NuY2cD%Nv!q~eL&O0Kplb<5~dvAGWQ zwyq`AR_1O^)1cGEy)u=b&QnVMVdQ6!aGh(7eMJb0R)By#kY#bH0E6nLbJQOvT;D;t zVl#U7lcV%ERf_~)LBFE$6maD;)YqS^emO{QQItUoycm}hpm14itB3ef#jPYDe zSFH@Uh{QZkKc%o+XbV6G3siKNP2lAuzTA&FwSTa0AHU9Q)bZygy9smqd9xbJ?3=^| zbmhN3%`!aWTDk!BQ2L~6CR6z;OcYe09w$W(b5G>Rdi(eksALU@I{hkY%0%xkl~6$7 zW5c}#uxXHy$K~~pCfGO*W&U~3gvO!6>z_6DMmxy#ah*@z{q+gjxJOLKwt%zKyD!^T zvZkn!i1XLKQ2fJGv!)#!5H(%ch4b%^{O$|zj{t(8l!5|uFQJl*g!pGdn-6qm8;`YC z9y0&^&I)xu)5`gJ>vYH`)>~=!M$_xpI)UOBwn{+7mFf|<-GU-l1j>mb9szMO=9a6A zzkUsasA%%=#QRke{G)@6rsl_MHqKu&BK?g#-{9hB<_F(Z#2d}NdMjO}pJT^xv5DL_ zmCo|J+GGF{ngmd3c|bBPeX6V+_;XnPu?*IKh!(w~m`+Ry0tvqyJA?Le!ZSS}PV%uO z{R5R-X|+J37?A#R_wWJ)7KqA_$cgfvCUJaM!B4>wL6xoh+NASz_obt_w{9zXiY~Ve zaLwh*$+Y;t#OKJm|E)d$Q0fi)M|#fc`8bKG3?-WE0o+2V0Fw;Ybim z(7pnGiU$ehUmXz(%ePWx$-qk>u!-J>J@=Rjzc%Y>2c?6)b#G8=eh&10B+oijBa`Pq zg9Sg|h#y!XAVsTlodkBdJUqA-61M^t<|UvzBJe@)MOd?Mq7e7>CO}6_9Pd3(8O-{* z+lGCCVTU|v^DXxs@}>Zp=MK6!U)9;mBU5uK*CkyCD*pM)@BQx z|*O`%IepZ(CU`YU`EcDFPTrP zbGxF8Pe#Tfm{Jc_tN6=N0#ao+zts8aHPWphM2Pwt!*z7WnO^|Aa49^dH}1=cj*DNh zo4o6SBAzJfwrP|Lv z)ZJ2+*}E&|$%!gtDF~(>RUoCs_(w4@F-qW=(9w~)=Jkf^XY&A>fahw4 zG*8^=X^fbUr@Oo0uZhb2>nk$~k?M=96DgF%f>f_#ml&&{S2}R!C4?uEKfeqV!a|x5 z3KtArUafgK5FT<01|z|N#hFwW6!BR(wRmr9u*N;&(bSjoAJ><t-2(OSd0s5TvM#esTEWkGy94a{u)h!2H@oOjwkJ`JxWgeIz5>43Rjvh zpOi>KKjD0~FE!Hs^}4RvG#Y!Xz4#NX@x8X_Dd9uAx0VCsgB^?vCG*md;?(ickR-PI z_o)R0h(%F3>#6*v#huJ={I1^PZ^K*0OS?}Lzm5^XN_BbKjJG%(_WD!y)MYwY*TF~< zBrtEbgLQ*I-+4#3Kcfh5{av#93-3#GOBI&48t3aF4IAX_#);^=2ZN}@0VxQoEU>#U z-`y5#_&k-L_iKv>H9NqHea5&1t1YzN42t>SGYR`#GEG;N4Z*K3>9r{M8{u-RW7nv=_$=$TUx}{G?upm zX<@XiF(eJ!J3B$MIuE&4k;+r3lOW`qo10;gkQ}HV%&Qjv1cEU^V{5_?z)i$O=&10B zyOt=AQCyK{pwvKwdgA$*!@SSYXrQ|TFRU>O=Y9N+Qt;~E<9?;$N8$lQ?3u0 z(97F(kddRk{jo$;%6$ur()q$d2~Giop%r%5s(G=%D2q-C4_R3Z!pkev#%s^h<87sK zcHlqPAQWc6?BQ|XX!1_)Yer)WIm}7UQHRX#!?*#dXYls|^jg8nN+Yj>{yN>OwH#EH zl{*srv6P-H8c`~j7ZWX0i?bqCJK{ZUtO7l4BSbJ1Bn*?U0ZDHteaO(kX~C#MK}W?Z zfvTE-V(H3vX6Q|CL%vOJ1iD{x1eA!Ax>XCjv=ZKr6DBrB0^6# zZV37@G)+ZxbpPVliQ^r@llu+4#8gM`CM#kjn7zD2PN7b(UYTzru5EwIGu?>{A4h^( z4@WD;k|lJ)z}zWQ?rMRTQk52lJ zdN(L|h{iRexj5!rYA-C3mlN5%YR7Yf_}`*Q@Z;4_I>h^n9O)Qgwj!Umq7S~)n)%Fs zv|e!Y6BHFOJ6AdP8%Iaao|9vCK4oKvU$m441;v1Xw>g~CTWUbE&vx1xoWcGP2|leh zJ$l^Bg*w!$oH7@6jnT+L1guRc+dqi!tRf&bavHMJLPn)M*YY0oi*dxs}DBfHD zbYHN6oAZl#rke9!gqCeWXzVF2F`tvUA3MCDrmCul4i-SXbVFBe+#(Kh6LDoXTRanxx##1cJd}LsMNd+)}RNY+gLL?0s2OE9>hEJ3E@{aj$N!c_2}I z*?T{Xu)&e8vH8(6Tu#?oDH#RIUr2*AbV|vx{cnXRNx%|KKh;m${w4nW8@)K3!xV8- zK6+0=Nsxb>lZHmxoEqf|3N$@;J8q6aTVjFDy&YqDshuox5zq73rS{e4M|Zpouh8X;tHx_sl4B#AI}=ofN^c z@S>Cpw64iNkp-tnla%V$5l_wm-4E^jf*qw+1Z{pUoH_{u3B2AmE~D#WZ*yIZSbmH; z=wMy#xKY!!{|ZYH*ZMmUiII56K*t+yQMWvHV}j@6fxZIc1sNmzb3<~cyj)-EqF=HH`S}v}nXlR+HBaa)_zK%ojUvC9f;;<2Okm!Vy zQpI8A-F}XmmuWv}v5QTMJRDL|Ta^0t?lwQ;u~sR$?N3&Zzm4kF-txi;l*QGIvfE+& zz4WlS*jT-HFNQ-N6pKEhKvD4fCI!~*likPezVR#u1}@%yHu^B>g~Qia|Mk|}?rH1$ zV54b$?X>#(29;`cNv!#K-I5;`FzGp+jw}Ce)8OTMpMz2CmlIGSZ0Li15?8WCxb4f-148N zzVYo8LekhUjqwUH^y$-DNr|2_b~9 zcG*m$NZZsDVz0cFG5kkoloILc7RtQyYJm!o_sNWy2V|(B()owY57s5JWh2`r=T)Fd zT210$VYMo9<7rx6<+_Vb#_;l4h56({$M-(UUWjJji<#dK=|48ZM%3W+mC%;PTitsn zUoI zE9V?XA6T3CE=hR?EF^qVLh9oE>x|3V+t^DO4;MnTP6%gq&}`6{ti91T1lVIbGE; zA}HTpU+0zV+edoOl=F$tYoze{-8xa$f)G&;Cp>M*S6SZst0(f% z!@o1pPtW&^Kn~0t)JQG9!=4@bqz)Z5d#LVhc-_6L5U(&$(wS*EgO{k8A0NAd0J1dD zON+{|cze`=dKZU=6Qkp;`e?QCe>_`oqHh8wH14h@_Or|w!E)kE*MaEvZHmg+jh~#MJK<@Iro?+!`1aA z31OCq&g(*mf{0t`+-$YcXU*vxIilUdPaU@RA-WX8pG;U#8?`h`)Wt=Zuq0BOtw8Pw z6K@aEI<-1=a0|rQ=QQ+=c_IX()fe>sHEVmOhc9Z|V-SgRyEdwaWYr%-%ev6UL2?<< zP{=IGJ%rxkdtNl%xuUR6@Gx1UcyjE;f5fMvPNwUtt+{Q-Um5x*RLdr}L4OHCdXGEk zB{{F<+;EOPqAEC=ABjMIHT`M*70+V@1#S7)TvHV$Z40{&emo>aMP=+y3Hy_*2C;xc zT{ST_^AI2dq@^Vs9T^?uaBf}NdaB1KCqE*?`+x~%_&P98yoU~aSkND$jxDJlEhh(~ zdG9YVp2=2r8xK-UBq5_;?{$(nZdk46qQURgiUZhC+Pr`A^A{Lz7VphPh8SLtfYy*1 zi__MlBph+OHzIgwlQnpV!hNMa0o(Dfk2E!neXnWv8rb)jmR=LES$^D}(>x^l0FW%SXDCB-t0&%;jvp{CQYour7Q{W41 zt;CSu`2k|#p)8pRct z5RfLwMSeB*UVNsX7A1gLFCl|_=5I_4Qw;rNcjZmk zn&B1s{-38W0Bo1$dlE&#KJ$-T@X%X&%_{1{I5k^lq=EuB{r&UxXkZm#ap7^Wf_FT! z^qd+P`SN;}7V}7AHmIs+Zy|w0OQ{q~Sj)@(R6N=CbXK9#)@T5A-5))5(I+7{BqlPt zKUi1?X3&BR|1E${&n;8~fTroEi}R-M>VTRpR2?GH&b_a*I|L#L#B~dkf33fzWhdDP z3!VpZmD7`^=$U$*nVPbLuT)KRoq<>(QCNWa$;j+1nur&O3IFgRN*GpLpn>~HIYXFH z;MnB2%;*M|r@>j-EnzrWWAAgYLSt?PfESwa{6uQ)SS_5IWbfBZI!Bf~y&v)*r9>LC z>MLwEgu)aJPNMwFYWKZQckmiWk^vN^wNj}C&2TsIDq{Z1nmh%XRUf{WU`piuy&9cP z5Ut!2{lZy@rVk~gSagF|A9Z9tkhK^+Z16V_<5m*J(`Pn9ZhU1?c=|Vq>9O zj%CYMtNLcKFwD;5)W#-(Q88XeE496kv@jqd?IO4~T6D7kc91{2tvE5eWKLq2QYI1sk za?aL6n0?bsz1<8#V63#GR}_AP)C@rl^*b^FcHg5>>cBk(lr;Gr|Df?Dl0HnQ(4otD z%9`0Jbljpnu+)lqdwYgdyu#v6FD*PbzRx+X;_js!jyJt*XQ7wqO(-sF3Q z*zat8^*dYHK;T`ltp@orRJ~cu&8+|cEj+}8B+E*k7BH$C;V!<18Yv?^B^xTG262uc z{B1fCi=A3-g}_T&`{_b+dvSUcJPixg?2^7d;*f%ytDnDGY4|gm3*0BtQB{>zpwo>P z!u;laa4%*~aqs2P2{&?gS& zpP30kwAzV}NTO`fVrpe#dknoDxzQ0JVCaJ}Mt+J#Yz6%KDhsr3J_0!O=Ay}hKdsqn zqv8QJJgE;+0{T%!F(G-i+V)IPUXOgEv^E&J7KTTb@ix=qrdj8^&h7%Wn}F}}?tE3$ zbXtr&CJp#*Ab+J$yv^+1qxZe8oPcmNdWnA(^M(snm1mnKCMT0UU)mVGPn{-sppwSS zLTR%>&_HfBV#Sw+pi02XOPrOfh>+XES`idy z5TKQ1daPGE)3iVgYXf>Xt6Kyab;OvOjZJam&Z$p|i)-%91B}maZz`WFtccx+4@4rN ze?LSN69F<`s3FQ?*4FXXI;8YoL4AJ~RR{-&@C*!kVCi0D=MvYEyFWIGJdc#sfUv6T zy?6npR>KwIFPn|$=dT6p+l@`%NcRL>AC-OkO6lctutHyL4FY*$?`*b$d{qjn@mY|c zc4=3`r(!WC7)o8d3&X#!&UaUJvnLk8@zi7q71jq29`k3Q8VkYlbEN~mPx!@h7Y*Kl zOYYDcDE#~P{**DItD!ef--VLLZgWQG2{ee>04o*l(X6X_7EY!SS26ty`{477{q-Pn zj@#32b(Z`j;3~=P49%7Ly>Uh|{|)QBvh{Y|a_5fLT1Jf9i0d>44y-$%-DMbv<`e+B z9%WESOthh;L$uB)14NOq;P}MU6krPfpo4NC=B4?OAyQ)x_>2|kv5ZFG9yVHUuXA3^ z>Hq;P@kLz0Ge<|fnwLt;0u_Gau8!rQ_f|{vS{D41-j5kpaid2u{)Sn`o&Wlt#Y&5y z&k7a5`+tD}6ud0oiFV- z3JV~y`3JK7#ZC9?cLda)EGPTuS5>6)vk^l!R)5$7+7WCQNl8Nt8wg2rEntTybRv6u zD813?|4`I$tz&U-z%*-IQi(C{iMX56j}uT)fh--2nOqh1(pFT6yFNGDneLWQC(ld~ zG~gj&zrKyf4)gf!8mcju9Yi;m8}DhWMMZrqo%XY`SWRxiwig%Y*Vomby=BS&_&>7x zuCH|gK4czq5<27q7J4`mAZptFC?&WcvY{WJodpvcGniED9?7fDFlAkyJz#`*9-9zr z+syQsm)}xbs=5>{>%og>a-;tJ3H@(WFtyJL7UUe@ zNeGGV&glCoz-S@1!{qe1Kd#5pd7vRqUoJ*tmv{7kxLD1ye{jTrs}N3QxA;qLU#Oe6c2}gndSAs?UL8pl&B{cD69?uq5xhQLe)Kz=f7`YD z!@lrYgKoPJp;b`Z$xeR~lf4l zOMNwU3$-g{-@AiJh>t|G1AiwRH@>dB$F(QQA1zrn1BY@Ilx10ZknOPu7aD-8;rJ01I4g^{|@zbss557jrf?i!MU<-x0 zpSwU0SXYsF0>e4MVbrYZ?jmhT!-jo(jG(Y#w!Ilgxe}AsGgy{&Dygf$dO*2!pTDlf zvq!&UdtZSVP;s6fbtYxO!yCWz)KBu1srspDuH4)2iogA}7|UX^$w`D63HUVF>>=|I zO>cy|Ut)K$WJ*qHRuU5AkH4iV#eot_vTV5lu*9$#>%%c?epO+kgQCwJBo4Sse1hjC zh0F$recMxVib|C@#Cxw<+C8qWs48X}JWPay_BKlA%Wbh(P!66WEAB6nXFD zT6B~j+tzWe7gb4(Koi0?53G6W-fH_A?uOCYgPTxt%!D%;^Aq2`qL8;w>PAbR3a#@uTA>C1jx~ErIHM_kBxy$ zfsIvr{#=rx{YvHLdQeHhv@pnsiVAzHdCpfd8np0ZT|6>}pPvQes97P&t-G9Q=8-STu{CLqB4ttUXL z9v5`~BrjX>tubi{EdZZV?CUJw{J{?6Uxd7g2uodQQ~V}#>7LSxD?ULUDvGDcQ9)ME zOWRB&nA6$vx17_lGTC^tl6NIf>N1p=KCc37^u$6Vv@t<(aj{X%(#3aavgdHbM?tr_ z*t5V8eZDj#O3_vzuDQmL&w0hY7cu8e?O$xC@<(5AiO0tlrFcN9%qbxcP@3}Tdg}J} z7v?R$rhk8opv-Ev{UlE=a@yE?>|6DRmzI{1(=$E0K!L-ix)#K1Uuh zs6V}Vp^n43xIL9@z2nbF^XK&N{!Sa`I_G}4NDDnMG<-O1Y}X*vPd}F{IA$m+YPy87 z2od~KaBvFMyLEPexVA=}XzK)s%h3^3 zc)Wa73}vwZNosiN?_A%(Y}ZCO`z;w47p0x+?PuKUh^k5+q*r|$O)R85Q-9)YyT$0l zB-9>PN6|&$Wqu0uq=4qS17S`!+Sww9jafJaR$F{n1D`ZuTih11dC zWb;i|pd)b}kdUS~Ee}Q`;mn*mQcMkF`{q0%x zR!CZuq>rG>%NR~nK?tDSGfkbu`9sq?EmNm7%*I4N4as={PiI`Fdb{qrXfaCU)LTGlN`tIYvAUBIhi#p?Gz0L*eEc$J)y z0EXLq6cF0~>swzS!hLF;xQ)1tA<;{8_>NONCboe$qF$9J0?R zmU)eRH@i{oA4E+f6xe@P(V@N;18#)oW*&% zQ}45HT-)#9y5qU6AMBl@l_>ahrF4UIITjDfOafkd2fRv9mz`_!V-{uh*Ir=Gsql+9 zs+4?tqt#mqB(H>hwSx#XH1r~xg!i7rorjN7l8uf5I}sdn?%4I{8JlIbJ^VtBLAwo0 zydIz8yb8R*=XP%y>2Bjazl-fzfLW?8q(;8Upb5Q(~M46Mn_q#zM%Fb>E zJZ3L7HBWF}wf+7OwFD4gjwY~HTnYkhPo6@{!*9bF-UZ$i-k70iv%SRscB3*F0|l&s+)71DyyhjX%D)6xGT_b5e`H zP_nZ_SOl_p)>)qiYL96Krrt@B5sJV50I+d3+t$|P{e1ysIGc7~3b?(?%{w&13M6Ek z-^gRz-5JR8Q*nx5Sf(WuPPXwdjk)4@sy@yD%}uIaESxi@is5_+({lF(EOn;?XkE~= z3IjCUvIqTNhjP}EQeyGvAl5A9$!yW98#PMpay818otfyBL2NR>HA*zc6l7Y1Ci9CS zEwj{Z87e=Jx`M(BHBB|LLD^lH6Ka4CV50A#{3jD#7G$)K<1g5b|HdJfo>5svC5t65 zs4e#I^1kI?th5w3S08Z--yF;*8xhiy02C5uk8~^~>>BwRjhS-*sh$~O4U{teVV}cG zi2+URCM9CunTM=G_tKuJJHE3ruH73BzE4xFe)FZ?0!uexgV{tc@ z?!t_Qm6coEFV>sLPTmxxz3dAczzGB|*~o^?Q3EK3DS_thcRZ^}MI9pSrqf{4_>Pq? zs;NbKyBwA?pOHb0WGl)Xm_U)h6s3fQ3$n$P6~kkMFMO{&_3xu3P^2UgMw!(azC~|? z7M?!DsHK}nlYc#rMx>yqNbgl+b5{<`vM{X&wjN_8sdSwVH`nC`_4g&{(fk(LWm#C~ zl+@HduT6nwAHaIiaWCKi;BtN;9@Oh`(CXy0Fls=9dcPY{kX~3=zTBKZ1Dp}ze@gUD zHM;7Y;lZ4PRhJI#WuU&HQ@T}NY12#TPZl}&eEubhMi6 z(31xCnGN4RvVSs`@hi1HONwc6y9PD%mPqQfqmBM2RybRI)#5V=s6l#zjNHB3J=-|3 z&!P>;@2%k+f+RsVt=<_sh5vZsuMK)bR-PzusVHzMbu%lqv<_8i+#Uk}jE(UR$NnmU z=hC{48fP?1l!3r1^~k%iQMCB|0SA(5=L67M1`7-dx_9_J96MWrmo+}7*+kw@HAY8M zlRD1oE+aTgZD9a%fT7m+(sRt@cH@CT0H}iR_QcR|eJt=~6>O!S&&VD^|eqbJIjEJyUaszLkSZK2W6BI`=F?j@$tFaoG>%8(?#T;tL+@0>Hi}?`2UewTp{0{ z=aL~lTk6|m-PJ|fT4;Zpg#3QlXoA_A|HmH}H^}!*{2D1y5h38EAfqf@`p7uo{{aHQ B1!4dI literal 59544 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 diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls index cdfb6ed7f..e7fefec1c 100644 --- a/promise/etc/promise.ucls +++ b/promise/etc/promise.ucls @@ -25,7 +25,7 @@ - + @@ -67,42 +67,38 @@ - + - + - - - - - + - + + + + + - - - - - + - - - + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 3a1ecfa01..1315f0927 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -21,19 +21,10 @@ * THE SOFTWARE. */ package com.iluwatar.promise; - -import java.io.BufferedReader; -import java.io.File; -import java.io.FileReader; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; import java.util.Map; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @@ -59,7 +50,12 @@ import java.util.concurrent.Executors; */ public class App { + private static final String URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; + private ExecutorService executor; + private CountDownLatch canStop = new CountDownLatch(2); + private App() { + executor = Executors.newFixedThreadPool(2); } /** @@ -69,67 +65,80 @@ public class App { * @throws ExecutionException if an execution error occurs. */ public static void main(String[] args) throws InterruptedException, ExecutionException { - ExecutorService executor = Executors.newSingleThreadExecutor(); + App app = new App(); try { - promiseUsage(executor); + app.run(); } finally { - executor.shutdownNow(); + app.stop(); } } - private static void promiseUsage(Executor executor) - throws InterruptedException, ExecutionException { - String urlString = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; - Promise lineCountPromise = new Promise().fulfillInAsync(() -> { - return downloadFile(urlString); - }, executor).then(fileLocation -> { - return countLines(fileLocation); - }); + private void run() throws InterruptedException, ExecutionException { + promiseUsage(); + } + + private void promiseUsage() { - Promise> charFrequencyPromise = new Promise().fulfillInAsync(() -> { - return String.valueOf(downloadFile(urlString)); - }, executor).then(fileLocation -> { - return characterFrequency(fileLocation); - }); + countLines() + .then( + count -> { + System.out.println("Line count is: " + count); + taskCompleted(); + } + ); - lineCountPromise.get(); - System.out.println("Line count is: " + lineCountPromise.get()); - charFrequencyPromise.get(); - System.out.println("Char frequency is: " + charFrequencyPromise.get()); + lowestCharFrequency() + .then( + charFrequency -> { + System.out.println("Char with lowest frequency is: " + charFrequency); + taskCompleted(); + } + ); } - private static Map characterFrequency(String fileLocation) { - // TODO Auto-generated method stub - return null; + private Promise lowestCharFrequency() { + return characterFrequency() + .then( + charFrequency -> { + return Utility.lowestFrequencyChar(charFrequency).orElse(null); + } + ); } - private static Integer countLines(String fileLocation) { - int lineCount = 0; - try (Reader reader = new FileReader(fileLocation); - BufferedReader bufferedReader = new BufferedReader(reader);) { - for (String line; (line = bufferedReader.readLine()) != null; ) { - lineCount++; - } - } catch (IOException ex) { - ex.printStackTrace(); - } - return lineCount; + private Promise> characterFrequency() { + return download(URL) + .then( + fileLocation -> { + return Utility.characterFrequency(fileLocation); + } + ); } - private static String downloadFile(String urlString) throws InterruptedException, IOException { - URL url = new URL(urlString); - File file = File.createTempFile("promise_pattern", null); - try (Reader reader = new InputStreamReader(url.openStream()); - BufferedReader bufferedReader = new BufferedReader(reader); - FileWriter writer = new FileWriter(file)) { - for (String line; (line = bufferedReader.readLine()) != null; ) { - writer.write(line); - writer.write("\n"); - } - } catch (IOException ex) { - ex.printStackTrace(); - } - System.out.println("File downloaded at: " + file.getAbsolutePath()); - return file.getAbsolutePath(); + private Promise countLines() { + return download(URL) + .then( + fileLocation -> { + return Utility.countLines(fileLocation); + } + ); + } + + private Promise download(String urlString) { + Promise downloadPromise = new Promise() + .fulfillInAsync( + () -> { + return Utility.downloadFile(urlString); + }, executor); + + return downloadPromise; + } + + private void stop() throws InterruptedException { + canStop.await(); + executor.shutdownNow(); + } + + private void taskCompleted() { + canStop.countDown(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java new file mode 100644 index 000000000..2cfad46d0 --- /dev/null +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -0,0 +1,91 @@ +package com.iluwatar.promise; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.Reader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Optional; +import java.util.Map.Entry; + +public class Utility { + + public static Map characterFrequency(String fileLocation) { + Map characterToFrequency = new HashMap<>(); + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader);) { + for (String line; (line = bufferedReader.readLine()) != null;) { + for (char c : line.toCharArray()) { + if (!characterToFrequency.containsKey(c)) { + characterToFrequency.put(c, 1); + } else { + characterToFrequency.put(c, characterToFrequency.get(c) + 1); + } + } + } + } catch (IOException ex) { + ex.printStackTrace(); + } + return characterToFrequency; + } + + public static Optional lowestFrequencyChar(Map charFrequency) { + Optional lowestFrequencyChar = Optional.empty(); + if (charFrequency.isEmpty()) { + return lowestFrequencyChar; + } + + Iterator> iterator = charFrequency.entrySet().iterator(); + Entry entry = iterator.next(); + int minFrequency = entry.getValue(); + lowestFrequencyChar = Optional.of(entry.getKey()); + + while (iterator.hasNext()) { + entry = iterator.next(); + if (entry.getValue() < minFrequency) { + minFrequency = entry.getValue(); + lowestFrequencyChar = Optional.of(entry.getKey()); + } + } + + return lowestFrequencyChar; + } + + public static Integer countLines(String fileLocation) { + int lineCount = 0; + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader);) { + while (bufferedReader.readLine() != null) { + lineCount++; + } + } catch (IOException ex) { + ex.printStackTrace(); + } + return lineCount; + } + + public static String downloadFile(String urlString) throws MalformedURLException, IOException { + System.out.println("Downloading contents from url: " + urlString); + URL url = new URL(urlString); + File file = File.createTempFile("promise_pattern", null); + try (Reader reader = new InputStreamReader(url.openStream()); + BufferedReader bufferedReader = new BufferedReader(reader); + FileWriter writer = new FileWriter(file)) { + for (String line; (line = bufferedReader.readLine()) != null; ) { + writer.write(line); + writer.write("\n"); + } + System.out.println("File downloaded at: " + file.getAbsolutePath()); + return file.getAbsolutePath(); + } catch (IOException ex) { + throw ex; + } + } +} From f16ae08bdf5e4949bc1a169ef44244d8c4b30a74 Mon Sep 17 00:00:00 2001 From: Alexandru Somai Date: Fri, 26 Aug 2016 12:46:30 +0300 Subject: [PATCH 022/145] Remove extra space --- .../com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java index 472325786..c7c75143c 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java +++ b/singleton/src/main/java/com/iluwatar/singleton/ThreadSafeLazyLoadedIvoryTower.java @@ -38,7 +38,7 @@ public final class ThreadSafeLazyLoadedIvoryTower { /** * The instance gets created only when it is called for first time. Lazy-loading */ - public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() { + public static synchronized ThreadSafeLazyLoadedIvoryTower getInstance() { if (instance == null) { instance = new ThreadSafeLazyLoadedIvoryTower(); From 095adda7e925e4d3925e9f6fe7d61b8018fc8d1b Mon Sep 17 00:00:00 2001 From: Alexandru Somai Date: Fri, 26 Aug 2016 12:54:20 +0300 Subject: [PATCH 023/145] Change access level to private --- .../com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java index 0c450c60c..6536978fc 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java +++ b/singleton/src/main/java/com/iluwatar/singleton/InitializingOnDemandHolderIdiom.java @@ -53,7 +53,7 @@ public final class InitializingOnDemandHolderIdiom { * Provides the lazy-loaded Singleton instance. */ private static class HelperHolder { - public static final InitializingOnDemandHolderIdiom INSTANCE = + private static final InitializingOnDemandHolderIdiom INSTANCE = new InitializingOnDemandHolderIdiom(); } } From 483c61a82af39ba7fc9c226bf112dc8db511e429 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 00:16:36 +0530 Subject: [PATCH 024/145] Some refactoring, added javadocs --- .../main/java/com/iluwatar/promise/App.java | 141 +++++++++++------- .../java/com/iluwatar/promise/Promise.java | 36 ++++- .../java/com/iluwatar/promise/Utility.java | 46 +++--- .../com/iluwatar/promise/PromiseTest.java | 46 ++++-- 4 files changed, 175 insertions(+), 94 deletions(-) diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 1315f0927..2b2ae78b4 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -29,35 +29,45 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** - * - *

    The Promise object is used for asynchronous computations. A Promise represents an operation that - * hasn't completed yet, but is expected in the future. - * - *

    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. - * + * + * The Promise object is used for asynchronous computations. A Promise represents an operation + * that hasn't completed yet, but is expected in the future. + * + *

    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. + * *

    Promises provide a few advantages over callback objects: *

    - * + * *

    + * In this application the usage of promise is demonstrated with two examples: + *

      + *
    • 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. + *
    • 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. + *
    * * @see CompletableFuture */ public class App { - private static final String URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; + private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; private ExecutorService executor; - private CountDownLatch canStop = new CountDownLatch(2); - + private CountDownLatch stopLatch = new CountDownLatch(2); + private App() { executor = Executors.newFixedThreadPool(2); } - + /** * Program entry point * @param args arguments @@ -67,28 +77,25 @@ public class App { public static void main(String[] args) throws InterruptedException, ExecutionException { App app = new App(); try { - app.run(); + app.promiseUsage(); } finally { app.stop(); } } - private void run() throws InterruptedException, ExecutionException { - promiseUsage(); + private void promiseUsage() { + calculateLineCount(); + + calculateLowestFrequencyChar(); } - private void promiseUsage() { - - countLines() - .then( - count -> { - System.out.println("Line count is: " + count); - taskCompleted(); - } - ); - - lowestCharFrequency() - .then( + /* + * Calculate the lowest frequency character and when that promise is fulfilled, + * consume the result in a Consumer + */ + private void calculateLowestFrequencyChar() { + lowestFrequencyChar() + .thenAccept( charFrequency -> { System.out.println("Char with lowest frequency is: " + charFrequency); taskCompleted(); @@ -96,49 +103,73 @@ public class App { ); } - private Promise lowestCharFrequency() { - return characterFrequency() - .then( - charFrequency -> { - return Utility.lowestFrequencyChar(charFrequency).orElse(null); - } - ); - } - - private Promise> characterFrequency() { - return download(URL) - .then( - fileLocation -> { - return Utility.characterFrequency(fileLocation); + /* + * Calculate the line count and when that promise is fulfilled, consume the result + * in a Consumer + */ + private void calculateLineCount() { + countLines() + .thenAccept( + count -> { + System.out.println("Line count is: " + count); + taskCompleted(); } ); } - private Promise countLines() { - return download(URL) - .then( - fileLocation -> { - return Utility.countLines(fileLocation); - } - ); + /* + * Calculate the character frequency of a file and when that promise is fulfilled, + * then promise to apply function to calculate lowest character frequency. + */ + private Promise lowestFrequencyChar() { + return characterFrequency() + .thenApply(Utility::lowestFrequencyChar); } + /* + * Download the file at DEFAULT_URL and when that promise is fulfilled, + * then promise to apply function to calculate character frequency. + */ + private Promise> characterFrequency() { + return download(DEFAULT_URL) + .thenApply(Utility::characterFrequency); + } + + /* + * Download the file at DEFAULT_URL and when that promise is fulfilled, + * then promise to apply function to count lines in that file. + */ + private Promise countLines() { + return download(DEFAULT_URL) + .thenApply(Utility::countLines); + } + + /* + * Return a promise to provide the local absolute path of the file downloaded in background. + * This is an async method and does not wait until the file is downloaded. + */ private Promise download(String urlString) { Promise downloadPromise = new Promise() .fulfillInAsync( () -> { return Utility.downloadFile(urlString); - }, executor); - + }, executor) + .onError( + throwable -> { + throwable.printStackTrace(); + taskCompleted(); + } + ); + return downloadPromise; } private void stop() throws InterruptedException { - canStop.await(); + stopLatch.await(); executor.shutdownNow(); } - + private void taskCompleted() { - canStop.countDown(); + stopLatch.countDown(); } } diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 7d8a97e84..870e1556d 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -36,6 +36,7 @@ import java.util.function.Function; public class Promise extends PromiseSupport { private Runnable fulfillmentAction; + private Consumer exceptionHandler; /** * Creates a promise that will be fulfilled in future. @@ -61,9 +62,17 @@ public class Promise extends PromiseSupport { @Override public void fulfillExceptionally(Exception exception) { super.fulfillExceptionally(exception); + handleException(exception); postFulfillment(); } + private void handleException(Exception exception) { + if (exceptionHandler == null) { + return; + } + exceptionHandler.accept(exception); + } + private void postFulfillment() { if (fulfillmentAction == null) { return; @@ -83,8 +92,8 @@ public class Promise extends PromiseSupport { executor.execute(() -> { try { fulfill(task.call()); - } catch (Exception e) { - fulfillExceptionally(e); + } catch (Exception ex) { + fulfillExceptionally(ex); } }); return this; @@ -96,11 +105,22 @@ public class Promise extends PromiseSupport { * @param action action to be executed. * @return a new promise. */ - public Promise then(Consumer action) { + public Promise thenAccept(Consumer action) { Promise dest = new Promise<>(); fulfillmentAction = new ConsumeAction(this, dest, action); return dest; } + + /** + * Set the exception handler on this promise. + * @param exceptionHandler a consumer that will handle the exception occurred while fulfilling + * the promise. + * @return this + */ + public Promise onError(Consumer exceptionHandler) { + this.exceptionHandler = exceptionHandler; + return this; + } /** * Returns a new promise that, when this promise is fulfilled normally, is fulfilled with @@ -108,7 +128,7 @@ public class Promise extends PromiseSupport { * @param func function to be executed. * @return a new promise. */ - public Promise then(Function func) { + public Promise thenApply(Function func) { Promise dest = new Promise<>(); fulfillmentAction = new TransformAction(this, dest, func); return dest; @@ -135,8 +155,8 @@ public class Promise extends PromiseSupport { try { action.accept(src.get()); dest.fulfill(null); - } catch (Throwable e) { - dest.fulfillExceptionally((Exception) e.getCause()); + } catch (Throwable throwable) { + dest.fulfillExceptionally((Exception) throwable.getCause()); } } } @@ -162,8 +182,8 @@ public class Promise extends PromiseSupport { try { V result = func.apply(src.get()); dest.fulfill(result); - } catch (Throwable e) { - dest.fulfillExceptionally((Exception) e.getCause()); + } catch (Throwable throwable) { + dest.fulfillExceptionally((Exception) throwable.getCause()); } } } diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java index 2cfad46d0..8d5be2538 100644 --- a/promise/src/main/java/com/iluwatar/promise/Utility.java +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -12,15 +12,19 @@ import java.net.URL; import java.util.HashMap; import java.util.Iterator; import java.util.Map; -import java.util.Optional; import java.util.Map.Entry; public class Utility { + /** + * Calculates character frequency of the file provided. + * @param fileLocation location of the file. + * @return a map of character to its frequency, an empty map if file does not exist. + */ public static Map characterFrequency(String fileLocation) { Map characterToFrequency = new HashMap<>(); - try (Reader reader = new FileReader(fileLocation); - BufferedReader bufferedReader = new BufferedReader(reader);) { + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader)) { for (String line; (line = bufferedReader.readLine()) != null;) { for (char c : line.toCharArray()) { if (!characterToFrequency.containsKey(c)) { @@ -35,33 +39,35 @@ public class Utility { } return characterToFrequency; } - - public static Optional lowestFrequencyChar(Map charFrequency) { - Optional lowestFrequencyChar = Optional.empty(); - if (charFrequency.isEmpty()) { - return lowestFrequencyChar; - } - + + /** + * @return the character with lowest frequency if it exists, {@code Optional.empty()} otherwise. + */ + public static Character lowestFrequencyChar(Map charFrequency) { + Character lowestFrequencyChar = null; Iterator> iterator = charFrequency.entrySet().iterator(); Entry entry = iterator.next(); int minFrequency = entry.getValue(); - lowestFrequencyChar = Optional.of(entry.getKey()); - + lowestFrequencyChar = entry.getKey(); + while (iterator.hasNext()) { entry = iterator.next(); if (entry.getValue() < minFrequency) { minFrequency = entry.getValue(); - lowestFrequencyChar = Optional.of(entry.getKey()); + lowestFrequencyChar = entry.getKey(); } } - + return lowestFrequencyChar; } - + + /** + * @return number of lines in the file at provided location. 0 if file does not exist. + */ public static Integer countLines(String fileLocation) { int lineCount = 0; - try (Reader reader = new FileReader(fileLocation); - BufferedReader bufferedReader = new BufferedReader(reader);) { + try (Reader reader = new FileReader(fileLocation); + BufferedReader bufferedReader = new BufferedReader(reader)) { while (bufferedReader.readLine() != null) { lineCount++; } @@ -71,11 +77,15 @@ public class Utility { return lineCount; } + /** + * Downloads the contents from the given urlString, and stores it in a temporary directory. + * @return the absolute path of the file downloaded. + */ public static String downloadFile(String urlString) throws MalformedURLException, IOException { System.out.println("Downloading contents from url: " + urlString); URL url = new URL(urlString); File file = File.createTempFile("promise_pattern", null); - try (Reader reader = new InputStreamReader(url.openStream()); + try (Reader reader = new InputStreamReader(url.openStream()); BufferedReader bufferedReader = new BufferedReader(reader); FileWriter writer = new FileWriter(file)) { for (String line; (line = bufferedReader.readLine()) != null; ) { diff --git a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java index de0ecb6d7..45c4c1d36 100644 --- a/promise/src/test/java/com/iluwatar/promise/PromiseTest.java +++ b/promise/src/test/java/com/iluwatar/promise/PromiseTest.java @@ -26,6 +26,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; @@ -40,7 +43,6 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; - /** * Tests Promise class. */ @@ -73,7 +75,8 @@ public class PromiseTest { testWaitingSomeTimeForPromiseToBeFulfilled(); } - private void testWaitingForeverForPromiseToBeFulfilled() throws InterruptedException, TimeoutException { + private void testWaitingForeverForPromiseToBeFulfilled() + throws InterruptedException, TimeoutException { Promise promise = new Promise<>(); promise.fulfillInAsync(new Callable() { @@ -134,7 +137,7 @@ public class PromiseTest { throws InterruptedException, ExecutionException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(value -> { + .thenAccept(value -> { assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); }); @@ -149,17 +152,18 @@ public class PromiseTest { throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(new Consumer() { + .thenAccept(new Consumer() { @Override - public void accept(Integer t) { + public void accept(Integer 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()); @@ -167,7 +171,8 @@ public 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()); @@ -179,7 +184,7 @@ public class PromiseTest { throws InterruptedException, ExecutionException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(value -> { + .thenApply(value -> { assertEquals(NumberCrunchingTask.CRUNCHED_NUMBER, value); return String.valueOf(value); }); @@ -195,17 +200,18 @@ public class PromiseTest { throws InterruptedException, ExecutionException, TimeoutException { Promise dependentPromise = promise .fulfillInAsync(new NumberCrunchingTask(), executor) - .then(new Function() { + .thenApply(new Function() { @Override - public String apply(Integer t) { + public String apply(Integer 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()); @@ -213,7 +219,8 @@ public 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()); @@ -228,6 +235,19 @@ public class PromiseTest { promise.get(1000, TimeUnit.SECONDS); } + + @SuppressWarnings("unchecked") + @Test + public void exceptionHandlerIsCalledWhenPromiseIsFulfilledExceptionally() { + Promise promise = new Promise<>(); + Consumer exceptionHandler = mock(Consumer.class); + promise.onError(exceptionHandler); + + Exception exception = new Exception("barf!"); + promise.fulfillExceptionally(exception); + + verify(exceptionHandler).accept(eq(exception)); + } private static class NumberCrunchingTask implements Callable { @@ -236,7 +256,7 @@ public class PromiseTest { @Override public Integer call() throws Exception { // Do number crunching - Thread.sleep(1000); + Thread.sleep(100); return CRUNCHED_NUMBER; } } From 5796e1967f85cd276d1b5fb311b3f1007bbcb8ae Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 11:50:33 +0530 Subject: [PATCH 025/145] Work on #403, updated diagram and finishing touches --- promise/etc/promise.png | Bin 55725 -> 59210 bytes promise/etc/promise.ucls | 38 +++++++++--------- .../main/java/com/iluwatar/promise/App.java | 5 ++- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/promise/etc/promise.png b/promise/etc/promise.png index 0aef198acb4e9abdb4cf5b34f61869b46e870c8a..cdb43eb6b49a11b317d7c8abbb8ef3fd43ec3779 100644 GIT binary patch literal 59210 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 B1!4dI diff --git a/promise/etc/promise.ucls b/promise/etc/promise.ucls index e7fefec1c..79a11e7d5 100644 --- a/promise/etc/promise.ucls +++ b/promise/etc/promise.ucls @@ -31,7 +31,7 @@ - + @@ -40,7 +40,7 @@ - + @@ -49,7 +49,7 @@ - + @@ -58,7 +58,7 @@ - + @@ -67,41 +67,41 @@ - + - - - - - - - - - + - + - + - + - + - + + + + + + + + + diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 2b2ae78b4..672c20bfa 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -61,11 +61,12 @@ import java.util.concurrent.Executors; public class App { private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; - private ExecutorService executor; - private CountDownLatch stopLatch = new CountDownLatch(2); + private final ExecutorService executor; + private final CountDownLatch stopLatch; private App() { executor = Executors.newFixedThreadPool(2); + stopLatch = new CountDownLatch(2); } /** From ad11ea46b11e638ee564b86eaf2e5763a3f780f1 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 11:55:30 +0530 Subject: [PATCH 026/145] Work on #403, javadocs updated --- promise/src/main/java/com/iluwatar/promise/Promise.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 870e1556d..3165142fa 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -29,7 +29,11 @@ import java.util.function.Consumer; import java.util.function.Function; /** - * Implements the promise pattern. + * 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. * * @param type of result. */ From 59cf1003021f32b3e51dfaa27b7f50fb17f2c8ba Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Mon, 29 Aug 2016 12:04:24 +0530 Subject: [PATCH 027/145] #403, updated javadocs --- promise/src/main/java/com/iluwatar/promise/Promise.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/promise/src/main/java/com/iluwatar/promise/Promise.java b/promise/src/main/java/com/iluwatar/promise/Promise.java index 3165142fa..e7e56837b 100644 --- a/promise/src/main/java/com/iluwatar/promise/Promise.java +++ b/promise/src/main/java/com/iluwatar/promise/Promise.java @@ -139,7 +139,7 @@ public class Promise extends PromiseSupport { } /** - * A consume action provides the action, the value from source promise and fulfills the + * Accesses the value from source promise and calls the consumer, then fulfills the * destination promise. */ private class ConsumeAction implements Runnable { @@ -166,8 +166,8 @@ public class Promise extends PromiseSupport { } /** - * A function action provides transformation function, value from source promise and fulfills the - * destination promise with the transformed value. + * Accesses the value from source promise, then fulfills the destination promise using the + * transformed value. The source value is transformed using the transformation function. */ private class TransformAction implements Runnable { @@ -184,8 +184,7 @@ public class Promise extends PromiseSupport { @Override public void run() { try { - V result = func.apply(src.get()); - dest.fulfill(result); + dest.fulfill(func.apply(src.get())); } catch (Throwable throwable) { dest.fulfillExceptionally((Exception) throwable.getCause()); } From e73867f9a15132a8160a84eddc8da4ff5882c190 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 13:24:53 +0200 Subject: [PATCH 028/145] Work on #190: Add automagic puml generation in pom.xml's --- aggregator-microservices/pom.xml | 22 +++++++++++++++++++++- api-gateway/pom.xml | 22 +++++++++++++++++++++- naked-objects/pom.xml | 19 ++++++++++++++++++- pom.xml | 27 ++++++++++++++++++++++++++- 4 files changed, 86 insertions(+), 4 deletions(-) diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index 0133e9ea4..6e9496ba3 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -35,9 +35,29 @@ aggregator-microservices pom + + + + com.github.markusmo3.urm + urm-maven-plugin + ${urm.version} + + ${project.basedir}/../etc + + com.iluwatar + + + + aggregator-microservices + + + + + + information-microservice aggregator-service inventory-microservice - \ No newline at end of file + diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 471ffda7d..48bfff9c3 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -35,9 +35,29 @@ api-gateway pom + + + + com.github.markusmo3.urm + urm-maven-plugin + ${urm.version} + + ${project.basedir}/../etc + + com.iluwatar + + + + api-gateway + + + + + + image-microservice price-microservice api-gateway-service - \ No newline at end of file + diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index b3e48dcb6..d416f2a72 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -317,6 +317,23 @@ org.apache.maven.plugins maven-surefire-report-plugin + + com.github.markusmo3.urm + urm-maven-plugin + ${urm.version} + + ${project.basedir}/../etc + + com.iluwatar + domainapp + + + + naked-objects + naked-objects-webapp + + + @@ -387,4 +404,4 @@ integtests webapp - \ No newline at end of file + diff --git a/pom.xml b/pom.xml index 6f3e0d698..80483b905 100644 --- a/pom.xml +++ b/pom.xml @@ -45,8 +45,9 @@ 1.15.1 1.10.19 4.12.1 - 4.5.2 + 4.5.2 2.22 + 1.4.0 abstract-factory @@ -422,6 +423,30 @@ + + + com.github.markusmo3.urm + urm-maven-plugin + ${urm.version} + + + process-classes + + map + + + + + ${project.basedir}/etc + + com.iluwatar + + + + java-design-patterns + + + From 36fe2499603cd103f20ffd3be9be583268415917 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 13:29:12 +0200 Subject: [PATCH 029/145] Work on #190: Add first batch of automagically generated puml files --- .../etc/abstract-document.urm.puml | 59 ++++++ .../etc/abstract-factory.urm.puml | 88 +++++++++ adapter/etc/adapter.urm.puml | 35 ++++ .../etc/aggregator-service.urm.puml | 41 ++++ .../etc/information-microservice.urm.puml | 12 ++ .../etc/inventory-microservice.urm.puml | 12 ++ api-gateway/etc/api-gateway-service.urm.puml | 48 +++++ api-gateway/etc/image-microservice.urm.puml | 12 ++ api-gateway/etc/price-microservice.urm.puml | 12 ++ .../etc/async-method-invocation.urm.puml | 50 +++++ bridge/etc/bridge.urm.puml | 89 +++++++++ builder/etc/builder.urm.puml | 100 ++++++++++ .../etc/business-delegate.urm.puml | 55 ++++++ caching/etc/caching.urm.puml | 100 ++++++++++ callback/etc/callback.urm.puml | 25 +++ chain/etc/chain.urm.puml | 60 ++++++ command/etc/command.urm.puml | 84 ++++++++ composite/etc/composite.urm.puml | 42 ++++ dao/etc/dao.urm.puml | 65 +++++++ data-mapper/etc/data-mapper.urm.puml | 42 ++++ decorator/etc/decorator.urm.puml | 29 +++ delegation/etc/delegation.urm.puml | 36 ++++ .../etc/dependency-injection.urm.puml | 48 +++++ .../etc/double-checked-locking.urm.puml | 20 ++ double-dispatch/etc/double-dispatch.urm.puml | 65 +++++++ .../etc/event-aggregator.urm.puml | 73 +++++++ .../etc/event-driven-architecture.urm.puml | 62 ++++++ execute-around/etc/execute-around.urm.puml | 14 ++ facade/etc/facade.urm.puml | 57 ++++++ factory-kit/etc/factory-kit.urm.puml | 45 +++++ factory-method/etc/factory-method.urm.puml | 53 +++++ feature-toggle/etc/feature-toggle.urm.puml | 47 +++++ fluentinterface/etc/fluentinterface.urm.puml | 71 +++++++ flux/etc/flux.urm.puml | 115 +++++++++++ flyweight/etc/flyweight.urm.puml | 60 ++++++ .../etc/front-controller.urm.puml | 50 +++++ .../etc/half-sync-half-async.urm.puml | 30 +++ hexagonal/etc/hexagonal.urm.puml | 183 ++++++++++++++++++ .../etc/intercepting-filter.urm.puml | 88 +++++++++ interpreter/etc/interpreter.urm.puml | 50 +++++ iterator/etc/iterator.urm.puml | 48 +++++ layers/etc/layers.urm.puml | 125 ++++++++++++ lazy-loading/etc/lazy-loading.urm.puml | 35 ++++ mediator/etc/mediator.urm.puml | 68 +++++++ memento/etc/memento.urm.puml | 48 +++++ message-channel/etc/message-channel.urm.puml | 8 + .../etc/model-view-controller.urm.puml | 69 +++++++ .../etc/model-view-presenter.urm.puml | 87 +++++++++ monad/etc/monad.urm.puml | 35 ++++ monostate/etc/monostate.urm.puml | 32 +++ multiton/etc/multiton.urm.puml | 29 +++ mute-idiom/etc/mute-idiom.urm.puml | 23 +++ mutex/etc/mutex.urm.puml | 27 +++ naked-objects/etc/naked-objects-dom.urm.puml | 84 ++++++++ .../etc/naked-objects-fixture.urm.puml | 92 +++++++++ .../etc/naked-objects-integtests.urm.puml | 92 +++++++++ null-object/etc/null-object.urm.puml | 40 ++++ object-pool/etc/object-pool.urm.puml | 29 +++ observer/etc/observer.urm.puml | 73 +++++++ page-object/etc/page-object.urm.puml | 8 + poison-pill/etc/poison-pill.urm.puml | 72 +++++++ .../etc/private-class-data.urm.puml | 34 ++++ .../etc/producer-consumer.urm.puml | 37 ++++ property/etc/property.urm.puml | 54 ++++++ prototype/etc/prototype.urm.puml | 81 ++++++++ proxy/etc/proxy.urm.puml | 24 +++ .../etc/publish-subscribe.urm.puml | 8 + reactor/etc/reactor.urm.puml | 151 +++++++++++++++ .../etc/reader-writer-lock.urm.puml | 58 ++++++ repository/etc/repository.urm.puml | 56 ++++++ ...rce-acquisition-is-initialization.urm.puml | 16 ++ semaphore/etc/semaphore.urm.puml | 58 ++++++ servant/etc/servant.urm.puml | 55 ++++++ service-layer/etc/service-layer.urm.puml | 158 +++++++++++++++ service-locator/etc/service-locator.urm.puml | 38 ++++ singleton/etc/singleton.urm.puml | 42 ++++ specification/etc/specification.urm.puml | 106 ++++++++++ state/etc/state.urm.puml | 37 ++++ step-builder/etc/step-builder.urm.puml | 91 +++++++++ strategy/etc/strategy.urm.puml | 33 ++++ template-method/etc/template-method.urm.puml | 36 ++++ thread-pool/etc/thread-pool.urm.puml | 35 ++++ tolerant-reader/etc/tolerant-reader.urm.puml | 38 ++++ twin/etc/twin.urm.puml | 25 +++ value-object/etc/value-object.urm.puml | 21 ++ visitor/etc/visitor.urm.puml | 57 ++++++ 86 files changed, 4700 insertions(+) create mode 100644 abstract-document/etc/abstract-document.urm.puml create mode 100644 abstract-factory/etc/abstract-factory.urm.puml create mode 100644 adapter/etc/adapter.urm.puml create mode 100644 aggregator-microservices/etc/aggregator-service.urm.puml create mode 100644 aggregator-microservices/etc/information-microservice.urm.puml create mode 100644 aggregator-microservices/etc/inventory-microservice.urm.puml create mode 100644 api-gateway/etc/api-gateway-service.urm.puml create mode 100644 api-gateway/etc/image-microservice.urm.puml create mode 100644 api-gateway/etc/price-microservice.urm.puml create mode 100644 async-method-invocation/etc/async-method-invocation.urm.puml create mode 100644 bridge/etc/bridge.urm.puml create mode 100644 builder/etc/builder.urm.puml create mode 100644 business-delegate/etc/business-delegate.urm.puml create mode 100644 caching/etc/caching.urm.puml create mode 100644 callback/etc/callback.urm.puml create mode 100644 chain/etc/chain.urm.puml create mode 100644 command/etc/command.urm.puml create mode 100644 composite/etc/composite.urm.puml create mode 100644 dao/etc/dao.urm.puml create mode 100644 data-mapper/etc/data-mapper.urm.puml create mode 100644 decorator/etc/decorator.urm.puml create mode 100644 delegation/etc/delegation.urm.puml create mode 100644 dependency-injection/etc/dependency-injection.urm.puml create mode 100644 double-checked-locking/etc/double-checked-locking.urm.puml create mode 100644 double-dispatch/etc/double-dispatch.urm.puml create mode 100644 event-aggregator/etc/event-aggregator.urm.puml create mode 100644 event-driven-architecture/etc/event-driven-architecture.urm.puml create mode 100644 execute-around/etc/execute-around.urm.puml create mode 100644 facade/etc/facade.urm.puml create mode 100644 factory-kit/etc/factory-kit.urm.puml create mode 100644 factory-method/etc/factory-method.urm.puml create mode 100644 feature-toggle/etc/feature-toggle.urm.puml create mode 100644 fluentinterface/etc/fluentinterface.urm.puml create mode 100644 flux/etc/flux.urm.puml create mode 100644 flyweight/etc/flyweight.urm.puml create mode 100644 front-controller/etc/front-controller.urm.puml create mode 100644 half-sync-half-async/etc/half-sync-half-async.urm.puml create mode 100644 hexagonal/etc/hexagonal.urm.puml create mode 100644 intercepting-filter/etc/intercepting-filter.urm.puml create mode 100644 interpreter/etc/interpreter.urm.puml create mode 100644 iterator/etc/iterator.urm.puml create mode 100644 layers/etc/layers.urm.puml create mode 100644 lazy-loading/etc/lazy-loading.urm.puml create mode 100644 mediator/etc/mediator.urm.puml create mode 100644 memento/etc/memento.urm.puml create mode 100644 message-channel/etc/message-channel.urm.puml create mode 100644 model-view-controller/etc/model-view-controller.urm.puml create mode 100644 model-view-presenter/etc/model-view-presenter.urm.puml create mode 100644 monad/etc/monad.urm.puml create mode 100644 monostate/etc/monostate.urm.puml create mode 100644 multiton/etc/multiton.urm.puml create mode 100644 mute-idiom/etc/mute-idiom.urm.puml create mode 100644 mutex/etc/mutex.urm.puml create mode 100644 naked-objects/etc/naked-objects-dom.urm.puml create mode 100644 naked-objects/etc/naked-objects-fixture.urm.puml create mode 100644 naked-objects/etc/naked-objects-integtests.urm.puml create mode 100644 null-object/etc/null-object.urm.puml create mode 100644 object-pool/etc/object-pool.urm.puml create mode 100644 observer/etc/observer.urm.puml create mode 100644 page-object/etc/page-object.urm.puml create mode 100644 poison-pill/etc/poison-pill.urm.puml create mode 100644 private-class-data/etc/private-class-data.urm.puml create mode 100644 producer-consumer/etc/producer-consumer.urm.puml create mode 100644 property/etc/property.urm.puml create mode 100644 prototype/etc/prototype.urm.puml create mode 100644 proxy/etc/proxy.urm.puml create mode 100644 publish-subscribe/etc/publish-subscribe.urm.puml create mode 100644 reactor/etc/reactor.urm.puml create mode 100644 reader-writer-lock/etc/reader-writer-lock.urm.puml create mode 100644 repository/etc/repository.urm.puml create mode 100644 resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml create mode 100644 semaphore/etc/semaphore.urm.puml create mode 100644 servant/etc/servant.urm.puml create mode 100644 service-layer/etc/service-layer.urm.puml create mode 100644 service-locator/etc/service-locator.urm.puml create mode 100644 singleton/etc/singleton.urm.puml create mode 100644 specification/etc/specification.urm.puml create mode 100644 state/etc/state.urm.puml create mode 100644 step-builder/etc/step-builder.urm.puml create mode 100644 strategy/etc/strategy.urm.puml create mode 100644 template-method/etc/template-method.urm.puml create mode 100644 thread-pool/etc/thread-pool.urm.puml create mode 100644 tolerant-reader/etc/tolerant-reader.urm.puml create mode 100644 twin/etc/twin.urm.puml create mode 100644 value-object/etc/value-object.urm.puml create mode 100644 visitor/etc/visitor.urm.puml diff --git a/abstract-document/etc/abstract-document.urm.puml b/abstract-document/etc/abstract-document.urm.puml new file mode 100644 index 000000000..c738b50ce --- /dev/null +++ b/abstract-document/etc/abstract-document.urm.puml @@ -0,0 +1,59 @@ +@startuml +package com.iluwatar.abstractdocument.domain { + class Part { + + Part(properties : Map) + } + class Car { + + Car(properties : Map) + } + interface HasModel { + + PROPERTY : String {static} + + getModel() : Optional + } + interface HasParts { + + PROPERTY : String {static} + + getParts() : Stream + } + interface HasType { + + PROPERTY : String {static} + + getType() : Optional + } + interface HasPrice { + + PROPERTY : String {static} + + getPrice() : Optional + } +} +package com.iluwatar.abstractdocument { + interface Document { + + children(String, Function, T>) : Stream {abstract} + + get(String) : Object {abstract} + + put(String, Object) {abstract} + } + abstract class AbstractDocument { + - properties : Map + # AbstractDocument(properties : Map) + + children(key : String, constructor : Function, T>) : Stream + + get(key : String) : Object + + put(key : String, value : Object) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } +} +AbstractDocument --+ Map +Part ..|> HasType +Part ..|> HasModel +Part ..|> HasPrice +Part --|> AbstractDocument +Car ..|> HasModel +Car ..|> HasPrice +Car ..|> HasParts +Car --|> AbstractDocument +HasModel --|> Document +HasParts --|> Document +AbstractDocument ..|> Document +HasType --|> Document +HasPrice --|> Document +@enduml \ No newline at end of file diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml new file mode 100644 index 000000000..88402c6d7 --- /dev/null +++ b/abstract-factory/etc/abstract-factory.urm.puml @@ -0,0 +1,88 @@ +@startuml +package com.iluwatar.abstractfactory { + interface Castle { + + getDescription() : String {abstract} + } + class OrcKingdomFactory { + + OrcKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } + class ElfKing { + ~ DESCRIPTION : String {static} + + ElfKing() + + getDescription() : String + } + interface King { + + getDescription() : String {abstract} + } + class App { + - army : Army + - castle : Castle + - king : King + + App() + + createKingdom(factory : KingdomFactory) + + getArmy() : Army + ~ getArmy(factory : KingdomFactory) : Army + + getCastle() : Castle + ~ getCastle(factory : KingdomFactory) : Castle + + getKing() : King + ~ getKing(factory : KingdomFactory) : King + + main(args : String[]) {static} + - setArmy(army : Army) + - setCastle(castle : Castle) + - setKing(king : King) + } + class OrcKing { + ~ DESCRIPTION : String {static} + + OrcKing() + + getDescription() : String + } + class ElfKingdomFactory { + + ElfKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } + interface Army { + + getDescription() : String {abstract} + } + class OrcArmy { + ~ DESCRIPTION : String {static} + + OrcArmy() + + getDescription() : String + } + interface KingdomFactory { + + createArmy() : Army {abstract} + + createCastle() : Castle {abstract} + + createKing() : King {abstract} + } + class ElfArmy { + ~ DESCRIPTION : String {static} + + ElfArmy() + + getDescription() : String + } + class ElfCastle { + ~ DESCRIPTION : String {static} + + ElfCastle() + + getDescription() : String + } + class OrcCastle { + ~ DESCRIPTION : String {static} + + OrcCastle() + + getDescription() : String + } +} +App --> "-castle" Castle +App --> "-king" King +App --> "-army" Army +OrcKingdomFactory ..|> KingdomFactory +ElfKing ..|> King +OrcKing ..|> King +ElfKingdomFactory ..|> KingdomFactory +OrcArmy ..|> Army +ElfArmy ..|> Army +ElfCastle ..|> Castle +OrcCastle ..|> Castle +@enduml \ No newline at end of file diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml new file mode 100644 index 000000000..2cee13dc4 --- /dev/null +++ b/adapter/etc/adapter.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.adapter { + class App { + + App() + + main(args : String[]) {static} + } + interface BattleShip { + + fire() {abstract} + + move() {abstract} + } + class Captain { + - battleship : BattleShip + + Captain() + + Captain(battleship : BattleShip) + + fire() + + move() + + setBattleship(battleship : BattleShip) + } + class BattleFishingBoat { + - boat : FishingBoat + + BattleFishingBoat() + + fire() + + move() + } + class FishingBoat { + + FishingBoat() + + fish() + + sail() + } +} +BattleFishingBoat --> "-boat" FishingBoat +Captain --> "-battleship" BattleShip +Captain ..|> BattleShip +BattleFishingBoat ..|> BattleShip +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-service.urm.puml b/aggregator-microservices/etc/aggregator-service.urm.puml new file mode 100644 index 000000000..5c2e1167a --- /dev/null +++ b/aggregator-microservices/etc/aggregator-service.urm.puml @@ -0,0 +1,41 @@ +@startuml +package com.iluwatar.aggregator.microservices { + class Aggregator { + - informationClient : ProductInformationClient + - inventoryClient : ProductInventoryClient + + Aggregator() + + getProduct() : Product + } + class ProductInformationClientImpl { + + ProductInformationClientImpl() + + getProductTitle() : String + } + interface ProductInformationClient { + + getProductTitle() : String {abstract} + } + class Product { + - productInventories : int + - title : String + + Product() + + getProductInventories() : int + + getTitle() : String + + setProductInventories(productInventories : int) + + setTitle(title : String) + } + class ProductInventoryClientImpl { + + ProductInventoryClientImpl() + + getProductInventories() : int + } + class App { + + App() + + main(args : String[]) {static} + } + interface ProductInventoryClient { + + getProductInventories() : int {abstract} + } +} +Aggregator --> "-inventoryClient" ProductInventoryClient +Aggregator --> "-informationClient" ProductInformationClient +ProductInformationClientImpl ..|> ProductInformationClient +ProductInventoryClientImpl ..|> ProductInventoryClient +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/information-microservice.urm.puml b/aggregator-microservices/etc/information-microservice.urm.puml new file mode 100644 index 000000000..e0a2ccb24 --- /dev/null +++ b/aggregator-microservices/etc/information-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.information.microservice { + class InformationApplication { + + InformationApplication() + + main(args : String[]) {static} + } + class InformationController { + + InformationController() + + getProductTitle() : String + } +} +@enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/inventory-microservice.urm.puml b/aggregator-microservices/etc/inventory-microservice.urm.puml new file mode 100644 index 000000000..90f327e07 --- /dev/null +++ b/aggregator-microservices/etc/inventory-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.inventory.microservice { + class InventoryApplication { + + InventoryApplication() + + main(args : String[]) {static} + } + class InventoryController { + + InventoryController() + + getProductInventories() : int + } +} +@enduml \ No newline at end of file diff --git a/api-gateway/etc/api-gateway-service.urm.puml b/api-gateway/etc/api-gateway-service.urm.puml new file mode 100644 index 000000000..3313f7059 --- /dev/null +++ b/api-gateway/etc/api-gateway-service.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.api.gateway { + interface ImageClient { + + getImagePath() : String {abstract} + } + class MobileProduct { + - price : String + + MobileProduct() + + getPrice() : String + + setPrice(price : String) + } + class ApiGateway { + - imageClient : ImageClient + - priceClient : PriceClient + + ApiGateway() + + getProductDesktop() : DesktopProduct + + getProductMobile() : MobileProduct + } + class DesktopProduct { + - imagePath : String + - price : String + + DesktopProduct() + + getImagePath() : String + + getPrice() : String + + setImagePath(imagePath : String) + + setPrice(price : String) + } + interface PriceClient { + + getPrice() : String {abstract} + } + class PriceClientImpl { + + PriceClientImpl() + + getPrice() : String + } + class ImageClientImpl { + + ImageClientImpl() + + getImagePath() : String + } + class App { + + App() + + main(args : String[]) {static} + } +} +ApiGateway --> "-imageClient" ImageClient +ApiGateway --> "-priceClient" PriceClient +PriceClientImpl ..|> PriceClient +ImageClientImpl ..|> ImageClient +@enduml \ No newline at end of file diff --git a/api-gateway/etc/image-microservice.urm.puml b/api-gateway/etc/image-microservice.urm.puml new file mode 100644 index 000000000..130dac9de --- /dev/null +++ b/api-gateway/etc/image-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.image.microservice { + class ImageApplication { + + ImageApplication() + + main(args : String[]) {static} + } + class ImageController { + + ImageController() + + getImagePath() : String + } +} +@enduml \ No newline at end of file diff --git a/api-gateway/etc/price-microservice.urm.puml b/api-gateway/etc/price-microservice.urm.puml new file mode 100644 index 000000000..9893c9c60 --- /dev/null +++ b/api-gateway/etc/price-microservice.urm.puml @@ -0,0 +1,12 @@ +@startuml +package com.iluwatar.price.microservice { + class PriceApplication { + + PriceApplication() + + main(args : String[]) {static} + } + class PriceController { + + PriceController() + + getPrice() : String + } +} +@enduml \ No newline at end of file diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml new file mode 100644 index 000000000..9a90d307e --- /dev/null +++ b/async-method-invocation/etc/async-method-invocation.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.async.method.invocation { + interface AsyncCallback { + + onComplete(T, Optional) {abstract} + } + interface AsyncResult { + + await() {abstract} + + getValue() : T {abstract} + + isCompleted() : boolean {abstract} + } + class ThreadAsyncExecutor { + - idx : AtomicInteger + + ThreadAsyncExecutor() + + endProcess(asyncResult : AsyncResult) : T + + startProcess(task : Callable) : AsyncResult + + startProcess(task : Callable, callback : AsyncCallback) : AsyncResult + } + class App { + + App() + - callback(name : String) : AsyncCallback {static} + - lazyval(value : T, delayMillis : long) : Callable {static} + - log(msg : String) {static} + + main(args : String[]) {static} + } + -class CompletableResult { + ~ COMPLETED : int {static} + ~ FAILED : int {static} + ~ RUNNING : int {static} + ~ callback : Optional> + ~ exception : Exception + ~ lock : Object + ~ state : int + ~ value : T + ~ CompletableResult(callback : AsyncCallback) + + await() + + getValue() : T + + isCompleted() : boolean + ~ setException(exception : Exception) + ~ setValue(value : T) + } + interface AsyncExecutor { + + endProcess(AsyncResult) : T {abstract} + + startProcess(Callable) : AsyncResult {abstract} + + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} + } +} +CompletableResult ..+ ThreadAsyncExecutor +ThreadAsyncExecutor ..|> AsyncExecutor +CompletableResult ..|> AsyncResult +@enduml \ No newline at end of file diff --git a/bridge/etc/bridge.urm.puml b/bridge/etc/bridge.urm.puml new file mode 100644 index 000000000..d9d7a4145 --- /dev/null +++ b/bridge/etc/bridge.urm.puml @@ -0,0 +1,89 @@ +@startuml +package com.iluwatar.bridge { + class FlyingMagicWeapon { + + FlyingMagicWeapon(imp : FlyingMagicWeaponImpl) + + fly() + + getImp() : FlyingMagicWeaponImpl + + swing() + + unwield() + + wield() + } + abstract class MagicWeapon { + # imp : MagicWeaponImpl + + MagicWeapon(imp : MagicWeaponImpl) + + getImp() : MagicWeaponImpl + + swing() {abstract} + + unwield() {abstract} + + wield() {abstract} + } + abstract class SoulEatingMagicWeaponImpl { + + SoulEatingMagicWeaponImpl() + + eatSoulImp() {abstract} + } + class BlindingMagicWeapon { + + BlindingMagicWeapon(imp : BlindingMagicWeaponImpl) + + blind() + + getImp() : BlindingMagicWeaponImpl + + swing() + + unwield() + + wield() + } + class Stormbringer { + + Stormbringer() + + eatSoulImp() + + swingImp() + + unwieldImp() + + wieldImp() + } + abstract class BlindingMagicWeaponImpl { + + BlindingMagicWeaponImpl() + + blindImp() {abstract} + } + class SoulEatingMagicWeapon { + + SoulEatingMagicWeapon(imp : SoulEatingMagicWeaponImpl) + + eatSoul() + + getImp() : SoulEatingMagicWeaponImpl + + swing() + + unwield() + + wield() + } + abstract class MagicWeaponImpl { + + MagicWeaponImpl() + + swingImp() {abstract} + + unwieldImp() {abstract} + + wieldImp() {abstract} + } + class Excalibur { + + Excalibur() + + blindImp() + + swingImp() + + unwieldImp() + + wieldImp() + } + abstract class FlyingMagicWeaponImpl { + + FlyingMagicWeaponImpl() + + flyImp() {abstract} + } + class Mjollnir { + + Mjollnir() + + flyImp() + + swingImp() + + unwieldImp() + + wieldImp() + } + class App { + + App() + + main(args : String[]) {static} + } +} +MagicWeapon --> "-imp" MagicWeaponImpl +FlyingMagicWeapon --|> MagicWeapon +SoulEatingMagicWeaponImpl --|> MagicWeaponImpl +BlindingMagicWeapon --|> MagicWeapon +Stormbringer --|> SoulEatingMagicWeaponImpl +BlindingMagicWeaponImpl --|> MagicWeaponImpl +SoulEatingMagicWeapon --|> MagicWeapon +Excalibur --|> BlindingMagicWeaponImpl +FlyingMagicWeaponImpl --|> MagicWeaponImpl +Mjollnir --|> FlyingMagicWeaponImpl +@enduml \ No newline at end of file diff --git a/builder/etc/builder.urm.puml b/builder/etc/builder.urm.puml new file mode 100644 index 000000000..262476329 --- /dev/null +++ b/builder/etc/builder.urm.puml @@ -0,0 +1,100 @@ +@startuml +package com.iluwatar.builder { + class Hero { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + - Hero(builder : Builder) + + getArmor() : Armor + + getHairColor() : HairColor + + getHairType() : HairType + + getName() : String + + getProfession() : Profession + + getWeapon() : Weapon + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class Builder { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + + Builder(profession : Profession, name : String) + + build() : Hero + + withArmor(armor : Armor) : Builder + + withHairColor(hairColor : HairColor) : Builder + + withHairType(hairType : HairType) : Builder + + withWeapon(weapon : Weapon) : Builder + } + enum Armor { + + CHAIN_MAIL {static} + + CLOTHES {static} + + LEATHER {static} + + PLATE_MAIL {static} + - title : String + + toString() : String + + valueOf(name : String) : Armor {static} + + values() : Armor[] {static} + } + enum Profession { + + MAGE {static} + + PRIEST {static} + + THIEF {static} + + WARRIOR {static} + + toString() : String + + valueOf(name : String) : Profession {static} + + values() : Profession[] {static} + } + enum Weapon { + + AXE {static} + + BOW {static} + + DAGGER {static} + + SWORD {static} + + WARHAMMER {static} + + toString() : String + + valueOf(name : String) : Weapon {static} + + values() : Weapon[] {static} + } + enum HairType { + + BALD {static} + + CURLY {static} + + LONG_CURLY {static} + + LONG_STRAIGHT {static} + + SHORT {static} + - title : String + + toString() : String + + valueOf(name : String) : HairType {static} + + values() : HairType[] {static} + } + enum HairColor { + + BLACK {static} + + BLOND {static} + + BROWN {static} + + RED {static} + + WHITE {static} + + toString() : String + + valueOf(name : String) : HairColor {static} + + values() : HairColor[] {static} + } +} +Builder ..+ Hero +Hero --> "-profession" Profession +Hero --> "-armor" Armor +App --+ Hero +Builder --> "-weapon" Weapon +Builder --> "-hairColor" HairColor +Builder --> "-hairType" HairType +Hero --> "-hairColor" HairColor +Builder --> "-profession" Profession +Hero --> "-weapon" Weapon +Hero --> "-hairType" HairType +Builder --> "-armor" Armor +@enduml \ No newline at end of file diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml new file mode 100644 index 000000000..a8f31700b --- /dev/null +++ b/business-delegate/etc/business-delegate.urm.puml @@ -0,0 +1,55 @@ +@startuml +package com.iluwatar.business.delegate { + class BusinessLookup { + - ejbService : EjbService + - jmsService : JmsService + + BusinessLookup() + + getBusinessService(serviceType : ServiceType) : BusinessService + + setEjbService(ejbService : EjbService) + + setJmsService(jmsService : JmsService) + } + class Client { + - businessDelegate : BusinessDelegate + + Client(businessDelegate : BusinessDelegate) + + doTask() + } + class EjbService { + + EjbService() + + doProcessing() + } + class BusinessDelegate { + - businessService : BusinessService + - lookupService : BusinessLookup + - serviceType : ServiceType + + BusinessDelegate() + + doTask() + + setLookupService(businessLookup : BusinessLookup) + + setServiceType(serviceType : ServiceType) + } + interface BusinessService { + + doProcessing() {abstract} + } + class JmsService { + + JmsService() + + doProcessing() + } + class App { + + App() + + main(args : String[]) {static} + } + enum ServiceType { + + EJB {static} + + JMS {static} + + valueOf(name : String) : ServiceType {static} + + values() : ServiceType[] {static} + } +} +BusinessDelegate --> "-serviceType" ServiceType +BusinessLookup --> "-ejbService" EjbService +Client --> "-businessDelegate" BusinessDelegate +BusinessDelegate --> "-businessService" BusinessService +BusinessDelegate --> "-lookupService" BusinessLookup +BusinessLookup --> "-jmsService" JmsService +EjbService ..|> BusinessService +JmsService ..|> BusinessService +@enduml \ No newline at end of file diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml new file mode 100644 index 000000000..273c9911c --- /dev/null +++ b/caching/etc/caching.urm.puml @@ -0,0 +1,100 @@ +@startuml +package com.iluwatar.caching { + class UserAccount { + - additionalInfo : String + - userId : String + - userName : String + + UserAccount(userId : String, userName : String, additionalInfo : String) + + getAdditionalInfo() : String + + getUserId() : String + + getUserName() : String + + setAdditionalInfo(additionalInfo : String) + + setUserId(userId : String) + + setUserName(userName : String) + + toString() : String + } + class CacheStore { + ~ cache : LruCache {static} + - CacheStore() + + clearCache() {static} + + flushCache() {static} + + initCapacity(capacity : int) {static} + + print() : String {static} + + readThrough(userId : String) : UserAccount {static} + + readThroughWithWriteBackPolicy(userId : String) : UserAccount {static} + + writeAround(userAccount : UserAccount) {static} + + writeBehind(userAccount : UserAccount) {static} + + writeThrough(userAccount : UserAccount) {static} + } + class AppManager { + - cachingPolicy : CachingPolicy {static} + - AppManager() + + find(userId : String) : UserAccount {static} + + initCacheCapacity(capacity : int) {static} + + initCachingPolicy(policy : CachingPolicy) {static} + + initDb(useMongoDb : boolean) {static} + + printCacheContent() : String {static} + + save(userAccount : UserAccount) {static} + } + ~class Node { + ~ next : Node + ~ previous : Node + ~ userAccount : UserAccount + ~ userId : String + + Node(this$0 : LruCache, userId : String, userAccount : UserAccount) + } + class LruCache { + ~ cache : Map + ~ capacity : int + ~ end : Node + ~ head : Node + + LruCache(capacity : int) + + clear() + + contains(userId : String) : boolean + + get(userId : String) : UserAccount + + getCacheDataInListForm() : List + + getLruData() : UserAccount + + invalidate(userId : String) + + isFull() : boolean + + remove(node : Node) + + set(userId : String, userAccount : UserAccount) + + setCapacity(newCapacity : int) + + setHead(node : Node) + } + class DbManager { + - db : MongoDatabase {static} + - mongoClient : MongoClient {static} + - useMongoDB : boolean {static} + - virtualDB : Map {static} + - DbManager() + + connect() {static} + + createVirtualDb() {static} + + readFromDb(userId : String) : UserAccount {static} + + updateDb(userAccount : UserAccount) {static} + + upsertDb(userAccount : UserAccount) {static} + + writeToDb(userAccount : UserAccount) {static} + } + class App { + + App() + + main(args : String[]) {static} + + useReadAndWriteThroughStrategy() + + useReadThroughAndWriteAroundStrategy() + + useReadThroughAndWriteBehindStrategy() + } + enum CachingPolicy { + + AROUND {static} + + BEHIND {static} + + THROUGH {static} + - policy : String + + getPolicy() : String + + valueOf(name : String) : CachingPolicy {static} + + values() : CachingPolicy[] {static} + } +} +Node --+ LruCache +LruCache --> "-head" Node +Node --> "-previous" Node +AppManager --> "-cachingPolicy" CachingPolicy +Node --> "-userAccount" UserAccount +CacheStore --> "-cache" LruCache +@enduml \ No newline at end of file diff --git a/callback/etc/callback.urm.puml b/callback/etc/callback.urm.puml new file mode 100644 index 000000000..8b27ee8a8 --- /dev/null +++ b/callback/etc/callback.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.callback { + class LambdasApp { + + LambdasApp() + + main(args : String[]) {static} + } + class SimpleTask { + + SimpleTask() + + execute() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Task { + + Task() + + execute() {abstract} + + executeWith(callback : Callback) + } + interface Callback { + + call() {abstract} + } +} +SimpleTask --|> Task +@enduml \ No newline at end of file diff --git a/chain/etc/chain.urm.puml b/chain/etc/chain.urm.puml new file mode 100644 index 000000000..c75cbc8d1 --- /dev/null +++ b/chain/etc/chain.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.chain { + class OrcCommander { + + OrcCommander(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class Request { + - handled : boolean + - requestDescription : String + - requestType : RequestType + + Request(requestType : RequestType, requestDescription : String) + + getRequestDescription() : String + + getRequestType() : RequestType + + isHandled() : boolean + + markHandled() + + toString() : String + } + class OrcOfficer { + + OrcOfficer(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + class OrcKing { + ~ chain : RequestHandler + + OrcKing() + - buildChain() + + makeRequest(req : Request) + } + class OrcSoldier { + + OrcSoldier(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } + abstract class RequestHandler { + - next : RequestHandler + + RequestHandler(next : RequestHandler) + + handleRequest(req : Request) + # printHandling(req : Request) + + toString() : String {abstract} + } + enum RequestType { + + COLLECT_TAX {static} + + DEFEND_CASTLE {static} + + TORTURE_PRISONER {static} + + valueOf(name : String) : RequestType {static} + + values() : RequestType[] {static} + } +} +RequestHandler --> "-next" RequestHandler +OrcKing --> "-chain" RequestHandler +Request --> "-requestType" RequestType +OrcCommander --|> RequestHandler +OrcOfficer --|> RequestHandler +OrcSoldier --|> RequestHandler +@enduml \ No newline at end of file diff --git a/command/etc/command.urm.puml b/command/etc/command.urm.puml new file mode 100644 index 000000000..015fb30be --- /dev/null +++ b/command/etc/command.urm.puml @@ -0,0 +1,84 @@ +@startuml +package com.iluwatar.command { + abstract class Target { + - size : Size + - visibility : Visibility + + Target() + + getSize() : Size + + getVisibility() : Visibility + + printStatus() + + setSize(size : Size) + + setVisibility(visibility : Visibility) + + toString() : String {abstract} + } + class Goblin { + + Goblin() + + toString() : String + } + class ShrinkSpell { + - oldSize : Size + - target : Target + + ShrinkSpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + class InvisibilitySpell { + - target : Target + + InvisibilitySpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } + class Wizard { + - redoStack : Deque + - undoStack : Deque + + Wizard() + + castSpell(command : Command, target : Target) + + redoLastSpell() + + toString() : String + + undoLastSpell() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Command { + + Command() + + execute(Target) {abstract} + + redo() {abstract} + + toString() : String {abstract} + + undo() {abstract} + } + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + + UNDEFINED {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } + enum Visibility { + + INVISIBLE {static} + + UNDEFINED {static} + + VISIBLE {static} + - title : String + + toString() : String + + valueOf(name : String) : Visibility {static} + + values() : Visibility[] {static} + } +} +Target --> "-size" Size +Wizard --> "-undoStack" Command +ShrinkSpell --> "-oldSize" Size +InvisibilitySpell --> "-target" Target +ShrinkSpell --> "-target" Target +Target --> "-visibility" Visibility +Goblin --|> Target +ShrinkSpell --|> Command +InvisibilitySpell --|> Command +@enduml \ No newline at end of file diff --git a/composite/etc/composite.urm.puml b/composite/etc/composite.urm.puml new file mode 100644 index 000000000..6f6e93c98 --- /dev/null +++ b/composite/etc/composite.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.composite { + class Letter { + - c : char + + Letter(c : char) + # printThisAfter() + # printThisBefore() + } + class Sentence { + + Sentence(words : List) + # printThisAfter() + # printThisBefore() + } + class Word { + + Word(letters : List) + # printThisAfter() + # printThisBefore() + } + class Messenger { + + Messenger() + ~ messageFromElves() : LetterComposite + ~ messageFromOrcs() : LetterComposite + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class LetterComposite { + - children : List + + LetterComposite() + + add(letter : LetterComposite) + + count() : int + + print() + # printThisAfter() {abstract} + # printThisBefore() {abstract} + } +} +LetterComposite --> "-children" LetterComposite +Letter --|> LetterComposite +Sentence --|> LetterComposite +Word --|> LetterComposite +@enduml \ No newline at end of file diff --git a/dao/etc/dao.urm.puml b/dao/etc/dao.urm.puml new file mode 100644 index 000000000..f751b967c --- /dev/null +++ b/dao/etc/dao.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.dao { + class Customer { + - firstName : String + - id : int + - lastName : String + + Customer(id : int, firstName : String, lastName : String) + + equals(that : Object) : boolean + + getFirstName() : String + + getId() : int + + getLastName() : String + + hashCode() : int + + setFirstName(firstName : String) + + setId(id : int) + + setLastName(lastName : String) + + toString() : String + } + interface CustomerDao { + + add(Customer) : boolean {abstract} + + delete(Customer) : boolean {abstract} + + getAll() : Stream {abstract} + + getById(int) : Optional {abstract} + + update(Customer) : boolean {abstract} + } + class DbCustomerDao { + - dataSource : DataSource + + DbCustomerDao(dataSource : DataSource) + + add(customer : Customer) : boolean + - createCustomer(resultSet : ResultSet) : Customer + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + - getConnection() : Connection + - mutedClose(connection : Connection) + + update(customer : Customer) : boolean + } + class InMemoryCustomerDao { + - idToCustomer : Map + + InMemoryCustomerDao() + + add(customer : Customer) : boolean + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + + update(customer : Customer) : boolean + } + interface CustomerSchemaSql { + + CREATE_SCHEMA_SQL : String {static} + + DELETE_SCHEMA_SQL : String {static} + } + class App { + - DB_URL : String {static} + - log : Logger {static} + + App() + - addCustomers(customerDao : CustomerDao) {static} + - createDataSource() : DataSource {static} + - createSchema(dataSource : DataSource) {static} + - deleteSchema(dataSource : DataSource) {static} + + generateSampleCustomers() : List {static} + + main(args : String[]) {static} + - performOperationsUsing(customerDao : CustomerDao) {static} + } +} +DbCustomerDao ..|> CustomerDao +InMemoryCustomerDao ..|> CustomerDao +@enduml \ No newline at end of file diff --git a/data-mapper/etc/data-mapper.urm.puml b/data-mapper/etc/data-mapper.urm.puml new file mode 100644 index 000000000..a16df0020 --- /dev/null +++ b/data-mapper/etc/data-mapper.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.datamapper { + interface StudentDataMapper { + + delete(Student) {abstract} + + find(int) : Optional {abstract} + + insert(Student) {abstract} + + update(Student) {abstract} + } + class App { + - log : Logger {static} + - App() + + main(args : String[]) {static} + } + class Student { + - grade : char + - name : String + - serialVersionUID : long {static} + - studentId : int + + Student(studentId : int, name : String, grade : char) + + equals(inputObject : Object) : boolean + + getGrade() : char + + getName() : String + + getStudentId() : int + + hashCode() : int + + setGrade(grade : char) + + setName(name : String) + + setStudentId(studentId : int) + + toString() : String + } + class StudentDataMapperImpl { + - students : List + + StudentDataMapperImpl() + + delete(studentToBeDeleted : Student) + + find(studentId : int) : Optional + + getStudents() : List + + insert(studentToBeInserted : Student) + + update(studentToBeUpdated : Student) + } +} +StudentDataMapperImpl --> "-students" Student +StudentDataMapperImpl ..|> StudentDataMapper +@enduml \ No newline at end of file diff --git a/decorator/etc/decorator.urm.puml b/decorator/etc/decorator.urm.puml new file mode 100644 index 000000000..6c44e6cc9 --- /dev/null +++ b/decorator/etc/decorator.urm.puml @@ -0,0 +1,29 @@ +@startuml +package com.iluwatar.decorator { + class App { + + App() + + main(args : String[]) {static} + } + class Troll { + + Troll() + + attack() + + fleeBattle() + + getAttackPower() : int + } + interface Hostile { + + attack() {abstract} + + fleeBattle() {abstract} + + getAttackPower() : int {abstract} + } + class SmartHostile { + - decorated : Hostile + + SmartHostile(decorated : Hostile) + + attack() + + fleeBattle() + + getAttackPower() : int + } +} +SmartHostile --> "-decorated" Hostile +Troll ..|> Hostile +SmartHostile ..|> Hostile +@enduml \ No newline at end of file diff --git a/delegation/etc/delegation.urm.puml b/delegation/etc/delegation.urm.puml new file mode 100644 index 000000000..c143a6ba0 --- /dev/null +++ b/delegation/etc/delegation.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.delegation.simple.printers { + class EpsonPrinter { + + EpsonPrinter() + + print(message : String) + } + class HpPrinter { + + HpPrinter() + + print(message : String) + } + class CanonPrinter { + + CanonPrinter() + + print(message : String) + } +} +package com.iluwatar.delegation.simple { + class PrinterController { + - printer : Printer + + PrinterController(printer : Printer) + + print(message : String) + } + interface Printer { + + print(String) {abstract} + } + class App { + + MESSAGE_TO_PRINT : String {static} + + App() + + main(args : String[]) {static} + } +} +PrinterController --> "-printer" Printer +PrinterController ..|> Printer +EpsonPrinter ..|> Printer +HpPrinter ..|> Printer +CanonPrinter ..|> Printer +@enduml \ No newline at end of file diff --git a/dependency-injection/etc/dependency-injection.urm.puml b/dependency-injection/etc/dependency-injection.urm.puml new file mode 100644 index 000000000..c22c658ad --- /dev/null +++ b/dependency-injection/etc/dependency-injection.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.dependency.injection { + interface Wizard { + + smoke() {abstract} + } + class GuiceWizard { + - tobacco : Tobacco + + GuiceWizard(tobacco : Tobacco) + + smoke() + } + class OldTobyTobacco { + + OldTobyTobacco() + } + abstract class Tobacco { + + Tobacco() + + smoke(wizard : Wizard) + } + class App { + + App() + + main(args : String[]) {static} + } + class RivendellTobacco { + + RivendellTobacco() + } + class AdvancedWizard { + - tobacco : Tobacco + + AdvancedWizard(tobacco : Tobacco) + + smoke() + } + class SecondBreakfastTobacco { + + SecondBreakfastTobacco() + } + class SimpleWizard { + - tobacco : OldTobyTobacco + + SimpleWizard() + + smoke() + } +} +SimpleWizard --> "-tobacco" OldTobyTobacco +AdvancedWizard --> "-tobacco" Tobacco +GuiceWizard --> "-tobacco" Tobacco +GuiceWizard ..|> Wizard +OldTobyTobacco --|> Tobacco +RivendellTobacco --|> Tobacco +AdvancedWizard ..|> Wizard +SecondBreakfastTobacco --|> Tobacco +SimpleWizard ..|> Wizard +@enduml \ No newline at end of file diff --git a/double-checked-locking/etc/double-checked-locking.urm.puml b/double-checked-locking/etc/double-checked-locking.urm.puml new file mode 100644 index 000000000..6feb98901 --- /dev/null +++ b/double-checked-locking/etc/double-checked-locking.urm.puml @@ -0,0 +1,20 @@ +@startuml +package com.iluwatar.doublechecked.locking { + class App { + + App() + + main(args : String[]) {static} + } + class Inventory { + - inventorySize : int + - items : List + - lock : Lock + + Inventory(inventorySize : int) + + addItem(item : Item) : boolean + + getItems() : List + } + class Item { + + Item() + } +} +Inventory --> "-items" Item +@enduml \ No newline at end of file diff --git a/double-dispatch/etc/double-dispatch.urm.puml b/double-dispatch/etc/double-dispatch.urm.puml new file mode 100644 index 000000000..725f009c0 --- /dev/null +++ b/double-dispatch/etc/double-dispatch.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.doubledispatch { + class App { + + App() + + main(args : String[]) {static} + } + class FlamingAsteroid { + + FlamingAsteroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + class SpaceStationIss { + + SpaceStationIss(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + abstract class GameObject { + - damaged : boolean + - onFire : boolean + + GameObject(left : int, top : int, right : int, bottom : int) + + collision(GameObject) {abstract} + + collisionResolve(FlamingAsteroid) {abstract} + + collisionResolve(Meteoroid) {abstract} + + collisionResolve(SpaceStationIss) {abstract} + + collisionResolve(SpaceStationMir) {abstract} + + isDamaged() : boolean + + isOnFire() : boolean + + setDamaged(damaged : boolean) + + setOnFire(onFire : boolean) + + toString() : String + } + class SpaceStationMir { + + SpaceStationMir(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + + collisionResolve(asteroid : FlamingAsteroid) + + collisionResolve(iss : SpaceStationIss) + + collisionResolve(meteoroid : Meteoroid) + + collisionResolve(mir : SpaceStationMir) + } + class Meteoroid { + + Meteoroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + + collisionResolve(asteroid : FlamingAsteroid) + + collisionResolve(iss : SpaceStationIss) + + collisionResolve(meteoroid : Meteoroid) + + collisionResolve(mir : SpaceStationMir) + } + class Rectangle { + - bottom : int + - left : int + - right : int + - top : int + + Rectangle(left : int, top : int, right : int, bottom : int) + + getBottom() : int + + getLeft() : int + + getRight() : int + + getTop() : int + ~ intersectsWith(r : Rectangle) : boolean + + toString() : String + } +} +FlamingAsteroid --|> Meteoroid +SpaceStationIss --|> SpaceStationMir +GameObject --|> Rectangle +SpaceStationMir --|> GameObject +Meteoroid --|> GameObject +@enduml \ No newline at end of file diff --git a/event-aggregator/etc/event-aggregator.urm.puml b/event-aggregator/etc/event-aggregator.urm.puml new file mode 100644 index 000000000..18b7621c9 --- /dev/null +++ b/event-aggregator/etc/event-aggregator.urm.puml @@ -0,0 +1,73 @@ +@startuml +package com.iluwatar.event.aggregator { + class KingsHand { + + KingsHand() + + KingsHand(obs : EventObserver) + + onEvent(e : Event) + + timePasses(day : Weekday) + } + class KingJoffrey { + + KingJoffrey() + + onEvent(e : Event) + } + class Scout { + + Scout() + + Scout(obs : EventObserver) + + timePasses(day : Weekday) + } + class LordVarys { + + LordVarys() + + LordVarys(obs : EventObserver) + + timePasses(day : Weekday) + } + class App { + + App() + + main(args : String[]) {static} + } + class LordBaelish { + + LordBaelish() + + LordBaelish(obs : EventObserver) + + timePasses(day : Weekday) + } + abstract class EventEmitter { + - observers : List + + EventEmitter() + + EventEmitter(obs : EventObserver) + # notifyObservers(e : Event) + + registerObserver(obs : EventObserver) + + timePasses(Weekday) {abstract} + } + interface EventObserver { + + onEvent(Event) {abstract} + } + enum Weekday { + + FRIDAY {static} + + MONDAY {static} + + SATURDAY {static} + + SUNDAY {static} + + THURSDAY {static} + + TUESDAY {static} + + WEDNESDAY {static} + - description : String + + toString() : String + + valueOf(name : String) : Weekday {static} + + values() : Weekday[] {static} + } + enum Event { + + STARK_SIGHTED {static} + + TRAITOR_DETECTED {static} + + WARSHIPS_APPROACHING {static} + - description : String + + toString() : String + + valueOf(name : String) : Event {static} + + values() : Event[] {static} + } +} +EventEmitter --> "-observers" EventObserver +KingsHand ..|> EventObserver +KingsHand --|> EventEmitter +KingJoffrey ..|> EventObserver +Scout --|> EventEmitter +LordVarys --|> EventEmitter +LordBaelish --|> EventEmitter +@enduml \ No newline at end of file diff --git a/event-driven-architecture/etc/event-driven-architecture.urm.puml b/event-driven-architecture/etc/event-driven-architecture.urm.puml new file mode 100644 index 000000000..55039190f --- /dev/null +++ b/event-driven-architecture/etc/event-driven-architecture.urm.puml @@ -0,0 +1,62 @@ +@startuml +package com.iluwatar.eda.handler { + class UserUpdatedEventHandler { + + UserUpdatedEventHandler() + + onEvent(event : UserUpdatedEvent) + } + class UserCreatedEventHandler { + + UserCreatedEventHandler() + + onEvent(event : UserCreatedEvent) + } +} +package com.iluwatar.eda.event { + abstract class AbstractEvent { + + AbstractEvent() + + getType() : Class + } + class UserUpdatedEvent { + - user : User + + UserUpdatedEvent(user : User) + + getUser() : User + } + class UserCreatedEvent { + - user : User + + UserCreatedEvent(user : User) + + getUser() : User + } +} +package com.iluwatar.eda.framework { + class EventDispatcher { + - handlers : Map, Handler> + + EventDispatcher() + + dispatch(event : E extends Event) + + registerHandler(eventType : Class, handler : Handler) + } + interface Event { + + getType() : Class {abstract} + } + interface Handler { + + onEvent(E extends Event) {abstract} + } +} +package com.iluwatar.eda.model { + class User { + - username : String + + User(username : String) + + getUsername() : String + } +} +package com.iluwatar.eda { + class App { + + App() + + main(args : String[]) {static} + } +} +UserCreatedEvent --> "-user" User +UserUpdatedEvent --> "-user" User +AbstractEvent ..|> Event +UserUpdatedEventHandler ..|> Handler +UserCreatedEventHandler ..|> Handler +UserUpdatedEvent --|> AbstractEvent +UserCreatedEvent --|> AbstractEvent +@enduml \ No newline at end of file diff --git a/execute-around/etc/execute-around.urm.puml b/execute-around/etc/execute-around.urm.puml new file mode 100644 index 000000000..66d23ce7a --- /dev/null +++ b/execute-around/etc/execute-around.urm.puml @@ -0,0 +1,14 @@ +@startuml +package com.iluwatar.execute.around { + interface FileWriterAction { + + writeFile(FileWriter) {abstract} + } + class SimpleFileWriter { + + SimpleFileWriter(filename : String, action : FileWriterAction) + } + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/facade/etc/facade.urm.puml b/facade/etc/facade.urm.puml new file mode 100644 index 000000000..f72bc2b62 --- /dev/null +++ b/facade/etc/facade.urm.puml @@ -0,0 +1,57 @@ +@startuml +package com.iluwatar.facade { + class DwarvenTunnelDigger { + + DwarvenTunnelDigger() + + name() : String + + work() + } + class DwarvenGoldmineFacade { + - workers : List + + DwarvenGoldmineFacade() + + digOutGold() + + endDay() + - makeActions(workers : Collection, actions : Action[]) {static} + + startNewDay() + } + class DwarvenGoldDigger { + + DwarvenGoldDigger() + + name() : String + + work() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class DwarvenMineWorker { + + DwarvenMineWorker() + - action(action : Action) + + action(actions : Action[]) + + goHome() + + goToMine() + + goToSleep() + + name() : String {abstract} + + wakeUp() + + work() {abstract} + } + class DwarvenCartOperator { + + DwarvenCartOperator() + + name() : String + + work() + } + ~enum Action { + + GO_HOME {static} + + GO_TO_MINE {static} + + GO_TO_SLEEP {static} + + WAKE_UP {static} + + WORK {static} + + valueOf(name : String) : Action {static} + + values() : Action[] {static} + } +} +DwarvenGoldmineFacade --+ DwarvenMineWorker +DwarvenGoldmineFacade --> "-workers" DwarvenMineWorker +Action ..+ DwarvenMineWorker +DwarvenTunnelDigger --|> DwarvenMineWorker +DwarvenGoldDigger --|> DwarvenMineWorker +DwarvenCartOperator --|> DwarvenMineWorker +@enduml \ No newline at end of file diff --git a/factory-kit/etc/factory-kit.urm.puml b/factory-kit/etc/factory-kit.urm.puml new file mode 100644 index 000000000..faf5727eb --- /dev/null +++ b/factory-kit/etc/factory-kit.urm.puml @@ -0,0 +1,45 @@ +@startuml +package com.iluwatar.factorykit { + interface Builder { + + add(WeaponType, Supplier) {abstract} + } + class Spear { + + Spear() + + toString() : String + } + class Bow { + + Bow() + + toString() : String + } + class Sword { + + Sword() + + toString() : String + } + interface Weapon { + } + class App { + + App() + + main(args : String[]) {static} + } + class Axe { + + Axe() + + toString() : String + } + interface WeaponFactory { + + create(WeaponType) : Weapon {abstract} + + factory(consumer : Consumer) : WeaponFactory {static} + } + enum WeaponType { + + AXE {static} + + BOW {static} + + SPEAR {static} + + SWORD {static} + + valueOf(name : String) : WeaponType {static} + + values() : WeaponType[] {static} + } +} +Spear ..|> Weapon +Bow ..|> Weapon +Sword ..|> Weapon +Axe ..|> Weapon +@enduml \ No newline at end of file diff --git a/factory-method/etc/factory-method.urm.puml b/factory-method/etc/factory-method.urm.puml new file mode 100644 index 000000000..ebc9d2ff7 --- /dev/null +++ b/factory-method/etc/factory-method.urm.puml @@ -0,0 +1,53 @@ +@startuml +package com.iluwatar.factory.method { + class OrcBlacksmith { + + OrcBlacksmith() + + manufactureWeapon(weaponType : WeaponType) : Weapon + } + class ElfBlacksmith { + + ElfBlacksmith() + + manufactureWeapon(weaponType : WeaponType) : Weapon + } + class OrcWeapon { + - weaponType : WeaponType + + OrcWeapon(weaponType : WeaponType) + + getWeaponType() : WeaponType + + toString() : String + } + interface Blacksmith { + + manufactureWeapon(WeaponType) : Weapon {abstract} + } + interface Weapon { + + getWeaponType() : WeaponType {abstract} + } + class ElfWeapon { + - weaponType : WeaponType + + ElfWeapon(weaponType : WeaponType) + + getWeaponType() : WeaponType + + toString() : String + } + class App { + - blacksmith : Blacksmith + + App(blacksmith : Blacksmith) + + main(args : String[]) {static} + - manufactureWeapons() + } + enum WeaponType { + + AXE {static} + + SHORT_SWORD {static} + + SPEAR {static} + + UNDEFINED {static} + - title : String + + toString() : String + + valueOf(name : String) : WeaponType {static} + + values() : WeaponType[] {static} + } +} +ElfWeapon --> "-weaponType" WeaponType +OrcWeapon --> "-weaponType" WeaponType +App --> "-blacksmith" Blacksmith +OrcBlacksmith ..|> Blacksmith +ElfBlacksmith ..|> Blacksmith +OrcWeapon ..|> Weapon +ElfWeapon ..|> Weapon +@enduml \ No newline at end of file diff --git a/feature-toggle/etc/feature-toggle.urm.puml b/feature-toggle/etc/feature-toggle.urm.puml new file mode 100644 index 000000000..762d49cb3 --- /dev/null +++ b/feature-toggle/etc/feature-toggle.urm.puml @@ -0,0 +1,47 @@ +@startuml +package com.iluwatar.featuretoggle.pattern { + interface Service { + + getWelcomeMessage(User) : String {abstract} + + isEnhanced() : boolean {abstract} + } +} +package com.iluwatar.featuretoggle.user { + class User { + - name : String + + User(name : String) + + toString() : String + } + class UserGroup { + - freeGroup : List {static} + - paidGroup : List {static} + + UserGroup() + + addUserToFreeGroup(user : User) {static} + + addUserToPaidGroup(user : User) {static} + + isPaid(user : User) : boolean {static} + } +} +package com.iluwatar.featuretoggle.pattern.propertiesversion { + class PropertiesFeatureToggleVersion { + - isEnhanced : boolean + + PropertiesFeatureToggleVersion(properties : Properties) + + getWelcomeMessage(user : User) : String + + isEnhanced() : boolean + } +} +package com.iluwatar.featuretoggle.pattern.tieredversion { + class TieredFeatureToggleVersion { + + TieredFeatureToggleVersion() + + getWelcomeMessage(user : User) : String + + isEnhanced() : boolean + } +} +package com.iluwatar.featuretoggle { + class App { + + App() + + main(args : String[]) {static} + } +} +UserGroup --> "-freeGroup" User +TieredFeatureToggleVersion ..|> Service +PropertiesFeatureToggleVersion ..|> Service +@enduml \ No newline at end of file diff --git a/fluentinterface/etc/fluentinterface.urm.puml b/fluentinterface/etc/fluentinterface.urm.puml new file mode 100644 index 000000000..436fcb2e8 --- /dev/null +++ b/fluentinterface/etc/fluentinterface.urm.puml @@ -0,0 +1,71 @@ +@startuml +package com.iluwatar.fluentinterface.fluentiterable.simple { + class SimpleFluentIterable { + - iterable : Iterable + # SimpleFluentIterable(iterable : Iterable) + + asList() : List + + filter(predicate : Predicate) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + forEach(action : Consumer) + + from(iterable : Iterable) : FluentIterable {static} + + fromCopyOf(iterable : Iterable) : FluentIterable {static} + + getRemainingElementsCount() : int + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + + spliterator() : Spliterator + + toList(iterator : Iterator) : List {static} + } +} +package com.iluwatar.fluentinterface.app { + class App { + + App() + + main(args : String[]) {static} + - negatives() : Predicate {static} + - positives() : Predicate {static} + - prettyPrint(delimiter : String, prefix : String, iterable : Iterable) {static} + - prettyPrint(prefix : String, iterable : Iterable) {static} + - transformToString() : Function {static} + } +} +package com.iluwatar.fluentinterface.fluentiterable.lazy { + class LazyFluentIterable { + - iterable : Iterable + # LazyFluentIterable() + # LazyFluentIterable(iterable : Iterable) + + asList() : List + + filter(predicate : Predicate) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + from(iterable : Iterable) : FluentIterable {static} + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + } + abstract class DecoratingIterator { + # fromIterator : Iterator + - next : E + + DecoratingIterator(fromIterator : Iterator) + + computeNext() : E {abstract} + + hasNext() : boolean + + next() : E + } +} +package com.iluwatar.fluentinterface.fluentiterable { + interface FluentIterable { + + asList() : List {abstract} + + copyToList(iterable : Iterable) : List {static} + + filter(Predicate) : FluentIterable {abstract} + + first() : Optional {abstract} + + first(int) : FluentIterable {abstract} + + last() : Optional {abstract} + + last(int) : FluentIterable {abstract} + + map(Function) : FluentIterable {abstract} + } +} +LazyFluentIterable ..|> FluentIterable +SimpleFluentIterable ..|> FluentIterable +@enduml \ No newline at end of file diff --git a/flux/etc/flux.urm.puml b/flux/etc/flux.urm.puml new file mode 100644 index 000000000..e4bece2fc --- /dev/null +++ b/flux/etc/flux.urm.puml @@ -0,0 +1,115 @@ +@startuml +package com.iluwatar.flux.view { + class ContentView { + - content : Content + + ContentView() + + render() + + storeChanged(store : Store) + } + class MenuView { + - selected : MenuItem + + MenuView() + + itemClicked(item : MenuItem) + + render() + + storeChanged(store : Store) + } + interface View { + + render() {abstract} + + storeChanged(Store) {abstract} + } +} +package com.iluwatar.flux.action { + class ContentAction { + - content : Content + + ContentAction(content : Content) + + getContent() : Content + } + class MenuAction { + - menuItem : MenuItem + + MenuAction(menuItem : MenuItem) + + getMenuItem() : MenuItem + } + abstract class Action { + - type : ActionType + + Action(type : ActionType) + + getType() : ActionType + } + enum MenuItem { + + COMPANY {static} + + HOME {static} + + PRODUCTS {static} + - title : String + + toString() : String + + valueOf(name : String) : MenuItem {static} + + values() : MenuItem[] {static} + } + enum Content { + + COMPANY {static} + + PRODUCTS {static} + - title : String + + toString() : String + + valueOf(name : String) : Content {static} + + values() : Content[] {static} + } + enum ActionType { + + CONTENT_CHANGED {static} + + MENU_ITEM_SELECTED {static} + + valueOf(name : String) : ActionType {static} + + values() : ActionType[] {static} + } +} +package com.iluwatar.flux.app { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.flux.store { + abstract class Store { + - views : List + + Store() + # notifyChange() + + onAction(Action) {abstract} + + registerView(view : View) + } + class ContentStore { + - content : Content + + ContentStore() + + getContent() : Content + + onAction(action : Action) + } + class MenuStore { + - selected : MenuItem + + MenuStore() + + getSelected() : MenuItem + + onAction(action : Action) + } +} +package com.iluwatar.flux.dispatcher { + class Dispatcher { + - instance : Dispatcher {static} + - stores : List + - Dispatcher() + - dispatchAction(action : Action) + + getInstance() : Dispatcher {static} + + menuItemSelected(menuItem : MenuItem) + + registerStore(store : Store) + } +} +MenuAction --> "-menuItem" MenuItem +Action --> "-type" ActionType +MenuStore --> "-selected" MenuItem +Dispatcher --> "-instance" Dispatcher +ContentView --> "-content" Content +Dispatcher --> "-stores" Store +MenuView --> "-selected" MenuItem +Store --> "-views" View +ContentStore --> "-content" Content +ContentAction --> "-content" Content +ContentAction --|> Action +ContentStore --|> Store +ContentView ..|> View +MenuAction --|> Action +MenuView ..|> View +MenuStore --|> Store +@enduml \ No newline at end of file diff --git a/flyweight/etc/flyweight.urm.puml b/flyweight/etc/flyweight.urm.puml new file mode 100644 index 000000000..98a2b4721 --- /dev/null +++ b/flyweight/etc/flyweight.urm.puml @@ -0,0 +1,60 @@ +@startuml +package com.iluwatar.flyweight { + class PoisonPotion { + + PoisonPotion() + + drink() + } + class StrengthPotion { + + StrengthPotion() + + drink() + } + class HealingPotion { + + HealingPotion() + + drink() + } + class PotionFactory { + - potions : Map + + PotionFactory() + ~ createPotion(type : PotionType) : Potion + } + interface Potion { + + drink() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class AlchemistShop { + - bottomShelf : List + - topShelf : List + + AlchemistShop() + + enumerate() + - fillShelves() + + getBottomShelf() : List + + getTopShelf() : List + } + class HolyWaterPotion { + + HolyWaterPotion() + + drink() + } + class InvisibilityPotion { + + InvisibilityPotion() + + drink() + } + enum PotionType { + + HEALING {static} + + HOLY_WATER {static} + + INVISIBILITY {static} + + POISON {static} + + STRENGTH {static} + + valueOf(name : String) : PotionType {static} + + values() : PotionType[] {static} + } +} +AlchemistShop --> "-topShelf" Potion +PoisonPotion ..|> Potion +StrengthPotion ..|> Potion +HealingPotion ..|> Potion +HolyWaterPotion ..|> Potion +InvisibilityPotion ..|> Potion +@enduml \ No newline at end of file diff --git a/front-controller/etc/front-controller.urm.puml b/front-controller/etc/front-controller.urm.puml new file mode 100644 index 000000000..17ccebae1 --- /dev/null +++ b/front-controller/etc/front-controller.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.front.controller { + class App { + + App() + + main(args : String[]) {static} + } + class FrontController { + + FrontController() + - getCommand(request : String) : Command + - getCommandClass(request : String) : Class {static} + + handleRequest(request : String) + } + class ArcherView { + + ArcherView() + + display() + } + interface View { + + display() {abstract} + } + interface Command { + + process() {abstract} + } + class ErrorView { + + ErrorView() + + display() + } + class ArcherCommand { + + ArcherCommand() + + process() + } + class CatapultView { + + CatapultView() + + display() + } + class CatapultCommand { + + CatapultCommand() + + process() + } + class UnknownCommand { + + UnknownCommand() + + process() + } +} +ArcherView ..|> View +ErrorView ..|> View +ArcherCommand ..|> Command +CatapultView ..|> View +CatapultCommand ..|> Command +UnknownCommand ..|> Command +@enduml \ No newline at end of file diff --git a/half-sync-half-async/etc/half-sync-half-async.urm.puml b/half-sync-half-async/etc/half-sync-half-async.urm.puml new file mode 100644 index 000000000..e733dd586 --- /dev/null +++ b/half-sync-half-async/etc/half-sync-half-async.urm.puml @@ -0,0 +1,30 @@ +@startuml +package com.iluwatar.halfsynchalfasync { + class App { + + App() + - ap(i : long) : long {static} + + main(args : String[]) {static} + } + interface AsyncTask { + + call() : O {abstract} + + onError(Throwable) {abstract} + + onPostCall(O) {abstract} + + onPreCall() {abstract} + } + ~class ArithmeticSumTask { + - n : long + + ArithmeticSumTask(n : long) + + call() : Long + + onError(throwable : Throwable) + + onPostCall(result : Long) + + onPreCall() + } + class AsynchronousService { + - service : ExecutorService + + AsynchronousService(workQueue : BlockingQueue) + + execute(task : AsyncTask) + } +} +ArithmeticSumTask ..+ App +ArithmeticSumTask ..|> AsyncTask +@enduml \ No newline at end of file diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml new file mode 100644 index 000000000..4102c5863 --- /dev/null +++ b/hexagonal/etc/hexagonal.urm.puml @@ -0,0 +1,183 @@ +@startuml +package com.iluwatar.hexagonal.service { + class LotteryServiceImpl { + - bank : WireTransfers + - notifications : LotteryNotifications + - repository : LotteryTicketRepository + + LotteryServiceImpl() + + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult + + submitTicket(ticket : LotteryTicket) : Optional + } + interface LotteryService { + + checkTicketForPrize(LotteryTicketId, LotteryNumbers) : LotteryTicketCheckResult {abstract} + + submitTicket(LotteryTicket) : Optional {abstract} + } +} +package com.iluwatar.hexagonal.domain { + class LotteryTicketId { + - id : UUID + + LotteryTicketId() + + getId() : UUID + } + class LotteryConstants { + + PLAYER_MAX_SALDO : int {static} + + PRIZE_AMOUNT : int {static} + + SERVICE_BANK_ACCOUNT : String {static} + + SERVICE_BANK_ACCOUNT_SALDO : int {static} + + TICKET_PRIZE : int {static} + + LotteryConstants() + } + class LotteryNumbers { + + MAX_NUMBER : int {static} + + MIN_NUMBER : int {static} + + NUM_NUMBERS : int {static} + - numbers : Set + - LotteryNumbers() + - LotteryNumbers(givenNumbers : Set) + + create(givenNumbers : Set) : LotteryNumbers {static} + + createRandom() : LotteryNumbers {static} + + equals(obj : Object) : boolean + - generateRandomNumbers() + + getNumbers() : Set + + hashCode() : int + } + class PlayerDetails { + - bankAccountNumber : String + - emailAddress : String + - phoneNumber : String + - PlayerDetails(email : String, bankAccount : String, phone : String) + + create(email : String, bankAccount : String, phone : String) : PlayerDetails {static} + + equals(obj : Object) : boolean + + getBankAccount() : String + + getEmail() : String + + getPhoneNumber() : String + + hashCode() : int + } + class LotteryTicketCheckResult { + - checkResult : CheckResult + - prizeAmount : int + + LotteryTicketCheckResult(result : CheckResult) + + LotteryTicketCheckResult(result : CheckResult, amount : int) + + equals(obj : Object) : boolean + + getPrizeAmount() : int + + getResult() : CheckResult + + hashCode() : int + } + class LotteryTicket { + - lotteryNumbers : LotteryNumbers + - playerDetails : PlayerDetails + - LotteryTicket(details : PlayerDetails, numbers : LotteryNumbers) + + create(details : PlayerDetails, numbers : LotteryNumbers) : LotteryTicket {static} + + equals(obj : Object) : boolean + + getNumbers() : LotteryNumbers + + getPlayerDetails() : PlayerDetails + + hashCode() : int + } + -class RandomNumberGenerator { + - randomIterator : OfInt + + RandomNumberGenerator(min : int, max : int) + + nextInt() : int + } + enum CheckResult { + + NO_PRIZE {static} + + TICKET_NOT_SUBMITTED {static} + + WIN_PRIZE {static} + + valueOf(name : String) : CheckResult {static} + + values() : CheckResult[] {static} + } +} +package com.iluwatar.hexagonal.banking { + class WireTransfersImpl { + - accounts : Map {static} + + WireTransfersImpl() + + getFunds(bankAccount : String) : int + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean + } + interface WireTransfers { + + getFunds(String) : int {abstract} + + setFunds(String, int) {abstract} + + transferFunds(int, String, String) : boolean {abstract} + } +} +package com.iluwatar.hexagonal.database { + class LotteryTicketInMemoryRepository { + - tickets : Map {static} + + LotteryTicketInMemoryRepository() + + deleteAll() + + findAll() : Map + + findById(id : LotteryTicketId) : Optional + + save(ticket : LotteryTicket) : Optional + } + interface LotteryTicketRepository { + + deleteAll() {abstract} + + findAll() : Map {abstract} + + findById(LotteryTicketId) : Optional {abstract} + + save(LotteryTicket) : Optional {abstract} + } +} +package com.iluwatar.hexagonal.notifications { + interface LotteryNotifications { + + notifyNoWin(PlayerDetails) {abstract} + + notifyPrize(PlayerDetails, int) {abstract} + + notifyPrizeError(PlayerDetails, int) {abstract} + + notifyTicketSubmitError(PlayerDetails) {abstract} + + notifyTicketSubmitted(PlayerDetails) {abstract} + } + class LotteryNotificationsImpl { + + LotteryNotificationsImpl() + + notifyNoWin(details : PlayerDetails) + + notifyPrize(details : PlayerDetails, prizeAmount : int) + + notifyPrizeError(details : PlayerDetails, prizeAmount : int) + + notifyTicketSubmitError(details : PlayerDetails) + + notifyTicketSubmitted(details : PlayerDetails) + } +} +package com.iluwatar.hexagonal { + class App { + - allPlayerDetails : List {static} + + App() + - getRandomPlayerDetails() : PlayerDetails {static} + + main(args : String[]) {static} + - submitTickets(lotteryService : LotteryService, numTickets : int) {static} + } +} +package com.iluwatar.hexagonal.administration { + interface LotteryAdministration { + + getAllSubmittedTickets() : Map {abstract} + + performLottery() : LotteryNumbers {abstract} + + resetLottery() {abstract} + } + class LotteryAdministrationImpl { + - bank : WireTransfers + - notifications : LotteryNotifications + - repository : LotteryTicketRepository + - service : LotteryService + + LotteryAdministrationImpl() + + getAllSubmittedTickets() : Map + + performLottery() : LotteryNumbers + + resetLottery() + } +} +LotteryTicket --> "-playerDetails" PlayerDetails +LotteryAdministrationImpl --> "-bank" WireTransfers +App --> "-allPlayerDetails" PlayerDetails +RandomNumberGenerator ..+ PrimitiveIterator +LotteryAdministrationImpl --> "-repository" LotteryTicketRepository +LotteryAdministrationImpl --+ LotteryTicketCheckResult +LotteryServiceImpl --> "-notifications" LotteryNotifications +LotteryTicket --> "-lotteryNumbers" LotteryNumbers +LotteryAdministrationImpl --> "-notifications" LotteryNotifications +LotteryServiceImpl --> "-repository" LotteryTicketRepository +LotteryServiceImpl --+ LotteryTicketCheckResult +LotteryServiceImpl --> "-bank" WireTransfers +RandomNumberGenerator ..+ LotteryNumbers +LotteryAdministrationImpl --> "-service" LotteryService +LotteryTicketCheckResult --> "-checkResult" CheckResult +CheckResult ..+ LotteryTicketCheckResult +LotteryTicketInMemoryRepository ..|> LotteryTicketRepository +WireTransfersImpl ..|> WireTransfers +LotteryServiceImpl ..|> LotteryService +LotteryNotificationsImpl ..|> LotteryNotifications +LotteryAdministrationImpl ..|> LotteryAdministration +@enduml \ No newline at end of file diff --git a/intercepting-filter/etc/intercepting-filter.urm.puml b/intercepting-filter/etc/intercepting-filter.urm.puml new file mode 100644 index 000000000..f5bfb54e4 --- /dev/null +++ b/intercepting-filter/etc/intercepting-filter.urm.puml @@ -0,0 +1,88 @@ +@startuml +package com.iluwatar.intercepting.filter { + interface Filter { + + execute(Order) : String {abstract} + + getLast() : Filter {abstract} + + getNext() : Filter {abstract} + + setNext(Filter) {abstract} + } + abstract class AbstractFilter { + - next : Filter + + AbstractFilter() + + AbstractFilter(next : Filter) + + execute(order : Order) : String + + getLast() : Filter + + getNext() : Filter + + setNext(filter : Filter) + } + class ContactFilter { + + ContactFilter() + + execute(order : Order) : String + } + class OrderFilter { + + OrderFilter() + + execute(order : Order) : String + } + class Order { + - address : String + - contactNumber : String + - depositNumber : String + - name : String + - order : String + + Order() + + Order(name : String, contactNumber : String, address : String, depositNumber : String, order : String) + + getAddress() : String + + getContactNumber() : String + + getDepositNumber() : String + + getName() : String + + getOrder() : String + + setAddress(address : String) + + setContactNumber(contactNumber : String) + + setDepositNumber(depositNumber : String) + + setName(name : String) + + setOrder(order : String) + } + class AddressFilter { + + AddressFilter() + + execute(order : Order) : String + } + ~class DListener { + ~ DListener(this$0 : Target) + + actionPerformed(e : ActionEvent) + } + class FilterManager { + - filterChain : FilterChain + + FilterManager() + + addFilter(filter : Filter) + + filterRequest(order : Order) : String + } + class FilterChain { + - chain : Filter + + FilterChain() + + addFilter(filter : Filter) + + execute(order : Order) : String + } + class DepositFilter { + + DepositFilter() + + execute(order : Order) : String + } + class App { + + App() + + main(args : String[]) {static} + } + class NameFilter { + + NameFilter() + + execute(order : Order) : String + } +} +AbstractFilter --> "-next" Filter +DListener --+ Target +FilterChain --> "-chain" Filter +FilterManager --> "-filterChain" FilterChain +AbstractFilter ..|> Filter +ContactFilter --|> AbstractFilter +OrderFilter --|> AbstractFilter +AddressFilter --|> AbstractFilter +DepositFilter --|> AbstractFilter +NameFilter --|> AbstractFilter +@enduml \ No newline at end of file diff --git a/interpreter/etc/interpreter.urm.puml b/interpreter/etc/interpreter.urm.puml new file mode 100644 index 000000000..bdbd369d6 --- /dev/null +++ b/interpreter/etc/interpreter.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.interpreter { + abstract class Expression { + + Expression() + + interpret() : int {abstract} + + toString() : String {abstract} + } + class PlusExpression { + - leftExpression : Expression + - rightExpression : Expression + + PlusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class App { + + App() + + getOperatorInstance(s : String, left : Expression, right : Expression) : Expression {static} + + isOperator(s : String) : boolean {static} + + main(args : String[]) {static} + } + class NumberExpression { + - number : int + + NumberExpression(number : int) + + NumberExpression(s : String) + + interpret() : int + + toString() : String + } + class MultiplyExpression { + - leftExpression : Expression + - rightExpression : Expression + + MultiplyExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class MinusExpression { + - leftExpression : Expression + - rightExpression : Expression + + MinusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } +} +MultiplyExpression --> "-leftExpression" Expression +MinusExpression --> "-leftExpression" Expression +PlusExpression --> "-leftExpression" Expression +PlusExpression --|> Expression +NumberExpression --|> Expression +MultiplyExpression --|> Expression +MinusExpression --|> Expression +@enduml \ No newline at end of file diff --git a/iterator/etc/iterator.urm.puml b/iterator/etc/iterator.urm.puml new file mode 100644 index 000000000..cbafd6595 --- /dev/null +++ b/iterator/etc/iterator.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.iterator { + interface ItemIterator { + + hasNext() : boolean {abstract} + + next() : Item {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class TreasureChestItemIterator { + - chest : TreasureChest + - idx : int + - type : ItemType + + TreasureChestItemIterator(chest : TreasureChest, type : ItemType) + - findNextIdx() : int + + hasNext() : boolean + + next() : Item + } + class TreasureChest { + - items : List + + TreasureChest() + + getItems() : List + ~ iterator(itemType : ItemType) : ItemIterator + } + class Item { + - name : String + - type : ItemType + + Item(type : ItemType, name : String) + + getType() : ItemType + + setType(type : ItemType) + + toString() : String + } + enum ItemType { + + ANY {static} + + POTION {static} + + RING {static} + + WEAPON {static} + + valueOf(name : String) : ItemType {static} + + values() : ItemType[] {static} + } +} +Item --> "-type" ItemType +TreasureChest --> "-items" Item +TreasureChestItemIterator --> "-type" ItemType +TreasureChestItemIterator --> "-chest" TreasureChest +TreasureChestItemIterator ..|> ItemIterator +@enduml \ No newline at end of file diff --git a/layers/etc/layers.urm.puml b/layers/etc/layers.urm.puml new file mode 100644 index 000000000..d67216ff8 --- /dev/null +++ b/layers/etc/layers.urm.puml @@ -0,0 +1,125 @@ +@startuml +package com.iluwatar.layers { + interface View { + + render() {abstract} + } + class CakeBakingServiceImpl { + - context : AbstractApplicationContext + + CakeBakingServiceImpl() + + bakeNewCake(cakeInfo : CakeInfo) + + getAllCakes() : List + - getAvailableLayerEntities() : List + + getAvailableLayers() : List + - getAvailableToppingEntities() : List + + getAvailableToppings() : List + + saveNewLayer(layerInfo : CakeLayerInfo) + + saveNewTopping(toppingInfo : CakeToppingInfo) + } + interface CakeDao { + } + class CakeTopping { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeTopping() + + CakeTopping(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } + class CakeLayerInfo { + + calories : int + + id : Optional + + name : String + + CakeLayerInfo(id : Long, name : String, calories : int) + + CakeLayerInfo(name : String, calories : int) + + toString() : String + } + interface CakeLayerDao { + } + interface CakeToppingDao { + } + interface CakeBakingService { + + bakeNewCake(CakeInfo) {abstract} + + getAllCakes() : List {abstract} + + getAvailableLayers() : List {abstract} + + getAvailableToppings() : List {abstract} + + saveNewLayer(CakeLayerInfo) {abstract} + + saveNewTopping(CakeToppingInfo) {abstract} + } + class CakeViewImpl { + - cakeBakingService : CakeBakingService + + CakeViewImpl(cakeBakingService : CakeBakingService) + + render() + } + class CakeToppingInfo { + + calories : int + + id : Optional + + name : String + + CakeToppingInfo(id : Long, name : String, calories : int) + + CakeToppingInfo(name : String, calories : int) + + toString() : String + } + class CakeInfo { + + cakeLayerInfos : List + + cakeToppingInfo : CakeToppingInfo + + id : Optional + + CakeInfo(cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + CakeInfo(id : Long, cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + calculateTotalCalories() : int + + toString() : String + } + class App { + - cakeBakingService : CakeBakingService {static} + + App() + - initializeData(cakeBakingService : CakeBakingService) {static} + + main(args : String[]) {static} + } + class Cake { + - id : Long + - layers : Set + - topping : CakeTopping + + Cake() + + addLayer(layer : CakeLayer) + + getId() : Long + + getLayers() : Set + + getTopping() : CakeTopping + + setId(id : Long) + + setLayers(layers : Set) + + setTopping(topping : CakeTopping) + + toString() : String + } + class CakeLayer { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeLayer() + + CakeLayer(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } +} +CakeViewImpl --> "-cakeBakingService" CakeBakingService +CakeInfo --> "-cakeToppingInfo" CakeToppingInfo +CakeInfo --> "-cakeLayerInfos" CakeLayerInfo +App --> "-cakeBakingService" CakeBakingService +CakeLayer --> "-cake" Cake +Cake --> "-topping" CakeTopping +CakeBakingServiceImpl ..|> CakeBakingService +CakeViewImpl ..|> View +@enduml \ No newline at end of file diff --git a/lazy-loading/etc/lazy-loading.urm.puml b/lazy-loading/etc/lazy-loading.urm.puml new file mode 100644 index 000000000..96c427553 --- /dev/null +++ b/lazy-loading/etc/lazy-loading.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.lazy.loading { + ~class HeavyFactory { + - heavyInstance : Heavy + ~ HeavyFactory(this$0 : Java8Holder) + + get() : Heavy + } + class HolderNaive { + - heavy : Heavy + + HolderNaive() + + getHeavy() : Heavy + } + class Heavy { + + Heavy() + } + class HolderThreadSafe { + - heavy : Heavy + + HolderThreadSafe() + + getHeavy() : Heavy + } + class Java8Holder { + - heavy : Supplier + + Java8Holder() + - createAndCacheHeavy() : Heavy + + getHeavy() : Heavy + } + class App { + + App() + + main(args : String[]) {static} + } +} +HolderThreadSafe --> "-heavy" Heavy +HolderNaive --> "-heavy" Heavy +HeavyFactory --> "-heavyInstance" Heavy +@enduml \ No newline at end of file diff --git a/mediator/etc/mediator.urm.puml b/mediator/etc/mediator.urm.puml new file mode 100644 index 000000000..0b3baab5a --- /dev/null +++ b/mediator/etc/mediator.urm.puml @@ -0,0 +1,68 @@ +@startuml +package com.iluwatar.mediator { + class App { + + App() + + main(args : String[]) {static} + } + class Hobbit { + + Hobbit() + + toString() : String + } + interface PartyMember { + + act(Action) {abstract} + + joinedParty(Party) {abstract} + + partyAction(Action) {abstract} + } + interface Party { + + act(PartyMember, Action) {abstract} + + addMember(PartyMember) {abstract} + } + class Wizard { + + Wizard() + + toString() : String + } + class PartyImpl { + - members : List + + PartyImpl() + + act(actor : PartyMember, action : Action) + + addMember(member : PartyMember) + } + class Hunter { + + Hunter() + + toString() : String + } + class Rogue { + + Rogue() + + toString() : String + } + abstract class PartyMemberBase { + # party : Party + + PartyMemberBase() + + act(action : Action) + + joinedParty(party : Party) + + partyAction(action : Action) + + toString() : String {abstract} + } + enum Action { + + ENEMY {static} + + GOLD {static} + + HUNT {static} + + NONE {static} + + TALE {static} + - description : String + - title : String + + getDescription() : String + + toString() : String + + valueOf(name : String) : Action {static} + + values() : Action[] {static} + } +} +PartyImpl --> "-members" PartyMember +PartyMemberBase --> "-party" Party +Hobbit --|> PartyMemberBase +Wizard --|> PartyMemberBase +PartyImpl ..|> Party +Hunter --|> PartyMemberBase +Rogue --|> PartyMemberBase +PartyMemberBase ..|> PartyMember +@enduml \ No newline at end of file diff --git a/memento/etc/memento.urm.puml b/memento/etc/memento.urm.puml new file mode 100644 index 000000000..aa63b4ebe --- /dev/null +++ b/memento/etc/memento.urm.puml @@ -0,0 +1,48 @@ +@startuml +package com.iluwatar.memento { + class Star { + - ageYears : int + - massTons : int + - type : StarType + + Star(startType : StarType, startAge : int, startMass : int) + ~ getMemento() : StarMemento + ~ setMemento(memento : StarMemento) + + timePasses() + + toString() : String + } + interface StarMemento { + } + -class StarMementoInternal { + - ageYears : int + - massTons : int + - type : StarType + - StarMementoInternal() + + getAgeYears() : int + + getMassTons() : int + + getType() : StarType + + setAgeYears(ageYears : int) + + setMassTons(massTons : int) + + setType(type : StarType) + } + class App { + + App() + + main(args : String[]) {static} + } + enum StarType { + + DEAD {static} + + RED_GIANT {static} + + SUN {static} + + SUPERNOVA {static} + + UNDEFINED {static} + + WHITE_DWARF {static} + - title : String + + toString() : String + + valueOf(name : String) : StarType {static} + + values() : StarType[] {static} + } +} +StarMementoInternal --> "-type" StarType +Star --> "-type" StarType +StarMementoInternal ..+ Star +StarMementoInternal ..|> StarMemento +@enduml \ No newline at end of file diff --git a/message-channel/etc/message-channel.urm.puml b/message-channel/etc/message-channel.urm.puml new file mode 100644 index 000000000..9e2b24032 --- /dev/null +++ b/message-channel/etc/message-channel.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.message.channel { + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/model-view-controller/etc/model-view-controller.urm.puml b/model-view-controller/etc/model-view-controller.urm.puml new file mode 100644 index 000000000..f8137bdae --- /dev/null +++ b/model-view-controller/etc/model-view-controller.urm.puml @@ -0,0 +1,69 @@ +@startuml +package com.iluwatar.model.view.controller { + class GiantModel { + - fatigue : Fatigue + - health : Health + - nourishment : Nourishment + ~ GiantModel(health : Health, fatigue : Fatigue, nourishment : Nourishment) + + getFatigue() : Fatigue + + getHealth() : Health + + getNourishment() : Nourishment + + setFatigue(fatigue : Fatigue) + + setHealth(health : Health) + + setNourishment(nourishment : Nourishment) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class GiantView { + + GiantView() + + displayGiant(giant : GiantModel) + } + class GiantController { + - giant : GiantModel + - view : GiantView + + GiantController(giant : GiantModel, view : GiantView) + + getFatigue() : Fatigue + + getHealth() : Health + + getNourishment() : Nourishment + + setFatigue(fatigue : Fatigue) + + setHealth(health : Health) + + setNourishment(nourishment : Nourishment) + + updateView() + } + enum Nourishment { + + HUNGRY {static} + + SATURATED {static} + + STARVING {static} + - title : String + + toString() : String + + valueOf(name : String) : Nourishment {static} + + values() : Nourishment[] {static} + } + enum Fatigue { + + ALERT {static} + + SLEEPING {static} + + TIRED {static} + - title : String + + toString() : String + + valueOf(name : String) : Fatigue {static} + + values() : Fatigue[] {static} + } + enum Health { + + DEAD {static} + + HEALTHY {static} + + WOUNDED {static} + - title : String + + toString() : String + + valueOf(name : String) : Health {static} + + values() : Health[] {static} + } +} +GiantModel --> "-nourishment" Nourishment +GiantController --> "-giant" GiantModel +GiantModel --> "-fatigue" Fatigue +GiantModel --> "-health" Health +GiantController --> "-view" GiantView +@enduml \ No newline at end of file diff --git a/model-view-presenter/etc/model-view-presenter.urm.puml b/model-view-presenter/etc/model-view-presenter.urm.puml new file mode 100644 index 000000000..64bcfba32 --- /dev/null +++ b/model-view-presenter/etc/model-view-presenter.urm.puml @@ -0,0 +1,87 @@ +@startuml +package com.iluwatar.model.view.presenter { + class FileLoader { + - fileName : String + - loaded : boolean + + FileLoader() + + fileExists() : boolean + + getFileName() : String + + isLoaded() : boolean + + loadData() : String + + setFileName(fileName : String) + } + class FileSelectorJFrame { + - area : JTextArea + - cancel : JButton + - contents : JLabel + - fileName : String + - info : JLabel + - input : JTextField + - ok : JButton + - panel : JPanel + - presenter : FileSelectorPresenter + - serialVersionUID : long {static} + + FileSelectorJFrame() + + actionPerformed(e : ActionEvent) + + close() + + displayData(data : String) + + getFileName() : String + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } + class App { + + App() + + main(args : String[]) {static} + } + interface FileSelectorView { + + close() {abstract} + + displayData(String) {abstract} + + getFileName() : String {abstract} + + getPresenter() : FileSelectorPresenter {abstract} + + isOpened() : boolean {abstract} + + open() {abstract} + + setFileName(String) {abstract} + + setPresenter(FileSelectorPresenter) {abstract} + + showMessage(String) {abstract} + } + class FileSelectorStub { + - dataDisplayed : boolean + - name : String + - numOfMessageSent : int + - opened : boolean + - presenter : FileSelectorPresenter + + FileSelectorStub() + + close() + + dataDisplayed() : boolean + + displayData(data : String) + + getFileName() : String + + getMessagesSent() : int + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } + class FileSelectorPresenter { + - loader : FileLoader + - view : FileSelectorView + + FileSelectorPresenter(view : FileSelectorView) + + cancelled() + + confirmed() + + fileNameChanged() + + setLoader(loader : FileLoader) + + start() + } +} +FileSelectorStub --> "-presenter" FileSelectorPresenter +FileSelectorJFrame --> "-presenter" FileSelectorPresenter +FileSelectorPresenter --> "-loader" FileLoader +FileSelectorPresenter --> "-view" FileSelectorView +FileSelectorJFrame ..|> FileSelectorView +FileSelectorStub ..|> FileSelectorView +@enduml \ No newline at end of file diff --git a/monad/etc/monad.urm.puml b/monad/etc/monad.urm.puml new file mode 100644 index 000000000..6fe037064 --- /dev/null +++ b/monad/etc/monad.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.monad { + class Validator { + - exceptions : List + - t : T + - Validator(t : T) + + get() : T + + of(t : T) : Validator {static} + + validate(projection : Function, validation : Predicate, message : String) : Validator + + validate(validation : Predicate, message : String) : Validator + } + class App { + + App() + + main(args : String[]) {static} + } + class User { + - age : int + - email : String + - name : String + - sex : Sex + + User(name : String, age : int, sex : Sex, email : String) + + getAge() : int + + getEmail() : String + + getName() : String + + getSex() : Sex + } + enum Sex { + + FEMALE {static} + + MALE {static} + + valueOf(name : String) : Sex {static} + + values() : Sex[] {static} + } +} +User --> "-sex" Sex +@enduml \ No newline at end of file diff --git a/monostate/etc/monostate.urm.puml b/monostate/etc/monostate.urm.puml new file mode 100644 index 000000000..3c09bb4ed --- /dev/null +++ b/monostate/etc/monostate.urm.puml @@ -0,0 +1,32 @@ +@startuml +package com.iluwatar.monostate { + class LoadBalancer { + - id : int {static} + - lastServedId : int {static} + - servers : List {static} + + LoadBalancer() + + addServer(server : Server) + + getLastServedId() : int {static} + + getNoOfServers() : int + + serverRequest(request : Request) + } + class App { + + App() + + main(args : String[]) {static} + } + class Request { + + value : String + + Request(value : String) + } + class Server { + + host : String + + id : int + + port : int + + Server(host : String, port : int, id : int) + + getHost() : String + + getPort() : int + + serve(request : Request) + } +} +LoadBalancer --> "-servers" Server +@enduml \ No newline at end of file diff --git a/multiton/etc/multiton.urm.puml b/multiton/etc/multiton.urm.puml new file mode 100644 index 000000000..c582a6379 --- /dev/null +++ b/multiton/etc/multiton.urm.puml @@ -0,0 +1,29 @@ +@startuml +package com.iluwatar.multiton { + class App { + + App() + + main(args : String[]) {static} + } + class Nazgul { + - name : NazgulName + - nazguls : Map {static} + - Nazgul(name : NazgulName) + + getInstance(name : NazgulName) : Nazgul {static} + + getName() : NazgulName + } + enum NazgulName { + + ADUNAPHEL {static} + + AKHORAHIL {static} + + DWAR {static} + + HOARMURATH {static} + + JI_INDUR {static} + + KHAMUL {static} + + MURAZOR {static} + + REN {static} + + UVATHA {static} + + valueOf(name : String) : NazgulName {static} + + values() : NazgulName[] {static} + } +} +Nazgul --> "-name" NazgulName +@enduml \ No newline at end of file diff --git a/mute-idiom/etc/mute-idiom.urm.puml b/mute-idiom/etc/mute-idiom.urm.puml new file mode 100644 index 000000000..d4efc2db1 --- /dev/null +++ b/mute-idiom/etc/mute-idiom.urm.puml @@ -0,0 +1,23 @@ +@startuml +package com.iluwatar.mute { + interface Resource { + } + class App { + + App() + - acquireResource() : Resource {static} + - closeResource(resource : Resource) {static} + + main(args : String[]) {static} + - useOfLoggedMute() {static} + - useOfMute() {static} + - utilizeResource(resource : Resource) {static} + } + class Mute { + - Mute() + + loggedMute(runnable : CheckedRunnable) {static} + + mute(runnable : CheckedRunnable) {static} + } + interface CheckedRunnable { + + run() {abstract} + } +} +@enduml \ No newline at end of file diff --git a/mutex/etc/mutex.urm.puml b/mutex/etc/mutex.urm.puml new file mode 100644 index 000000000..24ea83630 --- /dev/null +++ b/mutex/etc/mutex.urm.puml @@ -0,0 +1,27 @@ +@startuml +package com.iluwatar.mutex { + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class Mutex { + - owner : Object + + Mutex() + + acquire() + + getOwner() : Object + + release() + } + class Jar { + - beans : int + - lock : Lock + + Jar(beans : int, lock : Lock) + + takeBean() : boolean + } + class App { + + App() + + main(args : String[]) {static} + } +} +Jar --> "-lock" Lock +Mutex ..|> Lock +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects-dom.urm.puml b/naked-objects/etc/naked-objects-dom.urm.puml new file mode 100644 index 000000000..ea32b5787 --- /dev/null +++ b/naked-objects/etc/naked-objects-dom.urm.puml @@ -0,0 +1,84 @@ +@startuml +package domainapp.dom.app.homepage { + class HomePageViewModel { + ~ simpleObjects : SimpleObjects + + HomePageViewModel() + + getObjects() : List + + title() : String + } + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } +} +package domainapp.dom.modules.simple { + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } + class SimpleObject { + - container : DomainObjectContainer + - dnFieldFlags : byte[] {static} + - dnFieldNames : String[] {static} + - dnFieldTypes : Class[] {static} + # dnFlags : byte + - dnInheritedFieldCount : int {static} + - dnPersistableSuperclass : Class {static} + # dnStateManager : StateManager + - name : String + + SimpleObject() + + ___dn$loadClass(className : String) : Class {static} + - __dnFieldFlagsInit() : byte[] {static} + - __dnFieldNamesInit() : String[] {static} + - __dnFieldTypesInit() : Class[] {static} + # __dnGetInheritedFieldCount() : int {static} + - __dnPersistableSuperclassInit() : Class {static} + + compareTo(other : SimpleObject) : int + + default0UpdateName() : String + # dnCopyField(obj : SimpleObject, index : int) + + dnCopyFields(obj : Object, indices : int[]) + + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) + # dnCopyKeyFieldsFromObjectId(oid : Object) + + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) + + dnCopyKeyFieldsToObjectId(oid : Object) + + dnGetExecutionContext() : ExecutionContextReference + # dnGetManagedFieldCount() : int {static} + + dnGetObjectId() : Object + + dnGetTransactionalObjectId() : Object + + dnGetVersion() : Object + + dnGetname() : String + + dnIsDeleted() : boolean + + dnIsDetached() : boolean + + dnIsDirty() : boolean + + dnIsNew() : boolean + + dnIsPersistent() : boolean + + dnIsTransactional() : boolean + + dnMakeDirty(fieldName : String) + + dnNewInstance(sm : StateManager) : Persistable + + dnNewInstance(sm : StateManager, obj : Object) : Persistable + + dnNewObjectIdInstance() : Object + + dnNewObjectIdInstance(key : Object) : Object + # dnPreSerialize() + + dnProvideField(index : int) + + dnProvideFields(indices : int[]) + + dnReplaceField(index : int) + + dnReplaceFields(indices : int[]) + + dnReplaceFlags() + + dnReplaceStateManager(sm : StateManager) + + dnSetname(name : String) + - dnSuperClone() : Object + + getName() : String + + getVersionSequence() : Long + + setName(val : String) + + title() : TranslatableString + + updateName(name : String) : SimpleObject + + validateUpdateName(name : String) : TranslatableString + } +} +HomePageViewModel --> "-simpleObjects" SimpleObjects +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects-fixture.urm.puml b/naked-objects/etc/naked-objects-fixture.urm.puml new file mode 100644 index 000000000..65c44410a --- /dev/null +++ b/naked-objects/etc/naked-objects-fixture.urm.puml @@ -0,0 +1,92 @@ +@startuml +package domainapp.dom.app.homepage { + class HomePageViewModel { + ~ simpleObjects : SimpleObjects + + HomePageViewModel() + + getObjects() : List + + title() : String + } + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } +} +package domainapp.dom.modules.simple { + class SimpleObject { + - container : DomainObjectContainer + - dnFieldFlags : byte[] {static} + - dnFieldNames : String[] {static} + - dnFieldTypes : Class[] {static} + # dnFlags : byte + - dnInheritedFieldCount : int {static} + - dnPersistableSuperclass : Class {static} + # dnStateManager : StateManager + - name : String + + SimpleObject() + + ___dn$loadClass(className : String) : Class {static} + - __dnFieldFlagsInit() : byte[] {static} + - __dnFieldNamesInit() : String[] {static} + - __dnFieldTypesInit() : Class[] {static} + # __dnGetInheritedFieldCount() : int {static} + - __dnPersistableSuperclassInit() : Class {static} + + compareTo(other : SimpleObject) : int + + default0UpdateName() : String + # dnCopyField(obj : SimpleObject, index : int) + + dnCopyFields(obj : Object, indices : int[]) + + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) + # dnCopyKeyFieldsFromObjectId(oid : Object) + + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) + + dnCopyKeyFieldsToObjectId(oid : Object) + + dnGetExecutionContext() : ExecutionContextReference + # dnGetManagedFieldCount() : int {static} + + dnGetObjectId() : Object + + dnGetTransactionalObjectId() : Object + + dnGetVersion() : Object + + dnGetname() : String + + dnIsDeleted() : boolean + + dnIsDetached() : boolean + + dnIsDirty() : boolean + + dnIsNew() : boolean + + dnIsPersistent() : boolean + + dnIsTransactional() : boolean + + dnMakeDirty(fieldName : String) + + dnNewInstance(sm : StateManager) : Persistable + + dnNewInstance(sm : StateManager, obj : Object) : Persistable + + dnNewObjectIdInstance() : Object + + dnNewObjectIdInstance(key : Object) : Object + # dnPreSerialize() + + dnProvideField(index : int) + + dnProvideFields(indices : int[]) + + dnReplaceField(index : int) + + dnReplaceFields(indices : int[]) + + dnReplaceFlags() + + dnReplaceStateManager(sm : StateManager) + + dnSetname(name : String) + - dnSuperClone() : Object + + getName() : String + + getVersionSequence() : Long + + setName(val : String) + + title() : TranslatableString + + updateName(name : String) : SimpleObject + + validateUpdateName(name : String) : TranslatableString + } + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } +} +package domainapp.fixture { + class DomainAppFixturesProvider { + + DomainAppFixturesProvider() + + getSpecification() : FixtureScriptsSpecification + } +} +DomainAppFixturesProvider --+ FixtureScripts +DomainAppFixturesProvider --+ FixtureScriptsSpecification +HomePageViewModel --> "-simpleObjects" SimpleObjects +@enduml \ No newline at end of file diff --git a/naked-objects/etc/naked-objects-integtests.urm.puml b/naked-objects/etc/naked-objects-integtests.urm.puml new file mode 100644 index 000000000..1f2dfc4c2 --- /dev/null +++ b/naked-objects/etc/naked-objects-integtests.urm.puml @@ -0,0 +1,92 @@ +@startuml +package domainapp.dom.app.homepage { + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } + class HomePageViewModel { + ~ simpleObjects : SimpleObjects + + HomePageViewModel() + + getObjects() : List + + title() : String + } +} +package domainapp.dom.modules.simple { + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } + class SimpleObject { + - container : DomainObjectContainer + - dnFieldFlags : byte[] {static} + - dnFieldNames : String[] {static} + - dnFieldTypes : Class[] {static} + # dnFlags : byte + - dnInheritedFieldCount : int {static} + - dnPersistableSuperclass : Class {static} + # dnStateManager : StateManager + - name : String + + SimpleObject() + + ___dn$loadClass(className : String) : Class {static} + - __dnFieldFlagsInit() : byte[] {static} + - __dnFieldNamesInit() : String[] {static} + - __dnFieldTypesInit() : Class[] {static} + # __dnGetInheritedFieldCount() : int {static} + - __dnPersistableSuperclassInit() : Class {static} + + compareTo(other : SimpleObject) : int + + default0UpdateName() : String + # dnCopyField(obj : SimpleObject, index : int) + + dnCopyFields(obj : Object, indices : int[]) + + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) + # dnCopyKeyFieldsFromObjectId(oid : Object) + + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) + + dnCopyKeyFieldsToObjectId(oid : Object) + + dnGetExecutionContext() : ExecutionContextReference + # dnGetManagedFieldCount() : int {static} + + dnGetObjectId() : Object + + dnGetTransactionalObjectId() : Object + + dnGetVersion() : Object + + dnGetname() : String + + dnIsDeleted() : boolean + + dnIsDetached() : boolean + + dnIsDirty() : boolean + + dnIsNew() : boolean + + dnIsPersistent() : boolean + + dnIsTransactional() : boolean + + dnMakeDirty(fieldName : String) + + dnNewInstance(sm : StateManager) : Persistable + + dnNewInstance(sm : StateManager, obj : Object) : Persistable + + dnNewObjectIdInstance() : Object + + dnNewObjectIdInstance(key : Object) : Object + # dnPreSerialize() + + dnProvideField(index : int) + + dnProvideFields(indices : int[]) + + dnReplaceField(index : int) + + dnReplaceFields(indices : int[]) + + dnReplaceFlags() + + dnReplaceStateManager(sm : StateManager) + + dnSetname(name : String) + - dnSuperClone() : Object + + getName() : String + + getVersionSequence() : Long + + setName(val : String) + + title() : TranslatableString + + updateName(name : String) : SimpleObject + + validateUpdateName(name : String) : TranslatableString + } +} +package domainapp.fixture { + class DomainAppFixturesProvider { + + DomainAppFixturesProvider() + + getSpecification() : FixtureScriptsSpecification + } +} +DomainAppFixturesProvider --+ FixtureScripts +DomainAppFixturesProvider --+ FixtureScriptsSpecification +HomePageViewModel --> "-simpleObjects" SimpleObjects +@enduml \ No newline at end of file diff --git a/null-object/etc/null-object.urm.puml b/null-object/etc/null-object.urm.puml new file mode 100644 index 000000000..d0b2936c5 --- /dev/null +++ b/null-object/etc/null-object.urm.puml @@ -0,0 +1,40 @@ +@startuml +package com.iluwatar.nullobject { + class NullNode { + - instance : NullNode {static} + - NullNode() + + getInstance() : NullNode {static} + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } + interface Node { + + getLeft() : Node {abstract} + + getName() : String {abstract} + + getRight() : Node {abstract} + + getTreeSize() : int {abstract} + + walk() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class NodeImpl { + - left : Node + - name : String + - right : Node + + NodeImpl(name : String, left : Node, right : Node) + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } +} +NullNode --> "-instance" NullNode +NodeImpl --> "-left" Node +NullNode ..|> Node +NodeImpl ..|> Node +@enduml \ No newline at end of file diff --git a/object-pool/etc/object-pool.urm.puml b/object-pool/etc/object-pool.urm.puml new file mode 100644 index 000000000..9df1081d3 --- /dev/null +++ b/object-pool/etc/object-pool.urm.puml @@ -0,0 +1,29 @@ +@startuml +package com.iluwatar.object.pool { + class Oliphaunt { + - counter : int {static} + - id : int + + Oliphaunt() + + getId() : int + + toString() : String + } + class OliphauntPool { + + OliphauntPool() + # create() : Oliphaunt + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class ObjectPool { + - available : HashSet + - inUse : HashSet + + ObjectPool() + + checkIn(instance : T) + + checkOut() : T + # create() : T {abstract} + + toString() : String + } +} +OliphauntPool --|> ObjectPool +@enduml \ No newline at end of file diff --git a/observer/etc/observer.urm.puml b/observer/etc/observer.urm.puml new file mode 100644 index 000000000..33485d731 --- /dev/null +++ b/observer/etc/observer.urm.puml @@ -0,0 +1,73 @@ +@startuml +package com.iluwatar.observer { + class Orcs { + + Orcs() + + update(currentWeather : WeatherType) + } + class Hobbits { + + Hobbits() + + update(currentWeather : WeatherType) + } + class Weather { + - currentWeather : WeatherType + - observers : List + + Weather() + + addObserver(obs : WeatherObserver) + - notifyObservers() + + removeObserver(obs : WeatherObserver) + + timePasses() + } + class App { + + App() + + main(args : String[]) {static} + } + interface WeatherObserver { + + update(WeatherType) {abstract} + } + enum WeatherType { + + COLD {static} + + RAINY {static} + + SUNNY {static} + + WINDY {static} + + toString() : String + + valueOf(name : String) : WeatherType {static} + + values() : WeatherType[] {static} + } +} +package com.iluwatar.observer.generic { + class GOrcs { + + GOrcs() + + update(weather : GWeather, weatherType : WeatherType) + } + interface Race { + } + abstract class Observable, A> { + # observers : List> + + Observable, A>() + + addObserver(observer : O extends Observer) + + notifyObservers(argument : A) + + removeObserver(observer : O extends Observer) + } + class GWeather { + - currentWeather : WeatherType + + GWeather() + + timePasses() + } + interface Observer, O extends Observer, A> { + + update(S extends Observable, A) {abstract} + } + class GHobbits { + + GHobbits() + + update(weather : GWeather, weatherType : WeatherType) + } +} +Weather --> "-currentWeather" WeatherType +GWeather --> "-currentWeather" WeatherType +Weather --> "-observers" WeatherObserver +GOrcs ..|> Race +Orcs ..|> WeatherObserver +Hobbits ..|> WeatherObserver +Race --|> Observer +GWeather --|> Observable +GHobbits ..|> Race +@enduml \ No newline at end of file diff --git a/page-object/etc/page-object.urm.puml b/page-object/etc/page-object.urm.puml new file mode 100644 index 000000000..735cf2889 --- /dev/null +++ b/page-object/etc/page-object.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.pageobject { + class App { + - App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/poison-pill/etc/poison-pill.urm.puml b/poison-pill/etc/poison-pill.urm.puml new file mode 100644 index 000000000..58f9eb937 --- /dev/null +++ b/poison-pill/etc/poison-pill.urm.puml @@ -0,0 +1,72 @@ +@startuml +package com.iluwatar.poison.pill { + interface Message { + + POISON_PILL : Message {static} + + addHeader(Headers, String) {abstract} + + getBody() : String {abstract} + + getHeader(Headers) : String {abstract} + + getHeaders() : Map {abstract} + + setBody(String) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class SimpleMessage { + - body : String + - headers : Map + + SimpleMessage() + + addHeader(header : Headers, value : String) + + getBody() : String + + getHeader(header : Headers) : String + + getHeaders() : Map + + setBody(body : String) + } + class SimpleMessageQueue { + - queue : BlockingQueue + + SimpleMessageQueue(bound : int) + + put(msg : Message) + + take() : Message + } + class Producer { + - isStopped : boolean + - name : String + - queue : MqPublishPoint + + Producer(name : String, queue : MqPublishPoint) + + send(body : String) + + stop() + } + interface MqSubscribePoint { + + take() : Message {abstract} + } + class Consumer { + - name : String + - queue : MqSubscribePoint + + Consumer(name : String, queue : MqSubscribePoint) + + consume() + } + interface MessageQueue { + } + interface MqPublishPoint { + + put(Message) {abstract} + } + enum Headers { + + DATE {static} + + SENDER {static} + + valueOf(name : String) : Headers {static} + + values() : Headers[] {static} + } +} +SimpleMessageQueue --> "-queue" Message +Headers ..+ Message +Consumer --> "-queue" MqSubscribePoint +Producer --> "-queue" MqPublishPoint +SimpleMessage --+ Message +Producer --+ Message +Message --> "-POISON_PILL" Message +Consumer --+ Message +SimpleMessage ..|> Message +SimpleMessageQueue ..|> MessageQueue +MessageQueue --|> MqPublishPoint +MessageQueue --|> MqSubscribePoint +@enduml \ No newline at end of file diff --git a/private-class-data/etc/private-class-data.urm.puml b/private-class-data/etc/private-class-data.urm.puml new file mode 100644 index 000000000..0edc2c1a8 --- /dev/null +++ b/private-class-data/etc/private-class-data.urm.puml @@ -0,0 +1,34 @@ +@startuml +package com.iluwatar.privateclassdata { + class Stew { + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + + taste() + } + class App { + + App() + + main(args : String[]) {static} + } + class StewData { + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + StewData(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + getNumCarrots() : int + + getNumMeat() : int + + getNumPeppers() : int + + getNumPotatoes() : int + } + class ImmutableStew { + - data : StewData + + ImmutableStew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + } +} +ImmutableStew --> "-data" StewData +@enduml \ No newline at end of file diff --git a/producer-consumer/etc/producer-consumer.urm.puml b/producer-consumer/etc/producer-consumer.urm.puml new file mode 100644 index 000000000..9a6748552 --- /dev/null +++ b/producer-consumer/etc/producer-consumer.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.producer.consumer { + class Producer { + - itemId : int + - name : String + - queue : ItemQueue + + Producer(name : String, queue : ItemQueue) + + produce() + } + class ItemQueue { + - queue : BlockingQueue + + ItemQueue() + + put(item : Item) + + take() : Item + } + class Item { + - id : int + - producer : String + + Item(producer : String, id : int) + + getId() : int + + getProducer() : String + } + class App { + + App() + + main(args : String[]) {static} + } + class Consumer { + - name : String + - queue : ItemQueue + + Consumer(name : String, queue : ItemQueue) + + consume() + } +} +Consumer --> "-queue" ItemQueue +Producer --> "-queue" ItemQueue +ItemQueue --> "-queue" Item +@enduml \ No newline at end of file diff --git a/property/etc/property.urm.puml b/property/etc/property.urm.puml new file mode 100644 index 000000000..7c90edccc --- /dev/null +++ b/property/etc/property.urm.puml @@ -0,0 +1,54 @@ +@startuml +package com.iluwatar.property { + class Character { + - name : String + - properties : Map + - prototype : Prototype + - type : Type + + Character() + + Character(name : String, prototype : Character) + + Character(type : Type, prototype : Prototype) + + get(stat : Stats) : Integer + + has(stat : Stats) : boolean + + name() : String + + remove(stat : Stats) + + set(stat : Stats, val : Integer) + + toString() : String + + type() : Type + } + interface Prototype { + + get(Stats) : Integer {abstract} + + has(Stats) : boolean {abstract} + + remove(Stats) {abstract} + + set(Stats, Integer) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + enum Stats { + + AGILITY {static} + + ARMOR {static} + + ATTACK_POWER {static} + + ENERGY {static} + + INTELLECT {static} + + RAGE {static} + + SPIRIT {static} + + STRENGTH {static} + + valueOf(name : String) : Stats {static} + + values() : Stats[] {static} + } + enum Type { + + MAGE {static} + + ROGUE {static} + + WARRIOR {static} + + valueOf(name : String) : Type {static} + + values() : Type[] {static} + } +} +App --+ Character +Character --> "-prototype" Prototype +Character --> "-type" Type +Type ..+ Character +Character ..|> Prototype +@enduml \ No newline at end of file diff --git a/prototype/etc/prototype.urm.puml b/prototype/etc/prototype.urm.puml new file mode 100644 index 000000000..7bfc00e15 --- /dev/null +++ b/prototype/etc/prototype.urm.puml @@ -0,0 +1,81 @@ +@startuml +package com.iluwatar.prototype { + interface HeroFactory { + + createBeast() : Beast {abstract} + + createMage() : Mage {abstract} + + createWarlord() : Warlord {abstract} + } + class OrcBeast { + + OrcBeast() + + clone() : Beast + + toString() : String + } + abstract class Mage { + + Mage() + + clone() : Mage {abstract} + } + class HeroFactoryImpl { + - beast : Beast + - mage : Mage + - warlord : Warlord + + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) + + createBeast() : Beast + + createMage() : Mage + + createWarlord() : Warlord + } + class ElfMage { + + ElfMage() + + clone() : Mage + + toString() : String + } + abstract class Prototype { + + Prototype() + + clone() : Object {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Warlord { + + Warlord() + + clone() : Warlord {abstract} + } + class OrcWarlord { + + OrcWarlord() + + clone() : Warlord + + toString() : String + } + class ElfWarlord { + + ElfWarlord() + + clone() : Warlord + + toString() : String + } + abstract class Beast { + + Beast() + + clone() : Beast {abstract} + } + class OrcMage { + + OrcMage() + + clone() : Mage + + toString() : String + } + class ElfBeast { + + ElfBeast() + + clone() : Beast + + toString() : String + } +} +HeroFactoryImpl --> "-beast" Beast +HeroFactoryImpl --> "-warlord" Warlord +HeroFactoryImpl --> "-mage" Mage +OrcBeast --|> Beast +Mage --|> Prototype +HeroFactoryImpl ..|> HeroFactory +ElfMage --|> Mage +Warlord --|> Prototype +OrcWarlord --|> Warlord +ElfWarlord --|> Warlord +Beast --|> Prototype +OrcMage --|> Mage +ElfBeast --|> Beast +@enduml \ No newline at end of file diff --git a/proxy/etc/proxy.urm.puml b/proxy/etc/proxy.urm.puml new file mode 100644 index 000000000..4203174de --- /dev/null +++ b/proxy/etc/proxy.urm.puml @@ -0,0 +1,24 @@ +@startuml +package com.iluwatar.proxy { + class WizardTower { + + WizardTower() + + enter(wizard : Wizard) + } + class App { + + App() + + main(args : String[]) {static} + } + class WizardTowerProxy { + - NUM_WIZARDS_ALLOWED : int {static} + - numWizards : int + + WizardTowerProxy() + + enter(wizard : Wizard) + } + class Wizard { + - name : String + + Wizard(name : String) + + toString() : String + } +} +WizardTowerProxy --|> WizardTower +@enduml \ No newline at end of file diff --git a/publish-subscribe/etc/publish-subscribe.urm.puml b/publish-subscribe/etc/publish-subscribe.urm.puml new file mode 100644 index 000000000..1272f1f6d --- /dev/null +++ b/publish-subscribe/etc/publish-subscribe.urm.puml @@ -0,0 +1,8 @@ +@startuml +package com.iluwatar.publish.subscribe { + class App { + + App() + + main(args : String[]) {static} + } +} +@enduml \ No newline at end of file diff --git a/reactor/etc/reactor.urm.puml b/reactor/etc/reactor.urm.puml new file mode 100644 index 000000000..302f2663c --- /dev/null +++ b/reactor/etc/reactor.urm.puml @@ -0,0 +1,151 @@ +@startuml +package com.iluwatar.reactor.app { + ~class TcpLoggingClient { + - clientName : String + - serverPort : int + + TcpLoggingClient(clientName : String, serverPort : int) + + run() + - sendLogRequests(writer : PrintWriter, inputStream : InputStream) + } + ~class UdpLoggingClient { + - clientName : String + - remoteAddress : InetSocketAddress + + UdpLoggingClient(clientName : String, port : int) + + run() + } + class LoggingHandler { + - ACK : byte[] {static} + + LoggingHandler() + - doLogging(data : ByteBuffer) {static} + + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} + - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} + } + class AppClient { + - service : ExecutorService + + AppClient() + - artificialDelayOf(millis : long) {static} + + main(args : String[]) {static} + + start() + + stop() + } + class App { + - channels : List + - dispatcher : Dispatcher + - reactor : NioReactor + + App(dispatcher : Dispatcher) + + main(args : String[]) {static} + + start() + + stop() + - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + } +} +package com.iluwatar.reactor.framework { + interface Dispatcher { + + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} + + stop() {abstract} + } + class SameThreadDispatcher { + + SameThreadDispatcher() + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + class ThreadPoolDispatcher { + - executorService : ExecutorService + + ThreadPoolDispatcher(poolSize : int) + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + interface ChannelHandler { + + handleChannelRead(AbstractNioChannel, Object, SelectionKey) {abstract} + } + class NioDatagramChannel { + - port : int + + NioDatagramChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : DatagramChannel + + read(key : SelectionKey) : DatagramPacket + + write(data : Object, key : SelectionKey) + } + class DatagramPacket { + - data : ByteBuffer + - receiver : SocketAddress + - sender : SocketAddress + + DatagramPacket(data : ByteBuffer) + + getData() : ByteBuffer + + getReceiver() : SocketAddress + + getSender() : SocketAddress + + setReceiver(receiver : SocketAddress) + + setSender(sender : SocketAddress) + } + abstract class AbstractNioChannel { + - channel : SelectableChannel + - channelToPendingWrites : Map> + - handler : ChannelHandler + - reactor : NioReactor + + AbstractNioChannel(handler : ChannelHandler, channel : SelectableChannel) + + bind() {abstract} + # doWrite(Object, SelectionKey) {abstract} + ~ flush(key : SelectionKey) + + getHandler() : ChannelHandler + + getInterestedOps() : int {abstract} + + getJavaChannel() : SelectableChannel + + read(SelectionKey) : Object {abstract} + ~ setReactor(reactor : NioReactor) + + write(data : Object, key : SelectionKey) + } + class NioServerSocketChannel { + - port : int + + NioServerSocketChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : ServerSocketChannel + + read(key : SelectionKey) : ByteBuffer + } + class NioReactor { + - dispatcher : Dispatcher + - pendingCommands : Queue + - reactorMain : ExecutorService + - selector : Selector + + NioReactor(dispatcher : Dispatcher) + + changeOps(key : SelectionKey, interestedOps : int) + - dispatchReadEvent(key : SelectionKey, readObject : Object) + - eventLoop() + - onChannelAcceptable(key : SelectionKey) + - onChannelReadable(key : SelectionKey) + - onChannelWritable(key : SelectionKey) {static} + - processKey(key : SelectionKey) + - processPendingCommands() + + registerChannel(channel : AbstractNioChannel) : NioReactor + + start() + + stop() + } + ~class ChangeKeyOpsCommand { + - interestedOps : int + - key : SelectionKey + + ChangeKeyOpsCommand(this$0 : NioReactor, key : SelectionKey, interestedOps : int) + + run() + + toString() : String + } +} +AbstractNioChannel --> "-handler" ChannelHandler +UdpLoggingClient ..+ AppClient +AbstractNioChannel --> "-reactor" NioReactor +TcpLoggingClient ..+ AppClient +NioReactor --> "-dispatcher" Dispatcher +App --> "-reactor" NioReactor +App --> "-channels" AbstractNioChannel +DatagramPacket ..+ NioDatagramChannel +ChangeKeyOpsCommand --+ NioReactor +App --> "-dispatcher" Dispatcher +LoggingHandler --+ NioDatagramChannel +SameThreadDispatcher ..|> Dispatcher +LoggingHandler ..|> ChannelHandler +ThreadPoolDispatcher ..|> Dispatcher +NioDatagramChannel --|> AbstractNioChannel +NioServerSocketChannel --|> AbstractNioChannel +@enduml \ No newline at end of file diff --git a/reader-writer-lock/etc/reader-writer-lock.urm.puml b/reader-writer-lock/etc/reader-writer-lock.urm.puml new file mode 100644 index 000000000..22b303cf5 --- /dev/null +++ b/reader-writer-lock/etc/reader-writer-lock.urm.puml @@ -0,0 +1,58 @@ +@startuml +package com.iluwatar.reader.writer.lock { + -class ReadLock { + - ReadLock(ReaderWriterLock) + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class Writer { + - name : String + - writeLock : Lock + + Writer(name : String, writeLock : Lock) + + run() + + write() + } + class ReaderWriterLock { + - currentReaderCount : int + - globalMutex : Set + - readerLock : ReadLock + - readerMutex : Object + - writerLock : WriteLock + + ReaderWriterLock() + - doesReaderOwnThisLock() : boolean + - doesWriterOwnThisLock() : boolean + - isLockFree() : boolean + + readLock() : Lock + - waitUninterruptibly(o : Object) {static} + + writeLock() : Lock + } + -class WriteLock { + - WriteLock(ReaderWriterLock) + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class App { + + App() + + main(args : String[]) {static} + } + class Reader { + - name : String + - readLock : Lock + + Reader(name : String, readLock : Lock) + + read() + + run() + } +} +ReadLock --+ ReaderWriterLock +ReaderWriterLock --> "-readerLock" ReadLock +ReaderWriterLock --> "-writerLock" WriteLock +WriteLock --+ ReaderWriterLock +@enduml \ No newline at end of file diff --git a/repository/etc/repository.urm.puml b/repository/etc/repository.urm.puml new file mode 100644 index 000000000..49a2c8fdc --- /dev/null +++ b/repository/etc/repository.urm.puml @@ -0,0 +1,56 @@ +@startuml +package com.iluwatar.repository { + class App { + + App() + + main(args : String[]) {static} + } + class Person { + - age : int + - id : Long + - name : String + - surname : String + + Person() + + Person(name : String, surname : String, age : int) + + equals(obj : Object) : boolean + + getAge() : int + + getId() : Long + + getName() : String + + getSurname() : String + + hashCode() : int + + setAge(age : int) + + setId(id : Long) + + setName(name : String) + + setSurname(surname : String) + + toString() : String + } + class AgeBetweenSpec { + - from : int + - to : int + + AgeBetweenSpec(from : int, to : int) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class AppConfig { + + AppConfig() + + dataSource() : DataSource + + entityManagerFactory() : LocalContainerEntityManagerFactoryBean + - jpaProperties() : Properties {static} + + main(args : String[]) {static} + + transactionManager() : JpaTransactionManager + } + interface PersonRepository { + + findByName(String) : Person {abstract} + } + class NameEqualSpec { + + name : String + + NameEqualSpec(name : String) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class PersonSpecifications { + + PersonSpecifications() + } +} +App --+ PersonSpecifications +AppConfig --+ PersonSpecifications +NameEqualSpec ..+ PersonSpecifications +AgeBetweenSpec ..+ PersonSpecifications +@enduml \ No newline at end of file diff --git a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml new file mode 100644 index 000000000..847b716a0 --- /dev/null +++ b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml @@ -0,0 +1,16 @@ +@startuml +package com.iluwatar.resource.acquisition.is.initialization { + class App { + + App() + + main(args : String[]) {static} + } + class TreasureChest { + + TreasureChest() + + close() + } + class SlidingDoor { + + SlidingDoor() + + close() + } +} +@enduml \ No newline at end of file diff --git a/semaphore/etc/semaphore.urm.puml b/semaphore/etc/semaphore.urm.puml new file mode 100644 index 000000000..f85fff921 --- /dev/null +++ b/semaphore/etc/semaphore.urm.puml @@ -0,0 +1,58 @@ +@startuml +package com.iluwatar.semaphore { + class FruitShop { + - available : boolean[] + - bowls : FruitBowl[] + - semaphore : Semaphore + + FruitShop() + + countFruit() : int + + returnBowl(bowl : FruitBowl) + + takeBowl() : FruitBowl + } + class FruitBowl { + - fruit : ArrayList + + FruitBowl() + + countFruit() : int + + put(f : Fruit) + + take() : Fruit + + toString() : String + } + class Fruit { + - type : FruitType + + Fruit(type : FruitType) + + getType() : FruitType + + toString() : String + } + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class Semaphore { + - counter : int + - licenses : int + + Semaphore(licenses : int) + + acquire() + + getAvailableLicenses() : int + + getNumLicenses() : int + + release() + } + enum FruitType { + + APPLE {static} + + LEMON {static} + + ORANGE {static} + + valueOf(name : String) : FruitType {static} + + values() : FruitType[] {static} + } +} +FruitShop --+ Fruit +Fruit --> "-type" FruitType +FruitType ..+ Fruit +FruitBowl --+ Fruit +FruitBowl --> "-fruit" Fruit +FruitShop --> "-semaphore" Semaphore +Semaphore ..|> Lock +@enduml \ No newline at end of file diff --git a/servant/etc/servant.urm.puml b/servant/etc/servant.urm.puml new file mode 100644 index 000000000..48d48bd84 --- /dev/null +++ b/servant/etc/servant.urm.puml @@ -0,0 +1,55 @@ +@startuml +package com.iluwatar.servant { + class King { + - complimentReceived : boolean + - isDrunk : boolean + - isHappy : boolean + - isHungry : boolean + + King() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + } + ~interface Royalty { + + changeMood() {abstract} + + getDrink() {abstract} + + getFed() {abstract} + + getMood() : boolean {abstract} + + receiveCompliments() {abstract} + } + class Servant { + + name : String + + Servant(name : String) + + checkIfYouWillBeHanged(tableGuests : List) : boolean + + feed(r : Royalty) + + giveCompliments(r : Royalty) + + giveWine(r : Royalty) + } + class Queen { + - complimentReceived : boolean + - isDrunk : boolean + - isFlirty : boolean + - isHappy : boolean + - isHungry : boolean + + Queen() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + + setFlirtiness(f : boolean) + } + class App { + ~ jenkins : Servant {static} + ~ travis : Servant {static} + + App() + + main(args : String[]) {static} + + scenario(servant : Servant, compliment : int) {static} + } +} +App --> "-jenkins" Servant +King ..|> Royalty +Queen ..|> Royalty +@enduml \ No newline at end of file diff --git a/service-layer/etc/service-layer.urm.puml b/service-layer/etc/service-layer.urm.puml new file mode 100644 index 000000000..c67ffa645 --- /dev/null +++ b/service-layer/etc/service-layer.urm.puml @@ -0,0 +1,158 @@ +@startuml +package com.iluwatar.servicelayer.hibernate { + class HibernateUtil { + - sessionFactory : SessionFactory {static} + - HibernateUtil() + + dropSession() {static} + + getSessionFactory() : SessionFactory {static} + } +} +package com.iluwatar.servicelayer.common { + abstract class BaseEntity { + - version : Long + + BaseEntity() + + getId() : Long {abstract} + + getName() : String {abstract} + + setId(Long) {abstract} + + setName(String) {abstract} + } + interface Dao { + + delete(E extends BaseEntity) {abstract} + + find(Long) : E extends BaseEntity {abstract} + + findAll() : List {abstract} + + merge(E extends BaseEntity) : E extends BaseEntity {abstract} + + persist(E extends BaseEntity) {abstract} + } + abstract class DaoBaseImpl { + # persistentClass : Class + + DaoBaseImpl() + + delete(entity : E extends BaseEntity) + + find(id : Long) : E extends BaseEntity + + findAll() : List + # getSession() : Session + + merge(entity : E extends BaseEntity) : E extends BaseEntity + + persist(entity : E extends BaseEntity) + } +} +package com.iluwatar.servicelayer.magic { + interface MagicService { + + findAllSpellbooks() : List {abstract} + + findAllSpells() : List {abstract} + + findAllWizards() : List {abstract} + + findWizardsWithSpell(String) : List {abstract} + + findWizardsWithSpellbook(String) : List {abstract} + } + class MagicServiceImpl { + - spellDao : SpellDao + - spellbookDao : SpellbookDao + - wizardDao : WizardDao + + MagicServiceImpl(wizardDao : WizardDao, spellbookDao : SpellbookDao, spellDao : SpellDao) + + findAllSpellbooks() : List + + findAllSpells() : List + + findAllWizards() : List + + findWizardsWithSpell(name : String) : List + + findWizardsWithSpellbook(name : String) : List + } +} +package com.iluwatar.servicelayer.wizard { + class Wizard { + - id : Long + - name : String + - spellbooks : Set + + Wizard() + + Wizard(name : String) + + addSpellbook(spellbook : Spellbook) + + getId() : Long + + getName() : String + + getSpellbooks() : Set + + setId(id : Long) + + setName(name : String) + + setSpellbooks(spellbooks : Set) + + toString() : String + } + class WizardDaoImpl { + + WizardDaoImpl() + + findByName(name : String) : Wizard + } + interface WizardDao { + + findByName(String) : Wizard {abstract} + } +} +package com.iluwatar.servicelayer.app { + class App { + + App() + + initData() {static} + + main(args : String[]) {static} + + queryData() {static} + } +} +package com.iluwatar.servicelayer.spell { + class SpellDaoImpl { + + SpellDaoImpl() + + findByName(name : String) : Spell + } + class Spell { + - id : Long + - name : String + - spellbook : Spellbook + + Spell() + + Spell(name : String) + + getId() : Long + + getName() : String + + getSpellbook() : Spellbook + + setId(id : Long) + + setName(name : String) + + setSpellbook(spellbook : Spellbook) + + toString() : String + } + interface SpellDao { + + findByName(String) : Spell {abstract} + } +} +package com.iluwatar.servicelayer.spellbook { + interface SpellbookDao { + + findByName(String) : Spellbook {abstract} + } + class Spellbook { + - id : Long + - name : String + - spells : Set + - wizards : Set + + Spellbook() + + Spellbook(name : String) + + addSpell(spell : Spell) + + getId() : Long + + getName() : String + + getSpells() : Set + + getWizards() : Set + + setId(id : Long) + + setName(name : String) + + setSpells(spells : Set) + + setWizards(wizards : Set) + + toString() : String + } + class SpellbookDaoImpl { + + SpellbookDaoImpl() + + findByName(name : String) : Spellbook + } +} +MagicServiceImpl --> "-wizardDao" WizardDao +MagicServiceImpl --> "-spellbookDao" SpellbookDao +MagicServiceImpl --> "-spellDao" SpellDao +Spellbook --> "-spells" Spell +Spellbook --> "-wizards" Wizard +Wizard --|> BaseEntity +SpellbookDao --|> Dao +SpellDaoImpl ..|> SpellDao +SpellDaoImpl --|> DaoBaseImpl +MagicServiceImpl ..|> MagicService +DaoBaseImpl ..|> Dao +WizardDaoImpl ..|> WizardDao +WizardDaoImpl --|> DaoBaseImpl +Spellbook --|> BaseEntity +SpellbookDaoImpl ..|> SpellbookDao +SpellbookDaoImpl --|> DaoBaseImpl +Spell --|> BaseEntity +WizardDao --|> Dao +SpellDao --|> Dao +@enduml \ No newline at end of file diff --git a/service-locator/etc/service-locator.urm.puml b/service-locator/etc/service-locator.urm.puml new file mode 100644 index 000000000..085b05b28 --- /dev/null +++ b/service-locator/etc/service-locator.urm.puml @@ -0,0 +1,38 @@ +@startuml +package com.iluwatar.servicelocator { + interface Service { + + execute() {abstract} + + getId() : int {abstract} + + getName() : String {abstract} + } + class InitContext { + + InitContext() + + lookup(serviceName : String) : Object + } + class ServiceLocator { + - serviceCache : ServiceCache {static} + - ServiceLocator() + + getService(serviceJndiName : String) : Service {static} + } + class ServiceCache { + - serviceCache : Map + + ServiceCache() + + addService(newService : Service) + + getService(serviceName : String) : Service + } + class App { + + App() + + main(args : String[]) {static} + } + class ServiceImpl { + - id : int + - serviceName : String + + ServiceImpl(serviceName : String) + + execute() + + getId() : int + + getName() : String + } +} +ServiceLocator --> "-serviceCache" ServiceCache +ServiceImpl ..|> Service +@enduml \ No newline at end of file diff --git a/singleton/etc/singleton.urm.puml b/singleton/etc/singleton.urm.puml new file mode 100644 index 000000000..f5ec19879 --- /dev/null +++ b/singleton/etc/singleton.urm.puml @@ -0,0 +1,42 @@ +@startuml +package com.iluwatar.singleton { + class ThreadSafeLazyLoadedIvoryTower { + - instance : ThreadSafeLazyLoadedIvoryTower {static} + - ThreadSafeLazyLoadedIvoryTower() + + getInstance() : ThreadSafeLazyLoadedIvoryTower {static} + } + -class HelperHolder { + + INSTANCE : InitializingOnDemandHolderIdiom {static} + - HelperHolder() + } + class App { + + App() + + main(args : String[]) {static} + } + class ThreadSafeDoubleCheckLocking { + - instance : ThreadSafeDoubleCheckLocking {static} + - ThreadSafeDoubleCheckLocking() + + getInstance() : ThreadSafeDoubleCheckLocking {static} + } + class InitializingOnDemandHolderIdiom { + - InitializingOnDemandHolderIdiom() + + getInstance() : InitializingOnDemandHolderIdiom {static} + } + class IvoryTower { + - INSTANCE : IvoryTower {static} + - IvoryTower() + + getInstance() : IvoryTower {static} + } + enum EnumIvoryTower { + + INSTANCE {static} + + toString() : String + + valueOf(name : String) : EnumIvoryTower {static} + + values() : EnumIvoryTower[] {static} + } +} +IvoryTower --> "-INSTANCE" IvoryTower +ThreadSafeDoubleCheckLocking --> "-instance" ThreadSafeDoubleCheckLocking +ThreadSafeLazyLoadedIvoryTower --> "-instance" ThreadSafeLazyLoadedIvoryTower +HelperHolder ..+ InitializingOnDemandHolderIdiom +HelperHolder --> "-INSTANCE" InitializingOnDemandHolderIdiom +@enduml \ No newline at end of file diff --git a/specification/etc/specification.urm.puml b/specification/etc/specification.urm.puml new file mode 100644 index 000000000..0009a1bcd --- /dev/null +++ b/specification/etc/specification.urm.puml @@ -0,0 +1,106 @@ +@startuml +package com.iluwatar.specification.creature { + class Goblin { + + Goblin() + } + interface Creature { + + getColor() : Color {abstract} + + getMovement() : Movement {abstract} + + getName() : String {abstract} + + getSize() : Size {abstract} + } + class Troll { + + Troll() + } + abstract class AbstractCreature { + - color : Color + - movement : Movement + - name : String + - size : Size + + AbstractCreature(name : String, size : Size, movement : Movement, color : Color) + + getColor() : Color + + getMovement() : Movement + + getName() : String + + getSize() : Size + + toString() : String + } + class Shark { + + Shark() + } + class KillerBee { + + KillerBee() + } + class Octopus { + + Octopus() + } + class Dragon { + + Dragon() + } +} +package com.iluwatar.specification.property { + enum Color { + + DARK {static} + + GREEN {static} + + LIGHT {static} + + RED {static} + - title : String + + toString() : String + + valueOf(name : String) : Color {static} + + values() : Color[] {static} + } + enum Movement { + + FLYING {static} + + SWIMMING {static} + + WALKING {static} + - title : String + + toString() : String + + valueOf(name : String) : Movement {static} + + values() : Movement[] {static} + } + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } +} +package com.iluwatar.specification.app { + class App { + + App() + + main(args : String[]) {static} + } +} +package com.iluwatar.specification.selector { + class SizeSelector { + - s : Size + + SizeSelector(s : Size) + + test(t : Creature) : boolean + } + class ColorSelector { + - c : Color + + ColorSelector(c : Color) + + test(t : Creature) : boolean + } + class MovementSelector { + - m : Movement + + MovementSelector(m : Movement) + + test(t : Creature) : boolean + } +} +SizeSelector --> "-s" Size +AbstractCreature --> "-color" Color +MovementSelector --> "-m" Movement +AbstractCreature --> "-movement" Movement +AbstractCreature --> "-size" Size +ColorSelector --> "-c" Color +Goblin --|> AbstractCreature +Troll --|> AbstractCreature +AbstractCreature ..|> Creature +Shark --|> AbstractCreature +KillerBee --|> AbstractCreature +Octopus --|> AbstractCreature +Dragon --|> AbstractCreature +@enduml \ No newline at end of file diff --git a/state/etc/state.urm.puml b/state/etc/state.urm.puml new file mode 100644 index 000000000..a0951ff6e --- /dev/null +++ b/state/etc/state.urm.puml @@ -0,0 +1,37 @@ +@startuml +package com.iluwatar.state { + class AngryState { + - mammoth : Mammoth + + AngryState(mammoth : Mammoth) + + observe() + + onEnterState() + } + class Mammoth { + - state : State + + Mammoth() + - changeStateTo(newState : State) + + observe() + + timePasses() + + toString() : String + } + interface State { + + observe() {abstract} + + onEnterState() {abstract} + } + class PeacefulState { + - mammoth : Mammoth + + PeacefulState(mammoth : Mammoth) + + observe() + + onEnterState() + } + class App { + + App() + + main(args : String[]) {static} + } +} +PeacefulState --> "-mammoth" Mammoth +AngryState --> "-mammoth" Mammoth +Mammoth --> "-state" State +AngryState ..|> State +PeacefulState ..|> State +@enduml \ No newline at end of file diff --git a/step-builder/etc/step-builder.urm.puml b/step-builder/etc/step-builder.urm.puml new file mode 100644 index 000000000..cc1f88ef0 --- /dev/null +++ b/step-builder/etc/step-builder.urm.puml @@ -0,0 +1,91 @@ +@startuml +package com.iluwatar.stepbuilder { + interface BuildStep { + + build() : Character {abstract} + } + -class CharacterSteps { + - abilities : List + - fighterClass : String + - name : String + - spell : String + - weapon : String + - wizardClass : String + - CharacterSteps() + + build() : Character + + fighterClass(fighterClass : String) : WeaponStep + + name(name : String) : ClassStep + + noAbilities() : BuildStep + + noMoreAbilities() : BuildStep + + noSpell() : BuildStep + + noWeapon() : BuildStep + + withAbility(ability : String) : AbilityStep + + withSpell(spell : String) : AbilityStep + + withWeapon(weapon : String) : AbilityStep + + wizardClass(wizardClass : String) : SpellStep + } + class App { + + App() + + main(args : String[]) {static} + } + interface ClassStep { + + fighterClass(String) : WeaponStep {abstract} + + wizardClass(String) : SpellStep {abstract} + } + interface WeaponStep { + + noWeapon() : BuildStep {abstract} + + withWeapon(String) : AbilityStep {abstract} + } + interface AbilityStep { + + noAbilities() : BuildStep {abstract} + + noMoreAbilities() : BuildStep {abstract} + + withAbility(String) : AbilityStep {abstract} + } + interface NameStep { + + name(String) : ClassStep {abstract} + } + class CharacterStepBuilder { + - CharacterStepBuilder() + + newBuilder() : NameStep {static} + } + class Character { + - abilities : List + - fighterClass : String + - name : String + - spell : String + - weapon : String + - wizardClass : String + + Character(name : String) + + getAbilities() : List + + getFighterClass() : String + + getName() : String + + getSpell() : String + + getWeapon() : String + + getWizardClass() : String + + setAbilities(abilities : List) + + setFighterClass(fighterClass : String) + + setName(name : String) + + setSpell(spell : String) + + setWeapon(weapon : String) + + setWizardClass(wizardClass : String) + + toString() : String + } + interface SpellStep { + + noSpell() : BuildStep {abstract} + + withSpell(String) : AbilityStep {abstract} + } +} +App --+ CharacterStepBuilder +WeaponStep ..+ CharacterStepBuilder +SpellStep ..+ CharacterStepBuilder +AbilityStep ..+ CharacterStepBuilder +ClassStep ..+ CharacterStepBuilder +CharacterSteps ..+ CharacterStepBuilder +NameStep ..+ CharacterStepBuilder +BuildStep ..+ CharacterStepBuilder +CharacterSteps ..|> NameStep +CharacterSteps ..|> ClassStep +CharacterSteps ..|> WeaponStep +CharacterSteps ..|> SpellStep +CharacterSteps ..|> AbilityStep +CharacterSteps ..|> BuildStep +@enduml \ No newline at end of file diff --git a/strategy/etc/strategy.urm.puml b/strategy/etc/strategy.urm.puml new file mode 100644 index 000000000..2cc072863 --- /dev/null +++ b/strategy/etc/strategy.urm.puml @@ -0,0 +1,33 @@ +@startuml +package com.iluwatar.strategy { + class DragonSlayer { + - strategy : DragonSlayingStrategy + + DragonSlayer(strategy : DragonSlayingStrategy) + + changeStrategy(strategy : DragonSlayingStrategy) + + goToBattle() + } + class SpellStrategy { + + SpellStrategy() + + execute() + } + class ProjectileStrategy { + + ProjectileStrategy() + + execute() + } + interface DragonSlayingStrategy { + + execute() {abstract} + } + class MeleeStrategy { + + MeleeStrategy() + + execute() + } + class App { + + App() + + main(args : String[]) {static} + } +} +DragonSlayer --> "-strategy" DragonSlayingStrategy +SpellStrategy ..|> DragonSlayingStrategy +ProjectileStrategy ..|> DragonSlayingStrategy +MeleeStrategy ..|> DragonSlayingStrategy +@enduml \ No newline at end of file diff --git a/template-method/etc/template-method.urm.puml b/template-method/etc/template-method.urm.puml new file mode 100644 index 000000000..c98287bc6 --- /dev/null +++ b/template-method/etc/template-method.urm.puml @@ -0,0 +1,36 @@ +@startuml +package com.iluwatar.templatemethod { + class SubtleMethod { + + SubtleMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } + class HitAndRunMethod { + + HitAndRunMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } + abstract class StealingMethod { + + StealingMethod() + # confuseTarget(String) {abstract} + # pickTarget() : String {abstract} + + steal() + # stealTheItem(String) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class HalflingThief { + - method : StealingMethod + + HalflingThief(method : StealingMethod) + + changeMethod(method : StealingMethod) + + steal() + } +} +HalflingThief --> "-method" StealingMethod +SubtleMethod --|> StealingMethod +HitAndRunMethod --|> StealingMethod +@enduml \ No newline at end of file diff --git a/thread-pool/etc/thread-pool.urm.puml b/thread-pool/etc/thread-pool.urm.puml new file mode 100644 index 000000000..2b73e2d53 --- /dev/null +++ b/thread-pool/etc/thread-pool.urm.puml @@ -0,0 +1,35 @@ +@startuml +package com.iluwatar.threadpool { + class Worker { + - task : Task + + Worker(task : Task) + + run() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Task { + - ID_GENERATOR : AtomicInteger {static} + - id : int + - timeMs : int + + Task(timeMs : int) + + getId() : int + + getTimeMs() : int + + toString() : String + } + class PotatoPeelingTask { + - TIME_PER_POTATO : int {static} + + PotatoPeelingTask(numPotatoes : int) + + toString() : String + } + class CoffeeMakingTask { + - TIME_PER_CUP : int {static} + + CoffeeMakingTask(numCups : int) + + toString() : String + } +} +Worker --> "-task" Task +PotatoPeelingTask --|> Task +CoffeeMakingTask --|> Task +@enduml \ No newline at end of file diff --git a/tolerant-reader/etc/tolerant-reader.urm.puml b/tolerant-reader/etc/tolerant-reader.urm.puml new file mode 100644 index 000000000..9e2bc83b3 --- /dev/null +++ b/tolerant-reader/etc/tolerant-reader.urm.puml @@ -0,0 +1,38 @@ +@startuml +package com.iluwatar.tolerantreader { + class RainbowFishSerializer { + - RainbowFishSerializer() + + readV1(filename : String) : RainbowFish {static} + + writeV1(rainbowFish : RainbowFish, filename : String) {static} + + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} + } + class RainbowFish { + - age : int + - lengthMeters : int + - name : String + - serialVersionUID : long {static} + - weightTons : int + + RainbowFish(name : String, age : int, lengthMeters : int, weightTons : int) + + getAge() : int + + getLengthMeters() : int + + getName() : String + + getWeightTons() : int + } + class RainbowFishV2 { + - angry : boolean + - hungry : boolean + - serialVersionUID : long {static} + - sleeping : boolean + + RainbowFishV2(name : String, age : int, lengthMeters : int, weightTons : int) + + RainbowFishV2(name : String, age : int, lengthMeters : int, weightTons : int, sleeping : boolean, hungry : boolean, angry : boolean) + + getAngry() : boolean + + getHungry() : boolean + + getSleeping() : boolean + } + class App { + + App() + + main(args : String[]) {static} + } +} +RainbowFishV2 --|> RainbowFish +@enduml \ No newline at end of file diff --git a/twin/etc/twin.urm.puml b/twin/etc/twin.urm.puml new file mode 100644 index 000000000..b95325abb --- /dev/null +++ b/twin/etc/twin.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.twin { + class App { + + App() + + main(args : String[]) {static} + - waiting() {static} + } + class BallItem { + - isSuspended : boolean + - twin : BallThread + + BallItem() + + click() + + doDraw() + + move() + + setTwin(twin : BallThread) + } + abstract class GameItem { + + GameItem() + + click() {abstract} + + doDraw() {abstract} + + draw() + } +} +BallItem --|> GameItem +@enduml \ No newline at end of file diff --git a/value-object/etc/value-object.urm.puml b/value-object/etc/value-object.urm.puml new file mode 100644 index 000000000..223f91957 --- /dev/null +++ b/value-object/etc/value-object.urm.puml @@ -0,0 +1,21 @@ +@startuml +package com.iluwatar.value.object { + class App { + + App() + + main(args : String[]) {static} + } + class HeroStat { + - intelligence : int + - luck : int + - strength : int + - HeroStat(strength : int, intelligence : int, luck : int) + + equals(obj : Object) : boolean + + getIntelligence() : int + + getLuck() : int + + getStrength() : int + + hashCode() : int + + toString() : String + + valueOf(strength : int, intelligence : int, luck : int) : HeroStat {static} + } +} +@enduml \ No newline at end of file diff --git a/visitor/etc/visitor.urm.puml b/visitor/etc/visitor.urm.puml new file mode 100644 index 000000000..3f5689f71 --- /dev/null +++ b/visitor/etc/visitor.urm.puml @@ -0,0 +1,57 @@ +@startuml +package com.iluwatar.visitor { + class CommanderVisitor { + + CommanderVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + class Sergeant { + + Sergeant(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class Commander { + + Commander(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + abstract class Unit { + - children : Unit[] + + Unit(children : Unit[]) + + accept(visitor : UnitVisitor) + } + class Soldier { + + Soldier(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class SergeantVisitor { + + SergeantVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + interface UnitVisitor { + + visitCommander(Commander) {abstract} + + visitSergeant(Sergeant) {abstract} + + visitSoldier(Soldier) {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class SoldierVisitor { + + SoldierVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } +} +CommanderVisitor ..|> UnitVisitor +Sergeant --|> Unit +Commander --|> Unit +Soldier --|> Unit +SergeantVisitor ..|> UnitVisitor +SoldierVisitor ..|> UnitVisitor +@enduml \ No newline at end of file From 09037b0251cc949d13ce394462969dadb5655ce3 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Tue, 30 Aug 2016 14:06:14 +0200 Subject: [PATCH 030/145] Rename index.md to README.md to conform to our standards, every other file is named README.md --- data-mapper/{index.md => README.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename data-mapper/{index.md => README.md} (100%) diff --git a/data-mapper/index.md b/data-mapper/README.md similarity index 100% rename from data-mapper/index.md rename to data-mapper/README.md From 9dd1503e6f02d6cf48e549aa97b8273cadd74274 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 15:07:49 +0200 Subject: [PATCH 031/145] Work on #190: add postPumlsToServer.py python script Used to initially post all pumls to the plantuml hosting while preserving the pumlId's --- _scripts/postPumlsToServer.firstrun.output | 190 +++++++++++++++++++++ _scripts/postPumlsToServer.py | 44 +++++ 2 files changed, 234 insertions(+) create mode 100644 _scripts/postPumlsToServer.firstrun.output create mode 100644 _scripts/postPumlsToServer.py diff --git a/_scripts/postPumlsToServer.firstrun.output b/_scripts/postPumlsToServer.firstrun.output new file mode 100644 index 000000000..ea77a49a5 --- /dev/null +++ b/_scripts/postPumlsToServer.firstrun.output @@ -0,0 +1,190 @@ +parent: half-sync-half-async; artifact: half-sync-half-async +Puml Server ID: RScv3SCm3030LU819FRPXg5fIm552tnYPFiyjRi3RkbAaYkdoQr5JBy369vrxz7oaSv6XmPhL3e6TCaJ0msU-CAoilTToyG8DdKOw5z0GzcAlvNAN_WZSD1brBHHPmxv0000 +parent: abstract-document; artifact: abstract-document +Puml Server ID: PSjB3eCm34NHhPG599vtDyQn85L-ifzX-p3lxEf8Twj3MXGDQvyJMFubChxpKN767gucSq07iinEjSNDOACVNvoAUZr6MWoe3QVE_WRnxZ0Mf38b-hkIGlurX_MyehS7 +parent: tolerant-reader; artifact: tolerant-reader +Puml Server ID: NSZ14SCm20NHLf829ExfXaYChGn26lZ4xSVdtFRjSrZJx9AkZnFOyI9olkenSEOxGxmjWnXgMvE6viLWfmz_kNI9SLZP38XRqEIuWx1Kd0t5XVjjGVj_DNtMdLD_ +parent: event-driven-architecture; artifact: event-driven-architecture +Puml Server ID: TOhH3SCW30LNQGS0_tSRnrZ15H1adfFromBzkfFktZQaHT7mzgh0N1yYvoUVXXf7B7Mv1dGWozN9MZmCTlhopQdeidEaoO3wMDHvRI6zzvwAssPYbsfGGRYIGlxN7DxpZDv- +parent: publish-subscribe; artifact: publish-subscribe +Puml Server ID: PSZB3SCm203GLTe1RExT1XCKKs5YyMdMR--zFRsd66aTNAwFcRdZ1U1uzrDorgXWfykIBJjT2qJhnaI7Dtwm7HnoMjkOoMu12-C7s3LKOhQe4UGo63ZfVtlvwhkMVW40 +parent: facade; artifact: facade +Puml Server ID: BSP15eCm20N0gxG7CEoz3ILKqvTW7dpq-hhehERTJ7fMJU-l7PYn4ZbVPMlOyvEXBeT13KMEGQtdnM2d7v-yL8sssJ8PKBUWmV64lYnSbHJoRqaVPUReDm00 +parent: service-locator; artifact: service-locator +Puml Server ID: NSjB3iCm203HgxG7iDdtDeIWX0fZYqzo_MRTtUX9ynOZhPtBzNLchlW0EDxza3nhgs2dQScMdUO0qRenqU6B5xQTGmvh2pFPBM1WF07FSmbnqqcOqu6J_gsNZxvgw0y0 +parent: dao; artifact: dao +Puml Server ID: 5SR14OKW30N0LhG0oVrt4o6ZE12Ov4NR_thQNQlc5aN2sd82qtz4naywAixOmyNoK8WYvT6fjdWOR7JnpLiHhuTkam4nTUhiRwZm847-J64zpUZj3m00 +parent: model-view-presenter; artifact: model-view-presenter +Puml Server ID: ROlR3SGW3C1MkGu0-RzjKeXQJWcWFChwPO3xispvQBrmL0hbp-q-xGkWkFBL_8upZBICxjGzbo7GE1OwAlpmmLJ9sjNJH7VIRY1e6q169KvFevMcakrtI_BoD-HGoJE4Nm00 +parent: observer; artifact: observer +Puml Server ID: FSkn4OGm30NHLg00hFow4KO3PcpP8tr1-pYwx6smQz5Suv2mkbp0y1-HyPlEWYlsSB7S5Q98kJSgDLu66ztyy7Q8brEtmO2OEZNs2Uhxl9u9GVv72cjfHAiV +parent: intercepting-filter; artifact: intercepting-filter +Puml Server ID: RSfB3i8m303Hgy014k-vZN5DQkIuaJ_q-fGzkz7JtCL8Q-DolUsPAnu0ZcSVadizAzZfi6JBJiS4qJenqU6D7smRXmnh2pFPBM1YN05o_KwyKcoqb-ZFEEcVz_BPLqtz0W00 +parent: factory-method; artifact: factory-method +Puml Server ID: NSZB3G8n30N0Lg20n7UwCOxPP9MVx6TMT0zdRgEvjoazYeRrMmMsFuYChtmqr7Y6gycQq8aiQr3hSJ7OwEGtfwBUZfas0shJQR3_G2yMBFkaeQYha4B-AeUDl6FqBm00 +parent: private-class-data; artifact: private-class-data +Puml Server ID: RShR3SCm243HLTe1RFwx3S4eeSB4uf6itmpGlwkZ-nOZhS7b-ZeoLtm07E--InwrLR3JQScMdSu9edLZeiCNBso3GtPh2pFPBM1YF07BvSBaHeeHRJm_SD8VxkMphvhw0m00 +parent: async-method-invocation; artifact: async-method-invocation +Puml Server ID: TSdB3SCW303GLTe1mFTkunWhk0A3_4dKxTi5UdlIUuhIoCPfuz4Zjhy03EzwIlGyqjbeQR16fJL1HjuOQF362qjZbrFBnWWsTPZeFm3wHwbCZhvQ4RqMOSXIuA1_LzDctJd75m00 +parent: execute-around; artifact: execute-around +Puml Server ID: NSZ14G8n20NGLhI0XBlT865suoGa0n_NylNixSsxTvEHJTF7xGHsF8YShtfqdFdCK9TbK4ELDQcFl1ZizE8tbwRH3okR0NKBcXm_a7vK4bhOLreZXVnLJPzrvnnV +parent: monostate; artifact: monostate +Puml Server ID: HSV14OGm20NGLjO23FVj1YEZsGaa0nzjVxrvUszfLdlkaju_9p3ZI-HybwFXp2r3l0w364eTIgtdpM2d7r-yxXBji7Ko86v1ol60TDW8C8G4zLr9rp9J-ny0 +parent: thread-pool; artifact: thread-pool +Puml Server ID: JSV14SCW30J0Lk82GFzq8uF6a1624IUx_UIPt-xHhMXK2TTN0zP-4pa_-UfeSSOMBzCWXbpceAxnCDZfmpUdAhjVbXO3uhPfyFw1q5oufZMdag3yFuUFl6Be5m00 +parent: delegation; artifact: delegation +Puml Server ID: JSV14GCX20NGLf82LkxfXbN69OFeu2VRVdBCxRsdUhLiac6F2rZxHHHybwwuyimjKQT37ANEGMfvCpZepHy-ccpjVYm697pJuFq3DJ7f39rEWlhNaZ7Aoc5V +parent: chain; artifact: chain +Puml Server ID: 9SR13SCm20NGLTe1OkxTXX0KKzd4Wa-pVYlrdTxJN4OTMZ4U7LZv8Wg-ssdejLTgoELGHvDhaesw6HpqvWzlXwQTlYq6D3nfSlv2qjcS5F9VgvXjrHnV +parent: resource-acquisition-is-initialization; artifact: resource-acquisition-is-initialization +Puml Server ID: ZShR3S8m343HLUW0YV_PnhXMQvGumOzMOdhA1lqxkhgBABLSEQqzzeZfJm33isuIUxxIsMXei4QbqK5QdXXeyCO3oyekcvQ94MpgqD4lWB6FDEA2z4bn2HbQn8leHMponNy13hgvrhHUP_Rs0m00 +parent: fluentinterface; artifact: fluentinterface +Puml Server ID: NOj93eCm302_KXv0VEzlN6F0bMCYB_3zvjpRQ3IpY97MnkNwEZD7l04SdtP8dlMfOAVBaYqRNHr4wy54Xo_Uk6uSSjWwC9FT0Zh61DYrPY_pyXs9WPF-NIllRLJN7m00 +parent: service-layer; artifact: service-layer +Puml Server ID: LOl93SCm3C1MQGUmzUysgY8aAcJ5q96WszVV_aW2V8gHriRb-ZWoPxm07E--Inxrhc2dqv8jEvq3HEl6H8SFNjWs3jcjJSnaju21iG3MSmbnK_mkuwJ_qij7dpNq1m00 +parent: visitor; artifact: visitor +Puml Server ID: DSR14OGm20NGLhG0mtsxmSWeJa8oyD7sTo_xJczLgoqFIM_B1Spu43c_vLHSkMU8rs4GGwcZaxPy6UnqyyFR8Q6dRPC1SGlg7B_Gew4OJeBwVqdlPMPlNm00 +parent: double-dispatch; artifact: double-dispatch +Puml Server ID: NSbB3iCW303HgpG70Ezx6yTOWSeOv4zp_MRTtUZDCPGa6wV9gqTiVmCOtlKQqVDCPwEbmHgLreGXUMEWmGU_M1hxkBHiZ61JXud-1BILft1fmvz37JZetshQh3kd_000 +parent: monad; artifact: monad +Puml Server ID: 9SR13SCm20NGLPe1OkxTXjWeSMMm1Pza_LRgExsjMntP97syBc35cyZvAMV7bKU6U9q6CPGwbVh8Xy5E7xvvRnBzj7qn86v1ol4BwJHk9AZ_bNGjAtLy0G00 +parent: front-controller; artifact: front-controller +Puml Server ID: PSlB3OGm303HLfO24j-t6nCC13bEvC_IFk6yjz6JPgbIE3OAvS_fFkmBe7Zde_ePQnXfwU8adajlK3bkT5Iuy8Tf8wk7f87kf6BGq6R0hlD8xwQTUG9v-SCSslA8nWy0 +parent: strategy; artifact: strategy +Puml Server ID: FSV13OCm30NGLM00udktCS4AGOaJsTz5tRwSkBstLiqj3WbhombC_n0PtwbKdB67Y-MX44NAerDjSJFOwE8lRuTuBRfD1iJKgRC_88SnfFn8aD-ai9vczFO7 +parent: command; artifact: command +Puml Server ID: DSgn4OCm30NGLM00h3xR25i7vYpXaxx2-g59zugtTgiZcwIFvGHcV8YSdt9qdBbdYDVR88PIRwK-yc6mqyLVtff4FsoR38XRa7Aye3SgMoD1_RkaQvcfumS0 +parent: abstract-factory; artifact: abstract-factory +Puml Server ID: PSZB3OD034NHLa81Czwd6sCC39gVxEUWT1_ssLmTtQLqgR5fM7sTmFGtaV6TZu8prd0r6HtQaMKqAZLk1XjT_E6qgPUZfyc0MdTgx0-8LuUn8ErFXdr98NypXxKyezKV +parent: flux; artifact: flux +Puml Server ID: 7SP14eCm20NGg-W13FlU1YFLE0GpyAazVZk-rPkRLSrDqdKwW14l8kUxx0r7hXdYzJA8eTIhKzEy6UnqyeUNJQBjjWm6n2seS_n3Ryql2UgJajxBoAu_ +parent: event-aggregator; artifact: event-aggregator +Puml Server ID: PSf13iCW30NHgxG70Ezx6uTOX0eCih-JwvTzTwEdUJSjFKu9wwyBMFuXCdvoRRZY21ShKo6ANEQWrkDXiD6NRqwdUAkQ5WDYwZJOTv3SUqzSgqbbp0qeVvZ3Hbun-Wy0 +parent: singleton; artifact: singleton +Puml Server ID: HSV14SCm20J0Lk82BFxf1ikCh0n26ZZizfDVVhjRjwfvIhg-Bc35cyZvAQtZoYD3l4w364gTWxhcms2d3z-ydnAzsRuO4BUWmV43HRUcWcaagF-Lz55M3lq2 +parent: null-object; artifact: null-object +Puml Server ID: JSV14SCm20J0Lk829Fxf1cF6bWSX3JhYzfDdVhjRSx4yDCDU5p3NcoZugMV3bNik3HaETLGPdPhbm-2WcpzS3btjz38PqF15dTSFv6bMndwhW1Jo_vhHwynkNm00 +parent: multiton; artifact: multiton +Puml Server ID: FST14i8m20NGg-W16lRUXgPCYnD81Zxs-hfozzvJlOywf68yBc3bYoZuRgVYghrIea-7E5gVHZhgPd3Gcp-y7P9w-hOOaF0au_o1h0OKqqdG_saLrbRP-080 +parent: composite; artifact: composite +Puml Server ID: HSf13eCm30NHgy01YFUzZGaM62LEP7-NwvTTT_EaMTLgoqFIst81Cpv4payv5LVk6U9r6CHGwkYaBHy6EztyvUsGqDEsoO2u1NMED-WTvmY5aA3-LT9xcTdR3m00 +parent: api-gateway; artifact: image-microservice +Puml Server ID: 3Sp13SCm2030LTe1RFxTXX3aK1biOOZLxPlVlUujHZrFJk-lAsAk3u3ZhatYoYCNEmqBjgWq5AJdna27BzvOJbxIh4oCOBS5Yki1u9JIC7ZZ3pW8HB5nKI4VJtSBSKtNEbFx7m00 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'api-gateway/etc/image-microservice.urm.puml' +pumlid: 3Sp13SCm2030LTe1RFxTXX3aK1biOOZLxPlVlUujHZrFJk-lAsAk3u3ZhatYoYCNEmqBjgWq5AJdna27BzvOJbxIh4oCOBS5Yki1u9JIC7ZZ3pW8HB5nKI4VJtSBSKtNEbFx7m00 +parent: api-gateway; artifact: api-gateway-service +Puml Server ID: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'api-gateway/etc/api-gateway-service.urm.puml' +pumlid: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0 +parent: api-gateway; artifact: price-microservice +Puml Server ID: 3Sn13iGW243HgqmFeEpdDfGIoqJK8DJqzkFklyq_f56DYyFgvtOVymjWk78Hl-ECoKQzEJVFr1Mana97Wny-c2wUKbeQwCxM9YZE7O13Ka7dXI-m4mmJugH2rlVksSXXcaTe_GC0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'api-gateway/etc/price-microservice.urm.puml' +pumlid: 3Sn13iGW243HgqmFeEpdDfGIoqJK8DJqzkFklyq_f56DYyFgvtOVymjWk78Hl-ECoKQzEJVFr1Mana97Wny-c2wUKbeQwCxM9YZE7O13Ka7dXI-m4mmJugH2rlVksSXXcaTe_GC0 +parent: object-pool; artifact: object-pool +Puml Server ID: JSV94SCm2030Lk829Fxf1cF6bWU1XYDkFtdcjxiD9Qc3o-LrPQvu0pW-_HnvrLx1JgR9cfrimf1wCD7XnW-sWsESsXPcicl0nFW1RB-PiYqp0KxwVo-VVTMKBm00 +parent: adapter; artifact: adapter +Puml Server ID: DSR14S8m30J0Lg20M7-wEMnDOiPMFDA9j0yyUEtUkzMHJTF7xI1NF4GSLzaxZtncgDVJgCPIpobzv0N2vOKtjgRHTziMI7KBcOXl10thfxB-Nz9dMJd71m00 +parent: hexagonal; artifact: hexagonal +Puml Server ID: HSTB4W8X30N0g-W1XkozpPD90LO8L3wEnzUTk-xxq2fvSfhSUiJs1v7XAcr4psSwMrqQh57gcZGaBmICNdZZEDb7qsCZWasT9lm7wln1MmeXZlfVIPjbvvGl +parent: value-object; artifact: value-object +Puml Server ID: LSZ13SCm20NGLTe1RExTXX2KECBOmfza_VRQszDxDnVBNJFiTG9pVOY2dteqdBdbqf3XK4ULqQbPFWmEklZcikjgXvV9W8Olwhn-e9ijjOpjKW4fv2zgHgypktq1 +parent: twin; artifact: twin +Puml Server ID: 7SR13OCm30NGLUW0n7UsCS42eyH4zdUpFbNVwNtKQij3qjjo0ICs8kTPJiMLUuPuVGnYAFNff2qdWvrk_l9wIEXfws10t88wno-4gKQ2-az9xsLaRoy0 +parent: semaphore; artifact: semaphore +Puml Server ID: HSV14SCm20J0Lk82BFxf1ikCfOn06ZZizfDVVhjRjphobFJnQi2ADv7pKwwEbaU6U9q6CPGwbVh8Xy5E7xvvFoNwPVjYGDo2bEC72b5URRgGeFvNqhMirF45 +parent: message-channel; artifact: message-channel +Puml Server ID: NSZB3SCm203GLTe1RExTXX1akm9YyMdMRy-zFRtdCf8wkLmUCtF72y3nxcFbhAE2dIvBjknqAIof6nCTtlZ1TdAiOMrZ9hi5ACOFe1o1WnjDD6C1Jlg_NgvzbyeN +parent: poison-pill; artifact: poison-pill +Puml Server ID: JSZ14SCm20NHLf82BExfXiYCJGOX3NpYzkDZRllsgTwjTgcmnmciV145N-rGdFMkbEZJ8OxMvo2rkXWSzE4lRxka7huj1YGyQN3UGMjgpdkh6Gdwlrl5QAk6_G00 +parent: aggregator-microservices; artifact: aggregator-service +Puml Server ID: JOov3SCm301NIGQGs7iRXYPa1g8ayB7NjuiKwGvtmBrbKC-Tq_hhY5Y-0HXUjKaS-Kbdepc2HrIQ2jBpma23BvvOTdPfeooCO1iEYlu0O6l63MDQKI6Rp-CKOWSE-ey_NzEqhjH-0m00 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'aggregator-microservices/etc/aggregator-service.urm.puml' +pumlid: JOov3SCm301NIGQGs7iRXYPa1g8ayB7NjuiKwGvtmBrbKC-Tq_hhY5Y-0HXUjKaS-Kbdepc2HrIQ2jBpma23BvvOTdPfeooCO1iEYlu0O6l63MDQKI6Rp-CKOWSE-ey_NzEqhjH-0m00 +parent: aggregator-microservices; artifact: information-microservice +Puml Server ID: LSnB3i8m303Hgy016k-vZN5DQXGxaJ_jzUcMtKXFcgSOZTgvV3oEp1Kl0CUhTScZtXNiD2tPij5Ka54N9ZfyySHjvv1ksy9CTWjGZ3i0UtVkcDCt5V9vFquX3k0a4FjCLqoPzgUjNDig7Jy0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'aggregator-microservices/etc/information-microservice.urm.puml' +pumlid: LSnB3i8m303Hgy016k-vZN5DQXGxaJ_jzUcMtKXFcgSOZTgvV3oEp1Kl0CUhTScZtXNiD2tPij5Ka54N9ZfyySHjvv1ksy9CTWjGZ3i0UtVkcDCt5V9vFquX3k0a4FjCLqoPzgUjNDig7Jy0 +parent: aggregator-microservices; artifact: inventory-microservice +Puml Server ID: LSpB3G8n303HLg20ZUzqOxnMrYXn8d-oedjovJRIIEyfIYrFJckFAsBw2y3mBbNYodSw6mqDrYWqEaZB6mCDFhZmEDcbwZ4nWaqTEleEm5gDAyQmemlPsCOIOWSE0j6riM7VlrVIUfdPsmy0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'aggregator-microservices/etc/inventory-microservice.urm.puml' +pumlid: LSpB3G8n303HLg20ZUzqOxnMrYXn8d-oedjovJRIIEyfIYrFJckFAsBw2y3mBbNYodSw6mqDrYWqEaZB6mCDFhZmEDcbwZ4nWaqTEleEm5gDAyQmemlPsCOIOWSE0j6riM7VlrVIUfdPsmy0 +parent: bridge; artifact: bridge +Puml Server ID: BSR14SCm20J0Lf82BFxf1akCJ4R26ZZYzkE7zxLljJgoIVfu7S2A3v7pLRhYo3r3l9u6CPHwJjAH5uETllpZhKbejsqn86v1a-CExQwj2mdgqv8-oyev_W00 +parent: servant; artifact: servant +Puml Server ID: DSkn4O0m20NGLNG0G-ys63cDbv0SV7HzRUnUy-QYkSOkONKwWU4haV6JZe8pjd2nt1MYIBatAZKU1XjTVFEoYvT3by60c3erzW_qdPiL9CY_KrXB8rfz0G00 +parent: lazy-loading; artifact: lazy-loading +Puml Server ID: LSXB3W8X303Gg-W1e7jlqu66gIc5zED4JwzRTo_lpjeaEwN9xOpO_W0mlEhWEFD89sjBWpHgMnDOyi90WoU-i7Ho7besHf2fmqJ_0GG_xo8BE-i0YlONDMtMdLE- +parent: flyweight; artifact: flyweight +Puml Server ID: HSV94S8m3030Lg20M7-w4OvYAoCh7Xtnq3ty-Eq-MQlaJcdow17JNm26gpIEdkzqidffa4Qfrm2MN1XeSEADsqxEJRU94MJgCD1_W4C-YxZr08hwNqaRPUQGBm00 +parent: mutex; artifact: mutex +Puml Server ID: 9SR13OCm30NGLSe0n7UsCS62LB69x6zWV2hrdTxKhFRS9Br_3c34GkHybxtXo3L3l9u6CPHwAhMUDuETldpnl4cqtUR1WBW5ASSlf0bvI53_A-bQHcf_0G00 +parent: mediator; artifact: mediator +Puml Server ID: FSV14SCm20J0Lk82BFxf1akCJKOW3JhizfDNVhkRUktP9AE_Bc2kDr7mKqx5bKSkYJeSuYXr66dFXy517xvvRxBqz7qo8E6BZDSFPDAKCO84zP-IOMMczIy0 +parent: page-object; artifact: page-object +Puml Server ID: JSV14OGW30NGLjO28FVj9iOCua1Wme-sxnxtzjvMJLeS6ju-9p3NbyZvoQNYZ3sMkWo36hACJhN5ms2dYszEXwvQB4q6r6rHv_K3JIwQndwfW1Jo_npUyupUNW00 +parent: factory-kit; artifact: factory-kit +Puml Server ID: JST15i8m20N0g-W14lRU1YcsQ4BooCS-RwzBTpDNSscvQKQx7C1SDwBWi-w68--vD6Gur55bTBAM9uE3dlpcikcotSjaGCCNTLu_q8C58pxbPI25_Bzcz3gpjoy0 +parent: property; artifact: property +Puml Server ID: FSV13OCm30NGLTe1YEziumOBKYMEPN-3s9wUUdlltRJst2Izlmx0OYLolihUSEGdGxnEXIXAdODQpul1Jby-UTaasgwBCI2kGOFZ1pAV9ewR1FMVaZwAvUWF +parent: dependency-injection; artifact: dependency-injection +Puml Server ID: RSdB3SCW303GLPe1mFTkunWhSGG6-PEesxS3zFQajubIpyPf_NL6B7y363xra3XpJsUZgS4QbUO0wVbWeC65DvR6BeUMXH5iwZ3GVu36YxMnqgU8NamXKu63_aPD6tNbw5y0 +parent: layers; artifact: layers +Puml Server ID: BSR13OCm30NGLSe0n7UsCS62L8w9x6yGszD3t-bDpQhc9kdwEO0H2v7pNVQ68zSCyNeQn53gsQbftWns-lB5yoRHTfi70-8Mr3b-8UL7F4XG_otflOpi-W80 +parent: producer-consumer; artifact: producer-consumer +Puml Server ID: PSjB3iCW303HgxG70Ezx6zTO2HKso9_a-c7VtUX9y-vA8nkdZTSPiVm3O7ZNeyUPttGscXgiKMaAz94t1XhyyCBIsFkXPM44cpe8-WvODbiIMzcdfspXe7-jQL9NodW0 +parent: builder; artifact: builder +Puml Server ID: DSR94O0m2030LhG0mzzkC64KXs26GzlNZw_TcRLADagJwOWOlW8OFcNdE79B9wkN1ccKUdLWoGS33KwySMdalEioC89C7Jhw5zYIfNrIrFybhPUHNLu0 +parent: specification; artifact: specification +Puml Server ID: LSX14i8m20NGg-W16lRU1YcsE0d9mCTUNxVkthoxkVJQjQBVJc3bWoZuQeVXh6UbXao7EfhCGTRhOd3Gcp-yxPfs-BOOqF2amVa3vLAnbmd3ffD2_gTLZBPgz2y0 +parent: state; artifact: state +Puml Server ID: 9SRH3O0m20N0LNG0ox_RO2LQqz867hg-9jxNpKLpZLt2wdG2mrSYuoST1MTiuMAvAqIHSczKQZmCDhhuvcKNBuSkWm4nTMhiNyZ141BaVocifH6jlW00 +parent: reader-writer-lock; artifact: reader-writer-lock +Puml Server ID: RSZB4S8m303HLg00MtUw4R8cCP5bZpwuVL80jttxx4gIZTFaSKOiVm4OxdhqEFETpaPJWpKgpG5TScEWmGU_M1fxFxGiZ61JXu5-1nXZOolR-gqYaoxWe3-xfeswSiWF +parent: interpreter; artifact: interpreter +Puml Server ID: JSf13eCm30NHgz034E-vZGaM62Kcih_BzQ6xxjv8yr6hBJT9RzC1Z5Y8dE-oAuvSCyJhPH13gLSdRNapsEdaBy-RXEus3mR4BQXpl21zVnykFmlgVvVqNaRszW00 +parent: template-method; artifact: template-method +Puml Server ID: NSZ13SCW30NGLPe1mFTkuu0Lg6n0vZjPlpttzlIEFef6bN1zDM3jDv7paw-E5cTiyJ87P22NQTGr7WOxVVZcL6NtQwJ5WFZOPBn_88WjPKWoGPkL1EN_ShZb5QPV +parent: feature-toggle; artifact: feature-toggle +Puml Server ID: NSZ14G8X30NGLhG0oDrk8XjPd12OvCTjNy_UthpxiAPvIBhUJc37WyZvgdtWp6U6U5i6CTIs9WtDYy5ER_vmEIH6jx8P4BUWoV43lOIHBWMhTnKIjB-gwRFkdFe5 +parent: business-delegate; artifact: business-delegate +Puml Server ID: POl13SCm3CHMQGU8zUysgYCuBcJ5a4x9-l6_Fu84tzsgvYxf-Zg06HyYvxkqZYE_6UBrD8YXr7DGrxmPxFJZYxTTeZVR9WFY5ZGu5j2wkad4wYgD8IIe_xQaZp9pw0C0 +parent: naked-objects; artifact: naked-objects-integtests +Puml Server ID: LSmn4iCW30NHgoG70FMvZGmQ6ni48tt5ru_RT3kls7VJqgDAM7yTmF8FaV6TzuOZjd2nCXMYo6KEQZrk1XkT_ELKnTkkQJ4Wfaw3_GbIlgIckPrIu2Ge_vBQyziX3izX8wyO_GS0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'naked-objects/etc/naked-objects-integtests.urm.puml' +pumlid: LSmn4iCW30NHgoG70FMvZGmQ6ni48tt5ru_RT3kls7VJqgDAM7yTmF8FaV6TzuOZjd2nCXMYo6KEQZrk1XkT_ELKnTkkQJ4Wfaw3_GbIlgIckPrIu2Ge_vBQyziX3izX8wyO_GS0 +parent: naked-objects; artifact: naked-objects-dom +Puml Server ID: LSZ94SCW3030Lf82G7zt8mkDZOC4eyDkF_dcjxFlhZIoSTfudH7BDm33fnuzpjpJsMXgi4QbAT17FXXeSE6DfR7tGyl223Pr4FGVGF73hSpzOWe73lgVqgRKDAahPNm1 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'naked-objects/etc/naked-objects-dom.urm.puml' +pumlid: LSZ94SCW3030Lf82G7zt8mkDZOC4eyDkF_dcjxFlhZIoSTfudH7BDm33fnuzpjpJsMXgi4QbAT17FXXeSE6DfR7tGyl223Pr4FGVGF73hSpzOWe73lgVqgRKDAahPNm1 +parent: naked-objects; artifact: naked-objects-fixture +Puml Server ID: LSX15i8W30N0g-W187jlaq9igH1uoO_r-BfrDs_kJKkFAc7zTW3B7qJ6LzuRZjZ2nSfKY2ANEQZrk1XiTFARKnLlkwR5W9Ww3VOVIFabDStjb08dGVcVz6mVX4aE6td5w5y0 +I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file 'naked-objects/etc/naked-objects-fixture.urm.puml' +pumlid: LSX15i8W30N0g-W187jlaq9igH1uoO_r-BfrDs_kJKkFAc7zTW3B7qJ6LzuRZjZ2nSfKY2ANEQZrk1XiTFARKnLlkwR5W9Ww3VOVIFabDStjb08dGVcVz6mVX4aE6td5w5y0 +parent: model-view-controller; artifact: model-view-controller +Puml Server ID: ROl13SCm201NQGUm-NSRQgE42h258Lw_wR-_qvtkoTOaEwNBuuoOwmNWkEl1SUOx5taR5cHHsr1WoOs13X-yi7HQV5YP645k2nJN3Q2ZavIBQPVVwqFajXJjVwdfMcUgV040 +parent: proxy; artifact: proxy +Puml Server ID: 9SR13OCm30NGLM00udktCS62eCI9x6yesrEfx_Jcehd69c5rEe3X7oBZE-q5HwpXOhahH95oRrHgt0msEldYPHClkow30J5rQko_qB3-VKYG_qjXBOrezGK0 +parent: memento; artifact: memento +Puml Server ID: DSgn4OCm30NGLM00h3xR2AC3SvRiaxx2-g59zugtDgiz3qdlomNC-10vF-Lik7BF4A_388PIXrBh-J3OwUOlRuT4EssR38XRa7Ay81Lz_o11_RkaQvcf_GS0 +parent: decorator; artifact: decorator +Puml Server ID: HSV14SCm20J0Lk82BFxf1YF6LaP26ZZizfDVVhjRC-bPDRs_Bc35cyZvAMV3bKU6kao36ehCGQtdms2d3z-yLursshuOKBUWmV43LPNfZEcaaFzA-YWhH_y2 +parent: data-mapper; artifact: data-mapper +Puml Server ID: JShB3OGm303HLg20nFVjnYGM1CN6ycTfVtFSsnjfzY5jPgUqkLqHwXy0mxUU8wuyqidQ8q4IjJqCO-QBWGOtVh5qyd5AKOmW4mT6Nu2-ZiAekapH_hkcSTNa-GC0 +parent: caching; artifact: caching +Puml Server ID: DSRB4OKm2030LhG0m_rrWyWaE0bc-6ZxpujxsbMKUXwSrfSMCVq7OFYKAj5oJsUZIuCr2bq3fEU3WGOdthWTx59rcnZ1fWu3_GqGKXEjm47VIzeeCqV_0m00 +parent: reactor; artifact: reactor +Puml Server ID: DSR14OGm20NGLjO23FVj1f7Hx2Ga0nzjVxtuJc-f9YrtJM-V4vZn9NA-or5nvfQXBiEWXYAZKsrvCzZfnnUlkqOzR9qCg5jGvtX2hYmOJWfvNz9xcTdR7m00 +parent: iterator; artifact: iterator +Puml Server ID: FSV13OGm30NHLg00uljsOu85HeaJsTzB-yjfBwCtgrfjUKXwMovWneV8-IcduiezGxmEWnXA7PsqvSDWfvk_l1qIUjes6H2teCxnWlGDOpW9wdzAUYypU_i1 +parent: callback; artifact: callback +Puml Server ID: FSVB4S8m30N0Lg20M7UwUL4qYOciUFGXxSE9s-wp6sjjKgwF8tF6YyXnjxtdKMk5E5-MOjdu6jIrRYIStlXWsIJwRij4fhW53SGFn51TmIT9yZ-jVBHPGxy0 +parent: repository; artifact: repository +Puml Server ID: JSV13OCm30NGLM00udktCS42eyI9xE-YRjyUUtjlLQij3qblomNCU14vF-LKNBbdYDTX44EfevEsV1ZiTFERjqD2Jzic0-8Mr3b-89SvGZ7yGuBwrvBUoypUlW00 +parent: mute-idiom; artifact: mute-idiom +Puml Server ID: JSf13iCm20NHgxG7iDdtDjH62PKX5luarq-MtSsJvgtUHdR96AyTcEj357pLJR7dDvT4EnpYgEqmqf4NWuD-V7BfidJpCXcGy4N6wmcoX1Jj-lo2ziUQONMcZHi0 +parent: prototype; artifact: prototype +Puml Server ID: HSV13OCm30NGLM00udktCS62eCInxE-YRj_UUdjlRLfx7fBUbmkmU14vF-Lik7BF4AzJ8OfIvw3Mys6mqyrltWw9Tkfc38XhqE3uWSmd9Zuc9AZ_bVHHB4V_0W00 +parent: step-builder; artifact: step-builder +Puml Server ID: LOZ93SCm3C1MQGQmzUysYYqaAcJ5q96i7t_x8KXkh4soKvfypeZfNm33fnuSP-xfPEtI88tQhW4i-M2WmGzlB9sS3oqJ8yZKOQ0lWOLPzcJfAoZQtwXfeyuSyW80 +parent: double-checked-locking; artifact: double-checked-locking +Puml Server ID: TSdH4SCW203GLTe1bFzkGv1J6qGFeLc_MI1_x-wzkv94uJ1vDVUrFm26LwxTMnonsMYgitgcEQ1BNEXeyCKVfiAxLqqBtTbqmy1z0ygCGpXHOpgv99bqTgt0JW-LmqPUCUGF diff --git a/_scripts/postPumlsToServer.py b/_scripts/postPumlsToServer.py new file mode 100644 index 000000000..2f975d3d8 --- /dev/null +++ b/_scripts/postPumlsToServer.py @@ -0,0 +1,44 @@ +import requests, glob, re, os + +# taken from here: http://stackoverflow.com/a/13641746 +def replace(file, pattern, subst): + # Read contents from file as a single string + file_handle = open(file, 'r') + file_string = file_handle.read() + file_handle.close() + + # Use RE package to allow for replacement (also allowing for (multiline) REGEX) + file_string = (re.sub(pattern, subst, file_string)) + + # Write contents to file. + # Using mode 'w' truncates the file. + file_handle = open(file, 'w') + file_handle.write(file_string) + file_handle.close() + +# list of all puml files +fileList = glob.glob('*/etc/*.puml') +for puml in fileList: + pathSplit = puml.split("/") + # parent folder + parent = pathSplit[0] + # individual artifact/project name + artifact = pathSplit[2].replace(".urm.puml", "") + print "parent: " + parent + "; artifact: " + artifact + + # do a POST to the official plantuml hosting site with a little trick "!includeurl" and raw github content + data = { + 'text': "!includeurl https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/" + puml + } + r = requests.post('http://plantuml.com/plantuml/uml', data=data) + pumlId = r.url.replace("http://plantuml.com/plantuml/uml/", "") + + # the only thing needed to get a png/svg/ascii from the server back + print "Puml Server ID: " + pumlId + + # add the id so jekyll/liquid can use it + if (parent == artifact): + replace("./" + parent + "/README.md", "categories:", "pumlid: {}\\ncategories:".format(pumlId)) + else: + print "I dont want to program this, just add the following lines to the README.md file that corresponds to this puml file '" + puml + "'\npumlid: {}".format(pumlId) + From 58dce1bd89a5b448b238ec3e0fe8adc77b15c41f Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Tue, 30 Aug 2016 15:10:34 +0200 Subject: [PATCH 032/145] Work on #190: Commit initial pumlId's added by the script --- abstract-document/README.md | 1 + abstract-factory/README.md | 1 + adapter/README.md | 1 + aggregator-microservices/README.md | 1 + api-gateway/README.md | 1 + async-method-invocation/README.md | 1 + bridge/README.md | 1 + builder/README.md | 1 + business-delegate/README.md | 1 + caching/README.md | 1 + callback/README.md | 1 + chain/README.md | 1 + command/README.md | 1 + composite/README.md | 1 + dao/README.md | 1 + data-mapper/README.md | 1 + decorator/README.md | 1 + delegation/README.md | 1 + dependency-injection/README.md | 1 + double-checked-locking/README.md | 1 + double-dispatch/README.md | 1 + event-aggregator/README.md | 1 + event-driven-architecture/README.md | 1 + execute-around/README.md | 1 + facade/README.md | 1 + factory-kit/README.md | 1 + factory-method/README.md | 1 + feature-toggle/README.md | 1 + fluentinterface/README.md | 1 + flux/README.md | 1 + flyweight/README.md | 1 + front-controller/README.md | 1 + half-sync-half-async/README.md | 1 + hexagonal/README.md | 1 + intercepting-filter/README.md | 1 + interpreter/README.md | 1 + iterator/README.md | 1 + layers/README.md | 1 + lazy-loading/README.md | 1 + mediator/README.md | 1 + memento/README.md | 1 + message-channel/README.md | 1 + model-view-controller/README.md | 1 + model-view-presenter/README.md | 1 + monad/README.md | 1 + monostate/README.md | 1 + multiton/README.md | 1 + mute-idiom/README.md | 1 + mutex/README.md | 1 + naked-objects/README.md | 1 + null-object/README.md | 1 + object-pool/README.md | 1 + observer/README.md | 1 + page-object/README.md | 1 + poison-pill/README.md | 1 + private-class-data/README.md | 1 + producer-consumer/README.md | 1 + property/README.md | 1 + prototype/README.md | 1 + proxy/README.md | 1 + publish-subscribe/README.md | 1 + reactor/README.md | 1 + reader-writer-lock/README.md | 1 + repository/README.md | 1 + resource-acquisition-is-initialization/README.md | 1 + semaphore/README.md | 1 + servant/README.md | 1 + service-layer/README.md | 1 + service-locator/README.md | 1 + singleton/README.md | 1 + specification/README.md | 1 + state/README.md | 1 + step-builder/README.md | 1 + strategy/README.md | 1 + template-method/README.md | 1 + thread-pool/README.md | 1 + tolerant-reader/README.md | 1 + twin/README.md | 1 + value-object/README.md | 1 + visitor/README.md | 1 + 80 files changed, 80 insertions(+) diff --git a/abstract-document/README.md b/abstract-document/README.md index bf28ff999..c8755ce07 100644 --- a/abstract-document/README.md +++ b/abstract-document/README.md @@ -3,6 +3,7 @@ layout: pattern title: Abstract Document folder: abstract-document permalink: /patterns/abstract-document/ +pumlid: PSjB3eCm34NHhPG599vtDyQn85L-ifzX-p3lxEf8Twj3MXGDQvyJMFubChxpKN767gucSq07iinEjSNDOACVNvoAUZr6MWoe3QVE_WRnxZ0Mf38b-hkIGlurX_MyehS7 categories: Structural tags: - Java diff --git a/abstract-factory/README.md b/abstract-factory/README.md index 485599b98..2a8fecd36 100644 --- a/abstract-factory/README.md +++ b/abstract-factory/README.md @@ -3,6 +3,7 @@ layout: pattern title: Abstract Factory folder: abstract-factory permalink: /patterns/abstract-factory/ +pumlid: PSZB3OD034NHLa81Czwd6sCC39gVxEUWT1_ssLmTtQLqgR5fM7sTmFGtaV6TZu8prd0r6HtQaMKqAZLk1XjT_E6qgPUZfyc0MdTgx0-8LuUn8ErFXdr98NypXxKyezKV categories: Creational tags: - Java diff --git a/adapter/README.md b/adapter/README.md index ea3baa7fa..66ca63826 100644 --- a/adapter/README.md +++ b/adapter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Adapter folder: adapter permalink: /patterns/adapter/ +pumlid: DSR14S8m30J0Lg20M7-wEMnDOiPMFDA9j0yyUEtUkzMHJTF7xI1NF4GSLzaxZtncgDVJgCPIpobzv0N2vOKtjgRHTziMI7KBcOXl10thfxB-Nz9dMJd71m00 categories: Structural tags: - Java diff --git a/aggregator-microservices/README.md b/aggregator-microservices/README.md index e65f26d9a..75fe373fd 100644 --- a/aggregator-microservices/README.md +++ b/aggregator-microservices/README.md @@ -3,6 +3,7 @@ layout: pattern title: Aggregator Microservices folder: aggregator-microservices permalink: /patterns/aggregator-microservices/ +pumlid: JOov3SCm301NIGQGs7iRXYPa1g8ayB7NjuiKwGvtmBrbKC-Tq_hhY5Y-0HXUjKaS-Kbdepc2HrIQ2jBpma23BvvOTdPfeooCO1iEYlu0O6l63MDQKI6Rp-CKOWSE-ey_NzEqhjH-0m00 categories: Architectural tags: - Java diff --git a/api-gateway/README.md b/api-gateway/README.md index 23014ae0b..93b975e13 100644 --- a/api-gateway/README.md +++ b/api-gateway/README.md @@ -3,6 +3,7 @@ layout: pattern title: API Gateway folder: api-gateway permalink: /patterns/api-gateway/ +pumlid: JSox3SCm303HLP819FRUXg49cO542_nOyFPncUvUSszHwhbpMdyT4TCt0CDLcyIHdtGsEZLOez8vG7ek33JuueLbPvUcPM84cpeCz2S0fvI6mGjluA1_b-Tt2N5D6tNcw3y0 categories: Architectural tags: - Java diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md index 2d99820c5..b96c1d77a 100644 --- a/async-method-invocation/README.md +++ b/async-method-invocation/README.md @@ -3,6 +3,7 @@ layout: pattern title: Async Method Invocation folder: async-method-invocation permalink: /patterns/async-method-invocation/ +pumlid: TSdB3SCW303GLTe1mFTkunWhk0A3_4dKxTi5UdlIUuhIoCPfuz4Zjhy03EzwIlGyqjbeQR16fJL1HjuOQF362qjZbrFBnWWsTPZeFm3wHwbCZhvQ4RqMOSXIuA1_LzDctJd75m00 categories: Concurrency tags: - Java diff --git a/bridge/README.md b/bridge/README.md index 49dad14e4..6c1e70631 100644 --- a/bridge/README.md +++ b/bridge/README.md @@ -3,6 +3,7 @@ layout: pattern title: Bridge folder: bridge permalink: /patterns/bridge/ +pumlid: BSR14SCm20J0Lf82BFxf1akCJ4R26ZZYzkE7zxLljJgoIVfu7S2A3v7pLRhYo3r3l9u6CPHwJjAH5uETllpZhKbejsqn86v1a-CExQwj2mdgqv8-oyev_W00 categories: Structural tags: - Java diff --git a/builder/README.md b/builder/README.md index 5d1f3d24d..6a661502f 100644 --- a/builder/README.md +++ b/builder/README.md @@ -3,6 +3,7 @@ layout: pattern title: Builder folder: builder permalink: /patterns/builder/ +pumlid: DSR94O0m2030LhG0mzzkC64KXs26GzlNZw_TcRLADagJwOWOlW8OFcNdE79B9wkN1ccKUdLWoGS33KwySMdalEioC89C7Jhw5zYIfNrIrFybhPUHNLu0 categories: Creational tags: - Java diff --git a/business-delegate/README.md b/business-delegate/README.md index e6e249122..8e6e3456c 100644 --- a/business-delegate/README.md +++ b/business-delegate/README.md @@ -3,6 +3,7 @@ layout: pattern title: Business Delegate folder: business-delegate permalink: /patterns/business-delegate/ +pumlid: POl13SCm3CHMQGU8zUysgYCuBcJ5a4x9-l6_Fu84tzsgvYxf-Zg06HyYvxkqZYE_6UBrD8YXr7DGrxmPxFJZYxTTeZVR9WFY5ZGu5j2wkad4wYgD8IIe_xQaZp9pw0C0 categories: Business Tier tags: - Java diff --git a/caching/README.md b/caching/README.md index 2b89d0559..6432ffcea 100644 --- a/caching/README.md +++ b/caching/README.md @@ -3,6 +3,7 @@ layout: pattern title: Caching folder: caching permalink: /patterns/caching/ +pumlid: DSRB4OKm2030LhG0m_rrWyWaE0bc-6ZxpujxsbMKUXwSrfSMCVq7OFYKAj5oJsUZIuCr2bq3fEU3WGOdthWTx59rcnZ1fWu3_GqGKXEjm47VIzeeCqV_0m00 categories: Other tags: - Java diff --git a/callback/README.md b/callback/README.md index a408fd5e0..278aa9b0a 100644 --- a/callback/README.md +++ b/callback/README.md @@ -3,6 +3,7 @@ layout: pattern title: Callback folder: callback permalink: /patterns/callback/ +pumlid: FSVB4S8m30N0Lg20M7UwUL4qYOciUFGXxSE9s-wp6sjjKgwF8tF6YyXnjxtdKMk5E5-MOjdu6jIrRYIStlXWsIJwRij4fhW53SGFn51TmIT9yZ-jVBHPGxy0 categories: Other tags: - Java diff --git a/chain/README.md b/chain/README.md index ef18f6f64..5ab4b2256 100644 --- a/chain/README.md +++ b/chain/README.md @@ -3,6 +3,7 @@ layout: pattern title: Chain of responsibility folder: chain permalink: /patterns/chain/ +pumlid: 9SR13SCm20NGLTe1OkxTXX0KKzd4Wa-pVYlrdTxJN4OTMZ4U7LZv8Wg-ssdejLTgoELGHvDhaesw6HpqvWzlXwQTlYq6D3nfSlv2qjcS5F9VgvXjrHnV categories: Behavioral tags: - Java diff --git a/command/README.md b/command/README.md index a5478394c..9fb568f62 100644 --- a/command/README.md +++ b/command/README.md @@ -3,6 +3,7 @@ layout: pattern title: Command folder: command permalink: /patterns/command/ +pumlid: DSgn4OCm30NGLM00h3xR25i7vYpXaxx2-g59zugtTgiZcwIFvGHcV8YSdt9qdBbdYDVR88PIRwK-yc6mqyLVtff4FsoR38XRa7Aye3SgMoD1_RkaQvcfumS0 categories: Behavioral tags: - Java diff --git a/composite/README.md b/composite/README.md index 8b980292d..fce6ed6af 100644 --- a/composite/README.md +++ b/composite/README.md @@ -3,6 +3,7 @@ layout: pattern title: Composite folder: composite permalink: /patterns/composite/ +pumlid: HSf13eCm30NHgy01YFUzZGaM62LEP7-NwvTTT_EaMTLgoqFIst81Cpv4payv5LVk6U9r6CHGwkYaBHy6EztyvUsGqDEsoO2u1NMED-WTvmY5aA3-LT9xcTdR3m00 categories: Structural tags: - Java diff --git a/dao/README.md b/dao/README.md index 785a1c362..b1b655edf 100644 --- a/dao/README.md +++ b/dao/README.md @@ -3,6 +3,7 @@ layout: pattern title: Data Access Object folder: dao permalink: /patterns/dao/ +pumlid: 5SR14OKW30N0LhG0oVrt4o6ZE12Ov4NR_thQNQlc5aN2sd82qtz4naywAixOmyNoK8WYvT6fjdWOR7JnpLiHhuTkam4nTUhiRwZm847-J64zpUZj3m00 categories: Persistence Tier tags: - Java diff --git a/data-mapper/README.md b/data-mapper/README.md index 075e8eece..362f19c51 100644 --- a/data-mapper/README.md +++ b/data-mapper/README.md @@ -3,6 +3,7 @@ layout: pattern title: Data Mapper folder: data-mapper permalink: /patterns/data-mapper/ +pumlid: JShB3OGm303HLg20nFVjnYGM1CN6ycTfVtFSsnjfzY5jPgUqkLqHwXy0mxUU8wuyqidQ8q4IjJqCO-QBWGOtVh5qyd5AKOmW4mT6Nu2-ZiAekapH_hkcSTNa-GC0 categories: Persistence Tier tags: - Java diff --git a/decorator/README.md b/decorator/README.md index 63795114c..e65b0eb9e 100644 --- a/decorator/README.md +++ b/decorator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Decorator folder: decorator permalink: /patterns/decorator/ +pumlid: HSV14SCm20J0Lk82BFxf1YF6LaP26ZZizfDVVhjRC-bPDRs_Bc35cyZvAMV3bKU6kao36ehCGQtdms2d3z-yLursshuOKBUWmV43LPNfZEcaaFzA-YWhH_y2 categories: Structural tags: - Java diff --git a/delegation/README.md b/delegation/README.md index e5c0c6376..b294607ec 100644 --- a/delegation/README.md +++ b/delegation/README.md @@ -3,6 +3,7 @@ layout: pattern title: Delegation folder: delegation permalink: /patterns/delegation/ +pumlid: JSV14GCX20NGLf82LkxfXbN69OFeu2VRVdBCxRsdUhLiac6F2rZxHHHybwwuyimjKQT37ANEGMfvCpZepHy-ccpjVYm697pJuFq3DJ7f39rEWlhNaZ7Aoc5V categories: Behavioral tags: - Java diff --git a/dependency-injection/README.md b/dependency-injection/README.md index 735f589b1..b4ec12e6b 100644 --- a/dependency-injection/README.md +++ b/dependency-injection/README.md @@ -3,6 +3,7 @@ layout: pattern title: Dependency Injection folder: dependency-injection permalink: /patterns/dependency-injection/ +pumlid: RSdB3SCW303GLPe1mFTkunWhSGG6-PEesxS3zFQajubIpyPf_NL6B7y363xra3XpJsUZgS4QbUO0wVbWeC65DvR6BeUMXH5iwZ3GVu36YxMnqgU8NamXKu63_aPD6tNbw5y0 categories: Behavioral tags: - Java diff --git a/double-checked-locking/README.md b/double-checked-locking/README.md index da1fdd1a2..582e4aac6 100644 --- a/double-checked-locking/README.md +++ b/double-checked-locking/README.md @@ -3,6 +3,7 @@ layout: pattern title: Double Checked Locking folder: double-checked-locking permalink: /patterns/double-checked-locking/ +pumlid: TSdH4SCW203GLTe1bFzkGv1J6qGFeLc_MI1_x-wzkv94uJ1vDVUrFm26LwxTMnonsMYgitgcEQ1BNEXeyCKVfiAxLqqBtTbqmy1z0ygCGpXHOpgv99bqTgt0JW-LmqPUCUGF categories: Concurrency tags: - Java diff --git a/double-dispatch/README.md b/double-dispatch/README.md index ae87208a2..b0f2185f9 100644 --- a/double-dispatch/README.md +++ b/double-dispatch/README.md @@ -3,6 +3,7 @@ layout: pattern title: Double Dispatch folder: double-dispatch permalink: /patterns/double-dispatch/ +pumlid: NSbB3iCW303HgpG70Ezx6yTOWSeOv4zp_MRTtUZDCPGa6wV9gqTiVmCOtlKQqVDCPwEbmHgLreGXUMEWmGU_M1hxkBHiZ61JXud-1BILft1fmvz37JZetshQh3kd_000 categories: Other tags: - Java diff --git a/event-aggregator/README.md b/event-aggregator/README.md index ac07869e7..ce7f358de 100644 --- a/event-aggregator/README.md +++ b/event-aggregator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Event Aggregator folder: event-aggregator permalink: /patterns/event-aggregator/ +pumlid: PSf13iCW30NHgxG70Ezx6uTOX0eCih-JwvTzTwEdUJSjFKu9wwyBMFuXCdvoRRZY21ShKo6ANEQWrkDXiD6NRqwdUAkQ5WDYwZJOTv3SUqzSgqbbp0qeVvZ3Hbun-Wy0 categories: Structural tags: - Java diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md index 843e4c268..0f698273b 100644 --- a/event-driven-architecture/README.md +++ b/event-driven-architecture/README.md @@ -3,6 +3,7 @@ layout: pattern title: Event Driven Architecture folder: event-driven-architecture permalink: /patterns/event-driven-architecture/ +pumlid: TOhH3SCW30LNQGS0_tSRnrZ15H1adfFromBzkfFktZQaHT7mzgh0N1yYvoUVXXf7B7Mv1dGWozN9MZmCTlhopQdeidEaoO3wMDHvRI6zzvwAssPYbsfGGRYIGlxN7DxpZDv- categories: Architectural tags: - Java diff --git a/execute-around/README.md b/execute-around/README.md index f669f18ff..e0ae128b5 100644 --- a/execute-around/README.md +++ b/execute-around/README.md @@ -3,6 +3,7 @@ layout: pattern title: Execute Around folder: execute-around permalink: /patterns/execute-around/ +pumlid: NSZ14G8n20NGLhI0XBlT865suoGa0n_NylNixSsxTvEHJTF7xGHsF8YShtfqdFdCK9TbK4ELDQcFl1ZizE8tbwRH3okR0NKBcXm_a7vK4bhOLreZXVnLJPzrvnnV categories: Other tags: - Java diff --git a/facade/README.md b/facade/README.md index c416552c7..22ccd6911 100644 --- a/facade/README.md +++ b/facade/README.md @@ -3,6 +3,7 @@ layout: pattern title: Facade folder: facade permalink: /patterns/facade/ +pumlid: BSP15eCm20N0gxG7CEoz3ILKqvTW7dpq-hhehERTJ7fMJU-l7PYn4ZbVPMlOyvEXBeT13KMEGQtdnM2d7v-yL8sssJ8PKBUWmV64lYnSbHJoRqaVPUReDm00 categories: Structural tags: - Java diff --git a/factory-kit/README.md b/factory-kit/README.md index c25701047..b47cbff76 100644 --- a/factory-kit/README.md +++ b/factory-kit/README.md @@ -3,6 +3,7 @@ layout: pattern title: Factory Kit folder: factory-kit permalink: /patterns/factory-kit/ +pumlid: JST15i8m20N0g-W14lRU1YcsQ4BooCS-RwzBTpDNSscvQKQx7C1SDwBWi-w68--vD6Gur55bTBAM9uE3dlpcikcotSjaGCCNTLu_q8C58pxbPI25_Bzcz3gpjoy0 categories: Creational tags: - Java diff --git a/factory-method/README.md b/factory-method/README.md index 05549cf4f..a444ffbd8 100644 --- a/factory-method/README.md +++ b/factory-method/README.md @@ -3,6 +3,7 @@ layout: pattern title: Factory Method folder: factory-method permalink: /patterns/factory-method/ +pumlid: NSZB3G8n30N0Lg20n7UwCOxPP9MVx6TMT0zdRgEvjoazYeRrMmMsFuYChtmqr7Y6gycQq8aiQr3hSJ7OwEGtfwBUZfas0shJQR3_G2yMBFkaeQYha4B-AeUDl6FqBm00 categories: Creational tags: - Java diff --git a/feature-toggle/README.md b/feature-toggle/README.md index 51747ac09..3bb91ad5a 100644 --- a/feature-toggle/README.md +++ b/feature-toggle/README.md @@ -3,6 +3,7 @@ layout: pattern title: Feature Toggle folder: feature-toggle permalink: /patterns/feature-toggle/ +pumlid: NSZ14G8X30NGLhG0oDrk8XjPd12OvCTjNy_UthpxiAPvIBhUJc37WyZvgdtWp6U6U5i6CTIs9WtDYy5ER_vmEIH6jx8P4BUWoV43lOIHBWMhTnKIjB-gwRFkdFe5 categories: Behavioral tags: - Java diff --git a/fluentinterface/README.md b/fluentinterface/README.md index 767792da7..8bf43d974 100644 --- a/fluentinterface/README.md +++ b/fluentinterface/README.md @@ -3,6 +3,7 @@ layout: pattern title: Fluent Interface folder: fluentinterface permalink: /patterns/fluentinterface/ +pumlid: NOj93eCm302_KXv0VEzlN6F0bMCYB_3zvjpRQ3IpY97MnkNwEZD7l04SdtP8dlMfOAVBaYqRNHr4wy54Xo_Uk6uSSjWwC9FT0Zh61DYrPY_pyXs9WPF-NIllRLJN7m00 categories: Other tags: - Java diff --git a/flux/README.md b/flux/README.md index 7ac312c44..e36b73900 100644 --- a/flux/README.md +++ b/flux/README.md @@ -3,6 +3,7 @@ layout: pattern title: Flux folder: flux permalink: /patterns/flux/ +pumlid: 7SP14eCm20NGg-W13FlU1YFLE0GpyAazVZk-rPkRLSrDqdKwW14l8kUxx0r7hXdYzJA8eTIhKzEy6UnqyeUNJQBjjWm6n2seS_n3Ryql2UgJajxBoAu_ categories: Presentation Tier tags: - Java diff --git a/flyweight/README.md b/flyweight/README.md index a98dced8e..1e71f1c02 100644 --- a/flyweight/README.md +++ b/flyweight/README.md @@ -3,6 +3,7 @@ layout: pattern title: Flyweight folder: flyweight permalink: /patterns/flyweight/ +pumlid: HSV94S8m3030Lg20M7-w4OvYAoCh7Xtnq3ty-Eq-MQlaJcdow17JNm26gpIEdkzqidffa4Qfrm2MN1XeSEADsqxEJRU94MJgCD1_W4C-YxZr08hwNqaRPUQGBm00 categories: Structural tags: - Java diff --git a/front-controller/README.md b/front-controller/README.md index a462a08e0..c832674dc 100644 --- a/front-controller/README.md +++ b/front-controller/README.md @@ -3,6 +3,7 @@ layout: pattern title: Front Controller folder: front-controller permalink: /patterns/front-controller/ +pumlid: PSlB3OGm303HLfO24j-t6nCC13bEvC_IFk6yjz6JPgbIE3OAvS_fFkmBe7Zde_ePQnXfwU8adajlK3bkT5Iuy8Tf8wk7f87kf6BGq6R0hlD8xwQTUG9v-SCSslA8nWy0 categories: Presentation Tier tags: - Java diff --git a/half-sync-half-async/README.md b/half-sync-half-async/README.md index 8a091f813..55891e770 100644 --- a/half-sync-half-async/README.md +++ b/half-sync-half-async/README.md @@ -3,6 +3,7 @@ layout: pattern title: Half-Sync/Half-Async folder: half-sync-half-async permalink: /patterns/half-sync-half-async/ +pumlid: RScv3SCm3030LU819FRPXg5fIm552tnYPFiyjRi3RkbAaYkdoQr5JBy369vrxz7oaSv6XmPhL3e6TCaJ0msU-CAoilTToyG8DdKOw5z0GzcAlvNAN_WZSD1brBHHPmxv0000 categories: Concurrency tags: - Java diff --git a/hexagonal/README.md b/hexagonal/README.md index b1d0a7948..33c2ba9cb 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -3,6 +3,7 @@ layout: pattern title: Hexagonal Architecture folder: hexagonal permalink: /patterns/hexagonal/ +pumlid: HSTB4W8X30N0g-W1XkozpPD90LO8L3wEnzUTk-xxq2fvSfhSUiJs1v7XAcr4psSwMrqQh57gcZGaBmICNdZZEDb7qsCZWasT9lm7wln1MmeXZlfVIPjbvvGl categories: Architectural tags: - Java diff --git a/intercepting-filter/README.md b/intercepting-filter/README.md index 7d53472a0..4981299ad 100644 --- a/intercepting-filter/README.md +++ b/intercepting-filter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Intercepting Filter folder: intercepting-filter permalink: /patterns/intercepting-filter/ +pumlid: RSfB3i8m303Hgy014k-vZN5DQkIuaJ_q-fGzkz7JtCL8Q-DolUsPAnu0ZcSVadizAzZfi6JBJiS4qJenqU6D7smRXmnh2pFPBM1YN05o_KwyKcoqb-ZFEEcVz_BPLqtz0W00 categories: Behavioral tags: - Java diff --git a/interpreter/README.md b/interpreter/README.md index 87c1c47f7..7a09ab0c7 100644 --- a/interpreter/README.md +++ b/interpreter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Interpreter folder: interpreter permalink: /patterns/interpreter/ +pumlid: JSf13eCm30NHgz034E-vZGaM62Kcih_BzQ6xxjv8yr6hBJT9RzC1Z5Y8dE-oAuvSCyJhPH13gLSdRNapsEdaBy-RXEus3mR4BQXpl21zVnykFmlgVvVqNaRszW00 categories: Behavioral tags: - Java diff --git a/iterator/README.md b/iterator/README.md index d6be7758d..91cc64d8d 100644 --- a/iterator/README.md +++ b/iterator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Iterator folder: iterator permalink: /patterns/iterator/ +pumlid: FSV13OGm30NHLg00uljsOu85HeaJsTzB-yjfBwCtgrfjUKXwMovWneV8-IcduiezGxmEWnXA7PsqvSDWfvk_l1qIUjes6H2teCxnWlGDOpW9wdzAUYypU_i1 categories: Behavioral tags: - Java diff --git a/layers/README.md b/layers/README.md index 8e8eda366..d62c6b6b7 100644 --- a/layers/README.md +++ b/layers/README.md @@ -3,6 +3,7 @@ layout: pattern title: Layers folder: layers permalink: /patterns/layers/ +pumlid: BSR13OCm30NGLSe0n7UsCS62L8w9x6yGszD3t-bDpQhc9kdwEO0H2v7pNVQ68zSCyNeQn53gsQbftWns-lB5yoRHTfi70-8Mr3b-8UL7F4XG_otflOpi-W80 categories: Architectural tags: - Java diff --git a/lazy-loading/README.md b/lazy-loading/README.md index d40061293..4b7a580c3 100644 --- a/lazy-loading/README.md +++ b/lazy-loading/README.md @@ -3,6 +3,7 @@ layout: pattern title: Lazy Loading folder: lazy-loading permalink: /patterns/lazy-loading/ +pumlid: LSXB3W8X303Gg-W1e7jlqu66gIc5zED4JwzRTo_lpjeaEwN9xOpO_W0mlEhWEFD89sjBWpHgMnDOyi90WoU-i7Ho7besHf2fmqJ_0GG_xo8BE-i0YlONDMtMdLE- categories: Other tags: - Java diff --git a/mediator/README.md b/mediator/README.md index c7a0478c8..0e9f9c216 100644 --- a/mediator/README.md +++ b/mediator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Mediator folder: mediator permalink: /patterns/mediator/ +pumlid: FSV14SCm20J0Lk82BFxf1akCJKOW3JhizfDNVhkRUktP9AE_Bc2kDr7mKqx5bKSkYJeSuYXr66dFXy517xvvRxBqz7qo8E6BZDSFPDAKCO84zP-IOMMczIy0 categories: Behavioral tags: - Java diff --git a/memento/README.md b/memento/README.md index 463b5fec0..f43849329 100644 --- a/memento/README.md +++ b/memento/README.md @@ -3,6 +3,7 @@ layout: pattern title: Memento folder: memento permalink: /patterns/memento/ +pumlid: DSgn4OCm30NGLM00h3xR2AC3SvRiaxx2-g59zugtDgiz3qdlomNC-10vF-Lik7BF4A_388PIXrBh-J3OwUOlRuT4EssR38XRa7Ay81Lz_o11_RkaQvcf_GS0 categories: Behavioral tags: - Java diff --git a/message-channel/README.md b/message-channel/README.md index 8aebd0157..aa357ac0c 100644 --- a/message-channel/README.md +++ b/message-channel/README.md @@ -3,6 +3,7 @@ layout: pattern title: Message Channel folder: message-channel permalink: /patterns/message-channel/ +pumlid: NSZB3SCm203GLTe1RExTXX1akm9YyMdMRy-zFRtdCf8wkLmUCtF72y3nxcFbhAE2dIvBjknqAIof6nCTtlZ1TdAiOMrZ9hi5ACOFe1o1WnjDD6C1Jlg_NgvzbyeN categories: Integration tags: - Java diff --git a/model-view-controller/README.md b/model-view-controller/README.md index bc96f7ab1..9907b98bd 100644 --- a/model-view-controller/README.md +++ b/model-view-controller/README.md @@ -3,6 +3,7 @@ layout: pattern title: Model-View-Controller folder: model-view-controller permalink: /patterns/model-view-controller/ +pumlid: ROl13SCm201NQGUm-NSRQgE42h258Lw_wR-_qvtkoTOaEwNBuuoOwmNWkEl1SUOx5taR5cHHsr1WoOs13X-yi7HQV5YP645k2nJN3Q2ZavIBQPVVwqFajXJjVwdfMcUgV040 categories: Presentation Tier tags: - Java diff --git a/model-view-presenter/README.md b/model-view-presenter/README.md index a3b921ce4..04a1fa559 100644 --- a/model-view-presenter/README.md +++ b/model-view-presenter/README.md @@ -3,6 +3,7 @@ layout: pattern title: Model-View-Presenter folder: model-view-presenter permalink: /patterns/model-view-presenter/ +pumlid: ROlR3SGW3C1MkGu0-RzjKeXQJWcWFChwPO3xispvQBrmL0hbp-q-xGkWkFBL_8upZBICxjGzbo7GE1OwAlpmmLJ9sjNJH7VIRY1e6q169KvFevMcakrtI_BoD-HGoJE4Nm00 categories: Presentation Tier tags: - Java diff --git a/monad/README.md b/monad/README.md index 41edd3d92..bf6ee58b8 100644 --- a/monad/README.md +++ b/monad/README.md @@ -3,6 +3,7 @@ layout: pattern title: Monad folder: monad permalink: /patterns/monad/ +pumlid: 9SR13SCm20NGLPe1OkxTXjWeSMMm1Pza_LRgExsjMntP97syBc35cyZvAMV7bKU6U9q6CPGwbVh8Xy5E7xvvRnBzj7qn86v1ol4BwJHk9AZ_bNGjAtLy0G00 categories: Other tags: - Java diff --git a/monostate/README.md b/monostate/README.md index 3576dc659..8c47b5da4 100644 --- a/monostate/README.md +++ b/monostate/README.md @@ -3,6 +3,7 @@ layout: pattern title: MonoState folder: monostate permalink: /patterns/monostate/ +pumlid: HSV14OGm20NGLjO23FVj1YEZsGaa0nzjVxrvUszfLdlkaju_9p3ZI-HybwFXp2r3l0w364eTIgtdpM2d7r-yxXBji7Ko86v1ol60TDW8C8G4zLr9rp9J-ny0 categories: Creational tags: - Java diff --git a/multiton/README.md b/multiton/README.md index 0462ff0ec..a1154e7bb 100644 --- a/multiton/README.md +++ b/multiton/README.md @@ -3,6 +3,7 @@ layout: pattern title: Multiton folder: multiton permalink: /patterns/multiton/ +pumlid: FST14i8m20NGg-W16lRUXgPCYnD81Zxs-hfozzvJlOywf68yBc3bYoZuRgVYghrIea-7E5gVHZhgPd3Gcp-y7P9w-hOOaF0au_o1h0OKqqdG_saLrbRP-080 categories: Creational tags: - Java diff --git a/mute-idiom/README.md b/mute-idiom/README.md index bb674b648..5c3dbf10b 100644 --- a/mute-idiom/README.md +++ b/mute-idiom/README.md @@ -3,6 +3,7 @@ layout: pattern title: Mute Idiom folder: mute-idiom permalink: /patterns/mute-idiom/ +pumlid: JSf13iCm20NHgxG7iDdtDjH62PKX5luarq-MtSsJvgtUHdR96AyTcEj357pLJR7dDvT4EnpYgEqmqf4NWuD-V7BfidJpCXcGy4N6wmcoX1Jj-lo2ziUQONMcZHi0 categories: Other tags: - Java diff --git a/mutex/README.md b/mutex/README.md index 84755872f..78cda9060 100644 --- a/mutex/README.md +++ b/mutex/README.md @@ -3,6 +3,7 @@ layout: pattern title: Mutex folder: mutex permalink: /patterns/mutex/ +pumlid: 9SR13OCm30NGLSe0n7UsCS62LB69x6zWV2hrdTxKhFRS9Br_3c34GkHybxtXo3L3l9u6CPHwAhMUDuETldpnl4cqtUR1WBW5ASSlf0bvI53_A-bQHcf_0G00 categories: Concurrency tags: - Java diff --git a/naked-objects/README.md b/naked-objects/README.md index 66e6ac2b0..14391dd40 100644 --- a/naked-objects/README.md +++ b/naked-objects/README.md @@ -3,6 +3,7 @@ layout: pattern title: Naked Objects folder: naked-objects permalink: /patterns/naked-objects/ +pumlid: LSX15i8W30N0g-W187jlaq9igH1uoO_r-BfrDs_kJKkFAc7zTW3B7qJ6LzuRZjZ2nSfKY2ANEQZrk1XiTFARKnLlkwR5W9Ww3VOVIFabDStjb08dGVcVz6mVX4aE6td5w5y0 categories: Architectural tags: - Java diff --git a/null-object/README.md b/null-object/README.md index 0ed28a0af..bfaaeac66 100644 --- a/null-object/README.md +++ b/null-object/README.md @@ -3,6 +3,7 @@ layout: pattern title: Null Object folder: null-object permalink: /patterns/null-object/ +pumlid: JSV14SCm20J0Lk829Fxf1cF6bWSX3JhYzfDdVhjRSx4yDCDU5p3NcoZugMV3bNik3HaETLGPdPhbm-2WcpzS3btjz38PqF15dTSFv6bMndwhW1Jo_vhHwynkNm00 categories: Behavioral tags: - Java diff --git a/object-pool/README.md b/object-pool/README.md index cf36d9880..15fee51aa 100644 --- a/object-pool/README.md +++ b/object-pool/README.md @@ -3,6 +3,7 @@ layout: pattern title: Object Pool folder: object-pool permalink: /patterns/object-pool/ +pumlid: JSV94SCm2030Lk829Fxf1cF6bWU1XYDkFtdcjxiD9Qc3o-LrPQvu0pW-_HnvrLx1JgR9cfrimf1wCD7XnW-sWsESsXPcicl0nFW1RB-PiYqp0KxwVo-VVTMKBm00 categories: Creational tags: - Java diff --git a/observer/README.md b/observer/README.md index 6fbe3cdab..6a9e3f584 100644 --- a/observer/README.md +++ b/observer/README.md @@ -3,6 +3,7 @@ layout: pattern title: Observer folder: observer permalink: /patterns/observer/ +pumlid: FSkn4OGm30NHLg00hFow4KO3PcpP8tr1-pYwx6smQz5Suv2mkbp0y1-HyPlEWYlsSB7S5Q98kJSgDLu66ztyy7Q8brEtmO2OEZNs2Uhxl9u9GVv72cjfHAiV categories: Behavioral tags: - Java diff --git a/page-object/README.md b/page-object/README.md index b4f8246f1..2219a077c 100644 --- a/page-object/README.md +++ b/page-object/README.md @@ -3,6 +3,7 @@ layout: pattern title: Page Object folder: page-object permalink: /patterns/page-object/ +pumlid: JSV14OGW30NGLjO28FVj9iOCua1Wme-sxnxtzjvMJLeS6ju-9p3NbyZvoQNYZ3sMkWo36hACJhN5ms2dYszEXwvQB4q6r6rHv_K3JIwQndwfW1Jo_npUyupUNW00 categories: Testing tags: - Java diff --git a/poison-pill/README.md b/poison-pill/README.md index 0815b376e..8f673ad49 100644 --- a/poison-pill/README.md +++ b/poison-pill/README.md @@ -3,6 +3,7 @@ layout: pattern title: Poison Pill folder: poison-pill permalink: /patterns/poison-pill/ +pumlid: JSZ14SCm20NHLf82BExfXiYCJGOX3NpYzkDZRllsgTwjTgcmnmciV145N-rGdFMkbEZJ8OxMvo2rkXWSzE4lRxka7huj1YGyQN3UGMjgpdkh6Gdwlrl5QAk6_G00 categories: Other tags: - Java diff --git a/private-class-data/README.md b/private-class-data/README.md index 981208fa3..061cc9e77 100644 --- a/private-class-data/README.md +++ b/private-class-data/README.md @@ -3,6 +3,7 @@ layout: pattern title: Private Class Data folder: private-class-data permalink: /patterns/private-class-data/ +pumlid: RShR3SCm243HLTe1RFwx3S4eeSB4uf6itmpGlwkZ-nOZhS7b-ZeoLtm07E--InwrLR3JQScMdSu9edLZeiCNBso3GtPh2pFPBM1YF07BvSBaHeeHRJm_SD8VxkMphvhw0m00 categories: Other tags: - Java diff --git a/producer-consumer/README.md b/producer-consumer/README.md index 1bb84c35f..b3cb56af1 100644 --- a/producer-consumer/README.md +++ b/producer-consumer/README.md @@ -3,6 +3,7 @@ layout: pattern title: Producer Consumer folder: producer-consumer permalink: /patterns/producer-consumer/ +pumlid: PSjB3iCW303HgxG70Ezx6zTO2HKso9_a-c7VtUX9y-vA8nkdZTSPiVm3O7ZNeyUPttGscXgiKMaAz94t1XhyyCBIsFkXPM44cpe8-WvODbiIMzcdfspXe7-jQL9NodW0 categories: Concurrency tags: - Java diff --git a/property/README.md b/property/README.md index 0ac5c7a6c..c1502f75c 100644 --- a/property/README.md +++ b/property/README.md @@ -3,6 +3,7 @@ layout: pattern title: Property folder: property permalink: /patterns/property/ +pumlid: FSV13OCm30NGLTe1YEziumOBKYMEPN-3s9wUUdlltRJst2Izlmx0OYLolihUSEGdGxnEXIXAdODQpul1Jby-UTaasgwBCI2kGOFZ1pAV9ewR1FMVaZwAvUWF categories: Creational tags: - Java diff --git a/prototype/README.md b/prototype/README.md index 632daca93..fe9e17917 100644 --- a/prototype/README.md +++ b/prototype/README.md @@ -3,6 +3,7 @@ layout: pattern title: Prototype folder: prototype permalink: /patterns/prototype/ +pumlid: HSV13OCm30NGLM00udktCS62eCInxE-YRj_UUdjlRLfx7fBUbmkmU14vF-Lik7BF4AzJ8OfIvw3Mys6mqyrltWw9Tkfc38XhqE3uWSmd9Zuc9AZ_bVHHB4V_0W00 categories: Creational tags: - Java diff --git a/proxy/README.md b/proxy/README.md index a3e03708e..1c22c0710 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -3,6 +3,7 @@ layout: pattern title: Proxy folder: proxy permalink: /patterns/proxy/ +pumlid: 9SR13OCm30NGLM00udktCS62eCI9x6yesrEfx_Jcehd69c5rEe3X7oBZE-q5HwpXOhahH95oRrHgt0msEldYPHClkow30J5rQko_qB3-VKYG_qjXBOrezGK0 categories: Structural tags: - Java diff --git a/publish-subscribe/README.md b/publish-subscribe/README.md index 6a5b2dfa8..3265e42ea 100644 --- a/publish-subscribe/README.md +++ b/publish-subscribe/README.md @@ -3,6 +3,7 @@ layout: pattern title: Publish Subscribe folder: publish-subscribe permalink: /patterns/publish-subscribe/ +pumlid: PSZB3SCm203GLTe1RExT1XCKKs5YyMdMR--zFRsd66aTNAwFcRdZ1U1uzrDorgXWfykIBJjT2qJhnaI7Dtwm7HnoMjkOoMu12-C7s3LKOhQe4UGo63ZfVtlvwhkMVW40 categories: Integration tags: - Java diff --git a/reactor/README.md b/reactor/README.md index b9ba98948..7fd3982ad 100644 --- a/reactor/README.md +++ b/reactor/README.md @@ -3,6 +3,7 @@ layout: pattern title: Reactor folder: reactor permalink: /patterns/reactor/ +pumlid: DSR14OGm20NGLjO23FVj1f7Hx2Ga0nzjVxtuJc-f9YrtJM-V4vZn9NA-or5nvfQXBiEWXYAZKsrvCzZfnnUlkqOzR9qCg5jGvtX2hYmOJWfvNz9xcTdR7m00 categories: Concurrency tags: - Java diff --git a/reader-writer-lock/README.md b/reader-writer-lock/README.md index 40b711361..556b9cd85 100644 --- a/reader-writer-lock/README.md +++ b/reader-writer-lock/README.md @@ -3,6 +3,7 @@ layout: pattern title: Reader Writer Lock folder: reader-writer-lock permalink: /patterns/reader-writer-lock/ +pumlid: RSZB4S8m303HLg00MtUw4R8cCP5bZpwuVL80jttxx4gIZTFaSKOiVm4OxdhqEFETpaPJWpKgpG5TScEWmGU_M1fxFxGiZ61JXu5-1nXZOolR-gqYaoxWe3-xfeswSiWF categories: Concurrency tags: - Java diff --git a/repository/README.md b/repository/README.md index 67b3ea44e..501f39f9c 100644 --- a/repository/README.md +++ b/repository/README.md @@ -3,6 +3,7 @@ layout: pattern title: Repository folder: repository permalink: /patterns/repository/ +pumlid: JSV13OCm30NGLM00udktCS42eyI9xE-YRjyUUtjlLQij3qblomNCU14vF-LKNBbdYDTX44EfevEsV1ZiTFERjqD2Jzic0-8Mr3b-89SvGZ7yGuBwrvBUoypUlW00 categories: Persistence Tier tags: - Java diff --git a/resource-acquisition-is-initialization/README.md b/resource-acquisition-is-initialization/README.md index 821f220d7..df7ee294b 100644 --- a/resource-acquisition-is-initialization/README.md +++ b/resource-acquisition-is-initialization/README.md @@ -3,6 +3,7 @@ layout: pattern title: Resource Acquisition Is Initialization folder: resource-acquisition-is-initialization permalink: /patterns/resource-acquisition-is-initialization/ +pumlid: ZShR3S8m343HLUW0YV_PnhXMQvGumOzMOdhA1lqxkhgBABLSEQqzzeZfJm33isuIUxxIsMXei4QbqK5QdXXeyCO3oyekcvQ94MpgqD4lWB6FDEA2z4bn2HbQn8leHMponNy13hgvrhHUP_Rs0m00 categories: Other tags: - Java diff --git a/semaphore/README.md b/semaphore/README.md index 46ccd7b8e..071e061a7 100644 --- a/semaphore/README.md +++ b/semaphore/README.md @@ -3,6 +3,7 @@ layout: pattern title: Semaphore folder: semaphore permalink: /patterns/semaphore/ +pumlid: HSV14SCm20J0Lk82BFxf1ikCfOn06ZZizfDVVhjRjphobFJnQi2ADv7pKwwEbaU6U9q6CPGwbVh8Xy5E7xvvFoNwPVjYGDo2bEC72b5URRgGeFvNqhMirF45 categories: Concurrency tags: - Java diff --git a/servant/README.md b/servant/README.md index 3e82ab2cf..d14d35edf 100644 --- a/servant/README.md +++ b/servant/README.md @@ -3,6 +3,7 @@ layout: pattern title: Servant folder: servant permalink: /patterns/servant/ +pumlid: DSkn4O0m20NGLNG0G-ys63cDbv0SV7HzRUnUy-QYkSOkONKwWU4haV6JZe8pjd2nt1MYIBatAZKU1XjTVFEoYvT3by60c3erzW_qdPiL9CY_KrXB8rfz0G00 categories: Structural tags: - Java diff --git a/service-layer/README.md b/service-layer/README.md index 9b685d4e3..af393947f 100644 --- a/service-layer/README.md +++ b/service-layer/README.md @@ -3,6 +3,7 @@ layout: pattern title: Service Layer folder: service-layer permalink: /patterns/service-layer/ +pumlid: LOl93SCm3C1MQGUmzUysgY8aAcJ5q96WszVV_aW2V8gHriRb-ZWoPxm07E--Inxrhc2dqv8jEvq3HEl6H8SFNjWs3jcjJSnaju21iG3MSmbnK_mkuwJ_qij7dpNq1m00 categories: Architectural tags: - Java diff --git a/service-locator/README.md b/service-locator/README.md index af4d8c3ac..31d82b13f 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -3,6 +3,7 @@ layout: pattern title: Service Locator folder: service-locator permalink: /patterns/service-locator/ +pumlid: NSjB3iCm203HgxG7iDdtDeIWX0fZYqzo_MRTtUX9ynOZhPtBzNLchlW0EDxza3nhgs2dQScMdUO0qRenqU6B5xQTGmvh2pFPBM1WF07FSmbnqqcOqu6J_gsNZxvgw0y0 categories: Structural tags: - Java diff --git a/singleton/README.md b/singleton/README.md index 2a481f5c8..cd6fc131d 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -3,6 +3,7 @@ layout: pattern title: Singleton folder: singleton permalink: /patterns/singleton/ +pumlid: HSV14SCm20J0Lk82BFxf1ikCh0n26ZZizfDVVhjRjwfvIhg-Bc35cyZvAQtZoYD3l4w364gTWxhcms2d3z-ydnAzsRuO4BUWmV43HRUcWcaagF-Lz55M3lq2 categories: Creational tags: - Java diff --git a/specification/README.md b/specification/README.md index 564830653..dc47f4970 100644 --- a/specification/README.md +++ b/specification/README.md @@ -3,6 +3,7 @@ layout: pattern title: Specification folder: specification permalink: /patterns/specification/ +pumlid: LSX14i8m20NGg-W16lRU1YcsE0d9mCTUNxVkthoxkVJQjQBVJc3bWoZuQeVXh6UbXao7EfhCGTRhOd3Gcp-yxPfs-BOOqF2amVa3vLAnbmd3ffD2_gTLZBPgz2y0 categories: Behavioral tags: - Java diff --git a/state/README.md b/state/README.md index f5cb189fd..8e3256b42 100644 --- a/state/README.md +++ b/state/README.md @@ -3,6 +3,7 @@ layout: pattern title: State folder: state permalink: /patterns/state/ +pumlid: 9SRH3O0m20N0LNG0ox_RO2LQqz867hg-9jxNpKLpZLt2wdG2mrSYuoST1MTiuMAvAqIHSczKQZmCDhhuvcKNBuSkWm4nTMhiNyZ141BaVocifH6jlW00 categories: Behavioral tags: - Java diff --git a/step-builder/README.md b/step-builder/README.md index bc636e37a..65d356c2e 100644 --- a/step-builder/README.md +++ b/step-builder/README.md @@ -3,6 +3,7 @@ layout: pattern title: Step Builder folder: step-builder permalink: /patterns/step-builder/ +pumlid: LOZ93SCm3C1MQGQmzUysYYqaAcJ5q96i7t_x8KXkh4soKvfypeZfNm33fnuSP-xfPEtI88tQhW4i-M2WmGzlB9sS3oqJ8yZKOQ0lWOLPzcJfAoZQtwXfeyuSyW80 categories: Creational tags: - Java diff --git a/strategy/README.md b/strategy/README.md index f07397f67..697b6cc88 100644 --- a/strategy/README.md +++ b/strategy/README.md @@ -3,6 +3,7 @@ layout: pattern title: Strategy folder: strategy permalink: /patterns/strategy/ +pumlid: FSV13OCm30NGLM00udktCS4AGOaJsTz5tRwSkBstLiqj3WbhombC_n0PtwbKdB67Y-MX44NAerDjSJFOwE8lRuTuBRfD1iJKgRC_88SnfFn8aD-ai9vczFO7 categories: Behavioral tags: - Java diff --git a/template-method/README.md b/template-method/README.md index ad972f06b..65381d0ce 100644 --- a/template-method/README.md +++ b/template-method/README.md @@ -3,6 +3,7 @@ layout: pattern title: Template method folder: template-method permalink: /patterns/template-method/ +pumlid: NSZ13SCW30NGLPe1mFTkuu0Lg6n0vZjPlpttzlIEFef6bN1zDM3jDv7paw-E5cTiyJ87P22NQTGr7WOxVVZcL6NtQwJ5WFZOPBn_88WjPKWoGPkL1EN_ShZb5QPV categories: Behavioral tags: - Java diff --git a/thread-pool/README.md b/thread-pool/README.md index 9806fa8e0..473494ef9 100644 --- a/thread-pool/README.md +++ b/thread-pool/README.md @@ -3,6 +3,7 @@ layout: pattern title: Thread Pool folder: thread-pool permalink: /patterns/thread-pool/ +pumlid: JSV14SCW30J0Lk82GFzq8uF6a1624IUx_UIPt-xHhMXK2TTN0zP-4pa_-UfeSSOMBzCWXbpceAxnCDZfmpUdAhjVbXO3uhPfyFw1q5oufZMdag3yFuUFl6Be5m00 categories: Concurrency tags: - Java diff --git a/tolerant-reader/README.md b/tolerant-reader/README.md index be0085f2c..5d1cf80fd 100644 --- a/tolerant-reader/README.md +++ b/tolerant-reader/README.md @@ -3,6 +3,7 @@ layout: pattern title: Tolerant Reader folder: tolerant-reader permalink: /patterns/tolerant-reader/ +pumlid: NSZ14SCm20NHLf829ExfXaYChGn26lZ4xSVdtFRjSrZJx9AkZnFOyI9olkenSEOxGxmjWnXgMvE6viLWfmz_kNI9SLZP38XRqEIuWx1Kd0t5XVjjGVj_DNtMdLD_ categories: Integration tags: - Java diff --git a/twin/README.md b/twin/README.md index 3795236bb..092032a55 100644 --- a/twin/README.md +++ b/twin/README.md @@ -3,6 +3,7 @@ layout: pattern title: Twin folder: twin permalink: /patterns/twin/ +pumlid: 7SR13OCm30NGLUW0n7UsCS42eyH4zdUpFbNVwNtKQij3qjjo0ICs8kTPJiMLUuPuVGnYAFNff2qdWvrk_l9wIEXfws10t88wno-4gKQ2-az9xsLaRoy0 categories: Creational tags: - Java diff --git a/value-object/README.md b/value-object/README.md index 83223d8a2..a8e707b05 100644 --- a/value-object/README.md +++ b/value-object/README.md @@ -3,6 +3,7 @@ layout: pattern title: Value Object folder: value-object permalink: /patterns/value-object/ +pumlid: LSZ13SCm20NGLTe1RExTXX2KECBOmfza_VRQszDxDnVBNJFiTG9pVOY2dteqdBdbqf3XK4ULqQbPFWmEklZcikjgXvV9W8Olwhn-e9ijjOpjKW4fv2zgHgypktq1 categories: Creational tags: - Java diff --git a/visitor/README.md b/visitor/README.md index c1e24a624..712abad87 100644 --- a/visitor/README.md +++ b/visitor/README.md @@ -3,6 +3,7 @@ layout: pattern title: Visitor folder: visitor permalink: /patterns/visitor/ +pumlid: DSR14OGm20NGLhG0mtsxmSWeJa8oyD7sTo_xJczLgoqFIM_B1Spu43c_vLHSkMU8rs4GGwcZaxPy6UnqyyFR8Q6dRPC1SGlg7B_Gew4OJeBwVqdlPMPlNm00 categories: Behavioral tags: - Java From 5c1a4f1caf8145ca8dba90c34a838de53dcd95f5 Mon Sep 17 00:00:00 2001 From: Narendra Pathai Date: Wed, 31 Aug 2016 16:12:13 +0530 Subject: [PATCH 033/145] Added example that mocking frameworks use proxy pattern --- proxy/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/proxy/README.md b/proxy/README.md index 1c22c0710..a3cdbf788 100644 --- a/proxy/README.md +++ b/proxy/README.md @@ -41,6 +41,7 @@ are several common situations in which the Proxy pattern is applicable * [java.lang.reflect.Proxy](http://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html) * [Apache Commons Proxy](https://commons.apache.org/proper/commons-proxy/) +* Mocking frameworks Mockito, Powermock, EasyMock ## Credits From e425c2ef2f721600e14b59d67eb5ef27759113f0 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Wed, 31 Aug 2016 13:15:44 +0200 Subject: [PATCH 034/145] Add webhook for travis build failures to gitter Only the core group (private) gitter room --- .travis.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.travis.yml b/.travis.yml index bcbad6827..aed6ef420 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,5 +18,11 @@ after_success: notifications: email: - iluwatar@gmail.com + webhooks: + urls: + - https://webhooks.gitter.im/e/3319623945358a093a6f + on_success: change # options: [always|never|change] default: always + on_failure: always # options: [always|never|change] default: always + on_start: never # options: [always|never|change] default: always sudo: false # route the build to the container-based infrastructure for a faster build From 90c636abd312c40fee22c2bf7dc3c057e811ce75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 2 Sep 2016 21:28:45 +0300 Subject: [PATCH 035/145] Add missing license headers --- _scripts/postPumlsToServer.py | 23 +++++++++++++++++++ .../java/com/iluwatar/promise/Utility.java | 22 ++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/_scripts/postPumlsToServer.py b/_scripts/postPumlsToServer.py index 2f975d3d8..3929b3c86 100644 --- a/_scripts/postPumlsToServer.py +++ b/_scripts/postPumlsToServer.py @@ -1,3 +1,26 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + import requests, glob, re, os # taken from here: http://stackoverflow.com/a/13641746 diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java index 8d5be2538..d451600a3 100644 --- a/promise/src/main/java/com/iluwatar/promise/Utility.java +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.promise; import java.io.BufferedReader; From be2c7fdb2bf4279c6482cb794c1389ae9e83b7e9 Mon Sep 17 00:00:00 2001 From: NooBxGockeL Date: Fri, 2 Sep 2016 23:15:38 +0200 Subject: [PATCH 036/145] Update URM version: 1.4.0 -> 1.4.1, fixes #492 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 777ec6b69..302346993 100644 --- a/pom.xml +++ b/pom.xml @@ -47,7 +47,7 @@ 4.12.1 4.5.2 2.22 - 1.4.0 + 1.4.1 abstract-factory From 165d1f12986cd5d0735a4fb667a14dda905012ec Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Fri, 2 Sep 2016 23:54:52 +0200 Subject: [PATCH 037/145] Turn Error Tracing on when installing Travis currently errors and i cant reproduce locally, so this might help finding the culprit --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index aed6ef420..c502e8a99 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,10 @@ before_install: - export DISPLAY=:99.0 - sh -e /etc/init.d/xvfb start +# default install command is just "mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V" +install: +- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e + after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh From fa52a7f77eb038cf7e67bf9dfa32089b1fec096b Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Sat, 3 Sep 2016 00:19:50 +0200 Subject: [PATCH 038/145] Run build with latest java 8 release --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index c502e8a99..19d4614f4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,11 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh +addons: + apt: + packages: + - oracle-java8-installer + notifications: email: - iluwatar@gmail.com From ff23e90c4f6e08127c3b0b33539531f4c07e4724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 3 Sep 2016 08:43:35 +0300 Subject: [PATCH 039/145] Add puml for Promise pattern --- promise/etc/promise.urm.puml | 77 ++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 promise/etc/promise.urm.puml diff --git a/promise/etc/promise.urm.puml b/promise/etc/promise.urm.puml new file mode 100644 index 000000000..d97871411 --- /dev/null +++ b/promise/etc/promise.urm.puml @@ -0,0 +1,77 @@ +@startuml +package com.iluwatar.promise { + ~class PromiseSupport { + - COMPLETED : int {static} + - FAILED : int {static} + - RUNNING : int {static} + - exception : Exception + - lock : Object + - state : int + - value : T + ~ PromiseSupport() + + cancel(mayInterruptIfRunning : boolean) : boolean + ~ fulfill(value : T) + ~ fulfillExceptionally(exception : Exception) + + get() : T + + get(timeout : long, unit : TimeUnit) : T + + isCancelled() : boolean + + isDone() : boolean + } + -class ConsumeAction { + - action : Consumer + - dest : Promise + - src : Promise + - ConsumeAction(src : Promise, dest : Promise, action : Consumer) + + run() + } + -class TransformAction { + - dest : Promise + - func : Function + - src : Promise + - TransformAction(src : Promise, dest : Promise, func : Function) + + run() + } + class App { + - DEFAULT_URL : String {static} + - executor : ExecutorService + - stopLatch : CountDownLatch + - App() + - calculateLineCount() + - calculateLowestFrequencyChar() + - characterFrequency() : Promise> + - countLines() : Promise + - download(urlString : String) : Promise + - lowestFrequencyChar() : Promise + + main(args : String[]) {static} + - promiseUsage() + - stop() + - taskCompleted() + } + class Promise { + - exceptionHandler : Consumer + - fulfillmentAction : Runnable + + Promise() + + fulfill(value : T) + + fulfillExceptionally(exception : Exception) + + fulfillInAsync(task : Callable, executor : Executor) : Promise + - handleException(exception : Exception) + + onError(exceptionHandler : Consumer) : Promise + - postFulfillment() + + thenAccept(action : Consumer) : Promise + + thenApply(func : Function) : Promise + } + class Utility { + + Utility() + + characterFrequency(fileLocation : String) : Map {static} + + countLines(fileLocation : String) : Integer {static} + + downloadFile(urlString : String) : String {static} + + lowestFrequencyChar(charFrequency : Map) : Character {static} + } +} +TransformAction --+ Promise +TransformAction --> "-src" Promise +ConsumeAction --+ Promise +ConsumeAction --> "-src" Promise +Utility --+ Map +Promise --|> PromiseSupport +@enduml \ No newline at end of file From 2d9906190221ff46ed796b6526e0ce1631623828 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:31:41 +0100 Subject: [PATCH 040/145] Issue #469: Implementation of Event-based Asynchronous pattern --- event-asynchronous/README.md | 28 +++ event-asynchronous/etc/event-asynchronous.png | Bin 0 -> 31413 bytes .../etc/event-asynchronous.ucls | 110 +++++++++++ event-asynchronous/pom.xml | 42 ++++ .../com/iluwatar/event/asynchronous/App.java | 185 ++++++++++++++++++ .../iluwatar/event/asynchronous/Event.java | 88 +++++++++ .../EventDoesNotExistException.java | 26 +++ .../event/asynchronous/EventManager.java | 148 ++++++++++++++ .../iluwatar/event/asynchronous/IEvent.java | 27 +++ .../InvalidOperationException.java | 27 +++ .../LongRunningEventException.java | 26 +++ .../MaxNumOfEventsAllowedException.java | 26 +++ .../asynchronous/ThreadCompleteListener.java | 21 ++ .../src/main/java/config.properties | 1 + .../iluwatar/event/asynchronous/AppTest.java | 32 +++ .../asynchronous/EventAsynchronousTest.java | 73 +++++++ 16 files changed, 860 insertions(+) create mode 100644 event-asynchronous/README.md create mode 100644 event-asynchronous/etc/event-asynchronous.png create mode 100644 event-asynchronous/etc/event-asynchronous.ucls create mode 100644 event-asynchronous/pom.xml create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java create mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md new file mode 100644 index 000000000..59e6e8b33 --- /dev/null +++ b/event-asynchronous/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Event-based Asynchronous +folder: event-asynchronous +permalink: /patterns/event-asynchronous/ +categories: Other +tags: + - Java +--- + +## Intent +The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many +of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- +(1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without interrupting your application. +(2) Execute multiple operations simultaneously, receiving notifications when each completes. +(3) Wait for resources to become available without stopping ("hanging") your application. +(4) Communicate with pending asynchronous operations using the familiar events-and-delegates model. + +![alt text](./etc/event-asynchronous.png "Event-based Asynchronous") + +## Applicability +Use the Event-based Asynchronous pattern(s) when + +* Time-consuming tasks are needed to run in the background without disrupting the current application. + +## Credits + +* [Event-based Asynchronous Pattern Overview](https://msdn.microsoft.com/en-us/library/wewwczdw%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396) diff --git a/event-asynchronous/etc/event-asynchronous.png b/event-asynchronous/etc/event-asynchronous.png new file mode 100644 index 0000000000000000000000000000000000000000..a46ffe1c5d17773d38dac792349cc547adc06955 GIT binary patch literal 31413 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml new file mode 100644 index 000000000..60ab8f0aa --- /dev/null +++ b/event-asynchronous/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + + event-asynchronous + + + junit + junit + test + + + diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java new file mode 100644 index 000000000..3375fd4ec --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -0,0 +1,185 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; +import java.util.Scanner; + +/** + * + * This application demonstrates the Event-based Asynchronous pattern. Essentially, users (of the pattern) may + * choose to run events in an Asynchronous or Synchronous mode. There can be multiple Asynchronous events running at + * once but only one Synchronous event can run at a time. Asynchronous events are synonymous to multi-threads. The key + * point here is that the threads run in the background and the user is free to carry on with other processes. Once an + * event is complete, the appropriate listener/callback method will be called. The listener then proceeds to carry out + * further processing depending on the needs of the user. + * + * The {@link EventManager} manages the events/threads that the user creates. Currently, the supported event operations + * are: start, stop, getStatus. For Synchronous events, the user is unable to + * start another (Synchronous) event if one is already running at the time. The running event would have to either be + * stopped or completed before a new event can be started. + * + * The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many + * of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- + * (1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without + * interrupting your application. (2) Execute multiple operations simultaneously, receiving notifications when each + * completes. (3) Wait for resources to become available without stopping ("hanging") your application. (4) Communicate + * with pending asynchronous operations using the familiar events-and-delegates model. + * + * @see EventManager + * @see Event + * + */ +public class App { + + boolean interactiveMode = false; + + public static void main(String[] args) { + App app = new App(); + + app.setUp(); + app.run(); + } + + /** + * App can run in interactive mode or not. Interactive mode == Allow user interaction with command line. + * Non-interactive is a quick sequential run through the available {@link EventManager} operations. + */ + public void setUp() { + Properties prop = new Properties(); + String propFileName = "config.properties"; + + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + + if (inputStream != null) { + try { + prop.load(inputStream); + } catch (IOException e) { + } + String property = prop.getProperty("INTERACTIVE_MODE"); + if (property.equalsIgnoreCase("YES")) { + interactiveMode = true; + } + } + } + + public void run() { + if (interactiveMode) { + runInteractiveMode(); + } else { + quickRun(); + } + } + + public void quickRun() { + EventManager eventManager = new EventManager(); + + try { + // Create an Asynchronous event. + int aEventID = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventID + "] has been created."); + eventManager.startEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been started."); + + // Create a Synchronous event. + int sEventID = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventID + "] has been created."); + eventManager.startEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been started."); + + eventManager.getStatus(aEventID); + eventManager.getStatus(sEventID); + + eventManager.stopEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been stopped."); + eventManager.stopEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been stopped."); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + public void runInteractiveMode() { + EventManager eventManager = new EventManager(); + + Scanner s = new Scanner(System.in); + int option = 0; + option = -1; + while (option != 5) { + System.out + .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + System.out.print("Choose [1,2,3,4,5]: "); + option = s.nextInt(); + + if (option == 1) { + s.nextLine(); + System.out.print("(A)sync or (S)ync event?: "); + String eventType = s.nextLine(); + System.out.print("How long should this event run for (in seconds)?: "); + int eventTime = s.nextInt(); + if (eventType.equalsIgnoreCase("A")) { + try { + int eventID = eventManager.createAsyncEvent(eventTime); + System.out.println("Event [" + eventID + "] has been created."); + eventManager.startEvent(eventID); + System.out.println("Event [" + eventID + "] has been started."); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (eventType.equalsIgnoreCase("S")) { + try { + int eventID = eventManager.createSyncEvent(eventTime); + System.out.println("Event [" + eventID + "] has been created."); + eventManager.startEvent(eventID); + System.out.println("Event [" + eventID + "] has been started."); + } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException + | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else { + System.out.println("Unknown event type."); + } + } else if (option == 2) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.stopEvent(eventID); + System.out.println("Event [" + eventID + "] has been stopped."); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 3) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.getStatus(eventID); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 4) { + eventManager.getStatusOfAllEvents(); + } + } + + s.close(); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java new file mode 100644 index 000000000..48dc37236 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -0,0 +1,88 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +/** + * + * Each Event runs as a separate/individual thread. + * + */ +public class Event implements IEvent, Runnable { + + private int eventID; + private int eventTime; + private Thread thread; + private long counter = 0; + private boolean isComplete = false; + private ThreadCompleteListener eventListener; + + public Event(int eventID, int eventTime) { + this.eventID = eventID; + this.eventTime = eventTime; + } + + @Override + public void start() { + thread = new Thread(this); + thread.start(); + } + + @Override + public void stop() { + thread.interrupt(); + } + + @Override + public void status() { + if (!isComplete) { + System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + } else { + System.out.println("[" + eventID + "] I am done."); + } + } + + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime * 1000); + while (System.currentTimeMillis() < endTime) { + try { + counter += 1; + Thread.sleep(5000); // Sleep for 5 seconds. + } catch (InterruptedException e) { + return; + } + } + isComplete = true; + notifyListener(); + } + + public final void addListener(final ThreadCompleteListener listener) { + this.eventListener = listener; + } + + public final void removeListener(final ThreadCompleteListener listener) { + this.eventListener = null; + } + + private final void notifyListener() { + if (eventListener != null) { + eventListener.notifyOfThreadComplete(eventID); + } + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java new file mode 100644 index 000000000..77c1d479b --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class EventDoesNotExistException extends Exception { + + private static final long serialVersionUID = -3398463738273811509L; + + public EventDoesNotExistException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java new file mode 100644 index 000000000..305548111 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -0,0 +1,148 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import java.util.Iterator; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.ConcurrentHashMap; + +/** + * + * EventManager handles and maintains a pool of event threads. {@link Event} threads are created upon user request. Thre + * are two types of events; Asynchronous and Synchronous. There can be multiple Asynchronous events running at once but + * only one Synchronous event running at a time. Currently supported event operations are: start, stop, and getStatus. + * Once an event is complete, it then notifies EventManager through a listener. The EventManager then takes the event + * out of the pool. + * + */ +public class EventManager implements ThreadCompleteListener { + + private int minID = 1; + private int maxID = Integer.MAX_VALUE - 1; // Be cautious of overflows. + private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) + private int maxEventTime = 1800; // in seconds / 30 minutes. + private int currentlyRunningSyncEvent = -1; + private Random rand; + private Map eventPool; + + public EventManager() { + rand = new Random(1); + eventPool = new ConcurrentHashMap(maxRunningEvents); + + } + + // Create a Synchronous event. + public int createSyncEvent(int eventTime) + throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { + int eventID = createEvent(eventTime); + if (currentlyRunningSyncEvent != -1) { + throw new InvalidOperationException( + "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); + } + currentlyRunningSyncEvent = eventID; + + return eventID; + } + + // Create an Asynchronous event. + public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + return createEvent(eventTime); + } + + private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + if (eventPool.size() == maxRunningEvents) { + throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); + } + + if (eventTime >= maxEventTime) { + throw new LongRunningEventException( + "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + } + + int newEventID = generateID(); + + Event newEvent = new Event(newEventID, eventTime); + newEvent.addListener(this); + eventPool.put(newEventID, newEvent); + + return newEventID; + } + + public void startEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).start(); + } + + public void stopEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + if (eventID == currentlyRunningSyncEvent) { + currentlyRunningSyncEvent = -1; + } + + eventPool.get(eventID).stop(); + eventPool.remove(eventID); + } + + public void getStatus(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).status(); + } + + @SuppressWarnings("rawtypes") + public void getStatusOfAllEvents() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).status(); + } + } + + /** + * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most + * Integer.MAX_VALUE - 1. + */ + private int generateID() { + // nextInt is normally exclusive of the top value, + // so add 1 to make it inclusive + int randomNum = rand.nextInt((maxID - minID) + 1) + minID; + while (eventPool.containsKey(randomNum)) { + randomNum = rand.nextInt((maxID - minID) + 1) + minID; + } + + return randomNum; + } + + /** + * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. + */ + @Override + public void notifyOfThreadComplete(int eventID) { + eventPool.get(eventID).status(); + eventPool.remove(eventID); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java new file mode 100644 index 000000000..448c02e84 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -0,0 +1,27 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public interface IEvent { + + public void start(); + + public void stop(); + + public void status(); + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java new file mode 100644 index 000000000..4fd5b0eed --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java @@ -0,0 +1,27 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class InvalidOperationException extends Exception { + + private static final long serialVersionUID = -6191545255213410803L; + + public InvalidOperationException(String message) { + super(message); + } + +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java new file mode 100644 index 000000000..6817b1dd8 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class LongRunningEventException extends Exception { + + private static final long serialVersionUID = -483423544320148809L; + + public LongRunningEventException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java new file mode 100644 index 000000000..9f8f2891c --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java @@ -0,0 +1,26 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public class MaxNumOfEventsAllowedException extends Exception { + + private static final long serialVersionUID = -8430876973516292695L; + + public MaxNumOfEventsAllowedException(String message) { + super(message); + } +} diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java new file mode 100644 index 000000000..e5c910289 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -0,0 +1,21 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +public interface ThreadCompleteListener { + void notifyOfThreadComplete(final int eventID); +} diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties new file mode 100644 index 000000000..edbe90e05 --- /dev/null +++ b/event-asynchronous/src/main/java/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java new file mode 100644 index 000000000..8736fcf77 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java @@ -0,0 +1,32 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import java.io.IOException; + +import org.junit.Test; + +/** + * Tests that EventAsynchronous example runs without errors. + */ +public class AppTest { + @Test + public void test() throws IOException { + String[] args = {}; + App.main(args); + } +} diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java new file mode 100644 index 000000000..0ab901106 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -0,0 +1,73 @@ +/** + * The MIT License Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated + * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.event.asynchronous; + +import org.junit.Before; +import org.junit.Test; + +/** + * + * Application test + * + */ +public class EventAsynchronousTest { + App app; + + @Before + public void setUp() { + app = new App(); + } + + @Test + public void testAsynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int aEventID = eventManager.createAsyncEvent(60); + eventManager.startEvent(aEventID); + eventManager.stopEvent(aEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + eventManager.stopEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testUnsuccessfulSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } +} From e1836fee2f3fe7944faf89ff1b66b137294492ed Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:44:39 +0100 Subject: [PATCH 041/145] Updated parent POM to include new pattern (Event-asynchronous) --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 302346993..058dee5a1 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,7 @@ aggregator-microservices promise page-object + event-asynchronous From f11597136f8bebdcb208a141f8e0fd47ee4dbd1e Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 00:32:05 +0100 Subject: [PATCH 042/145] Fixed Checkstyle errors. --- .../com/iluwatar/event/asynchronous/App.java | 69 +++++++----- .../iluwatar/event/asynchronous/Event.java | 12 +-- .../event/asynchronous/EventManager.java | 102 ++++++++++++------ .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 20 ++-- 5 files changed, 130 insertions(+), 75 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 3375fd4ec..fa6116b46 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -50,6 +50,11 @@ public class App { boolean interactiveMode = false; + /** + * Program entry point. + * + * @param args command line args + */ public static void main(String[] args) { App app = new App(); @@ -71,6 +76,7 @@ public class App { try { prop.load(inputStream); } catch (IOException e) { + System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -79,6 +85,9 @@ public class App { } } + /** + * Run program in either interactive mode or not. + */ public void run() { if (interactiveMode) { runInteractiveMode(); @@ -87,29 +96,32 @@ public class App { } } + /** + * Run program in non-interactive mode. + */ public void quickRun() { EventManager eventManager = new EventManager(); try { // Create an Asynchronous event. - int aEventID = eventManager.createAsyncEvent(60); - System.out.println("Event [" + aEventID + "] has been created."); - eventManager.startEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been started."); + int aEventId = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventId + "] has been created."); + eventManager.startEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventID = eventManager.createSyncEvent(60); - System.out.println("Event [" + sEventID + "] has been created."); - eventManager.startEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been started."); + int sEventId = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventId + "] has been created."); + eventManager.startEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventID); - eventManager.getStatus(sEventID); + eventManager.getStatus(aEventId); + eventManager.getStatus(sEventId); - eventManager.stopEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been stopped."); - eventManager.stopEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been stopped."); + eventManager.stopEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been stopped."); + eventManager.stopEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { @@ -117,6 +129,9 @@ public class App { } } + /** + * Run program in interactive mode. + */ public void runInteractiveMode() { EventManager eventManager = new EventManager(); @@ -137,19 +152,19 @@ public class App { int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventID = eventManager.createAsyncEvent(eventTime); - System.out.println("Event [" + eventID + "] has been created."); - eventManager.startEvent(eventID); - System.out.println("Event [" + eventID + "] has been started."); + int eventId = eventManager.createAsyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventID = eventManager.createSyncEvent(eventTime); - System.out.println("Event [" + eventID + "] has been created."); - eventManager.startEvent(eventID); - System.out.println("Event [" + eventID + "] has been started."); + int eventId = eventManager.createSyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -159,18 +174,18 @@ public class App { } } else if (option == 2) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.stopEvent(eventID); - System.out.println("Event [" + eventID + "] has been stopped."); + eventManager.stopEvent(eventId); + System.out.println("Event [" + eventId + "] has been stopped."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.getStatus(eventID); + eventManager.getStatus(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 48dc37236..4b4fe1d94 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -23,15 +23,15 @@ package com.iluwatar.event.asynchronous; */ public class Event implements IEvent, Runnable { - private int eventID; + private int eventId; private int eventTime; private Thread thread; private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventID, int eventTime) { - this.eventID = eventID; + public Event(int eventId, int eventTime) { + this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +49,9 @@ public class Event implements IEvent, Runnable { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); } else { - System.out.println("[" + eventID + "] I am done."); + System.out.println("[" + eventId + "] I am done."); } } @@ -81,7 +81,7 @@ public class Event implements IEvent, Runnable { private final void notifyListener() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventID); + eventListener.notifyOfThreadComplete(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index 305548111..d3278594f 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,34 +32,53 @@ import java.util.concurrent.ConcurrentHashMap; */ public class EventManager implements ThreadCompleteListener { - private int minID = 1; - private int maxID = Integer.MAX_VALUE - 1; // Be cautious of overflows. + private int minId = 1; + private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) private int maxEventTime = 1800; // in seconds / 30 minutes. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; + /** + * EventManager constructor. + * + */ public EventManager() { rand = new Random(1); eventPool = new ConcurrentHashMap(maxRunningEvents); } - // Create a Synchronous event. + /** + * Create a Synchronous event. + * + * @param eventTime Time an event should run for. + * @return eventId + * @throws MaxNumOfEventsAllowedException When too many events are running at a time. + * @throws InvalidOperationException No new synchronous events can be created when one is already running. + * @throws LongRunningEventException Long running events are not allowed in the app. + */ public int createSyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventID = createEvent(eventTime); + int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } - currentlyRunningSyncEvent = eventID; + currentlyRunningSyncEvent = eventId; - return eventID; + return eventId; } - // Create an Asynchronous event. + /** + * Create an Asynchronous event. + * + * @param eventTime Time an event should run for. + * @return eventId + * @throws MaxNumOfEventsAllowedException When too many events are running at a time. + * @throws LongRunningEventException Long running events are not allowed in the app. + */ public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } @@ -74,44 +93,65 @@ public class EventManager implements ThreadCompleteListener { "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); } - int newEventID = generateID(); + int newEventId = generateId(); - Event newEvent = new Event(newEventID, eventTime); + Event newEvent = new Event(newEventId, eventTime); newEvent.addListener(this); - eventPool.put(newEventID, newEvent); + eventPool.put(newEventId, newEvent); - return newEventID; + return newEventId; } - public void startEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Starts event. + * + * @param eventId The event that needs to be started. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void startEvent(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).start(); + eventPool.get(eventId).start(); } - public void stopEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Stops event. + * + * @param eventId The event that needs to be stopped. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void stopEvent(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - if (eventID == currentlyRunningSyncEvent) { + if (eventId == currentlyRunningSyncEvent) { currentlyRunningSyncEvent = -1; } - eventPool.get(eventID).stop(); - eventPool.remove(eventID); + eventPool.get(eventId).stop(); + eventPool.remove(eventId); } - public void getStatus(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Get status of a running event. + * + * @param eventId The event to inquire status of. + * @throws EventDoesNotExistException If event does not exist in our eventPool. + */ + public void getStatus(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).status(); + eventPool.get(eventId).status(); } + /** + * Gets status of all running events. + */ @SuppressWarnings("rawtypes") public void getStatusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); @@ -125,12 +165,12 @@ public class EventManager implements ThreadCompleteListener { * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most * Integer.MAX_VALUE - 1. */ - private int generateID() { + private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxID - minID) + 1) + minID; + int randomNum = rand.nextInt((maxId - minId) + 1) + minId; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxID - minID) + 1) + minID; + randomNum = rand.nextInt((maxId - minId) + 1) + minId; } return randomNum; @@ -140,9 +180,9 @@ public class EventManager implements ThreadCompleteListener { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventID) { - eventPool.get(eventID).status(); - eventPool.remove(eventID); + public void notifyOfThreadComplete(int eventId) { + eventPool.get(eventId).status(); + eventPool.remove(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index e5c910289..88f300634 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventID); + void notifyOfThreadComplete(final int eventId); } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 0ab901106..392c7fba6 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -36,9 +36,9 @@ public class EventAsynchronousTest { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventID = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventID); - eventManager.stopEvent(aEventID); + int aEventId = eventManager.createAsyncEvent(60); + eventManager.startEvent(aEventId); + eventManager.stopEvent(aEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,9 +48,9 @@ public class EventAsynchronousTest { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - eventManager.stopEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + eventManager.stopEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); @@ -61,10 +61,10 @@ public class EventAsynchronousTest { public void testUnsuccessfulSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); From 233f1e69f9401611967c8be6cc70f16d2ac18e1b Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 22:00:19 +0100 Subject: [PATCH 043/145] Removed PUBLIC modifiers from IEvent --- .../main/java/com/iluwatar/event/asynchronous/IEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java index 448c02e84..bcd78b6c0 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -18,10 +18,10 @@ package com.iluwatar.event.asynchronous; public interface IEvent { - public void start(); + void start(); - public void stop(); + void stop(); - public void status(); + void status(); } From ab68129829b227cf6ac5637538775219a8463ac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 3 Sep 2016 22:02:08 +0300 Subject: [PATCH 044/145] Hexagonal pattern: move business logic to core --- .../administration/LotteryAdministration.java | 4 +- .../LotteryAdministrationImpl.java | 47 ++------ .../hexagonal/domain/LotterySystem.java | 58 ++++++++++ .../hexagonal/domain/LotterySystemImpl.java | 107 ++++++++++++++++++ .../hexagonal/service/LotteryService.java | 4 +- .../hexagonal/service/LotteryServiceImpl.java | 45 ++------ .../{lottery => domain}/LotteryTest.java | 38 +++---- 7 files changed, 202 insertions(+), 101 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/{lottery => domain}/LotteryTest.java (67%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index bc625b230..c6c034ac9 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -22,12 +22,12 @@ */ package com.iluwatar.hexagonal.administration; -import java.util.Map; - import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; +import java.util.Map; + /** * * Administrator interface for lottery service. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index a452600aa..2003849c2 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -22,22 +22,13 @@ */ package com.iluwatar.hexagonal.administration; -import java.util.Map; - -import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; -import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; -import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +import java.util.Map; /** * @@ -46,42 +37,24 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; */ public class LotteryAdministrationImpl implements LotteryAdministration { - private final LotteryTicketRepository repository; - private final LotteryService service = new LotteryServiceImpl(); - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - private final WireTransfers bank = new WireTransfersImpl(); + private final LotterySystem lotterySystem; + public LotteryAdministrationImpl() { - repository = new LotteryTicketInMemoryRepository(); + lotterySystem = new LotterySystemImpl(); } @Override public Map getAllSubmittedTickets() { - return repository.findAll(); + return lotterySystem.getAllSubmittedTickets(); } @Override public LotteryNumbers performLottery() { - LotteryNumbers numbers = LotteryNumbers.createRandom(); - Map tickets = getAllSubmittedTickets(); - for (LotteryTicketId id: tickets.keySet()) { - LotteryTicketCheckResult result = service.checkTicketForPrize(id, numbers); - if (result.getResult().equals(CheckResult.WIN_PRIZE)) { - boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, - tickets.get(id).getPlayerDetails().getBankAccount()); - if (transferred) { - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } else { - notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } - } else if (result.getResult().equals(CheckResult.NO_PRIZE)) { - notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); - } - } - return numbers; + return lotterySystem.performLottery(); } @Override public void resetLottery() { - repository.deleteAll(); + lotterySystem.resetLottery(); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java new file mode 100644 index 000000000..2ee114556 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import java.util.Map; +import java.util.Optional; + +/** + * Lottery system interface + */ +public interface LotterySystem { + + /** + * Get all the lottery tickets submitted for lottery + */ + Map getAllSubmittedTickets(); + + /** + * Draw lottery numbers + */ + LotteryNumbers performLottery(); + + /** + * Begin new lottery round + */ + void resetLottery(); + + /** + * Submit lottery ticket to participate in the lottery + */ + Optional submitTicket(LotteryTicket ticket); + + /** + * Check if lottery ticket has won + */ + LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); + +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java new file mode 100644 index 000000000..a9290520c --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java @@ -0,0 +1,107 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; + +import java.util.Map; +import java.util.Optional; + +/** + * Lottery system implementation + */ +public class LotterySystemImpl implements LotterySystem { + + private final LotteryTicketRepository repository; + private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + private final WireTransfers bank = new WireTransfersImpl(); + + public LotterySystemImpl() { + repository = new LotteryTicketInMemoryRepository(); + } + + @Override + public Map getAllSubmittedTickets() { + return repository.findAll(); + } + + @Override + public LotteryNumbers performLottery() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id : tickets.keySet()) { + LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, + tickets.get(id).getPlayerDetails().getBankAccount()); + if (transferred) { + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } else { + notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; + } + + @Override + public void resetLottery() { + repository.deleteAll(); + } + + @Override + public Optional submitTicket(LotteryTicket ticket) { + boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), + LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; + } + + @Override + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index 0056e794b..ef2202968 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -22,13 +22,13 @@ */ package com.iluwatar.hexagonal.service; -import java.util.Optional; - import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; +import java.util.Optional; + /** * * Interface for submitting and checking lottery tickets. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 58df1c7c8..5bce62054 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -22,20 +22,14 @@ */ package com.iluwatar.hexagonal.service; -import java.util.Optional; - -import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; -import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; + +import java.util.Optional; /** * @@ -44,45 +38,22 @@ import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; */ public class LotteryServiceImpl implements LotteryService { - private final LotteryTicketRepository repository; - - private final WireTransfers bank = new WireTransfersImpl(); - - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); + private final LotterySystem lotterySystem; /** * Constructor */ public LotteryServiceImpl() { - repository = new LotteryTicketInMemoryRepository(); + lotterySystem = new LotterySystemImpl(); } @Override public Optional submitTicket(LotteryTicket ticket) { - boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), - LotteryConstants.SERVICE_BANK_ACCOUNT); - if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); - return Optional.empty(); - } - Optional optional = repository.save(ticket); - if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); - } - return optional; + return lotterySystem.submitTicket(ticket); } @Override public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - Optional optional = repository.findById(id); - if (optional.isPresent()) { - if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(CheckResult.WIN_PRIZE, 1000); - } else { - return new LotteryTicketCheckResult(CheckResult.NO_PRIZE); - } - } else { - return new LotteryTicketCheckResult(CheckResult.TICKET_NOT_SUBMITTED); - } + return lotterySystem.checkTicketForPrize(id, winningNumbers); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java similarity index 67% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 27e5bb6e4..59c8c1930 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/lottery/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.lottery; +package com.iluwatar.hexagonal.domain; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; @@ -30,22 +30,15 @@ import java.util.HashSet; import java.util.Map; import java.util.Optional; +import com.iluwatar.hexagonal.domain.*; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; -import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; import com.iluwatar.hexagonal.test.LotteryTestUtils; /** @@ -55,8 +48,7 @@ import com.iluwatar.hexagonal.test.LotteryTestUtils; */ public class LotteryTest { - private final LotteryAdministration admin = new LotteryAdministrationImpl(); - private final LotteryService service = new LotteryServiceImpl(); + private final LotterySystem lotterySystem = new LotterySystemImpl(); private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); private final WireTransfers wireTransfers = new WireTransfersImpl(); @@ -72,34 +64,34 @@ public class LotteryTest { wireTransfers.setFunds("123-12312", 100); // admin resets the lottery - admin.resetLottery(); - assertEquals(admin.getAllSubmittedTickets().size(), 0); + lotterySystem.resetLottery(); + assertEquals(lotterySystem.getAllSubmittedTickets().size(), 0); // players submit the lottery tickets - Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + Optional ticket1 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); assertTrue(ticket1.isPresent()); - Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + Optional ticket2 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); assertTrue(ticket2.isPresent()); - Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + Optional ticket3 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); assertTrue(ticket3.isPresent()); - assertEquals(admin.getAllSubmittedTickets().size(), 3); + assertEquals(lotterySystem.getAllSubmittedTickets().size(), 3); // perform lottery - LotteryNumbers winningNumbers = admin.performLottery(); + LotteryNumbers winningNumbers = lotterySystem.performLottery(); // cheat a bit for testing sake, use winning numbers to submit another ticket - Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + Optional ticket4 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", "123-12312", "+12421255", winningNumbers.getNumbers())); assertTrue(ticket4.isPresent()); - assertEquals(admin.getAllSubmittedTickets().size(), 4); + assertEquals(lotterySystem.getAllSubmittedTickets().size(), 4); // check winners - Map tickets = admin.getAllSubmittedTickets(); + Map tickets = lotterySystem.getAllSubmittedTickets(); for (LotteryTicketId id: tickets.keySet()) { - LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); + LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(id, winningNumbers); assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { assertTrue(checkResult.getPrizeAmount() > 0); @@ -109,7 +101,7 @@ public class LotteryTest { } // check another ticket that has not been submitted - LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(new LotteryTicketId(), winningNumbers); assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); assertEquals(checkResult.getPrizeAmount(), 0); } From 3cb872807eee1173ef4724f51aab10a12e963dac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 4 Sep 2016 10:33:26 +0300 Subject: [PATCH 045/145] Hexagonal pattern: remove unnecessary repository usage from a unit test --- .../java/com/iluwatar/hexagonal/domain/LotteryTest.java | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 59c8c1930..3e114ddc4 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -49,20 +49,16 @@ import com.iluwatar.hexagonal.test.LotteryTestUtils; public class LotteryTest { private final LotterySystem lotterySystem = new LotterySystemImpl(); - private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); private final WireTransfers wireTransfers = new WireTransfersImpl(); @Before public void clear() { - repository.deleteAll(); + // add funds to the test player's bank account + wireTransfers.setFunds("123-12312", 100); } @Test public void testLottery() { - - // setup bank account with funds - wireTransfers.setFunds("123-12312", 100); - // admin resets the lottery lotterySystem.resetLottery(); assertEquals(lotterySystem.getAllSubmittedTickets().size(), 0); From 4493341ba6e122c605b05e0d1249631b8bada248 Mon Sep 17 00:00:00 2001 From: Markus Moser Date: Sun, 4 Sep 2016 11:00:24 +0200 Subject: [PATCH 046/145] add documentation to 'use latest java 8' change --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 19d4614f4..613f737d7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -19,6 +19,7 @@ after_success: - mvn clean test jacoco:report coveralls:report - bash update-ghpages.sh +# use latest java version available instead of travis default addons: apt: packages: From 22821ba8cc677172e802315a4c85592123286d29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 6 Sep 2016 21:35:36 +0300 Subject: [PATCH 047/145] Move Guice to parent pom dependency management section --- dependency-injection/pom.xml | 9 +- pom.xml | 819 ++++++++++++++++++----------------- 2 files changed, 416 insertions(+), 412 deletions(-) diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index 3472da240..88ccdd2d4 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -43,10 +43,9 @@ mockito-core test - - com.google.inject - guice - 4.0 - + + com.google.inject + guice + diff --git a/pom.xml b/pom.xml index 302346993..023579e97 100644 --- a/pom.xml +++ b/pom.xml @@ -17,413 +17,418 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---> - 4.0.0 - - com.iluwatar - java-design-patterns - 1.13.0-SNAPSHOT - pom - - 2014 - - - UTF-8 - 5.0.1.Final - 4.2.4.RELEASE - 1.3.3.RELEASE - 1.9.2.RELEASE - 1.4.190 - 4.12 - 3.0 - 4.0.0 - 0.7.2.201409121644 - 1.4 - 2.16.1 - 1.2.17 - 19.0 - 1.15.1 - 1.10.19 - 4.12.1 +--> + + 4.0.0 + com.iluwatar + java-design-patterns + 1.13.0-SNAPSHOT + pom + 2014 + + UTF-8 + 5.0.1.Final + 4.2.4.RELEASE + 1.3.3.RELEASE + 1.9.2.RELEASE + 1.4.190 + 4.12 + 3.0 + 4.0.0 + 0.7.2.201409121644 + 1.4 + 2.16.1 + 1.2.17 + 19.0 + 1.15.1 + 1.10.19 + 4.12.1 4.5.2 2.22 1.4.1 - - - abstract-factory - builder - factory-method - prototype - singleton - adapter - bridge - composite - dao - data-mapper - decorator - facade - flyweight - proxy - chain - command - interpreter - iterator - mediator - memento - model-view-presenter - observer - state - strategy - template-method - visitor - double-checked-locking - servant - service-locator - null-object - event-aggregator - callback - execute-around - property - intercepting-filter - producer-consumer - poison-pill - reader-writer-lock - lazy-loading - service-layer - specification - tolerant-reader - model-view-controller - flux - double-dispatch - multiton - resource-acquisition-is-initialization - thread-pool - twin - private-class-data - object-pool - dependency-injection - naked-objects - front-controller - repository - async-method-invocation - monostate - step-builder - business-delegate - half-sync-half-async - layers - message-channel - fluentinterface - reactor - caching - publish-subscribe - delegation - event-driven-architecture - api-gateway - factory-kit - feature-toggle - value-object - monad - mute-idiom - mutex - semaphore - hexagonal - abstract-document - aggregator-microservices - promise + 4.0 + + + abstract-factory + builder + factory-method + prototype + singleton + adapter + bridge + composite + dao + data-mapper + decorator + facade + flyweight + proxy + chain + command + interpreter + iterator + mediator + memento + model-view-presenter + observer + state + strategy + template-method + visitor + double-checked-locking + servant + service-locator + null-object + event-aggregator + callback + execute-around + property + intercepting-filter + producer-consumer + poison-pill + reader-writer-lock + lazy-loading + service-layer + specification + tolerant-reader + model-view-controller + flux + double-dispatch + multiton + resource-acquisition-is-initialization + thread-pool + twin + private-class-data + object-pool + dependency-injection + naked-objects + front-controller + repository + async-method-invocation + monostate + step-builder + business-delegate + half-sync-half-async + layers + message-channel + fluentinterface + reactor + caching + publish-subscribe + delegation + event-driven-architecture + api-gateway + factory-kit + feature-toggle + value-object + monad + mute-idiom + mutex + semaphore + hexagonal + abstract-document + aggregator-microservices + promise page-object - + - - - - org.hibernate - hibernate-core - ${hibernate.version} - - - org.hibernate - hibernate-entitymanager - ${hibernate.version} - - - org.springframework - spring-test - ${spring.version} - - - org.springframework.boot - spring-boot-dependencies - ${spring-boot.version} - pom - import - - - org.springframework.data - spring-data-jpa - ${spring-data.version} - - - org.springframework - spring-webmvc - ${spring.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.apache.httpcomponents - httpclient - ${apache-httpcomponents.version} - - - com.h2database - h2 - ${h2.version} - - - commons-dbcp - commons-dbcp - ${commons-dbcp.version} - - - org.apache.camel - camel-core - ${camel.version} - - - org.apache.camel - camel-stream - ${camel.version} - - - junit - junit - ${junit.version} - test - - - org.mockito - mockito-core - ${mockito.version} - test - - - log4j - log4j - ${log4j.version} - - - com.google.guava - guava - ${guava.version} - - - com.github.stefanbirkner - system-rules - ${systemrules.version} - test - - - de.bechte.junit - junit-hierarchicalcontextrunner - ${hierarchical-junit-runner-version} - test - - - net.sourceforge.htmlunit - htmlunit - ${htmlunit.version} - test - - - + + + + org.hibernate + hibernate-core + ${hibernate.version} + + + org.hibernate + hibernate-entitymanager + ${hibernate.version} + + + org.springframework + spring-test + ${spring.version} + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + org.springframework.data + spring-data-jpa + ${spring-data.version} + + + org.springframework + spring-webmvc + ${spring.version} + + + org.springframework.boot + spring-boot-starter-web + ${spring-boot.version} + + + org.apache.httpcomponents + httpclient + ${apache-httpcomponents.version} + + + com.h2database + h2 + ${h2.version} + + + commons-dbcp + commons-dbcp + ${commons-dbcp.version} + + + org.apache.camel + camel-core + ${camel.version} + + + org.apache.camel + camel-stream + ${camel.version} + + + junit + junit + ${junit.version} + test + + + org.mockito + mockito-core + ${mockito.version} + test + + + log4j + log4j + ${log4j.version} + + + com.google.guava + guava + ${guava.version} + + + com.github.stefanbirkner + system-rules + ${systemrules.version} + test + + + de.bechte.junit + junit-hierarchicalcontextrunner + ${hierarchical-junit-runner-version} + test + + + net.sourceforge.htmlunit + htmlunit + ${htmlunit.version} + test + + + com.google.inject + guice + ${guice.version} + + + - - - - - - org.eclipse.m2e - lifecycle-mapping - 1.0.0 - - - - - - org.jacoco - - jacoco-maven-plugin - - - [0.6.2,) - - - prepare-agent - - - - - - - - - - - - + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.jacoco + + jacoco-maven-plugin + + + [0.6.2,) + + + prepare-agent + + + + + + + + + + + + - - - - org.apache.maven.plugins - maven-compiler-plugin - ${compiler.version} - - 1.8 - 1.8 - - - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - - - org.jacoco - jacoco-maven-plugin - ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - **com.steadystate* - - - - - prepare-agent - - prepare-agent - - - - - - - - org.apache.maven.plugins - maven-checkstyle-plugin - 2.17 - - - validate - - check - - validate - - checkstyle.xml - checkstyle-suppressions.xml - UTF-8 - true - true - true - - - - - - - org.jacoco - jacoco-maven-plugin - 0.7.5.201505241946 - - - - prepare-agent - - - - report - prepare-package - - report - - - - - - org.apache.maven.plugins - maven-surefire-plugin - 2.18.1 - - - org.apache.maven.surefire - surefire-junit47 - 2.18.1 - - - - -Xmx1024M ${argLine} - - + + - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - true - 5 - true - - - - - check - - - exclude-pmd.properties - - - + org.apache.maven.plugins + maven-compiler-plugin + ${compiler.version} + + 1.8 + 1.8 + + + + org.eluder.coveralls + coveralls-maven-plugin + ${coveralls.version} + + jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF + + + + org.jacoco + jacoco-maven-plugin + ${jacoco.version} + + + + + domainapp/dom/modules/simple/QSimpleObject.class + **com.steadystate* + + + + + prepare-agent + + prepare-agent + + + - - com.mycila - license-maven-plugin - 2.11 - -
    com/mycila/maven/plugin/license/templates/MIT.txt
    - - Ilkka Seppälä - - true -
    - - - install-format - install - - format - - - -
    + + + org.apache.maven.plugins + maven-checkstyle-plugin + 2.17 + + + validate + + check + + validate + + checkstyle.xml + checkstyle-suppressions.xml + UTF-8 + true + true + true + + + + + + + org.jacoco + jacoco-maven-plugin + 0.7.5.201505241946 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + + org.apache.maven.plugins + maven-surefire-plugin + 2.18.1 + + + org.apache.maven.surefire + surefire-junit47 + 2.18.1 + + + + -Xmx1024M ${argLine} + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + true + 5 + true + + + + + check + + + exclude-pmd.properties + + + + + + + com.mycila + license-maven-plugin + 2.11 + +
    com/mycila/maven/plugin/license/templates/MIT.txt
    + + Ilkka Seppälä + + true +
    + + + install-format + install + + format + + + +
    com.github.markusmo3.urm @@ -448,17 +453,17 @@ -
    -
    + +
    - - - - org.apache.maven.plugins - maven-pmd-plugin - 3.6 - - - + + + + org.apache.maven.plugins + maven-pmd-plugin + 3.6 + + +
    From 348e577e8ed80b3cf7642b40d5b5378e6965fea6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 6 Sep 2016 21:39:08 +0300 Subject: [PATCH 048/145] Hexagonal pattern: Add Guice dependency --- hexagonal/pom.xml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index e9e2a502d..a73fea48d 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -39,5 +39,9 @@ junit test + + com.google.inject + guice + From 1b10ddbb73ff167e6a411b73442cfeb4b8ed754d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 6 Sep 2016 22:39:39 +0300 Subject: [PATCH 049/145] Hexagonal pattern: Use Guice dependency injection --- .../main/java/com/iluwatar/hexagonal/App.java | 11 ++-- .../com/iluwatar/hexagonal/LotteryModule.java | 52 +++++++++++++++++++ .../LotteryAdministrationImpl.java | 7 +-- .../hexagonal/domain/LotterySystemImpl.java | 27 ++++++---- .../hexagonal/service/LotteryServiceImpl.java | 7 +-- .../hexagonal/LotteryTestingModule.java | 52 +++++++++++++++++++ .../hexagonal/domain/LotteryTest.java | 21 ++++++-- 7 files changed, 152 insertions(+), 25 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 92ebf3572..8f99fb15a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -26,15 +26,15 @@ import java.util.ArrayList; import java.util.List; import java.util.Random; +import com.google.inject.Guice; +import com.google.inject.Injector; import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; import com.iluwatar.hexagonal.banking.WireTransfersImpl; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; /** * @@ -124,12 +124,15 @@ public class App { * Program entry point */ public static void main(String[] args) { + + Injector injector = Guice.createInjector(new LotteryModule()); + // start new lottery round - LotteryAdministration administartion = new LotteryAdministrationImpl(); + LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); administartion.resetLottery(); // submit some lottery tickets - LotteryServiceImpl service = new LotteryServiceImpl(); + LotteryService service = injector.getInstance(LotteryService.class); submitTickets(service, 20); // perform lottery diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java new file mode 100644 index 000000000..b51bff9f4 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal; + +import com.google.inject.AbstractModule; +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * Guice module for binding production dependencies + */ +public class LotteryModule extends AbstractModule { + @Override + protected void configure() { + bind(LotteryTicketRepository.class).to(LotteryTicketInMemoryRepository.class); + bind(LotterySystem.class).to(LotterySystemImpl.class); + bind(LotteryNotifications.class).to(LotteryNotificationsImpl.class); + bind(WireTransfers.class).to(WireTransfersImpl.class); + bind(LotteryAdministration.class).to(LotteryAdministrationImpl.class); + bind(LotteryService.class).to(LotteryServiceImpl.class); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java index 2003849c2..bef2f07c3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministrationImpl.java @@ -22,9 +22,9 @@ */ package com.iluwatar.hexagonal.administration; +import com.google.inject.Inject; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; @@ -39,8 +39,9 @@ public class LotteryAdministrationImpl implements LotteryAdministration { private final LotterySystem lotterySystem; - public LotteryAdministrationImpl() { - lotterySystem = new LotterySystemImpl(); + @Inject + public LotteryAdministrationImpl(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; } @Override diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java index a9290520c..e37185143 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java @@ -22,12 +22,10 @@ */ package com.iluwatar.hexagonal.domain; +import com.google.inject.Inject; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; import java.util.Map; import java.util.Optional; @@ -38,11 +36,18 @@ import java.util.Optional; public class LotterySystemImpl implements LotterySystem { private final LotteryTicketRepository repository; - private final LotteryNotifications notifications = new LotteryNotificationsImpl(); - private final WireTransfers bank = new WireTransfersImpl(); + private final LotteryNotifications notifications; + private final WireTransfers wireTransfers; - public LotterySystemImpl() { - repository = new LotteryTicketInMemoryRepository(); + /** + * Constructor + */ + @Inject + public LotterySystemImpl(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { + this.repository = repository; + this.notifications = notifications; + this.wireTransfers = wireTransfers; } @Override @@ -57,8 +62,8 @@ public class LotterySystemImpl implements LotterySystem { for (LotteryTicketId id : tickets.keySet()) { LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - boolean transferred = bank.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, - tickets.get(id).getPlayerDetails().getBankAccount()); + boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, + LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); if (transferred) { notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); } else { @@ -78,8 +83,8 @@ public class LotterySystemImpl implements LotterySystem { @Override public Optional submitTicket(LotteryTicket ticket) { - boolean result = bank.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), - LotteryConstants.SERVICE_BANK_ACCOUNT); + boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, + ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); if (result == false) { notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); return Optional.empty(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java index 5bce62054..c912bb0b4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java @@ -22,9 +22,9 @@ */ package com.iluwatar.hexagonal.service; +import com.google.inject.Inject; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; @@ -43,8 +43,9 @@ public class LotteryServiceImpl implements LotteryService { /** * Constructor */ - public LotteryServiceImpl() { - lotterySystem = new LotterySystemImpl(); + @Inject + public LotteryServiceImpl(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; } @Override diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java new file mode 100644 index 000000000..5fbeb8240 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -0,0 +1,52 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal; + +import com.google.inject.AbstractModule; +import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.banking.WireTransfersImpl; +import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.domain.LotterySystem; +import com.iluwatar.hexagonal.domain.LotterySystemImpl; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.service.LotteryServiceImpl; + +/** + * Guice module for testing dependencies + */ +public class LotteryTestingModule extends AbstractModule { + @Override + protected void configure() { + bind(LotteryTicketRepository.class).to(LotteryTicketInMemoryRepository.class); + bind(LotterySystem.class).to(LotterySystemImpl.class); + bind(LotteryNotifications.class).to(LotteryNotificationsImpl.class); + bind(WireTransfers.class).to(WireTransfersImpl.class); + bind(LotteryAdministration.class).to(LotteryAdministrationImpl.class); + bind(LotteryService.class).to(LotteryServiceImpl.class); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 3e114ddc4..331cc0d66 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -30,6 +30,11 @@ import java.util.HashSet; import java.util.Map; import java.util.Optional; +import com.google.inject.Guice; +import com.google.inject.Inject; +import com.google.inject.Injector; +import com.iluwatar.hexagonal.LotteryModule; +import com.iluwatar.hexagonal.LotteryTestingModule; import com.iluwatar.hexagonal.domain.*; import org.junit.Before; import org.junit.Test; @@ -48,11 +53,19 @@ import com.iluwatar.hexagonal.test.LotteryTestUtils; */ public class LotteryTest { - private final LotterySystem lotterySystem = new LotterySystemImpl(); - private final WireTransfers wireTransfers = new WireTransfersImpl(); - + private Injector injector; + @Inject + private LotterySystem lotterySystem; + @Inject + private WireTransfers wireTransfers; + + public LotteryTest() { + this.injector = Guice.createInjector(new LotteryTestingModule()); + } + @Before - public void clear() { + public void setup() { + injector.injectMembers(this); // add funds to the test player's bank account wireTransfers.setFunds("123-12312", 100); } From 0f2807b9cfd2863d6486e968bd32fca2d99c42d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 9 Sep 2016 21:36:17 +0300 Subject: [PATCH 050/145] Hexagonal pattern: More descriptive class names --- hexagonal/etc/hexagonal.png | Bin 165940 -> 148602 bytes hexagonal/etc/hexagonal.ucls | 357 ++++++++---------- .../main/java/com/iluwatar/hexagonal/App.java | 4 +- .../com/iluwatar/hexagonal/LotteryModule.java | 20 +- ...onImpl.java => ConsoleAdministration.java} | 4 +- ...reTransfersImpl.java => InMemoryBank.java} | 2 +- ...ory.java => InMemoryTicketRepository.java} | 2 +- ...ionsImpl.java => StdOutNotifications.java} | 2 +- ...ryServiceImpl.java => ConsoleService.java} | 4 +- .../hexagonal/LotteryTestingModule.java | 20 +- .../hexagonal/banking/WireTransfersTest.java | 2 +- .../database/LotteryTicketRepositoryTest.java | 6 +- .../hexagonal/domain/LotteryTest.java | 5 - 13 files changed, 188 insertions(+), 240 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/administration/{LotteryAdministrationImpl.java => ConsoleAdministration.java} (93%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/banking/{WireTransfersImpl.java => InMemoryBank.java} (97%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/database/{LotteryTicketInMemoryRepository.java => InMemoryTicketRepository.java} (96%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/{LotteryNotificationsImpl.java => StdOutNotifications.java} (97%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/service/{LotteryServiceImpl.java => ConsoleService.java} (94%) diff --git a/hexagonal/etc/hexagonal.png b/hexagonal/etc/hexagonal.png index 8c03d375f998e2cd843e7a1dbc3726dc3720c8a0..1ad077fd4fd0e9874c2f85f3f488ead2e3fe7ffc 100644 GIT binary patch literal 148602 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�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 A!2kdN literal 165940 zcmd?RWl)`4yDhlz1cC((E+JS5B)Ge~y95aC!JPmhS-~~9yL)hw;O-XOA-Fp|N%r~f z`R=)=>)xvFs;=twWB*{+Ui)2hJ~Ey$#`AtwkP}BmCP0QjAgGcOB1#a*V+RNX&f*a) z_)B(bO&bI@=vq=lP{lQAC;71_s`_?W6cP2=;&f8Acg_(){-yIVh)jyC)5e8cj~#&))VO zkKpCTuhx_#j3x^P*wH%JUGNA9qLrv6BJ85Sxg5iaZ!Y0|D#wQ9Oj~jU5^UVnB2uW1wwt1Nr*l0?Sf? zrVLqv9(IxFFf&vexs8Uyxl9PK`Lf_yMMNAktOACc1h-n znJAHUs1mKGjQKqo#&>PyXl$%8SFft195n1av)gj7`}2%e{ng?ZMmAdKMPt*_JhkGa z<*||ECyn(cS5@YtT>XhbfkEoB(uu_yUgvYFvI%3MUo6`POXdxc+QPVOX!S5C10dXE zjBKAD9@2{mbMcozI@w?=CuASOj^GJ*A#)BpXh3doBLuTMg>*pj3=^}$?YNJSy@I9I?yE|dUK|6Vc%kuh`xm?>_tS%&X<V6BLbN^8 zFaX0KNDMkC4*yu|00%r`p+I`8C^*MlaE{IJo;eM^re#D--h#f)J0C&7IxEmPZV)~^ zV%Jq7D+=%X7qX^bZYU#r7JOQwzT!`2h|Znu67nBOq?3BPCOVtAogS`uFlwEAa7WVn z*%O0xeP&%)5-%z0wV0dQtl8|D9}wVdZeA%_3sv>L$d3Vy)#M38~Mpi`i+!{k6(G+)f|WrRuHdbRlMB~q|HMjCMg`CVHAF*fqt zB_g=!q(t`08XH-1>+oz6{${E7y>;(@mFjScpj4WmlCYOdR=itrKl#+`M4wm_O{<(l zo9=zPo$Aw^E)`R;F=%SG*UI5+^)6fL-6pnY?{|OcJck!J{cxpvyp2YkI#*TiH*&UB zmA3K}%hS_i@Lmz!UhgBZ)L!uXiOw+SLv#|*@{wYuA*8}!!89XdBkRBqz_=28qg|A4 zHLXW+MeK!H!GkyJv;k>tf)ps^(hM^KonP@spJ?msd_p)O+mx+c+a>x%FO7kiTdYVs zE-7i&XSvN)Yp%&dFkJ{iv&8%ECSs6xGW}hzdsR^#TrP;nO!4!J-=&mQNwI7VI(5EZ z@Xp`l=b_J*z2)*aV%$}(KZ$~lS6fDH25)WU%jgEUe<8+rC?u}p!5FYR3b|9%6cR%p z+P{nV4#EX~XoT5-G$PJoAHXa!&!O@7tNI>(L98;u0{L4RBb3f94f414EpE}&Q9EAZ z_E{Yn#L2FtzHI!`yB zg=Xt7IP^*&3ch~*sP9!RT7Udg@6?Z4zQlGY&0R#~(XDO^pJsW%wASc;`uU%l=c@eY zQ7fkyD9mO+Ry_u(j|Ea+8-#2e2-#RfzER;;=1=Is`Z8(6sp25@3F$!U)BJy}%KpDx z#Q#sfem!A@x?)8wB-o9!`7Ri(c;h_d-Hf4op|AL#WB(=^jPYrs%%3=c5YwQkmE-H9 z1C}AmG8#Ly)<<^s5T-@u@mBsR_|(_l8ZXOmm*tDx7nsn2eu^tx23`TjcyxBTK$u!XDCqu7^blTZFfYoErZK18Wmg0}o_4_BRd)$3Z;u zBI%C5Ek6HCTn#i(Bpb0($n+MuBfson##u z5%vb2&wG@PI8nXmc$V)xm=d?(eZ8=~#`AuyC%xQgr6-#9ZqXlMbN^szj!}PpKbAYZ zcD-&bqfhiEZzXm)1D}CB++hv&D8FZ>TJq#PJ1%9CXSH&WZPRl*uR39}t03VG6^!#{ z-d7yVmv5VP#_q4w(^I`?+G`y5W}{_hB;Zy}k`lOf$~#GDmD=`K!t~i~eqA}$d+Q?F z<`cNEQr2E{i1>ch5DH6LTpStuC>eES;cYRy`&+w0nR{>-r;ft-i^YT`=oMZ?%gblG z+9xNYtfXEi`e8guG%t&!#5z3HE&K~%LVX9Ljb{0y8MGq}dg;|<(k#ipSB$?lav$0L zT`JG|3a0<{8}p%%xDF&Tb{pfhV#I~x$9$j>e==K_gQ4An;LN02Pq*CjTlFH579CdX z9(I&1@9Tdv>iooPI>O_~b$N^Z)>{K)5n}-LC-q?=@px=JqoDPr6s)xK>R5DhQGX)- z2wfksphIn}bDxO(ZvjoY&hoQt=>uamH=RG~lypI(MMU4r(3{v;mknwv2bu18c5qPe z{4*MH!mP`J*YNP^tOHgs``~G2fY6__hvFAgU=TR=$1_J$z_X@-x>hCQ2}+7=qDlDu zIL~u+#eiB2^Y%s@Wr`1~MR6{CplNi}aLCdhMJdI7BIG1iDpW&g7h5r#*h0olrabWb z40&A+C3$v#yH8*t5Qyv-Ll-kV`~BOmNxW#~I&-}UxfNZ^3Q^YIte}XXsoC4N+$Ct{ zDF_}}l{UWd?v5SFznRbqrV;uqI{l$vhZKeYPsV|4956!AH-}mhf+n7%#d)OF3URr4a zcbJqZZ?!vux8nKl;YeiZw+lbdg-lllo&25*RZC>a-%U5^x`yl#1lB#^#S}DojMDnR z&^NRx()8{^zDJ{O)Oer{E4sfV9p0$k(C_ySWaU0S)oxV^W@a|x@|v-g z&6pJ#E8zC_R5deOLn3{tP?5#2IasVc&2uhSL$9BcTx%nnuU1Tiy0kr6dN;1FoyA?L z9z}hQf{aW?Z$8fxeD!6rlr%dGjaW;F^!9q;=Yo$9E5neuPHdHFXDH@a@t75)@cl&5 z_lStM==?Vd4bCBVH^*eVlCioueY-HvpDhn(I*xZ@_iNWV7*1DEZDG8*Ep2K3#UrW; z%^Vxof_;F%PC+H5l(k%q zMpvt)AKs3N%qCAnv=m#uKE*6D1c|6xh=}u2V{vEt*L}}(dHQX3-^ACK^PVm}-BG`t zUh0n=9Va+hq|CY6E!9R7zwx?)6eOWN19+gu`aKknQL8|uoKie?IEB>vTc~rI%{zby zH(!)-MW~&;F3J#hrl=U{B=HEaNDCqLs!CxO*gZ>5V6Qcn-R;`dX$i_PYg~mP*H)?2 zgByU^G{C`&i>cNrQ6@03nPR$~)MpM+k+7y$D|%E7<>T^nrS%fglWwUj)@0BLk@X#1 zUG3p-JnC_^;JBAXkv&t?gz2bG87aCw1zmA9O?jBp0fkZi&oZuu|rK zKU-DvO{mCtuSnG!Cm(Kn-?JlOnLCONCuZtR&F_YLoQrDf>x+5|AKf#sMaRpWUe=^+YuSNixg`#99Cv` z1}A|PC7Xcn_C$7OshgTK_d-NWo|s#UN;)G<0(w5@vT%P_Ydv0oXTEZE+Py731cI*8 zaf`a!4heNzrguyjAn{)e@od%t&S%hAhTryKgNf&t8-#l;_jljEsbx7M+a`0fuk|ad zQ1MGuYDtqlpiFzwU^UE7CUD<2ZVx+?u^|Mk1=Xg5z1WB(J~zYscO0$!UYBdbDvwWB zH!=DVyR^IH1^r)fLARezE&#miDM7!D`?nM&yD$A1HqQ3Cu8A==7tKXoG?AgEQ~Z&7 zmNj$2pg%fjIk9EBFeePD@|UPkhV{RhuP%*=!7k*6bUA9JX{D_ zqdq2amjDCiz4eTt8>{0YR-W8yB8=C-XG$AoUuYB^S^`ZgPcB(1^p&!*a)Rk3He*Zb z(XW_|4s9)W#4ou&vDykTO66Vzs%n7j1GTTpcHwI*7cevX~4i0+~JuP0USF9O`$GFF!QPw(DUOPBa z_BO=*v|w0Bs>fi)bbmhZZ?Hub#j|-|Te~+Wb^b2*QP>@$rJyw8tA-N4vLy*&kWbMW z^oEhl(JwcAX445RSQR%{vwZv)>3kZdgB`YloHwpUnEP>Fjpo+oR}kCGc4f$EkLEaq|E*%Q8ynnK z@J@TP&J`ASL@KhiwnL4VJZVDQH_ExZETSvCSt3iPGZj{#RnJl`fysXs`wcuq=6ffuoRU!w_BZ8ZjN^rIM)G?0Gfzus`ztM> zaSPU$yFKs!OEm)-gv#J^+^;P&kbpyAD{q64M@xTOm2tDpBgL16gZh-URQ+fCE>>-% zN!I=CnQoM3GQr@EKoMuN(B8&8VPfZ z;5;K`7##|oMjHS5duQtP2UVf#zGRPUq#vNtnUq8Kf2Ne}=fND!<5;#H54vQnfuZ>N zl2@j1404%U`oBk_#0P}>HK#U%$2#QZ13a==zI0}I@oYb zHTIxNRmqS_a)#A`(fqY{$Li|cjtG_X{9UPO7LM1G!p(@ZW4aDFE2gxqGt`9In+=3W z8SbNytN$7t`0Ax3^9?R<0qSw=s(0!}q|ch6pb(nUv9Q=?>el?XDnm9?YfFT2057?7 z*h!i_esS@&K-C{0A4#Qphqdqw{qHyhwCraQT*Y|{$xQRV;*8-i{B7DyKR)5Jx|x8Q z%_qT*Z1In3+{K$}RavOE2nW3=FITKeV5Bb2`yUZ8ls~$=z!#~sIx=MbS?Eu-DK#me z91N+gI(dRC9|TY=xn8mia-rS!*v6{EMA49|hXcKUtR{K!CRO#fFW#0<33DAcs||;; z3*+*qJ^1-~Ezcumi%qL%KT$AvyjJTIudOx zhZ6C%x=Y2*phJqhs2LwxibZeSvA_FOuU_?OH?{3*uiNoP2;n^srt3&nGk~F-Uhah^ za73pFTL)lnCgHA$tPiU?3?iM6WeD_&?BbKjHyg+$-6v zz+!FXtfbUc<;mUIy4@T8%JsfyQYhRm1MkNm{d{Z7?MCK)83@+1fHMG5?(~Hg^E=%{ zZxDr&-28`+C*5ReBsYnE4T{am_FUezU2y6)AXRozVLi|192XNcmx9R@Kz zD`KLhO&9RH*&!_%OsdoU{O_HI?165h0)(Tu_+*qrJ!w`0B5J{8slKkmP-!^PH-!Ht z9R3A%ytVA->Q}`{Z`A=7{5?s1T$M3eILhLb+vFmCF5=B@GuKn1nAw=R@N*}qmy1Es zOzLA}X|?V!Ha6w^^40SFfG+f0)Kn4dqsa&2Dy6blc#w6kV;w&4uxv zNdN!``U{zh4EICT>-!*!O3GOw(=5{=USaj?Z0l!G+}ZYcy=JAt(VWG2ggY%+49(IY z3FFVSTk~4bc4WU|EL2-Q9|s(uP;9|8L^iNUH&MjY=k_$;k=*O5D*dbd;j*6F@kg}V zjp1(T*x+nLY})1X(@AZX!bs9=&El;ED;uZfpY1rWwIs+QSISQ2T)6YJMg~%N8ToG= zW#3rcebl>(wxHL&k|Yq15T3O1Z4A7@-pQq7sn7gPLG|#fYyXy$qpD?6A0iq6TZ=0c zn??~gb8%Sz1!lT;iTql8miiM-LkVya88_H5BRo1%DwUS8@YqzcVFOA8@_j z;&Zg>J(6r%IlBGzH7`{sC~S4*VU8ovjtw88{>uZHPnhh>(!z+OY~F<;EY)t(Bq^`} zI_P~X+weo$qxQaKjmpJI)=SRG=iY7NuBV1JM8vXjFC^#IY*&e-|7{@blHLtRv#!$8 zg9e9=t{+@+mSc!al~~q_{sl&^wsLh>#Wv}uK;7$&J;i>H1}!`$x(ruikvlrOWYr6X zUb&oKOd>Xa^O`vcR=JYyor|Ly^UCM-DqwPVqtz&bSPu^93F=$;!Cu0_rljSyI`1vC z@J@?Fy{N|N1<6~eR;&XkKFcv0d0}qJ@miykJz`-($tlm?2bMKywne87Qb5O4#^D45 z=Qp#etP%s#*BS)(BODZPgp6{MX3Dv%Pd=eLH{v{)5L$(WJ zAHdNw*Jq35|02Tti0d_Ef*+OX9b#H=wIaQM6+v`AK@AxBb2f5*U*0pGIkNJ>dFH^v z-1@JTLFErnSMY`m8Hk758mZSUdUo~m07o$YSRRAXjyMbFeg7fWv~DaK^y{fiB`5Eu z?H~^2ntaB=i(GkI-8zR1P!s{R{MB3Alkm^%t=;y>uN6&~c3li!qjd2RtN0=z77PU( z?-fMI{0|jH+1g%*=}}LR3d5%y!g2RKL!+JgBKYu&Q8^#R!y}lO_Xz)a1U;08N5IU& zVdw)>8h~tRdQBD^1J>+Kum2GqI@>lqV9Wi9ufAfgKU=8Ot0fyF@*9+ahvW)`xrx#A zyQ6X-88CRZwoi|aUpzU8Z`}p|uhxsXP;?QN*ZZF}6OM-P=m6bcN;h`BznLCQTyDvmD$YEfpNYoCbd>dA)E=5j7cGT^(Bcpa=+-QrbJ^3;sDGy zybBip+OB9@q;F`RCN||U@?GQa=$vsEm|HKJ{EMIe>>i(u&b19S13JdK5pp2nv2$mm z4}%-TT=YE3G~e^~G`6jcLp*i9KBga6f{vEfzrF62dX^#1->1T(tb%a&S(lrMlqG_P zik0Vcqhw?2VK^6*0guDMrk>rf9IduyIli$}Cl%A}Rc2ndI#1ZH zHtd&V>^{H>FEEgPLS6ZwKun!CqN7@18QV!IelEqlTo!`tb=dHoaRKKr!A5HyW&no9 zSDtxq6#JFL@RRxVtJA_%{#3zh<-)t${iOt6)b{e6L>RjORIeAFAM8^F3LP&UtyVg! z!-%!>Z7MUUg*)#KJ7Km_ye^iw`sYJ4teL!}A>FHPHa&~gn<%pUEQvNG6Ok$3DH4Rz z1z|RV5Ma#PJtQQb$-IzzfVbC2z5V(5KXDmJqzg}O+dIS}LMl?k4GoFnDH*~u29oN2 zi#cTpot?K_3l-vdt)j{Qu z1rSfomUe6Jc`a04?h02AZv$;w0s@%xi0`t=SF=8M%woFZCtGlO9@bBb2fn@z-_iE+ zgF|vQwDw*KG{xzp%Mwy8);gtNc&WpoRY}+HkzEpn`@eA~3Sr@|l1l$|AJ27%KOki0 ziC+L3Hm`E?oj;~vn2=Kv4~&YrPRw?~+F{pl(?~FfRi=>s4af}_(Z-y_SQpShkrziD z#`9wv$V8c46U|fK^>z#k85>W9@$Zf8nA3!U*zl#a+GJkB3ciNd+I^Ub8}IM72a*#D zeidzY_a$-WNynM&H&!Ms(42Az&9mXRQ281k@vy%;&^igsAx)5F591*NW!DTOIzkdD zE_@cw{@bCn0gtoBKK1S+=Y2f^6c!!xsWL<#h5cyS$AH-Sf?MxmdpB)LcyY(2G+kwm zs3{XhGHfha)nqx@rSH>!bN1`x?)F!dWVM-M9NLnSB1oCYcfHv^66_aS4K0?!$#pX` zet4XRl4F9L6`zr__P99|2b@ku3G`)TXrzND%yexgiZLsFsH#NZo~p^Zb>>33QHh)? z^x^a+MZNV#`vvr=`E*td190KTJ`nE=U1P%J*vyXMaMKq69PqDWUtR0H?hN@oo+Y`P zF5FNd%?9*lSG2s5Z*(}RbjuN$9@)?Ar^EuASItG8FH1g6I*uNp*`W!*cqZ7V=GX~B zO%B-ZPew}s?Fd-NP&Vt7SfUe_u{cOu+ebbuzVF}U(^Mx4fQmrCl2`JO1P_Aj(vqrX zA&Un$wC4K-1I+WNjXGNY$qYu8+aarZw^bNW$wZWCSZEe+%E-uyUC|$cp26Ku^VerA z%Z&lzfukvKTaD_a6Lpb239QOf1JZFXB2^^t=jufxS?>Wu3`S(@U-KleO$R?Jcc_v8 zUy;aa$uvC+@bmaDoPAri6K<|~UYnWgu=jO#+TWGed&8XuFrQBk9F{wj;si*{}N?6jNK5 zGz)8dynt;E!I<>|rNb?c{U*9FaW@Hix|4RNei!HeTtR}bSprd%ScLg}s=U39K$r7g z>mlzJloz%y+A@INLX7jUKoLQ=lpHj`lm#8y_xR@r->QrzG8jP6TWD!C zxjO*9b@Z3fUwlyWG!y3`qN-vr-I3@M>Zw{qAYIG~*|u;SkR3!%0y~5YnFV3vR5F!c z1~4Jz{Kn<0Y~K`l(k z#wSvzStT;D?*PO8c;u%Fo(m#?#eoEYFrj(AC=h>ReRO=++-5VCM!>_Q*xHvh>H z29Pd<#qT{Zc&)UW!v2C?L2AYRV~p}s75G2=dh!Qn+cKW=*wUKe8LybVUSADn7+$3< z6SCARWMkl&%g|gTQUIS|ybBrZU%2@E$WYq$ms`(Xnk6qqL8FNt{y*pgibW)N0jhtpawu7vj6L^hnJz3!aFxH zUC>WX3ry18^H#~>!{QC%Cnhb+hyX`N1N~2USF=xZA+&YQ3-PrZ7gEN9_Y!)!l7)8%$b{v%-`=!OBG0ecU=%$ zZ}h}Sb3e~Al zp`#Q68lf8K3!gvRv)h`O*NB@E^k91W?)Ok#LvUYvLp)JIB{q)gUdUQ(oN?z!0R~)#omKizu-otZw7F6gcZpt&$~Q>tvoyY* zV>1ZaW58d6{F`s{%SB&9dm_3ZD_>M5GgZv5D7`v|&7pLvIL7_lpFVe-{xv!c&RI^s z5s}beDb{XG6z}lp#K~z5>qe=Mm0?j&c!ObK-&5J`=YIrzLXdaySM9LkF0XINJRF|S zIYfj(d@Yxs_N%uDyyU1+tuKuAaaLP}HSNRaus3WpTmdtPpI@!|xL4eyergZ5UdJd@ zMP8s4YfO+(1 zpHqY;dDmTTuN1GO*@y@U$I}E)cXn_rgq@_dA})Y93#ipc;A>8d3j76&;`QYe~_c^I!%D4SrInT`L82iJD(s5#>(X zD#$2>6t8`7aDY24n`nM8|HemFsp~27D{P ztT--gl}tBsr~!Rgq*- z(f;RUeSLusv?9Or6X5Xx%1F5T@d2SYnfOBu$sn!gvzg_ zVwRNLBxIepCq@4h)J3nG!{0fHNIOFz?XLH{r&5_jPJP;bZ9(sIE z7L=?nC@73UvAyA??C!oL1^$5&UCJ%Hvr5r<5(_%iabO4XU3Dd}7|nUM!~SZlCWypc z;f{!suXVD{xZY@xXY|7^DX}gvrDbm;k6(8YcBFym`Vt)6=oO!%-Jlw*j|T4l{NeuF z>okG;*u$!3Bh`oG32N~TaJvg$JzMe~CJflDN z-&(b-D-?%@UuXT~R|u}dn=j`(t@G7tl_pGrxNMda2JH_?S?_3m*2~zC?J){9u8>Wf zSSn?Tu9`1)J+n$MDdcjo0so$YVP~!(T#jyT>t`}wQ_S7GjkDRBblh1v-|UaTOkl@& z$?v%_;0DTpnyG>WT_c^A25|o9=oISk!p9VrcwKi`p*R_bk$52#yeIxcK#mSMy?XuG zIkSaxSo-HpWaLwm0iw2=a4JI2M|8~I;tw?!Sh4u8t^BvvVYnTQx_2LmJLg^MW0c|G zCOq<$fgJqffq(0jbGI1xF%L8jkYm;h&4-j>*jGzIwbb%^aglGn?x$%abDeUY@zBw3 zlsll{g-#?nII%<>1G*rVQJ;=HT~}+yD&_2<5NF%aU9TZ?+X3Um@p{NtW>YgDb(#*) zn6`RfzB>mD-0wN=9JArhP*Qy=dHNdjW0qf?!z|cye!$02#sH=};%ppZ-J>szXOEGQ z)81u(c2?TVC8?Y4PU`lP!;${MyH?RCL|$~*D;NZGK?1y!bsX%42L~2m{juG(m9;!L^1!aHhiYGszcNPl{Vh&_96fHzu(O)j8*rCqs^$Lc@=w+XK_)U82vFC& zl&4p!rRC%6FjuvBr8A|Rr|_LlwP&K}nT(;w!FN~?o?L?nBMZsg8&2Df*OOXB=q?9~ zPu+4vdbad|o^uU4mPSv2pl%fqY9D%BQ?xxWQXYq^t+oih7#}8ANdEl?2Pb`e)tsAI zwG1YH&kL1OJ=XrJ49XRksyRiih>yIY(2KewT=(9$NZgL^0*r=2j@fxOmQ)8FkuAvY z9Wy-*JXANPM;m0|+J8Chcm4*9`6ES|o2IGO_^+4aL08(=lRiUz z$%VOy5YObOcd)nF-#{d8vTxS#U2I)V83VJ6&<>5uYL+7zkprB)IeyGn{La)yQw;yB zKv$SDj2~Arx8L{wTE9*9^C>nif1=w`Q=g~4HX>UPEa!6CehdEm$XZ{in%ATa0T*^w@Igt&Pzs^!=*MXOGeUMag-u3qcwu6@nKk85O z>kvXs;3Qc)9Zcdh>k2Oca*6H7O!K_%sOtw)qxG;PJy-oIm{VoqBgtCCoC3GKR&k^_ znGqdpg@x%EbvUlh7tp`j?5e%?=FD-#?=m!xCdSuj1P-Acby8!(1TTnNs7MPHmlqsc z$Ah1G;SFFZXO@TSUYvP6XzHJOVp5@>9&tKZWH!)koYMgoCUyVOXF$*{&ldgoS;))A z0E}iAQ&TiGio4RR?@5o`p}1bb3?gD5BNm)_JMv3mAEQZ=IcnLDG^=ZxM66+(gUA}K zqHq(A{0kJD_MOsO`^_+@v!f7@_oX+VI=nA@X*<(6)u{LqrP z$;U?@A5eMwqEFjR_?ra~NA3(eK9b&7=953s`P)c76x`K{%YqQoWiEET^F(xNbe*ku z4A8rJN+)oVfCwXW8?U?ynQ(*xe=WA))O&m&6GE?Om!n^9+1@lb-^z}?k}HV3n);R; zsJN|GKOE@=hawB_s`=tcGF~IMERb%TC|MJukz@r$$ZT6VnZ~5D6oBVY@$5P8i0JPL zR5Rn~LV_o-hp-0Z0jKd-{tLiFfkoXh`eSbn1sR7<0&X6(gcqF>p-mIr{hYc$DWjhU0B$As}wqpQ8`)`)yT(A1w; zQ!?rzyuaKKEZEq}wN#RJ#gS!p5m&ljbwW%$wA9a2V;*n!d7=*N2=-6 zzKZ8mwIEE6JH5^GmSismp0#4+$N8=RT4|-cp;`iVfj=FFUZ;WW^PZi%yG2UoXX_CC z(9fBVZ&Ovf`UfSXPe!4Ff-uEe;~Ux%cv+b2IXJA;xU04HTO)0_4BPd;9if(kyZz^v zq2XYFc{R{a$J01tq%4rH+W_mMS2Yu7vCan${Z$9YbB>}fL*}Bi)Rp)gg;6GQif_Wz zo}&?I5E1EI^~4DCKo#OX!ISySZ-GQt;D>h_%hNc0_IoV+_6n!iOH5X7xPx8Zdws!&$y-Nx!g1JwVYOI zd*T?|eK?*R{TcPzFOC~PRldLaEdh+HV0DIiG?gtGN>QYf0rV-Mm`%3jBlkIpr^Z?_%F4_?%X{!(qb%?ZTmM?m zQ|}B7p$wDqB0qCXwVVtcT1w^;d!JmD;dunTVVe`E2bK=Qe50Hag{H7ZA!i&4%A&)>`L}7q0a;S? z7NMD!Vg%RDaYO@Unup@B0X!#tjbu` z3KstGv;I?x8oU7fA5{w@a}nq+PQhinGs8{n&1EZ@G18Umqp-z2p$$#`?g5$J_AEXo zu=)xuDOz`8zhmgB(TH5HC1)Ny$6UfzZQg)>1iJ^#*EUGNz?}5yG{{VTf~`i%2=2^1 z?Eco4FvP+PzszcygO>VK36!L56(|tsn!)R*8|^KEczsIlZN&!o9*48E>-UKTAaOUEhh$_8<=6BLkouRdfk-QJK44CtlX#wXaCnf}7bdF4ZbXB(-_ zf`I7<_JGn>h$5q|`R^_=fpQipG(CuWYWbDdfK=74!H9kcf8Wh2Z{ zvtP#GV8_L1F1kk5CIT&W#M#3*p`}pL^}A`Cs- zY`)yA6WV?y<`R0qPES)BSC(K5z=m2lAhV;e)LZZ0!A&9m!Ix!*@K3m>(kUs4>fYPb zvWr<=8B7z$j?h_0?`M)M<>?a@Km79{ky$T23!=CVv0k*5YBC4jQ!+U*;1Qq^PZ)pUx{=!TBW)&f!hN`M*mVpTLB%;4};am@LsEgE1 zaQ89VE9?5(FCTreU_Nw-jJSXI0OrDw*y+3H6tQY!(;Xv#XFon8|TG6{fxarDXR5a9ko(eTmtZ)SvY>G zWzJf!X=%wxq2)s7J&u-@uPy@~)*~->xGE};LCY2wyAV+7&A-l(`^CRYiYAk(IzzJ{ z{yren)xPQ>S$L~&BvC}bjzz7NktqiWo2{^!qX@;R4yxCO-62y(I>X+ zA8?0$nmxy&Oa{Fg5AzRqAPH>(YkM=Pd?TLU!ml1*Kx^HMl*Tz;Y%AKun>8;cqirZd zPPbK!%teWN$2`{`lTvoef;O;v-SD3Z=s&2AwEr)vle5gzZ@Jn?wIz#lxbyt@pU+&Z zRVI23>WgJ~*~aWG$o0hPi;0%vFj86wFyx+t#g5x~IWJs*kwLh3UYxjqDJhw2&ietg8;XtWYqnbH z2ngf-KSuVy#Oh|(Bk5e{-wpO{UDihL@r8ZNW7vAm6~pYt7(>@>UDr{zCza{BraQw* z3v@1N2?{07oA1sxht)R)K-_Ugm}otKd`b!-v-?z^0q`5SLPB521?S4U?CU9j2?8tz zVLwDUdJvn{*8x2NC!n5;WT&!l#Wy}M$n zGJcG?QLg>lnSw}s`&RXp?u>Y^iYe$08-t`2_=3%Zn}iZ6seY_CHiH?TkGH|L zgiMwY=PPCJ0O$(phDn&H{XbRypwso|&JgBPKIWXkG{g2cPtHi)pm^c{aQ5n-7P<0PXGF=uBgOH#pWtlGS5aUR z61D>)H}!}N{fH3Ir;}M&s*b>_N%KElwp8Algy)+y@MiyWrgAAd3&1xw6LeRipK4{N zqkK;o@TVKz-J8Q0U&=)hU$QsDp5M7c0=S)n$yT0^hmXH|$1wO_OY!Y?W5m(z z+Jl|?MlK<|TvOVrR1T#L&6;u-x>QSR%tuLcWSz9n-4!ur&B%$?E4R_|GMc;Oc|{6I_vc4PWmic#>)el zVW_~x<{B&pOU}8Ews^^>9Z%gpV@4cEaPsIn1T<@b8tT>Qq|B%`Jprs7yhQltNxx6N z1RO}5Ke&)S;Xwp3MonQC5!zV19q~mP#26dt;F+z&h@32)o{!rhHh6 zbrE0qcvrknkMjK9FMiq~BGZGw3u=Jq%*C9z7=DYfO~9M1ro!I0!Tr#V$a!E2$qcj< z>%XycU*B!RGpz17oM%dT;kl$#gr)_0@WZzH|CyjM>f!&9L|;#%`IHln*%@Q!nou$UG|697X%Id?wemK1TdQX`qU}S2gDFj zCdIJu#}JCY{MYMA(~PvQ+A=KOqt)oXnqO(au8-AVz=y~$4wE+hO1(|UEs1K4%6s0= zrRfv)Kl^%hQG0^L7{$++p8|<@FvK1eefaDiAOpd8_P>_6Fu_U!>4$`4_%RJbCH8I}?FK@+}pZ`D^!rbzSyO8kwJVyu0RML1JGyU1$-A<-}fbP%dItJ=1Hj5$}E; zn!ZaVeZ_8}@+J98K{1d#Cf)u@9*#4!nc^%AL)I@<^2i!E#Y0~z*Q(vN4|f2SgWma9 zt*s(xM1dop(eVOa{qKl>;A9#%`QT5_VteDx@fh_^Hd2|hlHz)5Y~~ghzf&El5Q&%n zGf<8EI9kI3md(Rm#^x#0vZDZ7g{YC^qvHp%Bkn6uh&G zOI}al1Fqq6cMQ{O(t6mp*nbB0u$8jk_}n^)j7ZUsEU{W5l5&)3Y&bsr{`uiEAZPsi zQP2m>E7pVmSXGjM&f6v^fY`sU zX4QO!VEDbfcmxOl{M&d*oGbpLrDL=`iNRWzm?q`;RnHE1xoJdvUPGWiHy!#=L6YUZ{?K%u+mnH`(NI z{8xXd2l@n9M`f|nSShYG8Gd6u*!6XxL^l%%@4yPSGh0XNr3}hJ`YzD=p&=m_!x=Ve z6Aq(EqRx?7yj-K=H%Bfju|E1WNDZgChFS za6oV@40?qCJ`~r?32=Cr^^U&L#~5Qt{(F0=NwfE0Phxk5)nU5AerM8FYy=7%Vj03y zN0ozOrP^^X*NlGE@ShYhR-(FQKFR-qdx5FYFO%^@VDb}hJC!syrE{vKw z125As=@&9z0)6#?$=;RBZ3YYYbq+8KUbVt0Y9Qt0WOpYRlu{av>jB4|{ z$&yR(t4Ad1Eo$P)<>`Ts?-sa{uyd7t>`6V#J`zX9*M-Z02Sw5#1V$e@ZK)W#4sYCN zPn8aV+jk21eXez1u&$&Bk@OAdEwLtF*u5sHr*Q75NMqgKv8-AgPDFor=}EBNzst8& zo-YtvTCp^KT)-Ug0%2U(6;@5~zcAMX4Yx@3;6)F&%RehkM_oUL z(I~Z*0ymQ*Ha8FR`v;@8+0x|(_bYuq!N_bJ^^&BiT3hYYXRPw+b%{6Rd0^N+-_;iB z1>1oD&qm;$?djq_RZ^^v$}lnw{$dHZT_x%D0b)H+vBu<*8I^Q%Gds9vn(dZ01^u~t zYj-rz9^ng`PVPtc@~LIX{!fO6C4oQa^Nx?{H@hU=r9 z9(_#M29_(iZmXB8%;6B9Y_{>&yD=An{=Fmax-;(2Q|tP-i470KV;i0(MLOoP#r1i}z|9g?dWM z$OsoC&9IpcDAXXBi@M;b3&ja4f+Y#va)TrwF?V-JJTl&WnJgb$X1kC9oMH55#wJEt zR7ZIE4nS3Y{iWV{R|0VGsa`d2+di4<`FItGTR(B`g5rsB>=bUqjyAdh7s+^|fmK^a z$Y{5AWG{ty@>hK7^PhDE;Xf0j>EGouB;9VHUOInx<-9pmGFGEg-G+ITAS^_ellA1q z{r-uVxn7Qm$5D>#se1SRXM0Z%3V}Oa;Pbp|zTW%Q=$7-GS@F*4(YtXHr4rcNAptgd z{UKnzx`=f<78}AnTi)hbj9MBWiCf3kcS0+9G%c(jzysABjwK3YBA7niBUa7`@ac}c z14+eYZ&>Wjo6v7}Xj#3U}Do%}?{O0{@( zJ4Q6KnMq5OYxfq)W?X|2%j7NlHU3%o`*puH;U9qMyupKYcrws;q30@O+N$Q|T$>7MJy;mu@Q4ta80)Er?9^KG{aeqwungs?$ z?aiv?EU(gvK8jx2kpjaYuxIq+M?sm+A{c^rJ!+EpeX;-_?DbomyM6ym^{6Icl?M)U->wU9xw6elpFa@fLQOC(DE!Q%b zKZJzn=tiJzg{5-HsKtF+83L7Z;vtjMci_zjQISullJ>!CSU`0-1Fkl+I_F*FzOfQL z-r0QRHW2TC5mX#D`M6A;XF4z&g)x4!%y(l)TsS156|wgm3y#&F=-CL=hPLEpX#NvT zS9c`xDhgNlw3!Y2k+4Q?`WJSAmSd@$fr`mKbA`@nHMHH^AN28P@F));a`y z=$Phd_d(Bo3=ZRN+vn;ckDW(6O^})SJaK#`Z#!sX?(&H!D_%!>NBQ)MbfT|_G)wecf+4`ni-0jT9T5F z`_@NCmAeOZ>Z=AB%sDSnHUHWw>IxJTE7uL$pt~;8W4K(!G;7AVaHdWpeY9^(21%4a zO9BWRC`2}(A%mu_%Af8C$lmrzyT#UJ5WcH4)7t;A@7%j;4VkhD{R4?(%MNfF8g_82N>;pY*6#E(^K0Q4giBUWQq8o~y&`c@T< z82fYCK{xhn(=?#nwcwi@lEOAVJUrwwz5>Qg=-^=V15&wLB>|rNRD2}md`hTfIwY<( ziTa!a7#blm%BO_qprsWE?@sgc#X@d?HyZrt=D(^GfkJpga=$s>nqu#FT17TTCs*Sd zp{&iTWzoYbszN=~&%_N-Q{!CN*Gy38hgiJh0cMR({wtniuoH0nxWvTcV8PQen0E`t zQ0S_f^RR?KVPovrFSP9mYFXieGcmMA&yhyYe|;5>w|ya>qJRz!^$GiOfM8eg0e)Ee zv1g_6uZRX$poxizoD>wvO*I#57KlnF-cjyJ!)6if`#rqSI@qkxcNvw>MbXk4ON&$N zgBDC@wghhk4dMaHgn^6!AgkO-y_Z2v+Xk^i%0Z*@R7$e>A>o!%F0vD2d3_@*H$d8< zm=w$T`a5@3j}DDsBeuz=cAa>O@fKh@zVvK9dqe%|Rjp0@=&0dI;g#NIfriR4&>DN~ zRLC$f6=-*nmWkvDMV*34ZsjrskROd==Q^bd6qXy~P(8yWF;o`7$JF~)KY1x#Ln^AY zEAT4PX>4>gx0pfxXOC9tv99b>;cOs8GB=R+hBa+IDwE(THf3}!bHHaD$oZ)*Hg_r7Eq|UxLa*4p~K4O*^fgagQ?zl?&y8bQ4ppJa0U)Mc}I@ zPD(m#KA_9uHpu_`$_1Kt8hdOAkPq;~XCaq(6Zp)b=w$LO=o9BV?n$N4nFqY~w;*|?K~&$@`Ho-8sW zL>W*t7}#f^rR&h#&zsxaziBl2iGtkqAE(!wiv@ai&$mzrzXvh@ik=eJ_;Im6<(EQS z>lmb2B+Pl;Aq7xiV?~hgura32_`E37z|ORju^m?c_H475l#MSnMfZ0o9f@epYtbN9dHJdmSp!5DY_0+4{8a@h#BpE}f-skk{Qo^bU`trUDG`qwZHX z$LPssDz5urFzyok0>vf7bfcl5eFpmtPzxX;Ch|?}>>31@5>XQ?0b|kBW2w~~8T}zH zaWK!Lc6MeSw*Pl{o_JF;QM~6e1BCfW4>~(<3=gHQu@<@*cJ-Xn21w@f#*pHOaM z^GtpO+WZ#1VOf9_UI(F|KYWe^OblhOtId$m*q|PYlUIdLpcVDM303%hsaPKOCh?qQ zyk7ost_P@-YU1*u(ev*t9!^pW#;rHb3_E?_ucW8z9Y=EIaV-pi{l%CR=(?EVUU`bw zqkM2AA<@t}JlQPS3B>?$b{2ST!(quaef#Zo_RSmpAek}FJQ7meWs8Wg2-rZY1F*!(tUHxs_?l%lwd+`Fyboa+OY~wZ7G}mAjyIi%POyt5b0@D7zQZVe;&ev?o zLrBXTaBe^JFgAi?zdf8wd~F!pKtY>BefdCUFZuJNkHY!~?ayftx~No4czS)2N;q}n zXjZVLcCSBsk4aaZ+F-j?bvtsm*vg}o8*${#BQYV*Av};E6A`8h9<=jDGM{y89P_!C zYZ#)^`To2Q(5pp9hXHS$vIAKIAkBXI)+vz6#~tiXl#yQtlbmXf&IK$OG{B~Z;iD>+oFjJ zfr9mTnSPppA#`r{6_Kz;_{?%D48pN=V~|fZFGudd7`@(@Y3K`f!A(9@!i0t*;NDyS z@nwm=2DQdNSD!vRWWat^q-F}V2*8skt7Yq; zGR-cL23ZL%fG;Mfb}EB5-~88clwfn;a&rzs71tVaW_xq91y~5SF$xXrm$Fa55F>w% z03X23Ekc|=NL;R@KWk92II~k>#LE2s$km?{A1ukT)~#D|CrT_W)J{8ZVbU%t1s(~K zqXUnzR>dt2fRx=3fA>*Zt^~#1ao6RLnCZ?n&UbfzSVd5O%Au-{{vNP50jBe^<=%6* z9dn|h8}hZ>_uiL@O}E8It!L99O|VuMyP<&pCBPSs=3_12l>+rhWC&80z0b+sUJDc6&dr2W1=c5fG07!A%L z)(A~39e>VJH%J0u6O`9SpVe!4leX{w&hV%(+kV+2f_UWRopXrkSh{lQw-qM}|DN8c zeEiM5R|Qyg79(hzidrfoHB)JZO7Wje50W<_f1)t^&cCrX3MdR1h01FKu?m}zuyDM> zkBj$?${!m%gLXnW#6`n2zd@HGu{kPcX1`mSdw{G0dFl1tHTfIYtc~FU4$WB(2Ry^w z1@}}I!jC~x-V9*1>7KBG#G<@sLA+G4Z?o5gpD@UK=I!c!tazQ|M~;HR7tHNHLk;dituufh?=A^UWXL#T=6X*JMCbVsm= z6Ji5_4cM$NigG3%0w`WZ7?k%~>y&nWpTmy4N)!1zFe7zny1}}gN9y?g5yM19L}XXI zgp417{M0V&pYx&v84wu36CF^gc<=otV*Z{m=JhmRfYTPgDi;5%-4K4TMWBpX28#N6 z#=dMb*4Cm*OP2L?J`@uWU;>#~uR_Ef=f*=SPuO)BODSFWEzw00(lM*{cd?nP|q&00PXy)MqSqAHwoX+PcQ(zURfr2_b z-XU?vy`?~n1+t1a9~R#7ESRmU*8OMeL;SmugVq;IT`$5Xs00ic*#>P7+4hP@*kk3$ zt#ZJ*|I_re!KuUk=hQ8U+TvJuSy6+kr8pAy2D3LfP!celAmnucXF;rOq4)iM5F==M zf~S9)-mnPND?lw>@mk6pb&ADvpkR$HLm+)CHLi*_V@JTwfJ+w%bv(=?HS!%EESVHs zmCiN2tEBb6_9Q8p_c5&Rs`Tc3yT8U2+A3wiYn}F|0s5A|TR!=;c>pj5&Bl>e^rNtM zfQ6gP$`22fHZQG6dRS|>J;#b$%?y}H<7W&2=Z6X~XB&X*<4ngVnstVVgy+Y>T$>I) zh=T1<=TNBp>{w3Y9{C#Ku!?=PC-~#WBQ_^_KYYJ|DmZ~hvQ=P1dV5HUi2{kb5Xj_S z@CNWNu4I_4i-{GL8Jy~vf1O9(Tp06wjn5p4N$x}Q;rD0GfC{hr%`1Z_Iqo1J zP6AT(D=;b-s`OInmFMbQVo`p4jwhD>?AZc0yI&;g?_bHv z=B6IyzkO!0tZe9KR+(6_0G}EDe_>}rak%-1WTDytj*$$yvBdgglX=_1uQ;~1Uh~@oeER3M3%q) z@gZGCfRM#mr{W{w-x{p{HC^RCTK{Ir2k(eyi>i_QaQBgSS!}2A|RV)nN?t&b3~6E4x5@{EqcYuIDb5C zJCZ9{*w}I>Fbtq9kSBF%odLpBzP~4iWmJ5xZuOgCE3f+$4*~HDg+RYKVbKZo;RNse z;%)IfUn3?yp02hWt+ky7X{>-#NCn`!!WA-63UJk z4eKOifO+}+aS-VGPI;keY(UNrazb|i@$_|w9q9hOL&oj@0u7ZiS1$HkEv83DE#yuF1Rsp z|D6dMo9R>0I-IPChw8FHdWEkOP+YUBV;t(k`J4=0wYDy?*{*j6XBP1*rtlfGzEuoc z!v@hI5m8b0TJa?55Gc(#jRRshe}z_M2mc8M&vL9cY~THXVfodNN1#E^HvnOz&Z%*cky|YuvwMwF&4bKAZ3?hvx|`N`U7d`B<_hf7c7lo8 z3~45Sw85+S_`&s1&e7y}r>4L#F>{JSAxJ>VTdD4>B*>fLArJ3svH)^Y8^6dGDwMaa zi$Q!ut|S6=QWvdG06FQBAx2OkvIAD-PY+4n;6vx#GCK5EJnh-oKh28N?(bIzM9nrR z3y6(Z1vez$z#p~lS|l!oHs;UYuGRj;m>d2qyBJy)KimGVFyR^XHI!7@i!Rp@{MVq_ z*)(gX_hn7t7(Ju#n0&R(zR-SDd(9^_f!`*Tf&fAf1~qgg9NguPWiRI)mlX%Ps6YFp zV4@to2icl0KU1iZPu={HDo_XO{Oivw)>oo9*{G@}XwH@PUe{9{J?Inr(>pUh5TZ(R7M~d=da?NDTk+LxJCi zRcN-Jj!~XM*2HKU1Oa?<*?t7DS{N-OH2%}!kk|ZaN)c#Qb?^ebQZo4?m}Gk90rvS>5~YD< zzSDl87w;VHywQ5?1l~J1Ws;T31>n+?NinU6LB0kY{^EhH9pPJ_DyhHSt7k3Qfc@u( zH6Ja204b(-OI2!mx}3^2P}kceb;iy|1Moz?vY_g}quD0ub6Sl`TJo{872y1gO_Jrd8y z&rR)pz3^F4;HeZ4_{%mI|97&46a`-&c{e(GQ1MpD7$g;d?%HXg74rnlff1QqKabZ= zccvEZ95zr9f@<-icRJz)t!wfeqWnVeH zcUG}8=77Juto!_Vwd$~g@ESNrc+Hj52+ZSM0PSuI-cTKQsAeMqX!G-O%=x#tW}1?~ zy3SQ;3}L@C-%1#IsP3l?TGn5-pg?P+1l5I!qm;-{y7LAY@nYl|ePzGKocchi6u)S% ziG(~?Lp;u}{{UDH)MNJwJaKeFF zN8{RGoUG%V#V1y<)F@MAQ9(4`z*ra4;EVl~&?+!^5JYg(sO?3dJNu|q9->l{`5Lb62D9FS zd|ffm;`n3I%FWmLMBB$n^^R+Ej(bnf(|gnIZ_I!T5LQ)n2^Tj9C@*QvvtSWsAli*V zIzT4XPX$}nxlwXxrSEX98ZGScngJhQDT2iLdbE&MY!~F@v0XAYh7hQb8pdaM5;0v) zR!C4KKb+O5G+w*8Ja1LU$wumwEk*la6C&Ti4HvTaojjP**lx_CHOAoO3TNGT_Xlms zEtH<%JGchy@|^eYRU&FIEMsM#Drcp^+O_#JXL=qUs(|!v0%>Ae8H}d&bit)152cUM zk~%C(r0)5HT%6{#M1;>t4&SMq5FO&`K4n(NrKf5Bz{)6(Nu@qtcZhv~5*QPwm(ex% zIb@k2uC4tM{7vJ8r4rpuULsQZoGOmeVz_D0+V)7kpt@49Ft9W&cGLNY|I(kP|_6JulAce`J_Q)(kwoR?cEc@9rKAXKsVJ^E`8N%1W>s0ly zlzRC;^c3x$42-Fs;n}1h9A{=up`ops{fM#}K}?s&A9dew%Tlc4e|R*I!kRgh=-I9i0p4`Gs&rdRoNT|7gdjjI%1zL?k_kcWo^$ z)5?T(c;Y>&z#sH15k(irDQ!i4LwerF6Ib_BqBPp->ZR*^8XPIO!9d!4gWZk*4u$h$ zR)_yK2^UTO;!ObBrS{v};jq??jjkvW4^4+%ap9G3xb$$r=!$8*MxC8!R~O6HnSmbO zG|!j)%;24~v+jRsF*gD4{$9@aANtZ2&5gqM!h-wQQJE7 zO{xu!D$e(6^D-z1vppu$2Uu~U?%)F%iPQ(`laaBJcjg;i*%g+p=WYtxiM4^#nLwIN z=&2@afMr{*F8XjtfA}PWr<9GB{q0o{o!(Q|z1DD!H59FlWM0Q^F5mm>aIE=qUg@yc z*8iSm<@+>&YT92&*z10&N?9 z*98h=xNgLI)9SrY?6;WLLF zyY&3r9p)K6^ffWDFT_MW1Cf+dd6+_pECt8gU+7<=8Fy7Zp{G1^Pn$hJMIoWc*5*wE%zm( z=A$#H6`K+p7m{b;pYR)9cAq7*LQn$4Fojjb5ByIwtq*#BL<%=rO~J0T9AK=!$tC4u zbBKoo+K}iNy@Wv3vuHqLI47i~L7-&ixnVUl;1Yv?i&;^NIQ+d6^`hzPt zn-!&d@f5-hy{Q(H&7SDQRpkU8S0^?{93?*}xCsbEddG>F!>YZzcjd*-HbRr4eaT~Y zWhk5XH}luSEwQVyYnbbU+xXaPoVS%LVRF8Li$_AoB(U2*kuIqoqGhT4SVSb&aVGIU zepkAMq80p}iLAdRj_;Si=jSx5&w^Yqu)2nLo5{r=#g9qPF=w_4==z3y6}enFUy%EI z3_G;PgX**n=bjICJ76y4;YW0GNl8T^^|U(9eBD#^Qt_l&CXnf(C;&}ZLh zHvct0>zT_@Rl7}g)3`S&o`Dls*B`_+SANoOGH$|( zQF0LYVIJd6b|kb6v1)2$bi0&#jW;Z|7ShEmi03eG5e+IB+x0UrL??#>iT zO{c8N=UsUleUwTWrCzu#-HVbnxOIOvLr0EVyxQ07Xop16*GMlSsv zpx$itlI2m!hGIr{<942l&Jv1J@{2*WdcxH+dG*rKRMC3|cFK2*;PNa7Ad6OvY9EBgK3x1AY_acsI!8-W z@8S*@BdACHYQ`-FTG-jspFU}9fkbzMK?$X}=u`g%@|UVhV(tc?(xS0WYg?M1#YZc! zisQ{Sp6hpggLmpj1wzd)ncc?y;p|v0-x$9)s@aO=F3uDcIb%%hX?iUZA~G^)H@T5` zc`rwt8bH3o)q#?>_U2K1@jr(SE`WZY`-$(JLbbq5e9$-9O#sL>9QXVA^Gb&=O?_QC z%cb%>`TipFPSD95H2|j4j*Kaw4mvHSR8Cn){g1+>j8p+n^q|&;{MUx$Qw#FoW6Gfa zng`oKkA~-mkv{C>e^onQY9dTn9MSYr_8fx`UIZJVYDK4w81WESDm9KZig{SDJ_!Q% z(w{T7^jUik)m~lmT<%5ondCRyU`MSVqO}Yk{!V`I7?dIgL)nGKLq)TV6Hfbc#5SOJ ztwFkSZ_#i~! zyVBJO97)qw_&XQj{LIu8-B^x}yo&j*aN`2jzdi<2$h|a=50hl(Xs*I@0$GyPbj3yH z;Nzuh6o})uX0^J#<44VUa!KBIbVM^A2#%ZT$KSIbt5Isb<7&A==MDeSQSlK(eYLH# zJYvLv@tg=LRyv9TZkDNz*p3|-8JgOss_7Y|CHSlC7V$P{fVKvp6}nWCnb@#Sbzl_a zF^_iX!Si--@KWe=R4*HUb}p0e4Eg*~ir`u1ej9?%o%>~hO6>;|V=%YMW<0^08Bk!Y zF}F8za-zHWRie!pZc-)9GdpzRzb@)reKX<-)(6-1GHjvxMb z%0ackm;`1)rzi7I=RqL~J@Qi$6}}(vwK-glvVE%4XjYmI`nl-!PPNaihd5H5m-|}k z(+?XgXKJ7jx;Yk>I?csfSE{e%;fvmsqkYD=p%nSqbEKoWk;IK}r`zxZq3r)CgOn^n z!ncYcZx!+mSMHr?w7yqk>%DBqfUK0>N;ypbv$Om=>^`M}0mgh@)i&`rvm1DPsc#9A-q8D#r(=W@c|IJ33D1zN|kNOY9i|{np82oATZb zKeb<7f7h|@qrLfwq`IQqppk&2CnNtz7#NyAa%;+jyl-`Xd_Y!P)`%y*Kb|gofi8-@ ziu~`QVg#hYSZd}6znrNeaBDdA^tl6@>;HeOE$@A+)HY%o7_Ke=!;03*fExLVXRvWENuP9xa8@MB~Rd*-*@;AuKP>Ik`>IOau+-R0 z8=*d^$Bw3T@m&&9q-eZsI<@T5lNF=)vYIMjgRE5Ho3?L!ZqXR`)R6&xtE56wiJJ9w z#wK#}BnL9a`s@y|znAi$6~dPz*4bRX=j*UWH~6x9FYa(HW3|loXq?* z(!vh4O8xzE)p7Mtf60@YEBHF_A^`nNeZypUI2IQt`z;uh+*GF%ACZ%D(d>;D?qUkZ z)mTqEbZhI2pdkpKNFoax>ZKRzx1<}xKe&(Hs_$LJGmO?Q5Mq2t7LoMa0L@ZU;-FKa| zgfl#vLJ0(|HBj;@8v4y()-Fu?LF-Tx|D?(^9r}O{hnc;?@fl5w?0Kj+!(;Q%o+wE< z@k^!8SwS@M;!pGNqwb%}`&?NspJ#yo>gvar+%j+>t1@(+e>Oqyh!8-LjRXE-DiZiF z{J;0Zgi6xPGM*)MWzw^V;xHqxidMWM2vb8bNrT*=x7>e!&MK$x+O?2%(;TV(wp4x% zJTpmpNtU1|*l#6i;6u}3aY7{|?y z=ts}RMZx#)8(+OWjm^w-nzfG0e0LiTJ4roG!!p^}y&t;#El=gH`z>_z9X?l^7*D(p z7C_=>MS22)AlMf`Jn0ar%?0k%$e`W9-SrQboykX!D;?$JZV)Ns+7#R5%j17G7f9y- z$n~7R(C5N%>U2W?{+A`V%tRRzY|tm!$_!JK*h794BVWuWz=+=cO8cnCiN2}p&T$=s zD8k6VH(zlVgptXV!gq9jH#bkx_qp^~?`OeI%2y;uCRL8$pT99veJT@WLxA_uT2MV+ zg4M5Na&l7d^3t(Xrx3dyJ^^Bx)y<=KkiR>v9ya~_0jzFfFEN@&h+K#oiapJPP)QUV zK}0&7P^XMZGn#;ef*aXSbaD6JNwk<$nd6MKV#J-WM=d=;DNzo_ux`1`>sj3 zh$NfpGR-1;@dm_3?FjD4zKvK<_Y|Tv!kfMSj(g6bC$zhsQEZ`QzcJ_|pV%KLT;A-u z{|nRw-8uvOCOG*$5Vl5+gxpiR0sqwY<^&Pb`Mf(ugMI0v1(Q`<6w3j$?xu^1ml%YLHz ze?7nM`;ZszhlUGRxPs&@`S`xV7yke`+8yj|X;n)=M7{Ux^w$I`;pg4y+IqLckoKgw zu=hJRQ)XpDRZEA<9o|j}ph_dCwc$O$v;nHrN{y)KEYBrekgi>+$Hz@x!lT$D{K zZJA2_>afa&_yKaWnV)m89ZCLBX-K8gqycAc>@EP2pN>J=@qB%)|0BO`rL2~UvgtSJ zk+9$2SQ5Z3WnX_05{wVq_)d?pw<_@{y{PlOZ|MH;vZ!dB7)>HyiIU&67g!I210J^a z^I403sZ^EPp?mB1|L-L-_uPW7L}IaOofv!N-dd;dg{)snIgRnw$4f7Lb6i_HH;=(~ z?uNKfxV$;bB_8j1n88C4c*1DVHihOb`# zv?#y=+5$*HZkSSf&HISm)59Oris>^nOf}HTVn;517Of@Y6}ipnBhC_t^NTha8Hd?S zZu%(|7E<66m0<>R{m*9z+$xtbvd@{az!F)d{7Ka2lXOW)pp{iw?p@i+7llc?uh!p3^R{Ib4B{ve)EDWo2wScRS-4 zCHTamBQ~~UBtOcijL8kTt-UThlYv26yCkSw*na6iA*IrAxsRX!d*_+MO+q%UdT$p| zhXSiVosuwb8K_uqQ)$q<6Mnxm{8kz#3jw(Go@cwBTKU@pdH9V^792F(S%MZ)HM=^m z6mxz*%~XNv*)jI=iCl}0!)0e;79-^*0;SDCGTSSRj+p`oy9G zpVhQ4nb+^m>GX|4txZ~&gxktk08feAl4Q4atyC+_$pWnmA5acy_i4c4*EwzWc7*4r z3A!$mMMo2W6YyAJD5e&c0<6+=jrgJ$nV&5uF#NmagSpNAKCZ)hi&Z}PA z3a)U$XH+CpJbO>fO=R3zMe~rF{RT_adfvxc%%Zq@(3k|1l$rSu7V*r`bDx0SCh#i; zlCU-e!csf1l0L`8p(`NpS`t`?rs03mdB+61erJEmSEKH8N48KmAc*Sz%Saoia z2$p!c{1HvIgkQh33O&$#vr9`K2?`2=NYI?2xcGQ@X^-bvgP9;v$F4;cl}6CKTB~@B zr5aD~0gsKi__xUlqwVeO+ywYV)(h*Iwma6e(y(1N1trRk+7L}J z)^qfovcDE4GMsEGP`k%DYjOR!66$7)O{MW#B`(&9vR>HcG-?7jHq=igkVrc#siLlaANtt)#UMk5-K`d$eoU ztS8IO0nRAAHYBspoibt(h5=omD=M1ZETk5W2iCuLoHE27FlpqmE=rwY661jTB40eF z^!|6tE1zxgRT_q$+_eP^M%C*MHbgLae@H4Hs>(K)t}h~ssal%)@=CelXQEkbnSbrU zPmUi-8Fv!WI|$9ys5?`JtW=oou_#lYyo70rhr}(G zG@sAO8{bV~#@)k}sqI<(nPtrNl0KExlZ`1~1P=paq5`&t_sk95jC=u0*lQ^b#$ zwyUz+lQef4Jp2t5W9<5p>p_DDg6e$8wD(+7cOnO+(LHJnSe4Tuu!qRTS8 z&qzYV)QMNmMi(A~W<2rLI|V?zpkRD#%2qnS`D`_V=01?F+ZHPBDrC@VjWsu9w6*Q( zvN=op3frnyE zxzdOFS*)1LRviH(V@@yL!qgMYxa@WOYXkbi;UsVZ#iQvpDgH$qbhAh;g3XU9N*$G+yy(OMx9CB*7s#Lq~yN zRDhFau=noPGuE*ikNzV*-cJlGR+e;seds@Y9|06G~% zphQt)T*|rAMJ*t3loO?AI*?sEpcf-ps@>v4jr^rQP4HkTyuM1~Di}>q*fBBcogBq) zR#ct#asXV3;rQI*H^8 zl*fW!h^S#Kq$HF0|@{TmBF~ zYOAX5SeBL0Sf*SWvPodz_BOv#6XG@PHMqFi_uYrd$%&7S#)2PX9Znjo%Iailkwg99p*n#W1p4OpQQr@klr5|R!SZ+2bcx6UET#PN+ zKN3YOG$qs}U+(2avvHMdkoy3rJTp#cxT*<<18?Q=F_Ujuz%w`m9rr-^>vXwkzWcEc zDZa}L7bt@DKc3x)QS!c{TAr*hOJw(a&`DuDkS+BR4ALurx)H{)YMop@{E=@xRT(IR zgD!@KB`s5|K0v)1MJ^Z^*Qvlt5@8e;Mq&Tt{tAzHins^yY|D9VD=C;eBAy;_mfAxf z@b~?{o~7A+=ve|cs-q!Dn41^;GuDgj3Jbw$?x-yXPFZ23T6$e*BIbsk4yRUM&prM*NxlXsL#P|DqLS-=a|Kjt||L6I#(yCBt4vGLvs_m~hbW+=|^)Q{^B1QA?Nzt#LYgK&p zVbpxg{;2dk2o>`CfXz8ia5^mIM9rO_j2vfpJ+}NF@?@_#=7uwYS`gmR*1!^AA$5Yz z4R^W?RrjE>_^TU^gweo~1&BXs6 zi{$pFQ7}tTM84c`=zN|M1KP}D>lmL{0Y=eD+bjF6v!Q|lE1(@6rmeA^B3>`V(2{}? zlf6dXFbG?ndwv!#cGWpdk(DOq4q%iR53z~tRso7OlDGe{#bAI|F?kX9>o>(cE~^_D zv1f8}Lhrc@KK|w10E(#H{Weg6_1goWuxg2RCYpJ{IzDa#tRIZtXIEgBqUr~@5n%o{ z02H=eN+6c|sqK8JV`O2Q)b?{QHsK{Dy*g-4>J}Ye%!*@UUw4=L35Wlgj%=XxU_8S5 zKuaMcSTNN-zx{MDhG6?*d5ho#GmA696BKhO0KptM!E}n`fe={jCw!T*8iZ4o&l18TAV8Sh#;dB)Gv)`p`LSi zyvo?M^Qb)-#EF`-XqLHwl0Mt$`tIA*qab`~ojxzum$C^TA(Awt`Tw3A(>9Puwd&K{Ckr2+A)73pbv{Tx0z{bQoPrbZLA{^7=BIO-|{x* zRAZyD&d;o0?QNh_GoBu(J=DofV8E%PdH?johvn|#j@1{$if}w*FEr2>d7DE}4ik=P zlC0F%I>)fEK^YRB@bZ_dj+V<;7XT}zp0>7Sg+`TDiB?H}PfyY3&nMat%tvbAJ`@7M zzP>*6^bSS$w>`lO(OM|0Spd~I&@1bLout)}QB;f^^`{c@XM`C8MHb3rQlEeqod^kh zmSc>ef>3gJ9WZr^AA+&-G57vEhr2T%U;jI1b7*LNF|`3;Bz4!sn?|6$y99gvZ?e{( zzXQQw?>$oVo^n=BC_r96&tVYRO@jhpfqZ%@2yiu}bS#=69+I<>-y1q;Cs0%>a(+0> zA6C201|SO(!Drk_osN{jG;qjZyoR2zHfa|Zgg9Kn3D1Fa#?5};o8?zT{l2GTBI;?# z;1tKd__2f1%p0qX;`V2(ZVwL6j1Tn=Rl+NO^uCIheJZWu2JH9ABHlWH?pwgy5bhEr zgwH>9`xa6>6^cs_hUu2V0{;-e`&QcjJ4;9ZALN?HMDO;No*z>Cc5^TQ*qfwP7>;^;If>9DWn}L5eG%yiCo$s|CEvE6n=V-%jix6nDHjr`Zk~9oE9>jqR%kd0=*r55 zv>7n5vK|jTd|x-o3^NMr9zpZSX-eoBe9fCNxit=yQ!;~(2JsT_(2##Mc|HKmVryrr zh+MF=Z$&%=hj=QfsrESzpXcYc*k)$5Rxdd+1-s3T@{Bd28zqb21DnXHcsI+rT!8+- z_6ldQA{)H^hjBxaGdMa{N)n~;#0D9;`=0P{;#O%0EPL`z49t(q-l4f;j%Ga~bH z=^5!HVZy^GVg@&PY(G=Wp5<{qIA*rzY0D*S_bD6>i^W`=CHum9INl_GsdRww(NkO< zr!~${YsmhtZ~4dlC-`sgj-%&&8>~HszSC>_l^_1dh?b=U@;F34B(w!eg=o1fIv?oU zz|M}|2NxR0eLMJca`)`$$RTH_K(!DD2gkwD(bCc~MMY6U0``$h4{V`!VXm?e8-e^m zQv!~TqoibDw=v3^SChCO48E#X4<8VVBJiz>wuM7!xYdskAwUmaZq`Q3`zF^JZ&MB= zd_{<9dv@tb4oAcJ_7GQ<`6OSzptzWJWCox>xw*OK46>h6HwU0|g?0%fXNZV`q7+ix zXUctovzJ;8Ol}R6%%($R{Q})RJuFO2(OS_D#H_7LAjR=oDEBZYjKadYIYV!S<=9cP zkz3!QFtbDDt5AmVAREdsY6428eP7gmIBa-={IT2L!5#|R2i>_-A&Xe^Y0JtYomXJ; z;L@q~$I+S7mBChNuxXV*N+4z?Ca)ln$^peTuB4BD7n{}CAv zmh{ljA@A$6uKxbFo_K80&Gw}S0DN31Ez`L?o%zEp3bG(4X}+82&&>$P7+m&L*9y*k+*kq$Lpa@Cu(YH zgn+D1pH{%Fd~F?yx>;3Kc&y(EL!zQEzabimCAOZZs;Vj%;=%mK0~3PS;+bd3<|;>8 zNTGm7R<^syFbrbw@+KIeED=jdo%zt{^&Y7;CmKDXjFhwIdYbB_h_?qn+5DiL@`2=B zKcGMM>uANwSr)s1-ZMvfR`DE-7-u9)LK_I=P@Y{Jw4VcoCeI{v58d;51XgS;ydxk7 z!dSmnl!>BnTIrH*dk8-g<}?5x{@|-_@St|}eV$TKQknogK_$6RT^3j)0o~eg?EyO$ zABV?Ue{~fB$Zt?&9xO9{Qr$ppF{qsm4-4xO9Pa7q(NU1g{{3XU{5xOfD}o6^p#%~j zn?pPzvNf&1ClXD%*$YGtFUvJdQ5LG%qvd=Zo!(Zq^)0L${J z3fvV2Wxa8W8z25d!=Br8#1T>=l!{i29C~UUP6W7z@`Rh5fx!>%OMvLT&TRdc9@Ao= z`J0{kGNI}Ou{3=B`h=r}EaQp&1uDl%c`mZvQaFc;>n=<57dmK)Vj0kdeYzPC<)8#e z>664ydm1orU9P9Ktj}Q)?}~;B78%=K52O~FPn7ZsORYqLl)S&(BF-a^g9U^-*S#39 z@~!oypoz}fwP(~_p99{_-9ZjOgaL*dU|s5bPmDGOACDk7U4`5SGX8V|ZYBP+K$PlJ zv38i4n1JdKQ$+))wL;|{YV*&aXmIF^ZaRTK8ElVBOl+t4y@Euiq)eNO;vP^HN&_?5?12<=;xSae8Fn7oPUyQwFRMzSCKTNlXl1g`nh=7!I zcXtU0w=_tqGzf?i0wUer-Hn8_lz<|jfFM#TNc}hJoHOUQo)^!&nzdZceD5oEd}3ef z_?1n9W0En7nG}-|b@`?=kr`bW4^2$sF`v%!4Sh65oARD0$8?*txpU&|1t72Q&obgS z_+4jL=$Fk`K$&I;K+fE4;0NKCES12)*mmYFZ9q%~6&4Nz>R`*^Q!RLY8Z@|&pU(g{ z)w&~z53CRll88&zYAE^H!B+M2a6ZjHbZF-T0jI|=MCw@d&9m)f%-!8rL8sdSf=W|a z=kv1%IaFfjEh+WXh>fMrOVFb-Z`S^m$1|%rUIRjV^~+uiJ`Qa2&{0@hZz=vZ%9%CJ)KJGMTIm)5qEpkl(U6F5J*#-Gf|$ES^8%|Ux< zTMNGunXlg;pkY+E`Q{B>T7Raj2$eXT^za=)yHoRDL=NwC| zv4kO~K#sRrbZ22wr}QtjJAIfGN)>+W0sN|otP^(a#ph1T%9qMaN!+p6CWF(ZMw1g0l9G~7 zWwVo$B-g!{w*M(PQ3!FY@TAFQyihACu$*I? zUXZ^)3RvDBYJeIYm|y?$7SsjrHUh8hg#ci&+_bgpf#LqpCMqdu`0!wSXf?m0A^}>P z+2IM0$=$j?jixsdTSI5xvDWrT4Okzvd4|$;MgL#FQd@mw`1mL&_~d;7Lqm^jbwM6`O_)rvl)UzAtCv#%>eYql85P}rk^v>SIZs70Hu z0hf!`MY>W5W z%B?vFw^4cwVi&j`uoqk`R2yZYB@~VWkW=>d*ZePEEmug$aqanZrBGaR3*0;{Cwo_N&gS^SZ&>G6W zotcrED%584W+EN1%m?&5kSn*Oq{Q1>qJcZIH*0bOb_tLw1qFo+rp)5vVrG?B47EfO z!EfHY>FSa!%#Efp&o?(?Wo6~vk_x1tdM0cv)1{KTWF9Q%p)8?S>6%`aWhvBS%O|$x z`hOB7&0WSHJOY?1ZCu9AR^u(>ae95m!N}3N?z!4@wG57e|DrD)Z`be@74K2A(S1^e z8swLQ;Tt2Ez94e^J$3e3pHC1|+qXH);YUqs1?v0j}w;^0k5_E}(mJ-dLT_&IQgQkE|bv{Vz@!>}vqpgwDBLWSO z(L4~;db+XUS!Pr(ULn5wyW3Du_(KRvh4yfOZ1fE&_3f%kn9a;@g#N@G%ma?tWfKt> zYT*U@Xi#5Wl#7Q#j}f+lSfaG2zZN~igdQMXQ}uln0kPkoR4nJmxM|ZYB~eeO|I90C z^&-}Z9$6)@?}|__cj+`R`kj7%%yLb3Z~HC7(B}cENJjjh3yr8$bqysG5wHo@qxx*B zkGeci$Yf+>ptgFcb%6H$`}c;`Y-XhDT73@nFo29)y;@!}pvaI2J5Vk0cP>m!Of0EB z5Hf)+runYt(QHyYrK_u}$SF^d+0Y>P(yY86y}(RW75|X;t++vFoC_v7cJm$arHLu? zV7;otmtEZjFY#|Iv>OmKLtoEOi!E{dN3OKqE41tLF_0ctncZh358y;_HyHALZ#$MH z$CGOOG&WICHt1LIb2sblZaAqtyt217<7p}UYMz|$9Xf5YBGj3bBX2BcrU=p8 zBJgLz#kcP?CUB2Z@GS9TNv|XSZ2)-@0N#D$HV3@#qsj8lN4hB4QxK+N<4Ey3+#A)c zwIft~Mr$V{fsBLm*~M(~@Ni|4%7;az;wk{B^Z9B&L9~jfU0j3In#ZNTw3eQ@@0fD- z4XMpzb9%rsNkupWL`IW&Q2_+HrT8-&ztTbeF8uXJy;H+SwFRYSMfS6u?vKDKj>T)B zru`{cpeuU$v;2OHK+(6GljcFEwM7U@X&z9P1Ucd~aZD^G4Q~(#z(n~w0-mAWM~Wuv z=^zYJSi@Lcx}fsRzXR%#Lyjh?4v&A{Tu7r98U=MlC#&b;ExcbxCe6BWlLC$p(b`Zl z?*wDvJ$twuehkGZ=B0_Q(C(19<#?%j1G~m{7zK`cDsuD_yIh}*>IoYDGAM!q@vyNq z?$3y!weLAzIzd;H-`4SByy$y(VwbMZ8fD)CyMpk^@u=Sq&|cFpGK$@};RAq)$XXZw zHO}dePl-NGQ&4He|8RT9u?M%z#`K_UWl9s4(K8kl<(P zdxad?J5j91{jjUsjVwyGYMU!-4pIeFV2E0wTeozsx_##KFf=jw+Kekvj-IGqsBKtE z!Y4Prm^f@h&s~q3rekW2vKNK{VdhGSZZXi@Ny-HgyaCKB_}ec7F(&M_-}E)L%)Z2z zoi#UNkY)}(b-rD7sdMLo!5ga7xS@u$P0*csn6mR9_1KhcY6IIoG^Q1PP=(OUQhGAH zeJirI*)0jJiCCRu(9=y{Gt2&a{H!^sHQ_zKxT=hRId>RgTcZ3`>6COkdSY z^tyxWch=bCb}xQdcf3$bI16dPg;7uM(;aQ;!KA(PrSCnG=)&T93F`=(!a_nbeA?p> zG3TvW+MD;TcPw%k@VcFy_%5-laP7RAqsTY;{2`TAG-u(W!J+uuP)Un)H6)T; z^As`Ol^Do-^`HXT%o~$JqygD7GX1d-&OBrTu`@67;41_N;s;0;n8CcvsxQXSQxsCh z$DE_3H5eHq?~S(A^?g77<`fdcFVGZdj&4~6Ixop10%jMv#Z`ZBvwu5_u&k*;;=ja zsjuFo$=r`^rSm+je|#!=)iw>ZK)s}Hc9jQr*jt!Z$|^41;<1!Y=|19agc{gW@Z3sD z@%Z?ISujCD=?zVLZ|}23Pcx&ilX<5Ppi-2Ht+9OcNHnPzI%-Z97OWiR$0yiv14M*` zL8G(Y=oxVq78d$RY2A+Xq{PH_eAin|)!r;zrtwp^g>gik=C(g9^M{d)`w_JOn1_ai z6;C0m=e9mal7B)SdsrY~%Ug(!tttL#DOpiXEes#6SO$N`YC%Fu*O6vew%5L7 zC^BlpJg1PhNy_$WLMsJ1@jluTM7i)$*8M$YkT$U0x^>MlNyLCfpSf)C;ddQe?<{o3 zuX)LmULTvdaqh{HIQsx=%xzrJl}0ONiBECJF5KXJYr%YeD0SLt!d9|waFmVMh`bCD zUPpgSXx|Z?P&1Btc>z7*%OQwBi~pPJd@M8VFY#z8~CKw!~dKk+gM>KA4wnx!=RZG(#L-usPu9$Q$J@7>@E^CD5zy3U4f6( zavhS-zYGkwyAVDMNnN;0W1I0@{rR<7w_1GxLF6%A*%1s!pD(l z&tSqfKC2&`=^Q>scWb_9tZY6#A5UKEf=rx+8V0LBUrQggpbk6-tL}np!+c52f*FkF zj4gY_$wGBy1JtV3)i?X8oi6l;U=Cbeyw1+>lWi+S=1=`O^3QqF)BC9i#s`}bzODDI zp~tUeudnUCdP=?~p>aLx75kfw$!1!-kf{H9^gz6kRIDp1{{sdM&79lyzI*pBIyxFa z1iyEx{(y(vv$A4fU@*V_CK6D#`!i+nh7m}}3S6?%(*6m*9Cf;znuN?aJidhU{DW3N zEE_rrXQ!ij$o5M^DL1I4R13jsg}E*&DJ8Yr8yDLRDN$fZ2Gx$A&E~F&sl75LeN^cB z!VPqaCFdC3Rg-5?5#A`yVPxAqYsQa{zmi{A>_R0S=0jWeRSV4_9NmQ4@xln3*3)Cl zZ~W8e<5oY4YS_v^-CPRHz22Ye3@QGIF>ktE({`MVeVhGGv9`TKLlNVr~0 z&a|SxefzBSr>W(CPfn4r6o2xdDtq9a27Fq|j5Ed=^YeLZlz<(|GPjRc9QA$HcsHwv z!*ovuyV;l6w^pgTxn}S>aUNxELFsGm{t>8JzNEgy#5E}-_ub_ydTQ4u_UzohLvJ?j zZ_^uLppujE$i}SLGXJ(2Eq?n>Ta@8oFimiRts$*|w z$7-^r?Pzdg&qgj)6>~WzCk9W2IQVIJI4e7QoME&%X2D>K_kK=-Kl7iGiZwc2^+)Hq}NnfWR47SE$?^haE^Oe`3ZrI z`of?l>wV+KD^S%32iF{~XD24V&~@*(`=_u952>Qec*5v&e_3hlapR{N2Xa~d*tnIC zUrM~2R>oh&=-R)9ytDfMi#{7e>Xg2q&$i9oU;VMI{ie^9oabtmg}=S{v3;5d`eOY0 z?BaLtI8Tp%=8V3e7CKP2w=W+`e11EnyA<5TtqOKBx`x~37fc!h-KS4UB;08jW*@xn zilTWyPxTr0Kd1OMF4I}7na??A{lY2?s82d&*7^b$y>~!j6gmDHAz@rh3~en< z0CrHP|LAKk%CXBtq@?#Rq;xA5RFNDWMVf^tEwgs}=GAjcec>HKsbx7i(qgz`My1mL zOc&;g8y^dxz6^b<*-vD?bo=&g;9(<-j0DY>1{#7wOBQSsq9W$>6G_P@JD(iW3NfZg zN-lqEzq8QvpkRrC<06p|7^CShh%v{UKxYBSAt{&t7ty!;Q?v`rWB!0p0)sB@_v z3-)G`og~b&CwVn0spHO2u zas{$=XVL)z3u-%Nl0grDx`g%8aBhiXVg>f9&l}I=vMmMPkvFW2M4Qq=W8L4!`X7l# zlCU2Byww{&dhT{MClCtTrMnpYrSxSUW(w*1&qm!9H{h z(CkD{*|LaXN%s-6V3qi#NBjIx^5H}~4<#^y-!bA8l>Hae6ZC?|NV;_Q5?rZ|LLd<2 z<>p%Oj~i9Y*#n!v6sLfYKp_DX8iZyDRun>y=GLkzUNNz@YXdS>ZMFbWgZFB4!SWs|`)dY&`zRoG6C0-D?J)P}c=Ib~<+lPMpw?vDNx0`K&(u zH#!KDUAa6yw{#9gsB`IGHthcHS*$$QYuR2A-;MLY!yNtH&Iy#%i z@QC%(i=a_38}$QYTUmg%70L{{m?*73o$V8^;ML{E?DU%sHJ`bQjZw&VrkZU`^?u@@ z$;STVr>lV-?A<1Qbf4#q2M}D}Wd0$zUNWK;66j=`?FfDO^2O)-7wby>91?~Oi8Qu2 z!w7L%KrBtmnFnNcMUXu!wc-XC>U&j_ksIiXr(Pu`6?$~p^B*7G;An71AlR5Htwi5%Lc0@0K`1O+yf`4`M2v{J@3VtF7V+w!9V!Ce}ml1`gd}Y#rMkv(W0#TQ`N{CEN99#iM5}Pc_Np4ev@jCkcxg$q zAd$dp)YRp=#nn|+gF{1)UVSYtEAtSXyxG=_mgU_)FaR20Ky<)bP8<>%NdTK!TJJu zoo;z$MIt>Y$1R#da9dnaY3?F0b>zX0{3b9+>}1d+&!T&T2%eaCKOnn9Yb^x}TX{Fk zmJiWsHqPyZ3qCQ;LhZ+(CfR#XSrS4Yjeh_D)mP;uA-eI0ax70*o!6O?9Jps-jh9?1dE#||o0eTrz#<~jQdXvlkO;g2=xqZ><~N$ES=JLP-Xz01d+hXvk<8!t zHqdkk<~KIg$7;57_WY1F9~5AT6OxjUC~}5$Azd%~t#@Q={N}%c{~s>pPP81Z3|q** zl@;R8{a*xw36GYR6M&dTKb&9amo4~{eZPMRsm|4e%3SJnh0gaq`dfsZU7>0DWe{2> zDbumKjXmT+j#msBkijY7itN4m`wHB=d2MCB6fDSL`?n%G zm{OlT?j+2~&wt(1(=d3A<|GK}fQRPeMg?VMOt>jspvg!|z6VPa*((_vc{s}@1HG%p z2&gK9I@L8byS}m`?%(eQ;1%1<8d;n2z!=Us&N>Ah9UU((@0gtr;aXv< z0>)X?DdJV9JZ+YYD;llIm(+dZ2GCCN#PT;yjKjp`FKItUle{W#?c=yCltLoK7Cghs zRoZ`B=hqi04DA21mu5GGgm^hQ8#6y1y z`1p)-sJS$8uhRH)kqVJOIY#s15>^<^%NK??=yx|Jay29Uqv@Ru@d}ARGI$8AaKw}K zvGwrz`tnr=zVQFCspRMM`0Yp-R2MfZpfm^_{ztCx2*4TB$KFY+LPU5y8+XyV?f`%< z-K6{1R5Vun8@+!613!GfM_kB9FRE~UfAB-* ze)Z}Sp;OB)1|5%u-3H-s8OxXZ8MEL~div}cnq(4?*EHU7tL`%Y0RF~}bn4742%-`+ z3Gaf$g*t~u+3dW_tW5^RFJSbR$m5omo@Bp5^0G2BX_=Uq=;@^p5(Mplj;zp2s{~FV z9QrKtw}Ti+>FMYWd_>Fk2STQ>3#bnP!hb8CUZ$HoZ1d1diM2wXW6T+@kQYz_o9<1t zQlU%22BK(!ZNb54C9-H;g#MHg_RotOd{HPg=+Hh6)Xai`j!Bhb`-pW|J_JiOd50+x zjqqBamcw$jxO|ejx1G#3T>-qq+IbZ?^+s~K6Yv3UjR5}*BNq18(#ukXlI<5x;GNrmkrl#AI_Xj8dX7_0|t?0_s5ceg#dR3cE2f!x-gKJ!+9QWQ% zPX~%Aamvv6gYYjiv$wZbb0|yRe!lKfbvRaVSV3W-gzz4YvvfE?FeDUM%_lj;$%*~8 zn%B{Rmrm2=`Mu!a^$(P}UB#k=Uaqw;>qj@B_!Rpa_xL$pgfN z3y^#mK^Obwy!!6}bW-RI507SBe?CJ)zIruly6lnsjYz8n^gXP!hd zWIr&)vJ^f-6|i&p)$*0Y_A1K4?piq-g95wBz6E!!e%K)|QNKs0s^GfK2`>ht10I>P z(Zh#tH_OX&g){v^alu@sk>;1FC*qcuWzswiFusd5Mn3AczkPQtSVmFM-@C|l9qtM> zNCQ~AzW=eU?-?a1wvk)lHT#>;C1SS4Ry;ROHI*D=cBQ27OfIF^E;lyoW+zD*0S>=E z*TA!HI_F9Er}&d8z2mMf!`OUm6CHYA89MAE1}Z{FoG)z5_nKt0+rG7p_b5qL3M}j} z&-zD{f}5pmw$=AubU-Pl>;0X=$?xgBGM6pQ4aW)JPEE;3OD|nj*pmherebba;uLlu zhsSklYHBK~?NTUO-M@o=8#FfKwhwo$a_D&MCURt(pZ)sfBPg3c84?^!h8GI&jlAI= zBv_rZwZHBZAgBxRmzcelLlMQBf+Brfjz*-j%%s^6hFhDMa6${~KQ^X*@Dz(AO*s+- z6c+#E?Ev+?pm0c+5?@6=#U0Zv8#Za@cfkTlD{L_m`L_LdPvvcToZ`;=^E9XJdR@3V zdAGe9mV;tdZf(NY2iK8i4GD zHMEf``i7IK9|#VY18-z5ztwnPZc1BQYjk11@Acn=LYlepI=YC6Mp>_nN*>>wZa~zAAP&Ot*RNk=>bS-%JkT1gnzL7z8MFFrFeN4-v1~Ke z1sqf2?4Z;W*(Nq723Zu93w;Z}KTc^bFPp>siJqF{ zSyb{L)Ne$d%jiePV0?A|k3S}jo;>1CFYtYK^TT_MRHJv&QJaK4wg_COFW;dE<&}6& z?f39q0;iIqB8ghM*fZkia58uww>;arcGG4DWV6##trOXDiAgsvHG7sfy5@%B?0_*9 z{2Hs?FA_MNKnKY_=eE(=BKdck95L$1l1mgLKA17-?oXR8 zl^T!*Z=CP9R`^{oiQohf?0=mFS|{<~!>d>y7r#uo**a8gKh=v{#!q~(Y|`u@_yEx? zf2&FlO#xgCqKu3dUBL`HFCoKaQ%%yYGODG&S2^?kIeWLSt1E2Iho-&`jME@0U^vFS zyd7!R`?(WQTclmIG!Vi}zjq16lmzf8^^Ns#a5}=DqpFVxcSp8il(cK60z*2YBMV2Wm3w@9AA!pqL22#;t zeN&CB)M9CoM)i$2E+p`H7o7UWjxR=HGNO!^UVcuq&90X`;eF$qCi~t@;`Y5AoZmJb ztUTgG?oo#MKh}zuD(q#y{S!$G)n1!e$^V+4w%QXOJMS2IQSzMmfXfz6Kx8XHJpqqD zSc`>Io*(Fff8sOp9SvlDr^oJJ-o{u3h()L85PPq=Mui!{|AvuPm71R5EP$CR;H$ih z2nu)U`r+MCwIA?Dz`!iz=3CqYud&S=o@luP{d383tdH-G_MIk934DiBlW{C4Ky^+4 zujOKNt5?1LgQWg!dslafo*Z)Gs<~&HZce^ieQzrncPUFl9h>p12lrZD5qUPW#KI;m z0`e@~3irAd#4sN(1}ZWHR1kZ_`mFdUhv9O2AAfzPCN@X)cZ3^Ne`x>`pPz`cIW@2@ zsDIke3o|Zmpe+Sd4xJ*4YNWzZPoC_YTjr}sg3Pkhe9V?FBqRhm$(SMT{(D2iOGSUU zs^1pJON!4%A1FK-98`SRn#B!5qw>iOQ1tB_(dzkAQL$qN><|EfIu-@>L1afjL1!37eOJ2F`uz{K_qdP9>SQlR^Rgwv7HOc z=Y<)2&H2A(Y%A`b>m5J7vJc>6L58J5G@EABK?KxE8JwFl+^bxHgZnR^-6^Y(=s}enz5_^d z&gVIw44Iho!AGax&x{-!u_q%`WbEwh^!3TaXmg05LdINck@fa&R0c@!D&SV)=Cy_n zmHH@oQ(GRs2t?6<)t<(Yx}CZSs9V|M2W|Nz-7uA>SkcFNptC_v!cU}7FJ293`EBTm zxk5vf#Ag@#R+ENcFm%`k_p@Kx(e(yXEJ=d*sU&xq9lR!)0@V~6lW;8ZRb~e$Vr0|@ zh@y&X=u*)o2NU*G*hdx`U1yo@o{@VW>39QoXnuFL&+ftr< z&i{B8kU8_o@$+q%nPh`JxQqqS4_+qfq9ORc+c$5 zD|oI)H8wVCDt%;zz5wWIo%LXR1ukHnlZQVe^+!vvTUlEl(XMHmnO%PL;vfGgBO@cS zy02n@ZgFpW@*%!@{a9&|GSizyp%jw{({i~-QZypc4v1So_IR4Ym{SYaO~E)$s;Jc30-fKX<+3|3a{L;xz$+CG%f(Sc!nbd=J51 z@g8ObL%Ec(O7~B7#c+Vd`|apd?aO_Jas@1b(rL-rg7Ob`yis9$iz4IsrdLk`*@_UR#j+CTPK=4k9JdYhMt3bH)Km%(XCWU;isoUhpqCm5u#*X zj((n;F@L``F`ct`RwtXC#%CvebL4wrqI5gWlc!Ig!Y@kkS7!kU6v|$JO!H#dSg-1$ zoQX;5fK?!Qu$uL-n#3fq#q#VoZRH^ekK5vb2jK#hyZus&R}7u6zSBI~Frq^a5G%#p zDt!)^=7C(IdtX`_tqmDJUl0O)b$dx&!l~`ieHn>T$EAqgVgyvtB;90i7*i zbXOA+#4^VV1|rTcU2;9)CjW&_)$cnVigzvWw#=ujx;hZfV`}2=kqBSPF(Q-(DfoaD zfSeKX01;zb*Ba7e-K{kQ1ylYMqLbu}%}ufWRg+nJ{xLOJZ5;7$`M)#B>IY&k+#01A zo$^3&mMg30`kG2th&pQMBnbpLL$z@(B>w!WDPpoTC#VNl5+ko(t@XQ}fG$2dk$x~? zowUK$)|Lz=$3t|xaQcLR>4bv=P!}}`=!ezP&@VuSZXiTBNMem&d~J1_uLFuz*ELNs zs3-`bEHCf>itl<9;Ec%W2|q5sMciuJ9=YS6#>33aEFvO8Mzt8k#mK;brh^O1Hf9Hx zhWJ7~Do=n+r!yCdz`_ogw1nAFAdj(H`4d^hpA)3i%zp?S4Gi?Y9_0hd-q@iP{Bdq| zK0dylo*pYJD|t?=E?j7?f#B6t8drwBm6DW1c~GhK6S?*R1}nv`W9=^V7~{LWM%0LO zJCo^w5aoLOZ%1zDNftBQY~-B}=;%Zh13GM~zkit;vgcQVFS|ds;SO;C zzKjogN9%j{;(Aqg3wgQvgOb%b0ZPFCh6_SWGz+3v?=!sb$19@SXzJ@fv3qKhk&)41 z%csaG{;%o&BpG{nSL zu4K+^=9=J-Tk)cI%;RK=WnPbxrFZw2{<#`mrGwKiY@C}wM~0j#4(G&QlnuuOXQZE= zfGP`~fTBag%VI#jxB&lO%VhuVt5Y0amT5YGt|sAg(uxPTJQ%u#+JKrv4YeZ%9w*4O zZ#5f&7ah6T+36!wV-w#iaK`Y8ryIXT)IN6n0G=WLlXGP4!YRb?$Vey~Q2S7LAxHCl zzm+iolGmLWO9gd3J$}iu8NyYUq9+B$NPg_@G4F zKG!E6&8Zw~ng#}Tyh%GIlmWpIN1-1vE@NKO(ACAL<1WR66{1FJt@+L1eH($gX$C;_ z68*}qNNYA+_4FlCi2xloVtp+O3tAtkK=?`CVwWkk;RJ!p1ZiGR(bVGn?3B}}4zx2@ zur6zun26d5JqeI->Zdz%d7ttq-;DY@9U9;_eesQkymo8lb z{VYgo+X%5~iNUD_F@5AICMtm0;F3b*1#x)bh=y}dm|&4Wt&b6ejA*W?7&EGMsGdW4 z{838)S;r98h3|T}BGdsie(%WH7$7bcq*=sscF1RPMnp#Dl1Ss_Xa#b~uwGL~ldPi| zPkoq>E&jVVyPbdWMACN{6;!bclZIASvN;mwz;$xT3`9+1m3NyjgcRW016~IzJN$c} zdBJ%OKYAf1-`g|bfCNJM#U7z`X}w5D=;Dcgb9Fh;3Hh&rE{a@)g^g`*A2o6;079ah zo0|*9+$acQRFssoil@eKhEaa-TXZ($Op<6vVuwf?bPZe?Biy$>$cE16_8~ge zCC$_1MyFqUeL64E@tx14J$O9#hCyY5;bz>!JEK1bZYC+)jJ!$t`DpZw@YU(OI zxX;Nw_m97QYjOTaHk|3V;U2WNN9B6st!e3Z6`^s0-j@L?t zE`yOnlV)cQz0{HErSr;!2U89KFgo$Wqo30JaAEuD<9jGB8n zpLjIw(WCjgFoU+zd!ru&DdhAe9fuOEIWEhcFILj$w>@5iCrjq21)NL=HT_f8xewSM z{a7FW+WZaiD*5^IYlMD>JCpW0CxI}|BtDk+RU5o#iJa}iK^%nBO(N)$bvSJlCT#Re z`s{nTwf!f@;#}hEc;N@%Vdr{!IuA(CDAbZae{*{9efnY}{mxj1mF39}$K`ybM`s5l z<=NrAGo%jkG%hdRn>TM=S8;G~IE+N=!r1hE0Z~e)z6F{7&Yg7CN9)s0_45N!1=?(- zx{;l#jOa>SeLX$;93<4oEGq-C9T3WBq;8Vo;e-T=MMg$uXKD%FVk@wty;xrj0|SL9 zAzp9RhGkG4wwzt^H}Cv47-@n-gESE#PM~Jjo9iSJUw3bS{hQOV&gg{RtBs~NgYo;n zzHf>;E)3_;N@3W4NP{=dabei|o5z$lnM`>~5BEoF4Gfs3DJ$;JjF>&r8Mx}|ajb-j zabVCerVl4~xwi=wlmETj^MF&A&FN2(Br2YZ2joun6Pr$XcdD#1t8>3C<*6vm79FAw z4H$F~KV5yLn}Y)E`b_-3UbD^cqx&=OM?Z6ziA4@}r?1#eo}8AJy&sG%XLtvJX*4{o zGA<-q7Y!EAUjTtXG&HAdnN|;2xz^$3(<2(i`!lsfTvz2ink0ypodqF#n>p!yN8e*6 z+tMb9?q=TCVmRU<@Ev6jqBc?@mrwPx;=heo3uO9@U zb67ExwXqqHja6=dbb#uPskUibfD-#ND$&)`pPr8MKXytC;%58PU0g&PYqF}X$tEHq z@@?Tgh}|-~UeEX5Ok$=hO_?bv=tBvMQht2bUkmvSpTl&Tr=<~Q6;s8-_BmR4x|0!( z#U|CC+ZGn^_`~Xq)OU>jbLMp4N&L94IJllISO2dS6K99G3z_@g5IUO>u?#ju-re0@ z$l_apNH<{gW9J}y^Cgc6qdwPw1+gRKTus)O##SGZoWyYH+~o&^`en7*;$^cd4NI@g z4M`VI$vVH+Zd3ox&cUJ0LUiE=`5@#?_WP7+Xlcczr^6lh7l6GA^ipIm+MoE`-eR>8 zK+IV+!TcyUscfj_`}DV~S20W*oZrmFs-Nr)%>XC*^#$u){jxHpMAg^NNymBX7x`m9 zpX_wcFE+8h_w!r(GWGd2RkD&B$kiqq_hH70?#NYF@cNaOe^A(OKmDOnYxqeBs|e26 zN|6pOx9Q2JTVdZ?_LbAcX5eMjF>2o*C&SP0w5`~0;3&*n7B7=-?R?CSi@gdL{(OZR zQ!Z)(CP3alSf{lgE(^-gv`FE!lQ~1g#mSAKXd|UMjt#_dV~t!{;Pdl~(Zy5#bBV>t zJdDD`h;c%u5X8-bG!G#S<537g-ysLf8biCU#}@Cf)o58if0LIN7Em?6+YOsHx3Y5F zrn+*KE>YU5RkfE>AFVsIl>Sc16ymqEB|InbO^llPb9Q8GNq#C)(tEr~U32J3{EAe0 zIXMjWMrbs27EVgN>I7B(t;Pmhnn(j5SxXz7KfH4p%`3lC<8#_MrAB3}Lb#Haq;zwU z#P?)3Bs@F_e6m&cbE3n;ui>ek3s0xqQ!>80eWmsU+OOhLM3zT{<9Kzz~jNfGjm^1EmL3+(u70VS~#x z{&m60n3)ZC#oSmrSLm%5!cxEn(6LFJi+4@97InZkj5cYQ?WV((Gc^A?kaOMu5UTO@ z6Bb%gQOfMMC}L?75jog0m*1bY93{cCjBM2T*uTE$UA3LaiGgEV%NS)CnLt)Y}AYYX4dn7*8QiKROQnbPAQugpA}ECfX5$y5#68N>R6kx zfwZJ9V%|JI=50F;&yQ~((qm(@#%*mzQm)0UxG7#sCmmKqBx?I_^$=j&=K?~8OFTuHkW!m6&H5m$fa_ajD`RM zGR!e}@3Xmc=hUwJ`B1vZk>vRq40twOIc5SYlf!75@i?JPH20qmf8x==+_S&Gzp;nm zG!S73Q}W^!8Mn8Q1xh$;n#daQe@F_pnm+GTr6Sp3i}YY>BVyGUYjl z9;*ah-&whMdwcKs=+MUB;pf-QyFFeIsGP<&`|`$J$+z**DcBTPNDi+pD{c9%uM&Mi zzsOhL>}0TFpZy8`S=WDHqyoL;Wu{y_W$9ehZ!?-ET3K1S*L34j7wTLa40muZkGM4Y z1U@4zlw`ribwDQKLWPt?#4(turEXv_!28Q8NViyr%S0a9%r8$l%x%ilSt24L0&7QL ztZJ%iSPL&wXW#;>YeW?)OlicLh=?{0uAze?NDu|35|~Zz4K31{H8F$C6Mfg?YV>ue zc%V$ZEwhAOJ(mKODWTU66_fDasvN}V64%kuc}^uN!WdA%tL_i@ykThqLgwWV2AMkv z)Vlqu8ytYdMfTF|(mrwEPv&0C2#_{c%hce&QdB#DTDhg`ntz=Xsk@bmWg-fiysWG& z%e6OU?hAtUPydq;vnTpD(JenKyTd+v!SX(nD8cQz`kO~DI z9UXxaJX3w_9%Nhh(A1o!kJj$iGzMVG?{5OZ{pl%v>V2UVZCWV}Hl(ZwsBFT2%3-$o z!{J8z*IHP?6f`ga&#s3!D5JvC;>BK5sBpv-O2zkl5J1|{sR|6D*QaN^%yW8XvjIknp{2t*N{4P#AGBz&m zk-fg=_q$42t;pi@wBIPn$&CyQSUn`fjV?-rz#2LRQrh29OPU6HSrZ3vub~&WKWSgd zLt&ND=`!cZ$;(@&qTWGkjH#)qnVr?=e?m8P?Nx`cc^0zJ47*+#-eA#eHYcL?6#~B{ ziYOz_2r_G0TA5}cU|my)T#rEg5E!xH>TAayjjJ8Ax4$2+XkET!>~aYe|lgcB_*Y-tc5P|>3NK0o4pAc*_9y(paHdGgi6cU>31OA1cw!-p+%_9V?T;)cvvb1*raP>17oamsH};L8EFpo9b(|FBT=h}*DdbW3@*V?}`^LzrG(Yep z3J5!F^`tQXyd#n}HX=RZe4@{QM2G6ywXw0`MZ!!6XA6ls6GK*4H^Iy=lb^Fzx#4wk z!$+Z?-#$=-h3$THM$n(HBcJ`0J7=G#G+eP5OK2U^j=JS>#02zK%#2bFtu-C=iQI(n1XyiBZjmprQKfV29G zMEn)?Y;;iv?w`7eH>0kOpNq?Jce&S+6s0mu}f_3J+roGm0>&r&U(3eBdzOmT;atzoTTot}U(M>M_#E+K z*d{yDFK!@M;82l7NJZOdUD>YIP{_dkAh51|oORk`$;$4o9fCjpIq<>IYL-M6wjcyF3P=m zZ$$ble0iXyT!eoaX4#t&hBM6ep^5Lb!O z^&2Yc6}*ehltkjzWq6^OoA^EgytRTMsO=XE3O9fg3>9#ZX73X*qSSujsf&LKu)TJn z_Bs+#NJXbA?>x)3GC&vn|O?>)I_`6n^7YxU>wFi_cE9)OU$e{_`jPPR7N z#tYC)V~$y7wog(^X^0SDP;g?2;fU9jsI%;BD;w)x)LH5*>o?tT7@Y z@=55T<2`tH&oJ6mDl8(5-E`wFZxV>oUGlQR)bTvNpf@#X!SbmwasSwwn{4)UYFzk` zFQlujE&bg9t^=0@ejsk@EmvIlz*TDKa6y0hK;8&(GRBIpwkuAMNtl|R{^5(IHe*$F zH7h@@Dbf;lQ(a(yG%c|D>%8dxo+!SRblFgaF)@X{%W_XzfQJ_l&YD3C{QM-@%3&z* z8(?2uGMMmdz3lmuXVG`Dp%_v!W9@!#$7l0~Y?UY*nSZvh)0~FdvQoe6_L9Tn$B*Hn z&+=ow!%yX%dk~m^-PF)f(M2wMoTib5e!V9~I4d*?O0D9l)l{e!(W&|KlN58WA}euK z)IT~tJ|Ith#A{JKM)Uv zY4c%-7G6-QPXJr8VX0#+`&9zUrpI~3wv|fby6|N&#xGd4QK9@YEq@*!ZsDZqu0*Ff z+htqt3YGt&WhL4eA~(*FH>p3^MeZ^ZMsr&1j5Q4w9mKOSwt!asnCF|##1bqWyXf#u zlK8&XM9rDH>gxW!>@-i*TO*!7hYAWpT`&QlWxbR)^^w46q!lLb|4ht?+ zf>3zroPjK7AuOKq?dMD;bwk5fknYLwfCYFH6r)ycSZbMw?-kT4R{eD|6KAaUzFyUb zx^f&OgX(*uR8{Einon)u_-E)`Y%_HZvDBq!-0(RBu9{j}P!Fx9snpEbla(tY`1twz zUcWZtAc4Cii9)`mT|V==uP@ZQLW{d%9G6j(YzZTkvWz)+)*73)#*9c|3h_Wf0y!{- z!s2$(!GXIgjZmuN=Ql|pK(Hjmfd>r}b&GE$DNDF85%I3)X!+`IM_C}flQc1*RHhlc z$XMTMKJ`X8(v*@RoFBpo^N&{7(SghUvxiVkkp9yTu3O)DU`{*G9onzy@}#ahhj112 zP;R{y2Az?-jZ$=9pX?s?+}xZcDMN7d2l{L<^LtImw5ex5X>1tcYE}lNY^iQbivgm7 zDz}sJG;KsU_5PlQ;3CaCfS#u|++$osvEE_TeqlXVt}dYLFB@L{}F;CR<7PH;x3**(`XIlJc?ybyj%oE)$fdtKL9w=y?AY z%U?MOrnpQ!tW-i~K$a2B9%s3zJh(c2DzgafddGrRaN)zykohyBE&@h>4;TIu=Yqmg zZ8pZ)@JnbIuw(g?Fj-{-Vg{wV{tJBgC>Y$8ks-bn7))M86gQBg&Qic;sek)6==M6N zjD&*tlT~1odwR&C`al_SAWMucw3SJP9$?K?*ojw-9&^b6h>}dWa1$3rSPNkZDZ3B= z9ioD5@Fn8O=HJq;iFj4aqGMwMLmlh54+FPn=3=w5PqV^-Jc^;0b2U3X^_>NZqg}J` zX{?yS6*b60e3-%xwUz>WeCnc48Nz7H`pKo;A`*+}rgV7een}_L;c4sWEP^&p;wW)x zajLHm)N7Ki;j0!fp3lYQhEPr@GGaIuJtSX>xHs`=e{IqLp4igT(9j^O!xBHFO$MGC zVhi;BcXnJ|)|9sH`u)U*c2T;hxHunl9y4YOh_n3k$M3)l)XBL3ByZyc2jN_7#{&&UP1 zn3==cG}-HXe{PdRhSb`wjik;OYUR&6wL_c64WsZOxY5f#_4-;n!)4sS192IfkZIk$ zsij5a?8DLKTH?p8x$2eI&(%~_Rh5-FxVe9};pVHRD$2{l>yjspZfj-jXFiC|$v05P zAYM1W&jEjjy`v+SW7GgT|14{>9#v?k>XD8kNX@jPoEkroymGEHhIw-Q4hQSwFvcwZ z-N<6qw-4>-H%CRf1<>kmMc--pQdHd1Akm3=N${oMA+*&q&Jc zhIDeeekbJ=K^J&rWA&Oc(`2qv;vsD;HFBfALfaEd#zyoyesHxUaJcUozfQV&4-z1_ zZM4~lQ;j3)wsM7?DILO;^MFY|w!K>x(=`xDPfSc42S%}5_ep9qV9uJ{_s)U)@SqpH z?lpa3#CVpBO?46zlmU&OOzQt1VQ(E)<+}9?E1{%-lz<3=(%pijgi3>SBP}2$NGphx zNJ)2>bhm&=cXzjhbaMu4t-bgAo%8PN`*W{rFV>v%xu1KC@vFgOHkgS^EmIJJLCn#5 z_6!k)SI92YZ~fqOsUyN)3wxIP+@EiRi$&0{^2xKU?dkmEN#55d^QA>aMO$@emb)c` zWvEkIp3j?0iB(ioWTQEJvj=wOn&2GB*oXeirQ)j{b}FW{!-D84MSG6q8(<-e}%8#>@q_xJThJ)k!hnn)tBO%QM$)si&( z05WBX+gJ~vNDbQ6*{$8>w2+PE5w=h>>*LjW@a?sG;myx?Y74BnWP&10akf+ z1A=dy4u7v%@l0NJ>j>6kfEQ6O;J5q5h7Qc;J9X$JT;{6P_KY#wMFuZm=g5#*KwzMQ z& zSBZyjJ0n@WvB;LapSvL$Fr~pLoH0LYpq2sjSZeglo6V!D)G zkHw8yKFguV#*NYuZAodKXk0QfP~eg6z9~A2-X7Ml2KhPk{dQ=aSd$nHit54a_dhFS z;koFu6&!yp(a&u7t5K;D4#@cZ+uVM@FWvLt)a0apeUqA2h;bR-;UZHr0y22px}rHX zCG8+I7m~gX;*~qw+SI_3THcu8e)$AW#6b_3lr~g!T$c`^{W>v(8jg4iL$$(6&(ZOu z*`Kg>yRkn_vi$8#cRIJ_b>*ur{ zS~U)hb3M3llfqY2edRa*c?cg#3s1fybM)Jui0>A#l$!dNx>60^y1V_)s99R z9D^YTL>RXo5qhFLxouzHHr7vTF_12Oj@=gcVV}8Hdj{$O{2mo~T}H)*1_l)tjf5k8 zy}d$Lq)*>l!G3XAb^`_HOC$5>0BYTrd%uZkkL5ocFc{Pez>#}JcFagouDfVY9n9Es z`u<>R%6TYTDNp06!`6MZKTwxo6;&UssB&Va^SFbJ?#F`{*R*G^fK4&Uv^6>Y!&3gs zHb!N=bN#%nB>m~V?Rcm4EC`BDVMRP|&L!T9F+Pv}UAtWDKwqSR3;#TU_4O~os}tD_ zZ+}shXFII9&jaimZd)PxZ$qgf{;O08-KgR*z$A_a*~oi&T<<;;Xv!ih!C-JlFQ|lP!z5$}_7=hcM@PU+M?$ z=hh=$Q`!ZrW`i_U`u90Zdclq0ApC4LzhP`1ceKFU#mbu-4{)J}uJe4FHu4z*Vx83= zn*wuQKV11yOXzn#5zeUb*Uh@7GotOh>4A){+QWWrTd@hVjM?jWmztIaZQ?-2Ul81& zXhZ9d>4z!cNoZ?p(+>YLm_9FYlehT#br_&m{ytjc$X+kDc)qRsQUpil19N7+>Uq2VHg|77brAP!Gep61BR`mZ`ulSt) zCGOvk=fma2nWE<{LT?wq()O|ZXED2=NrFbepOAUouIjoL2)IV8m&jn5HpAdT{rGX; z?$vRNpm+eWSl33&1LrSp{C2ByP)=dUe24yxDa`61)h4*>ayr51br~yiJ;sB^}2l{IHkrpbxaxTY>=F zYFOM!$e`M5CXZt$bgajZ#~Lq`=xX~hc-(~Bn2U9_vy^D(r zg~1IFg?b>$o_XJqmXYc1>VnPge~$kwO#NNY$Xs?BAS6Vjqt`eGH3JKxNq1%&ASOF0 z>}X?T8-C@%3c+{lco#pVS*+ejp^a|ZXR}P|X54*IyO(ocYgs-w-M<`OTX``XN&3sp zx9%IGg_IlC0p9yM}38@1>#;xWDNV_ z<<^B?Pi|n{wl?~QA6B`)Jh)#R6FO*{T|#%NIg@*QIPq5T!{t%^RhLd+%)|5j0og&h zn!RqmEJtz7W_X2mNf0sEf&e-!uSNMEor);uZz{@a3t>i*$&v$Pv-LS4uV@$`(-a)L zI7cu@dB5?0m3UVQk1Y?^7#KCU{%7i3tKV&i<3i{hL1k~>ZSGN8Ejce3hF=IX4JzK& zJT#JfweSIus#zU%&oZHR`Czfn82A(F|D4{>1JdNK*0|VJ^E@ZnV7}u%p#dX#*K3qe zYRF6wp1J59a_3buNQLRu4h{~k>p7vv9TaLiVZ2f%WTac}tJ})81UKZpbc=h52izW6R>L(uU)F6%1Zb zv2n!&JjFk;;Po8*;u~)u1X$3jlcE<#u1xb+e}6?9ygbAe1ajU%QRu+yVX`a|-3e&C zapNn{VrH?o4V5Zn@&CpHL`h+O1zBpw0PqiLjoNKZcj*8tWoDL5lJn8Z^zq2!3Vt5M z06^!~cqzTp*30sj($bh)AxJpkOUXU(a0@%`&INU(`J^l7(3aGmuIB)&aJ*H$J*qJe zX@f4%Gl2@SPanSP^#v9e3|NbR>uJHH9;`%^AaniMPgnB5OyvhP(hX82Gx<*zJB~$J zFeUS@;hAM|szUPnnDXK1>p=OgZ`%S2%>Yn8|;m63k%EdpWj+LH|h`(KllP>GOqD2QkCLX$_EgNZa}M8l;S7#R&ELcW?6#8V-5 zhfjV{$P&$G&T6>DH&pV$`9(bFH8ZP(lj`aOn3(QZ)5R7LhyW{c`_BE=WD>Pgpwir6 zFBo{-hrJ3RgGdLqU_h4e5ke%C1Hk2hbc9SQ_?iGSix0*Hu_j~Wb$@Px?2?Rv4j1Q7 zm^-kLVdKlU2ru@06RsG_*A5?~vT}0JXE#%CRCv5GzM<|lZUnfYg$~xgzr!aOHAfj6 zVfchK%PG{ty4SNW=twlVf>X-M2rdLXo8$SNS9uor&H6vqI)@n!08tE>TgLbA7}GzP zVZL_WUu|$FE{AA^C(Q+gZWpH&1a&9NF|cqrPbFX5U|Tuo9n3>84UGNWqD05Un7&Z| zoQ4nmTlZy5NQezgc5sZ}*Y5A#!RUwPsQz zu(lb$0N8Tf`nP;cr`*l+qk(a!$(*aY%fPi#lvehU@DL{x6BA87_6>`VwN1X5_R*oC zo((b=UiUq;^H=J2XJ`<47zjE#qd;sJ=VvlL#x{!;b3-4Xng7(9@a{WaHNeH3FLT!u z!8#L5%dPol|CT_q?fzk%!#-*Dp6_I=T>D@rJGdIh2$h zpSNN1uYJ2EZEV=|IZ%)-jhb)GVzyhk+~B!>-?)KI7~v+LHTDfs#^C%5vog327B)Bk z-5N{@{1F(%9Ts^sEV+Bd$k{lAGqSSaEL7C;F)}kVv$HFHr)Ud?dn9LQowvoD3p}wY z{vkf_WOaol*qE5UZWQ>uc_J%Ul8o3~gttGIuOs*+hf125n7H*d2q0_*TlGHSm8y3g zj5zp*%!~9@R#ZeHASodNuLhF$;P;!$;+bA$?pJPRHzZjui0o*F;q?Lfs&1vTrzfHn z6~X4yTV>|DV+sTg6Ta9tHn1neJ4uX@@W$O{5DK-Vj)(ZRq?!CZvyo#}Y-u97KSw$p zhk6BXqK{GyO%lX$q$yNRt7{E;_=ts&f*K)@A61GfR3^j!bIgbSPv$2Z$aVQXdNnFhc93>sOj;=DfomF02se`~d{*8FhChCSO zXlUdNtRZNiGKu{IMH9^q$93qk0AtFh!3I3`=c(a_$2#vpCJ!U{Tpe&Yi%Uzsst?9k z^?#xaM|8|tb;dqn=j6m!;y&+XZPCqm=s{Peo|{fDM4h+vG;w6(#Pb9`((bL~Z?}(8 zNFGIHA3;~Y^I$UZTIRu@Bih0R=>1rKC!|u}tu)Y1iWaht9T&vAW;OqOzKScdqQu}O zqzd(wK_YA!3wUm)igfcvF)+lo(sr0Y6~!{1z;f#9Mzx!>FF~n1BCMG+1VnyVc=)aJ zQwvSMf9A1}&uQAwH7`Uv!I}vo9xHNb=f~Lxfb&7hfQHwjokiTE>+3p>p< zQa;uH=`%Pu2#_Lg4KQlUv+CUe5E%nvhs)44yYFzP(oFYxmCzs~UN$}^%o<;~>BS|| z$MWP!)E!#2+s!S&RX(XQv2g`mh*NER`&$lTSkfT!P3IE{&L{hXI|53np9y;PaE_5X z(LUW=j)tBz1#s`8LfJxe;JTqhG7qxJ| zzuX(*lNW(BjSX!U^HCPNksZ>omj|aC`B|G*re5=_}DRL+D&esjwdyu~b%DtC!0+H(YO8%GVeS3SAM}AG%ur*6YpgN2=aEP5eXJ^K)*`c7U9XP>8X!|}2OrWCrlDJ}f`-V+8U@X)Djgl4PN;}-?P=Y$g z1e$qxn0=#XDL#H zjw=;NZ-`(uW9_gm;cf%1xf5e?t_ogj^m5?YjgAO;weeT zLx)I+L3j+_qF=RGDxXUepQ1i!Lx;~~R(iVI!5moga7(R<0>W-`hWZ86bBBK%7D8y+ zWGf8Lx47rgNN-n}k)OSklcbh1|MKo+X=0+6h}XGWP>w#8z3sB zJKU^56q~6)c3T!eDmSrsK1ue;?YXy(Lb^dYUjhQ~tMPus$sJ$k$AT`o&jgfentMKQ)l6E579NqbhRjw4%e`{bK~X-r ziGns4*v-O!BNc-I?}PG5z0-!eXIwWf_M^OU-}RqjGp;BePf@#XdZ;VmMf)~cr2n8ToZ>jh2%WIXM9t#bYr!=(H117+5i+oCE75Rc#9Ys+R@w2fZa9h=~uER*csV zxE0UYh+7Eo`Ns!ek(T93=y~4sNF|Safb>GGKn}%B={dbfKYlC^lCXz|?T18Vd?h3O z^7?P_@yQBW7A%jTlOLbsp*a2HP5xXP&9kRuViQczW~G2nfoc%mE|9%P3k@L5500wJ zE0+l{T6idoN$1yBCeD^)VxEcA_GOx}2nE?84S15%q7$GA=c0iEtjYry{+^uG9qhsE zAnH=EKRjodboL^rQNZU)XbbU&RK~wsP52c})P-E0+?SxE-y6ea$6NS5 z{G_zB$J<1suUGd2IVxKEZD9oh20RaSe2@X!AyeNG4oaN8PoAkHRuENzX-{0mmy3-g z7TEZkUCl;5Ne(_Y2O~Lia*&FmVo;Q0gz|(=uCiLLt%tx*2PE!$nZ)`Ww;A)|XG zDtnbAS7Uq@O)Hw%T`C^0h)+*ncidCRuPu|PcdG^I@%f*?G>+nIvpp?`$NqJJHaM~G z_i1SCk3fe{%3E@@zcF3=##IefZKE}q+t*EqeA~korC2w~=9Or)@6@FNJlN6EJn;op z40}4^1{I-QrA*i5>p{g)e18fnYnI64F`5V01S0PgId42Xf4tkCyJ)|B%vnS~>+%IN zMrtFWa0gMU7W5|4(;V)uWt_pX`lcTrtz%!lRQ~L)qq_e+i=j2kJ{W%i zcR3XAbZr(e1I|WKpuOsXaBKN z6|Dj$dsV+5B%WWkQm!?9o`RjgIZwN+FV#DkHF>z44^vTb#aI8b^dz5@s6zTsnG~pK z0bju{N9G|F)V3RtzL?_LJU`IGy^LNP`JaBDhRuy_cO5n`T~G}eSZ*t|0dr~0Z)5nRS5hkfS2O1?lfLlvq$J-(RXuxWN;zSe=aD*X8hA_8XWFO+lUH`&&lFg7xhA1^EKHgvQ@H$N^81n}xjck_YeIM~?{;Mlk zoI=eQ8qI4oCqk*#z7WW)S+r<*`Vzg^um(xk&9rj0ZRs+1_z9ekl51at$CB@>rZ3?o z^9fhyY)S~cE%|j-Fj(+5Jc(6hq(&Sq+tfRKiJu9+AGuK1rq8$44o;iM?B$den1AFZ zWv=!iqklQqFF&L<$Zz7aU#xI7yh?lij1c2hAb@-01FU zQQ0}=hikT7Cwmg6ybtweCCE0DuthG%$L1%u>zpYx+(7#+@%5=B`q5S{Py!al9hB4z z0)NUCWa2I+br*liEvu$XI^PFi+jdtHCbT|Qi@k3FoW{BogV`h4K8L7 zGr=HTtqv>HtDE(ckilFIqO$U*j4T%8C4DIpid|%!W`lqispdZ5Fp&efXsYrh7g`x5 z4>j83jWQ<#bnc*s)C_~hUSrx2X>@+0MV7vwZA5FtzkTmu9^#KldP5w4^^KZ{H4ce12MANch8c~U3=Xia)KX1t`mTyZH{8XK*n zrk4J<=M6;vkfs$@!s7gCF<7D>9nGyn=Yz&G>p!peR%GY$qm+0q$u7P779|rG}g@65ntqhZ5MR}wZY`g{${w`mPwmzv*-3+^M*3qm~ zB%L|aJ9sa4x7Z_K)^$Cz=c=_{_ys$F-m9}Y_51B$#4r2MjL&G-0uORh;(4_vI08BH zu>C_#QSrxCu#%F+)=b9`h^A1b~d&)0nv%oh4RtP+5Awa-k^ePIa=Ogu(rLSA= zge$uM1DM@vytenp^VUA^WHJ{%?lxt7tH+$cMLe<1TBlAn=r}3wcAJdQNW*F_g zQo};L8|;zqW3F?P^RU*x8k6g_svblB3RU1yUcv?(Q>{k2!&?`q)K)?tu zmxX$x<)^~-dY6;kT#fSa%|l>b7&I#iYHH2_*03}c%RbR8*D7-X$Q1&EV{Er30SBl6 zC{^%uHHFV%>&tBWjCh0nBNM*l=i(LzCb*X5&tz0Hd(et&->>Jc63n)Da9f!+_oa9j zlu(p+zIDUxxPBbMf{s`o=Z!l9ewCF(=6Y-;9v(Bh9SdLN{(5Jqd5<9G{pr3OvAc}= zI1=v>$I4f}5Xi!iW=y@dOX*6ASD*S%+jjE;YS z_erFP1s4iAbAyVH8(Ly-TXm0}^ka2~aF8a;N{Y*>2jf2VtY~q+IBhP`Nqt+FWk01e z;%opt-|il}X~uZ@+qW!_pzEwM>9gbA{Vjek=e#4Fk)DPo5!Jg1@F(jLEeGiYfw%-^ za4#7igfq&7$#7 zr*fpH8#?YpB@x$mnc#8aK9u%B@|2$b{vk^pWFU@NF+68~Qc5M2@i9k8t#sE)hqv-3 zDF%USSc+A5tjM|56 zP*d!eM2t6W?%zc!Eo?zByF$50An*X+H?UAT9Sz*hf@!O2O+W0tE^AkyHAFZm1E?Q~r&TZi|t0pWfSg85!(rqQ~1rd=$@642fE4+vqBjl~j z7=-*$!ToIT`nKLWIE@+mF?+r%nyn{ps*V%lHT&}+%3ZuzM5NMe?5G!iH7$3KV3$q( z_E1hlpwYb5eOj7hMlF`t1&Syhsp-&e{t6&jSXl7BL)uH$vION4+WtNW;M@ECfb=C{(ft8BH`9;!^vb>YPPG7&AM6h?<~}#!hz4XoY4@32 zPUB<7|2AlSh`RsuJ=l!869fexKQ;t$39MJ<7%(fPdi*%xAJNt|&n=BRyd8eG;#~X?!Nv;~89?xR4}no}Okp#Y<$&17$z;5u1LZA11+?vF+Mz&kT=V*7 z)QSH;Urv&~=wY|d-$YKXTCvgQc&T~!?otQH4%hi}BSS-rp=%kpd(rkX#oAm-s^$27 z7h}0ehRl$|=J-iYKHXHm-<#pda{JAT?>{;sdd92XIlenkKKx2%+~JJ!mDIW0(%tmO z2LW_K;*Jh(%^Fqe(~mG4Q+UDIEuB}ngv4;bhyiN=v3zl6W@cd_P8gYo2mMb{l#n9b zBp9xPls~1?^H`&JAR({Z5YBCvU?X9X)P)BmxsK)ezxaF#!Ln92D6Iqrh{(^(7YA!)R-J*0*) z_Gin^rqRnj+Sw=su@!iBMO(kwH#Wk%_zM=NchlCEIHYYwnep+KJZyI;Mj3wMeJi1m zhAdF}xg9z=4istsA4pf&H+OTc9~ywafYm`zt#S^S#^7x?P;q-@ zLnA6O860iZL1=@|z=3yq?)f_HiNn)`teGmIvsgn6>4E!wlE}ymsrar#(IW}zy*5{Q zo!?!t*!%ViI2BFmRt^1MHTe4Hk=FCQh>#tF9FvlD6kPbXqM4n>5={tVJ*gXv9?CrA z42_PChBM{l z|NxLPfDT6_3>J6>tU4cuGM*SG_G}4=O;?}`NRbx%=e5qh&f4tg*!9+cb|0!vo0y(aIeqxom)LfqJ-E7@vFFu?>;)ZVXNabNN#cBstG&AUVolb0Z3J52 z(RHWMkjd)0^Ha_b{PW>&-*pA$%-%ecQ@7jReRWo^WZn(FZ&1Z4r$wKPp=9 z3`g?fpg!2`%r2LGXY`mtSQIl+Kc=6#XnHqtKvB2``yWi*+jPfNa^Br>ovL*X*cO+t z_-y~Q6&cxjq=M^Lv;VtfvK5xh&9M5Uq>t*z|8ackqQmxrBH*&~qQrbv+|-bSh5g_E zX;!kROvPtloRGM_Ql_jW`i&F9yjN$d3D|tNF&{ntNxIg0q*2$W`x_gVfukauNxSl7 z`lEMzN4DUxM~=2OAK!NTm30Th<=M=3o~oXo{{|86K(GKi`}XPOS$V8QofPgl`1CYN zkl9l4y$;}|OEDjKFcxtz+%jaDez}~0#cw~`r}rkk;$1*KgblRpoNk;=TRn?iU5dz` z{V6u6bG6^&sCe}BWOafWKhB1q&7Jq6p2PU{;lw^YV-vw)jwz@4D5gJQ&#bKTNSfqy z-nvF9&&+@yUXHe7vv|ktotzB*U4{3SC(0ujm1x}`nJY&bMogSnlQ_6ChDwqXqk45d z->ID@%G?z7Ttn`Y0^cF*uGh}52PrcHf&Y|cL>8Rn7fjkqF>kZ^9O^uXZ}jjVwX*ko zUxkr2Lx$D;>a5{|%Do25o4XVojOQUbm)8L@XU9G650Xub9qFV)yDrad8C`1+hUd`4 zBGS{%>iQgudf`EGTb39YO^a<13A){r4z0U9#oWHymDD6jQ9?GfO{TBF{0t8a#D{4L zFS-*qJ|3q=`BvP$X?Wx-;v6EPl!h zpw~m*%e+D!Rc_2ws-L93t@;0ckUrP5(1879?~tY;dHS~`vJCadG-D%+@Y-B^2m36QJ#G9-$w(l=knKH#g^K?Vm1 zrT9G9#bykBQDihl`HSnA&Kj~OXX;+ijqP5I>Sej^v2%Hs7>y+*OK%r?@pI-*JLel5 z?~uIK=RI5>^1N?nyJx+1lO7E#Z=i%_tV5dnV0)0(Xpm-8=SqF~oKr0_v^Y_xetOXd z?pqodJvEOX_t%_lvF|Ioxlv67B{m16p{v+!q$lRWcnjm9#>#l<(SUfh-J0liI_&G7 z`1-Yhm1xNPc%pSKps@=>xNIbgN4P=R8Zkc2^v~uCo9&Rsroz{YWHJ?7!5VXGBlGhu zdDVG@xqkBl)Cu1z8Mmv9F0FAHzO06ciZ(pic}xg52Xu5~UNt>}S4%i7BPGd42v$ZS z;w}8rhYh#WVmE6p?Da$%f6(XumZH~FUc(4j`_rZ4f#w$l#m&YuE<8NEeM*YF{t;n5 z5>_E#hAJH6$dnqPJ`ec{kLUed@odBs8&An6(2}K&celf!R z{Z^%f37H!A{PBBCt;2p5Hu2M!mP%^*0{{yBXaT~oUAHbYH$ zs^x9csQSx`(8|Y0tZMemZKIVwc0FZ|C0{mlGJgTecM2isYcG9C!h(mW!`98Ri)8u3 z#_}GQKjcx|*9!N!zMpm-Z$GG58Il?hpt4uJ6XxDo#}t1-=Nqnvj?R^Pd3ofyn6j|I zsiYJ`=3+&~Snyin4riGO1)~MRkjseaZZ|2Pc6z3F5{JsE( zOi`1T$?Z#BNO5{XX6?H%!wA62&oP?yyAx?w(&i5x99LY>7d-nu zS*?Pj$%uDTWT27xWQibb2B@hyHh+b@a~o6-59FN(tra>6Zca~f7k_*V zkiiljDE(xPdo<&|l98}Q-6!*IbKDW|ho=`RJ2DZotT-GLc|m!GJN=qROEfQ}efaJM z>y*2sG>nP~Qr^X3;P4qgoXreb!hKo1O9E}zE!9!rCHYdhABTof zt4jwfr{DEdvskam6Ee0;ogh>TIj3ZsVD7CQ?iV#^>2b_;;JoHReZ=TuHFw&GkWl&R zwmTj4+jl&y?NRi-GN+;~--&~bj5``}jf_3J>v&>y=ewnR+fwu5h6V?|`WVtWHL4N^ z&d^}~=Zfu0C^PO}B$O)Ot}s?W^(75=t(Gay=WriR-P}vxGP*vwqn>Sm{C`b{KpPsZ zA+WmadXr73M}6NY-`IzgDU;LQB*yM=rq7Erw6|C`DvqWn#?S!e>Y~qE1${5G21t@x zfj28WiD&qljNWBz?NRx@JD1%*1)U^1FY~=$I_;;hOQoMTb4C{-1`R<54W@=b!GsCEnns~PSXC@y-J^GYk{FN>7%KRtc_RJ zKGWL^YYV}c2cZ%A`ue~WrD~oS;vt<}pUlzxQ0l<-(LXj@%pS;Aj7bv{87R4Bf+RGp zl5Il8%i;>qc`gAKh8q6mY~?(2!G3QkMAcEI%aZNXRUSU+UfZd#OIct ziFIX;eFJ;92qV2MW$HgN*0^2h^CG;hs|>4upq#eSC7HG+D>&5?ZR?>l?r?pHqW)so zHf=^J%m3HSDUlx&T^dO7Y0p2sKEQcb=Az0pHEw&;dFf(go2f`@+^+o1Te0kDOU@7_ z3FQO;jfxd?@$}!j$<;>P2bMmS}4ueH-B$H8=xsU>5boE5@A5r|%G)5?!n`LheO9zK@ zef-IgJcIz2kD_|^&=ow2frXfQq-O5x2?!?p20twbd;pNIh#j|;xve?J3V@j2gO%L! zI~n!Afthc??;i&|RyFz|^Y%mIH`-!WrO62lnVbLIZqMN=a6VEF@zny+=|0F8zcqNC z0mt3??V1Caik^w}a$@CnQ}1)mnaKapUCQcWM_FWo|6V?mzP@SB1Vk9QxoNi`j~oOP zQZHVtO_akD&doHA8?WtJJW7dWz!kP#+lV+JU_MP*VfnlRAj#5{f&$%jgnQqmLof>>8G{^ zf8r(f-nco2ENQejq$i6x5Olllq+M;qPUP#nt-9lQaq#r6QhNvU_K)kUZLRJsy>A~_ z!z^u?*i775+EXS;QLA~1Y?A@!9Ny{IkU!I?DIgphkic#~FKTVJelPk2__O$3vhMKAfvu4ii(6P*E6^pppA1sA z!;MAMu_;tz%3U>YyU?*8Gekv2j1ntRzX5NRaY9$Juj|<`pT_6lhlp1|^N1KYx$F_v zB9xd>fl>oxUCzZgP45Z@;Jmos{`U>FG~3Vxk^;?~4$R zmRDV%t!k1qNpMs6D}N;Ps@heQ^i&5gI>h$CLZs!TCD7$56sIO73AcyoFbT^Q+?Ylr z8VR&o5J#7WT>(_G$g>&vV^om%hDF3Cl(gE&i|tHXGZDws@OmDPjiRBtFqdnjmhAt% zo(`9%wR9h-7r7w#rs2@6mAEgs_pg(=mUw^tar~!=hNIxg*x26NiidegdI`F= zG6gTsEO9QyxijQCvvV)f&?%h}5&=czOT`ysj_%}+cH4Q! zzy>!gAqSH5`&oL}&Pr-?bzFnpwXR*9Q)~3qOCx4Cfo-`uEJvg3hq6p6aJ~SDy=ywYlWfU-!EJJh~Gzx2{Rl^K;| z7*W2{cGu!`T{mXAtJLUb)x9M#<$Zli z?S4Oxq?y0(ENe;|Q@FEo!rv z5cbZj8-ReQ1V2hYS;o*X&-8AMeDtcto8J5%9hpgK<<`aD9hv-%au>>pe|Kcp1Fz%J zd5`^uXvhS4X2uM|R4`|hTbg0TQkGG_aN_AXG!Nie5ej36_@l~`ivMxU8X^zsuQY~| zo;llGSIUF33Gu2a_piEVBIjU%6?00c*>sX6Puwi7kT(@RA*VC`2h5PAMl;zC9 zjw?Qia@|rktgEQn>5f=cv3zkUH~1>b^zzSw*djNdeNBzS+a;il#~OihwW$W0;5XOB zFOnGG65Ln1RWOLy601%lR{T$-=@p!u(7IBGNzT@r=S@ywu+VWdT!HXZJuF9gAv9(6 zV0~0CqAICm{xrFt4?bF7pRTKu9_Hd&_9&`)khX-aLxAB^#vVb-=k!&m-NJH31!^ND z6tDn1O;f$_uR_SH!OLh`dI2Nz&(cv5J*W6i9kcn)PaU+t=FHx)r;}j+cpkz~gJud7 zYLoX&rkq_;K)*ZG3%xy8&AEe-Ba5Srg|zPB8oybyBoypug3ES?E1@cg^Bt3~*3mJ$ z1W}fO@TQyVNSWJkVrkX}C5bM@RvdExxe~N^;lTeFc zDyFETYKL>95wU)qGkU>3gFKc(2kchPTvFf&1E~5DK-Iocs2#Mh(R>1GNP2p@2^X{^ z4LL&c097FbAzdKieW+2n{BNoloN#5kmqj5k*84eUND@duC!k<<#yFEGNif6%ep1XT zw>E}oj>q-V)4`mM62lhzF9tYT7&B1Xok0R9990e#HZGvk&5>*|l4D7yPJCMtU$a8+x zZ4h~wsRW^RFW|?)MnXlqmXYEQDS0RM<$u zu^<~wR-UG>e?!-&U--9O6%rkHC4Km?9~u5{e>QB}s0jMwhm5de>b?51KXctZPP1=; z<>-3w_u6DYvMEmw?@|+%U@Ac)IIv-`n*f?Lj^DYgvhoC8v3*?b1|fAEkMq0#l@eM? ziHhZYNB8jI97J0H6HS6uh`HG+2cTP`Tr}(39;pnfG)Sd%5R15mCUf6|T)5$h zkX+7gpA?t*XaNW`xM~gBLO}7~(Cl_RcYDV9;SVWtl#sZ;eyPlJ>_>jOPXFz<2{bCQ zTkD4cR%|+QMEwU6c03YM2!K-^L<5>m6h{i{KmT=ic>rCQYxjR1ZjO;~tBaI}5$b4` zT23YJY-?p6(c5|8Mts+0OI=^jjf!$`6cnxN+&?PtzWQ)2qOC(8USEqm2N;8QDsd&j zeUP~$B$x=YSdmq%s01ER$amlQYqHEwZl^vjjj=?RoMBJg^_Gw~WQ(oQVLPh`d5$k7 zOW|R0%l>C0%tQnX9I76^$bbEhn<%EuSBgKB&O>F}i;AS;-7G8xE+L;CcZWFd@Z-@N z$sE}WN#DnxWPR@HOH-7gJfwYC_7d6DBQy6* zg^O=xb=hf;2idMvWJp_{utRhr%{RZ9dXNG$gtTFvA_{hY=r9&8D zdBP(ODjN&&Nkj}r^QLF!9{i?a3!aVJFQ1AU(iABgr8%?4ZH=O4$0rF*iSo!jBSp`S z)R?Eb7EK-6Z`~iy)wp+lvDb4E!R+3fB60lVqgG0ahTC3ORTw>WW6PffT=jCxr_P5y zsi^|;@?G1L5O(K@-5JLh;?oEMj5`wrwj+6tuYc{MVSLA&XbH?`(79COS<1;De)LH6 z;!lfqCK1n^RG;IW-Z;^1bS_DgEx$w2ZIKz zJ#*SdblkNdcIWz{taBL~899QJSO`26;=-k zzj`ZoMX`$vM_R3{Gu`sJ`v6}ZyO+wO9CZiBlB^pO)8hTUN%*J{)2z4c)#)2SDI(Ai zm;O%u@P-7fo>nNVFOO~Vo7zHcCVh0(n_;b|atC%u?*@#f21ix&zi^=Fm~x5*ejy`F6q^Vfz|5(I6rwr6uyC!7-N;C%nhTF7E3FL$y*Qz@2CxD$QZ7Ba9<$a@`aFuos65HiAuUO=rW_V3=b?QiMlNOvb(pYT}vU1pz9v^V9x!#5YAvZ*1cXL(Il;ET^=t#o^rB0 z^t&(eMPlGN>6^^a=kNRYn7~4D+6|?zok7wOfEH7bG{+UdRyfn7whhL)AtaBxiZ*E!afq2X5@+8Q1#fI13{2}c< z+&=`^ujfB<;aYgu$AHN^he@;5Vwge5oSksz?D*T;{?t3BVgLP=Fu$SHBcEtF{ym2H z?B#dG{Wn$qADe_R4V!N%Nr~VWr&3YL(0503y7VQh_%VFbWIlyyNe&y)E4F_2Te89|BM!1 zEe!0(PkW7$rF$)LXDJ=24*s-s%ng4VmqNUxRlL>U^3mm?mc7D{QcSNIyu@}B6(Y<3 zeqv3-W4-zRIj`TWLQCeCI>_v{r>i6ace>-A!G(1NC&KB_oLg6D1+H~ah==wUxL#eH zkt_8#QNSoNp=WGkGdbHZB_8k91D9~S!ocfPfIlLY2ok2tFv81_w{IR(#Iij{M- zW4Mj2&XTw)m}>ow-`whrs&nNldpiSCkaD58=M$}G3KiL3A9R^bRk6B!VtKRJ8vl~} zx!H2Jm%E;N-}pa2us_|in1+j^P+8?qpSgaI(g$ZXu#P{C<_*=R8Ry61}xdO&`IH?QCm^-e#gqttixk-MDD1ENLeO z;ra~N9{%R~>!L`PAx%v-N&HNgr{?fSXlMXuP&($F;%gn>chQem^sq9M@LsXLVr9I( z+mj~{{f@?rw;O+fkIldUW2lxt?`8mxK6`u?TRbB1Z3l$Xp%;nKZQ07`@@z&d~o5kg#_7CIQv8$;IWR~kYl0ndpIeqtrw z*PMeXi6Msj91YPJQ|$??2*y#)m>54Uwh!zSoSFbE`?KO)@RR;}3S1@$I!}B@H7F)6 zIi*P+qaNd$oOC1L&5yVY;Cd1<2!4f0%ay*^Q>z-kB`4Sz{G2~lS0lhp@ioN|<@GA~ zZ?}cF{K#-cI7KjEr_h$<`bPS7FMx;m&!?|^zG6G|qq7El^xPb$?tp${5l-;=k#UdU>}YUfnCX6MRqv`OT~r&f zJ5MQ#k}9l8Ns^$F-M4mqTQ^*#t@OFJ{iP}&x^s$nr-RAPB=HO!5kBJo{>@8FPa;_L z|M=3=m0AzE%L;x?07}xxh`30e05W*!D3|~(*g0@uVtkeym@#lcq#~|R>gXbok#Wyw z&t#P%*8Dsj6gxaQ1@B(JSR@hT|JskY9DfuLFt)a4=CFFZTQf)!6eD!k3|Coer3!y@ z0`}Iw_o^(l73z-b4;L^f0y9MiQNUoF@vOJ*7Dr3>Xvj8TuRAL=gbNfn+{r2-i^ z)+&|!(m_(+$YX?L*7syy%9e_sJBb-Jm<|qfOYD|&SiAIK_^yui!NPN!GI%FY&(hnNh(fhl zNt}X$g5mOvw>LMlrRo<0QKIn$^~Q6hI7648eMzH3Eo;GJ(e`BMqn!ObiBy1CEO zxl0HBo%AAJtUmcaevuu&`!DCvLsc3Q^A;1mUy5L)s$$`CRnz}Ql3NxG*tjRk%;1a3 z9|7}jR=$oMxE&MSj&UEX1Gz+8nPyQ9m9Er$x}N0*6r$70CONuTxrtI$#J}KcjCd z{I?^N%k{h-!uzHbnV&m-vv?AllG6+SHZwZ|9@ZQ!KHMNc$8}l=!LZAx+I26U8<^;u z*?%?6Z|;l}w^&YTogkGz z0}A)!H&?6J1@#oR-IA+j>XJkW8m8|I?{#0Uz*9bTf)zo5{ksGfK(B^QdP6}H!Or=d z&1JHlYbm*AK^TS3E+K5T-I)N9mX@T1PjO73PNVWJx)lhz~imosT{AX3l@IV_A|ar z0IQy;jzI?kWS!vFFqGbGd~lFYot&R6h z)Pm?*n;i^+;i;XvhZ9fUXVcF*3c$;j5ChDKx7y zD+g3Og_wqlkXzR;DAzX8FOw{9K6RpdG50B;tu#ykYnQ7372I4Tji$5=tHBBww5VQV ztnF}8%dE@A<7AxVDNs9B?V=^WRG>rj-1zy*oVmlKW+8X9<*vwPnewz(5q@F(teXdc|ssF((3B` z++c$s+-Ngbnx2-%Z!X6w%~Ao_8BE0G%R}v)Z`8$`jb-{qELRLQ)+Qj48WG1$i-UY#!e)GZvaAT9oN?Q<7_7+b3c z2843p`6imb?x&;~Jg$n@;MyYLjKo?A?TUw>jdf>-<1DRLSP?$2WFJ+i?s=>GJPS7? z-x-|pqwMbaCM<6O?n#Ka_bxS{k=E1DNc156P1CHl9{z(WHH7AcKhUAQyhKQ22SGb1nu3p}Bv3GiA14Y3s8?Bg2C znL;QrXfU)h1Z{XYtzy=;CL6rUBto57g1v6uZ$~~rO$d=flivSy@hwrI296s01ou1( zV_6+`tNC_gtD}RCv`2*TE4LkF5=jhi{1Jk9PI`m95|HrQFOM=V_i-MpMt~M$ttEl= z7>YrY8!Tu z2QBPKGPC%ls;RFp>O8c=RI^h>bMs}6{5l6{ZR1k$!SY99wg`}R-o^C6y2y^!Mr|xT z*1)+VBv`^YhI3~qz_BXNseR6J4O5`7{;5~}8JsJ*Ay^LFflBy^YW3^`dPaAupMzia zJ9yEFZuNk`UlwBoEW=MpRhae8!3&hzP^M8Za@z>H(n;IqvBbnXTO0LOWcwD`6peK? zBfoBcVu5t=QfNbUrRv3vUlPQP(`?d6_RH5zm!K)k<%%`k99kXV@zaoyYCgBxOb}Q&h8gb=HX>>Fso3x>+X`9(_L^vaclaf?Y8%2C;xuK6p z**C?f%A7T1+s&rV>FF_(pt#*9afyi_c*n0esLgh$%j6+ZN$Oe(k?Jo!7PSHzu+*M} zy$nl9$dblPe8^vw-&zU>@_Q8nO{se@1{3E3PP%+a8#nOB>EB+hN>IZb!;=_#--Y%( zQn326PI{B>%Cll_E$!Gtces2-lZsgEvOg<=m7tLrMWpuCc%@Ii1C>G5YTiT()tk~m zJZWz=(K-Clxl7u!2|u-j7?+*sx0_+c^HrpY*bhq|pfhH|yYGHQ{QCKJ9jpFM&^tSX ztD1F~ObGkyS|+=vE7JuJlV;U1jIVN~-W~xbL*P7F1Z%jJcnKXc;xzOXw0az^?eMTu zH7#59<);dclCVcR1{LYtQq8s?p>^I`qlVJaVgJ;xqd|qJVvK@X2_EebiQ;yz=p;71 zCtnzOuS{D2qiiNTOCu`m^QrZCigwz~cOw|VXYuzF?4Y%_o}t0uGVo~`1&hB5C^$Bb z^pm&KUQvPEOLh!pDC@GPG8JPWO&vODEJw*w1dUEy?PS7xvr4Wdm%1+;$x?YYa;K!D zVQ64#5RjhCwZ3R^Uj>@}0@=Yh*834b^Pm?$K`m-@r&C{yjsypf7y0#Q`37U8`{?J6 z&{etPYU^CC`btd0Uho1KgZ-@49W^kf>%OxMq@dZE@O{~>Qdj6E+{$?R9ON6UdxRcT z?Kk+Fkzng!AmAyG{0-v6&1G;H5~1#?($i;(xP?#ryh#Akys@X5vvvIs`Z_PCC3!_pRIA$$f z;N?Gsj-`G>dhxYxVl9YA9UDd^DcstoC*K-k=H{9~J^3^|kE%GnM6Qyq;QQAoNSKx< zPP6L21$f*Jk0lO5$Zn9=f*elGY^nw}js~{L#iV{DwI!jJJQWf(kmpEGr6>JOY_pWY ziuBGXrfAh{qDng=F~F~&@^`j+^%^}Ys_(8DQbPO6{{*>fS&mts3u(J%8GtgSnpLjH zdeJgE%HAvk@+-5;InR>Z1qMKO%8vc107vpDleo@)dtf4EjrNIPuflOPKsGJRX+8`* zAitp^t@lSTXP=$64AS{L@|!IVKBa-Cq(0uzXK`a^C(jFkoYz>GvQivQMdfzJji5VA zl!zXL%s_hWZEqnc1|9D^t_u4GC6nmCf2=VXH6yKy-<4swjmDzGzBVs4E|5v0BE%Ta z=Bbg{6X{wQpPlUkfNHD@dA^b&V<~G?+4SQTon~Bls4xd4B(4P3d06;Q!E8bKIR4w) zJZIi?sf@iXkP-m*;`dP>Rvq@I0?~cSut#k^L~|BMjU;3}MBaNGE(B!4DPzt)eo{1V zsF*V<%-i^Rw_fRYRAA#TZhSB)w#``O36+&ALqSN>B8+)a1!VGvR7atQbPuGIS8h@ zK&J2%uaswG%1--9LacY_BqjaK?n}S#U*~| zgiPY=UQjjRTUKKNE!U6Rg9alRUbovpxkzF{Zih8GCv*UJpurG0F*7{!M|)$^=IZ;5 zsq#+dS8IIdBpb9IecLvS*nL&|w5WT7R_k-h*9Sn0RwslWLNBoRXr6_IS(dxcy;qW; zKIq-7`ajNp`h(4ewH#x-S4fzAll@hvrSx>TY)bUO9Ao^?GvdqG)JP6akK=8JwM-}% zoaPk}WYj^awg6Z)1;Ww=z+22s>)XDEfq{2jtEY$AoVA%p17-L>&R=DUgUr&v$jA;c z6ci6aMPW3YXq&QxYQcfBjIBA#QRa5D?^h>(;51|(FuJ|@Y7O7RsNYIOmm(uMy!7<4 zjjUWG0eds8P#(tU5_#ahH%RZ_!XW?EpLAgIjXaP3Asrx~G5HU@TZP#`T?yD#@23r0 zVU8$Nln&$X6Di~45o2BXU|t$pP)>_uS6*CQYg~@qIy!)5s}rZ8penNRr7sP;LVge1 z&4#9>Zzr7~0jVohqn@rc6F0w*Cf=j3MnR2Y2c4HsHlPjU3sE zEMqX22|C-JWAoVTv{{}oXAE8S4u-djGy7S>1q$HP9R1=VosH7y61E!7|%Z(zANcBQI3->dMpQonv z_4r}ya+VtIQQ28mw-$WzW?nA73TR8%?v+~Cr;1M3YW{#ora~D?NUDab zdlTW0avJAUh zJnX_S$xO2ob12vDAb{*J-uQVVWI%sMs@?)OuG58;pU z4(2=rdi@!I|39_5nK%xt^9bFT22qvY$)vccPvc!KOM~W91&sHHN418BRePup9lJNJ zYaC^Y6qr4)qG5yfL5-@{yJ@1a1n(ZDU6}>2nEfSA;Fa|Q{3Z{#(7l~X%=G>ugX|WN zP$>LcNloxsI7g$^Rl2s5MViqE@Dr2DlP@qjP>FA$U860no7scBw%3rvDY|M*wsNF% zb5V!!UNpGk;Uz$UUI@c~Dh4!lyv*1kkMG1y7xJ4VG*%sjw#eH0>Zct;t;8)n+NR1; zZIINy`ur)7w~2Y&_OAm1gzxi<&xySwukXjDP8N+mrR-VJB0k*EWhuRg-6` zjJA(~F+w(fxUt*nm+2*FP*b25;R~E-m`xqg~6jJ zUl1Q_7RmvHT1=P)y)z9P$8Mw0 z`)iY`W8!zap#Xvr#{nd{$A@4q7ZMB;!_P9HNYuT`eol<`9Im!-fHRZ-hD1!(^Md?c z{;{V`yj4`-V%zG4lB&^K=QMlvH#yEJxC}UT%JF22vV1kkDr*Rf_(#MgIWNbYr-@n8 zRwy>#o(9l@_29^|TA5XmDId?nwt2ojq z!tuw{wZN9OF+3TAU6_3RG|H+!gB!iU>dEW<*;`OHe5%MO_c|fFHP)i6tb|^_F*fcE z4E*H}nD+g(DDyu5LgA_xiaJYvAdKU=)pmQojy?Ume_oh#3V9h;osuOM{PVM@*;E80 zVq^nQc7*~wlPDwO`OkwJu2Fip`1P(Uod4tBXS|JQ9(=%KRGhDN_=#@8WwNFVz(>-Z z4oBQ1_`l;PD6NWJZvrU+^8d&vZ#NdyX%Z+7OtfP{as91nkG zd+O0y)el1SyE*U8{g8=IT?ZHwP&477PVDi3vjM%NlQ2dEAfyljED`KzHfwTDDMMLu z_Q^xz*yDo*r5M*((eL{6aV}1t_sw)aP%Dw9eVDN97?wmBeH6tshyi3vA{k+?ndp8Z zLXe9+5sgg&KAUV9pmPpC>*eM1(+N4V^MM(7y*ItKlU*j$_;;w-VU#H9Kj?xTw7;PX zu0K`8ka!&`FYvfp(i6!Qs}uyJXws|9ZN&&lT)~W{D8_cs)z<;p$RrntPrGG!@0%|K z3&5b%3o2$^PV1HFE}5}LC>^k}Ueygx0Sssdq}6`|n|~8w|Kbb2Z^h^WD}?o2spr-J zd6+aK@cdKJ4G9T&pH}FSN{c^j3&$DF7HE@icPh@coPLMRA9XZ4bqTC>2}S+VKHqh*UXUMU^U)Dz@Z;)~|Xk4m22axj(09rs!h z9fFs2`y*x_%YXaQX_N)tZb!VPlbBZWvFmR<=|uV{)T-?ntMPMZoz!;)i(rY3zGMQZ zJ*a3Zn!z_hBfnogSD1hPkh;GOw5aKM-mU{3QFE5iuSETVk&YN+9jBuu>7!-tq>6AQ zt!r|RkvPiH{ZAF+$2UMGls^XWZJ?Y(Mk!yd&|O$=&Pb<nYypUxayXAU}$0d|BiSy!kYUa*%=yQEQgBvk?6U#4b}@3uJC6O z=)|C2td0Xb7E7goJR@kZ%z*~$Fpkdy=$Dd)W8*Fm59Y662sl0MZ_jgA#=$iIPN0qX z?E9HnAXB5=>q-i+HhZW+3~8P2*E*2-JkY3@fw~OLy0hth*V);b zq|;!HI9sM^(jU$ZnoA2)l!BteB~EnvQs*zu4<}s!PMTJ+Q!`KEOJCpa;o;LJhaD3g zWlG*ISWZ@lDL<$zvwOGiUvs(apcS)PX%Psy)yYuH3otLn|X!W#vgr*5)#2gFKXqkoZQeOt@^S=Kq*O1?q(;bzjaVr zGP2Ov&7>}^ZRR9GsHQ>k3GE7E*O%YiFeN@^D@{qI1^xiojXW zMbHfc18SwaHbkXVuw14({=|0TAaX51-#;+n75Za_bEM+TZIK*wU|P}wTtI3;;0~A@ z%Nb9pmO7UO3SDB{PrK)PsEuws721t64{Zi|0&1hIm{sG!>~LFoZE>dkvgx3Slf#DQ z;311TeSG_CC&jmKvGGm+omN;%QWh&|pgB@uRu0c&3)_EjM_tTdas>aK@A4)781Kni z7WP;zXeVV0Jkql>B+vp;uj$+cr5WSWhOPHIfgRL4a37qq1+q#6uzN z8CZ~FZ<^5nR+pj4N`=;nU;%Llkm@q<@dbYZ3X9k*NilKG5c1&v!2bDG)_?pB_HX6? z5A0u2j*(#l?9*kA{w2;I$u31@tJl$5^)l$0={SsFp=gLXnZYJS+BR_;0hG3>_;(-_ z5gxm{nTOzo>Hy^94C9EWE+KdDm4pH2Ps5Vh@YsqhRI(k0L{QK&x}dLC+`qCy(0$)-zB|I6yfM(4VNrKM|VZ{O;E%Q2|Sr&Lc|x4 z6R$I6=G4Sdf9me9ouuLaLeTShgc@A(6=0P?$ZjY0!`VHmV`)39zTR?8>I}H;L3BMP zLRuomn_t`9bsW9iuf#^|0X?s)Vtj2n#{J79m?AVdxD0CY$U@Azk@Pu?Vkc7|C`He@)J&%=7jR+8=rZF|pqWa_-BWu~0YP$pF#pXe-d7*B1oL8$~%dT1!k=2)pz zr!C>(dHXmUr|Ve>#fR)CYP(oUFHE#vT|d+cjZf`07(6rWijaJN9C_sl&fA1keGH0F&sxY{jH} z2pLH`L|vh(Z=K~X;Ty(Mxf7ZU)kG4YlQ8PB z)cH1&)c)=r9muSLf?q_OEq%i|i$SmP>j&PCn;Sil?f~uSoz1HH_3332{yc}bcaMHA?Xa{>pQf+Kkc@xmCz*4Pp zjWNPw<%fdE(7OR%44Mk|u2MSzzJ?C*g`B|yivSIrJ`XH{u*d_8fcZa21W_zpv>@e> zxHfM-(sGX3@9*zdp1`GA1hDyE06srcZCmF7_=y*jfK!0l5y}4X-1>Y4h}2<(##o5{ z#T7X6WWD_!cXxhtTyC=BhxP{!}QR)5k>0YcjytH}dv! zv5id2D3p>SJ+_<{k(5ZWve`;iV!rZ~;{q6n$D(~k|L1Bbz^zkyJd%P-wW9Dh3Vxt8 zSir4dzV@ARJO=kX>mAng-XfN`%7R-l(-@_4C{2TUte;f zL7I}hnMQx#0f+F|D5V#ol1+2+6gG*bcsNBf{4A&Io+_)*p{SLrgPQIoj-D|xxo9_x z>DW8{BsA(z6#R}1i&meGP%sYfV=*!o7-PtyHM<`@`WQjRi?f_Vbt7X}J@cJ6?`n5j zRXkXLws?{{$9bTdMfcM4{%#5~vRu`x7b2Iz1)G_8$n3IebCga;(~!2wKd-Y)&G=of znZlZJetA5*eUu?$dhgnY!G^u$)a4}|ypgf+7atb&c-5#k-zA(T26JCFtA1mL<35Rv zGUfTDg5;|Be(g~*6;t`tj}t{op&)jv6rD&FBT5>3tD4tj&( z(0R{3*>iNcq^b8`xxShHVcIJR5F!}s&mbw*>X)iX*Fq^= z^@XXg=O!d<4PB9C`?U4}`Abd$ls)Eng{}oydQv?X!0LO_LI3TWve^i~%i)5vyZLmD z39@sfiXBLjn4-?campNbDuYjIF zb?e36yqJHYQJFbZl3&ckVi`C+@}ZVJLB+6VHx^Np;kGfce`+K7XilFVBS1PTa5r0R z_MI%F^S-b`9BSU&d^s*$Hgetz2gV;-$*L4?QCk3riC@jk&0F55iQ|;1jR;SWjqpd9 zPuRy5uDAU|F34~*1o3WHS*m2>c>yqp2ddFG%iV$wnRfWpFyc>~G>Smsv63(@!z-L+ z9lgn#@;dj=3P6fP5mA_4ZL&%cuWQ`18XZ{VAqMI)KiB{XgJ`o0YNh$F3EzyfHprnEAhd`hpNwsULU-lQ{;xe z$>GL@fW^?Yg=w>`I+pRrkh3NdyUK^q=;sit@8VY|XAY7Y zhKgMd#=_6RV2xKaGsWY(-&rKjxI(xI$PE2r+B_y$l{X)=eU?dpx*R;g$nx{1Vx92U zmU%BYA^J!RZ+P|@#{(|1;K^p^sL#wQQeapYVDo^=*DBG@f^DN1Kk?Er0rp{7(dP3@ z?Toncv(NIytg0dq=qvLZ^FJ_}!VC|un@@DRsL=zh(Qjln6AtlP%+$axgD{A4&+5W; zzs+$!@=egxDQSmIqWklpG$&SieiW0CWVl^R{vR1aOpdX3UpV$33Bo}6)A9(5`WSs2 zQ0{iTQ@0X-2#Ga@urxKtzjA|L#SI{pcD0@ntL#Y>D22eIWJ)_39KKTF+*uyw2|nU#wt=nNd@H}-4;Vh}r|@kX>ADK+GTBk86 zWqv1&MAVxFzY%8r(tHf*MoVQOPR_WWvAZDVk{DC?fb8k5T+bNaEag*(p^QIE2W2%m z5R`yq@mG7HUZx_F-01LLd|H;I%#)#}b)g2*T^!l4{PBb>owMYY&`=b!&r&NzLvDq? zLEZfAz;S)#O8s7oRrQp$H1wfqjB2gdk&ZSmO&o=cx4#jyV(2E zkE!2efCOP<#m{e_QlcI4R`GXeK>b+A73F9q(ju+q0sa0=ivemo8oT@AY};W)!j;ijaHpG6rjHyi%iwu^!cDz_B9lP>LV0NE|;8&fGvmP zW|ON4zuZjRcbTL|$sr|)rnedU#04}-;ekPWJ zvT02W3D^Bu2tcXuvwVK>#?kxB5dKFABCRdoR}bkch7qv}b57zc^Zn&UOM6-_7`~=4 z%c>m4REV=jZJKhZ@yykDr~2M3Dt@G16@|YqTdn5N_5~QEd;L;1_iB0uVP*$WX0;f5 zKeh|xr8?mxSfC9iqw!)diCdS z>%{}5gHtx+Wn}ALnFiA6+;MdH06frTRX*SF3d-jaxC>4?LbV`~TW=p}~y`2!??(7#FCoo?Utsm&_U48b5 zuoAzZKd-G4v}Rv)=y9I5M~laY2Lb{C z5D@$8`#H_7MI8Av{a-MF$HUm@Z#DCBr4NGZ2wmK7(sz{b(@J}12w-ys!J}@8DRT5X z^|H^K$4@k2s=u9fVf5|sZ=Tju1n?mJ|A3SLR+_iRE#O;HqXX~M!Xq?d9nXVLk}(38 zbT8JY3p#*58w|rOuW~48v5O^^roc0UO;(XBLt=g{E~OoZ*O21 zZ~NB*T{c*_>_&3%f+&YC`y;$IxAJnJfJFl6${*pWST`OvgKc4)z-%C$z0hYE`< z##PHeyZQ8~D3DxgVsR{PEC5+H*d|herD=a-+KAd@QIS-R08EbQ{lkx#G&}(DO1U5J?^2ML zxgALMCvzA5VPw^&!|Q)J8dA#&KGcxaGeB4V<5GU_8?(-A2=8J&M8>2!t5(17_~%iR zJ|?Doe8N`Zv6MT{1FlAxoR+pysb6guE^Hl)iGplHbvJ(peq)?S$)grX`)cQ>T zkY_wE`9P8uKkeHWipQzpb*%T$vjT&pnTPdXOm_9JtIr{?EPxc3fU8`Z;btR59`Gwc zzAxx25W$SWaf|FXt<|TZeiephJUJ;q?E~z3c=%dvx=qz>9x>hHkDuHIhahm;;65VU zURTaDCgiTf!QSq&TV_hv_&A=82_WQUhxt0Y>%F2w2KBp}p*a{$+tpSlfQYlPI36c6 z&v;!$Q%Uw_fi18yDjZ5c@&D?BXK`^@l$H3GAdrv$gJHTYeT>VbRh?S7Ysul}@ide+ z@Sm2i2b)vY`(J1`B6zmlgW$0jHu_3dvP)eoPAf3`x}biu)~pK5vtV93j1jUgQ7L!{ zEU@hC8;1&B)8S(o^KG7Ph8O!u<5{D*R{alcZb^xL+t&u$MPZW$VZg>(y#FO+xet84 zLld}jU_9u3scsV(TKQX2w^UuCiomRM&hR$#nb{=YN&CiZJ%v(H^vS$o6_~(1gf3)j ze8q^0aD%x6dY)?3qY$vqe&^zFG5RO>vuE2M&^cV3%_RX=9RqexefoD=X4kF~b*2#2 zpCk~qCL1&i2@g9uyw<$A#li6q){1@)g_#Mzia1Cla&izx+xeLGgn)q8=2Q9BU<}`~ zV;$TFCSY-s2tUQ>%h@fa(W(Z9HgT_>N$4rO$g+Q!>Hzc(!+{XBlrDxA7zOzV`S?o4 zs=vJa8+ahFV(vkHaqK=^q_m#Dy8`jFr(nO){oZFJxx%m!Fw+NdjKH>0WYr&pJc#mi zI!nNLXS@Z3Bd3P!^{=bdlMZfbLBUeFGz);0vJ~PlYO>HiW)-oU&Q~y4qnNG+(vSQ% z{{9VBdc$?J6?E=#sT$=f1-&Jz2mOS~1ZemHKpZ{{$g1yPl1(~WZdH{7b@OwjDubxM zJRR@j*b}qCpk@ADiRNz)CLeZs^&H@_VQrs;=apx;Euqky7*05uD^`BN&d$r{H2_4W zy!kj4-j5lsIyK#lh7eG8nN|&-s<;Qqc}6y9#_8LsFn9_%WN`VjZ8N03S@cX~P?OZN zpDmp+bNJl2CH+G(`mn|Qh|1>VWjPl(xuG~SE>}dj?ds}k$H8b~FjoUCj|O%v7z8x1 z4FGha@zD}1aIyZ5QP;{=+~duZq2iHGH(nq?M%7xDetS4ZiSufqxe#6_#|RlHG=`wZ zW_7wZ>IAQ~GAnC`6bkx3lxIETD{)>N>T66LA&5#PHr4)?lbB111xkp0*Z>CS96sY8s;W;w6PJS;*ae8gqfg8zjaj!g{lFFrmL$*<($b+Kv^5$7 zUbA^Okv@<{LC$nSUY0O6TQJoJ)~rga#ugx8fe0R3OOJNYfN8|u<>FCL5b1;MLl+8f zY&=_JCVW!7Qi^dJ0=y+$3!L_5Qp|fc5t_<6cJb$DAJaZ0Zi|+XBq7mndp9%Hjz;OI z2j=A*g_vLe&}@6{6qAL+OI0z1q$MKK1J-Fk^71;`+h2-`Cd9{|egCfS(AQwHY&7NX z=zlF8Sx;!Og4stg5P3=jT{OB*2~;}aA@mh~%+El3bxTbXx?_ow^4s+%Q$Sr?dHPSj ze(a7ONsH>pMqBr}Q|-|=$GTuEagYp+*u`sT>oI=i**(E1cpX@#ZIUfaOkE`dgMP%# z$d}}9^q!|396xwy8*{}i>C0zDY=2J65<~KIYXzi#sCv7jFo04P>~t)2RgA#B5K*v| zVg&RV*a$*hnRX@{?Gya9xE=!Ka0KwTorxVSzVyadDswXdhoa0b@>JBQpSs(df-QUg zgU#_uRnft;1YWIckuxHC;KBo`^}#%n)Cz7cA{h9Az(ZAm2-dr305SCxg2p;GI9};m zB8oB~Yf~<>saYD!;0Or#IlL^A8w_CW_uLNHw%nji-M&Lc7CHo{pQwqIh@A+Rn8;~z&!wKAojm)F>X-m%?}pw6P^D2PJa4r zLg*@mK|Q_@r|a67lURwG)9K?D3u%d(g}c-MW`**p^CMXM;rN=(qX9NI;(}&5S^%^rSik$`%^OOINJK=$)3dYi z=;-m05nkWBcALm+fu{?cRa!bfJRNAdgRc$UtNZ-dx8?c$(B2{UojlM%|4L%g$&rDU zuc^%nXhxD^F)p?f`hhTSfS#4dYZ0fqA87-!?(zx-f?5r;{uT`NAbtxwFJ~cW{6S5R zTq_J{I*N})1&kd3a-^W$e_7I2s@CqOpsz4vrYDs67Jm4FY^vL{R{O()F&n)1kcB5Y z)vsXOsvuR^!~#3)R_)}iMmU4=7&|{Uzlc?pK0}y)rca&8mqMvfVD>|-WE?22dhS4d zDExuUU}VB~c`+#>F6{cX7|Kqcz_}9ww}FntXCA0PfnLQRjgosalNKOTtg~-uApnRl6`{&KL;Mp-Gpru7RiN z;Iu$)TsHcrVMNV2JlC_46dmPE$=AgcSDI{rdVWzY*gs`DzRm;y|(C zo%_#$N@BgI2N_JbK)4c77nN=3j(l1*TcO(ldNwGiImyV^ai&9|d^Az8=(3#$AMQ?@ z%*PnOxE%^BuW7c^tsKDzFUp}TW4OLp6*6g=`giy(;g_t|0$QdaqH>Hf;@ZMWuPpiX z0@PA7i4eS?VQd@#L)6-G(z%R?7i3_D;Cg@84<5U1^OoO!lT_@}$WxE8^yk&5MvSd) z$b>xAj0;h~=oK=HGJv5YWjMxAMw31rK1d;|P4#L+i8(^xr5~CxY=y%VBN*PrWYEqdD7ZE{ zIyy1I3f5S%vQCspw9k~Xe6T->LPL}XDZxjiwud#Firrq z|1TgWhNxnn>H)EN5bzcXXZAIyu0C9m3S)fWnNp=0@0Q87`&S)s`3`qhsPJPDGw z!%q{%UGBCL5Afvn%w2VLu(P_b4BLr;d6`zm?WL=jPXm0@CAhRNJWe7<`iI2c@>9Um z%4~%5{GS)&1){O^eHVa0F<$O%gq+}73{pYA=XJw)TvR0Y^n9GpA$BmXmc6veLwhg#(K67rdllNz&uG@&%x za`Cy~?6Y|>l?YC@y^0r#`Ku^@2@(%x!Z(;JSyMkido94!GhIp%wX2<*6@c0#0P1ME z0B2G4H!;KlMzgCg0b5XndU%A&G;a$$h*S2!=XMn8PmAz^CA~9#f<3iI0_7YWpTeA6vF+Q@^sRc8yzj^!KmKKbQ zMH3Pty&PzX2>&Hq3iiEZa@ip+qC+(ygXS>yJsa48gMfelgj1P6-__*gbk|6{ogH6f zKajBh`F79y*#;%R9A6=fZdw;J#P30WGbvc**@`B^WjA_PG{xf>tSMg7QcqFTF2iUP zfzt~w=MSJTep4xrRD982tF1v8ad*vBP)s?}hR+{9U=e_~-oJ;7y@s?ql_!a)_=>m@SRsQ7BxE~B{Q#2YyR z2QAl?!<1TSCh`o8Z5CQj^`sZg$$JdQ+)2qtPYQTnVF`df-&8Sy#!bxQmfwEDm*UsHS;CBKNJkF!%`wtG+0al^HAlL=Yj$u zH00;6GX3UQMz0^W5stO1)OQdKfr=r0wL=8pq)e1>0M&zjnuh5pn7_wuw}eefYS{Vq z4JqmS{QP`kK6gFP)A~@}Y3+YcIHQqYspub5t!nkJToI8Q&+;cCEGnGH{)!05 zdG#3ss0%gdaNgjie*f?eL80A+whIipNlP1MOG`-<2xw)@r)AOSt<z>$f?B@4XpTA){?PmxhZ`$e_L#R6d*OH&9F8Vx#Yk_VmJxA_ zWS&H%BwUorxHvM;s0SRX=`<=P?t`gCig>Qu)8PuGdR!Gl0wkD$PYN8xiCClZ9Af=? zNH*|h!f^}0&RPL#8Ga~CGO>ODAki7LFMzhUfxJRW%o{X=AbzE<46|tn^W68(6|;>F z7n(tLEke}!#zg}3S&kJZgUO@&i64r7n#P#C&z%DB$U~Y)2rs0XZ13(~0(|TZ7&2E& zv5x^zKZse~er~d9d{{oNjnTBOOSV+oQ~23GL=aqr3zAeyc)FzwBZ(cxYdhQhBn9tE z_k9@OSA>_<2$tH1H30XdLVSdbh@d6M=*%cOGJyK8tWnkli&JX1%!OD9&53~U1I**+ zt0!qCH;9OMM;1`7|Kl^pj11`B%`Ar|L1hM&w&GS?HXrB`A$IZ595hW^kCZ_0TmQa-(tKNj^m|2Cs+?V1q`=>Y*F8 z$PeFjts|Ax1~&GB#qi&$%wlqmk*LiF1byB@zXfw4e8R94G1__>|L)mvReSQ#JM=(0 zKxw6PKh&x@r5PTb{XO;h+T?LF{pMT4OXo~Y^|U~nw95Ffia#Nt2iP%w=OEp{PKx8T zl0P*S7f&3jX5Y8zXSV~7@eeV503xoukIO>z;p<~C?TeniBxWEbC%y-Q$)l!TXPy*A zI8*Qg2#WEOBAMQFFr(mBJT+w^C$|PyA5ZS)akO`^#7uZC6vq@9D4&3E#%K~yJ=B#i z_Z|Nx02+I_)&vuKQTsa+?rM;j@_5c|K_WImuK-&z-MLb;OZk@=MWX&&Jt zPs=&c&TFFMRlv6$rfiUeQ{948Kp~ItS;m~8d!>c){c8xmcBS?(rDLU%)yVm;tCPU* z(5&e2*1MBvAtOg3_r3@~P5i{z%HA&lr)3x3ZwH+|3ffr2W8aw_h)Cz{X0Yag-BxzKFW^)UZ zoktnze*^~scct5UHy~_M_a-AY`nAw@&joe`RVXlb_TsB#rHd} zOaV-TlH}6#nAvsg3GMLI#H6K;@}X&|2&kZIaSLtOO7@3!xRUS=^XFhWoRE-V9wxfsrA$Z@0@LtlYQ4$txhQ-i3_RcRrtLGGBrziJIeJ|h zog8aUsuywgzif%|uav*i!36k>mYnj+>$O)n8gL{8w2inef7w+OKa!iDUR0ceA~Q>; zg`M)rL}Xc#cuUa7BYt3hv2j^=hMo`>rLUDo54$+vdT#-@Z z9B=9u7t!dySRM`rMOnhh91;0514O*KH!i`+AA-?iw43Z}kMqb{0Q`%K3r5SM67jH+ zk#!Fag4L8XjEo0BY`rh>w(rx^{^_N8FAgX!*Awb8jQ$vzHm3n%5hmjW#ECZpJr|uH zxHOCNVxIgv=-@Q}CZs_N=e1?#g3dNTp#hi*WRKmO{U%xZ4<89VvP>O(B(17?$npRD zME;+;EgxWmYM}lZ=-ID_h&#F|G1Z4gyjfz(Vxa?j7Qq;r=&-PFXJ-MjwA2@1kV<}A zI0YibrtwyHvLsuts9Zp_^+RpLq*afH!i?eF(@gMZL=+03leD%mLyB-P`;>Rh`jC;4 zH>QDT7W{hv#>XLEea2`5X63$U^#CRj*hqkmQv9<{`p>@<*+~!%7oj}@(_%)g*-y-~X3d(J6$Ux?FOD-|`MtFwIC8ZY zK^l+o>i;`3WXQvcBLbI=d4G9(B@&7t-Mvc&w*oU|N)telM1h^e#j0l%<0)|>zU?>84x*hrL5Q)zBvis1zICn-g+dORI(Og)1zBS^?O(!9Rt#SpX&+-YbM zyR%cIcg+X4{cWkC3ar~d$rMOX&vz34AERoZCT09@F)5df>xnPUeGfp^3NsuU=|H9t zh-g#2f|a8pzWUN|*aQF7{Wtu?4MCbkrUhE9ZJr%{hHU1ILst+j6mdp7nnY1j*M zh#zx>i`+`*G#^1kg37{>fu26*hM}yjZBcZz^3$hJp=+m>R^sr>FEDH8(AqxZ6zXVQ z3pm~>A9Plpd0TOhHN{XGdr^=FGH^w_C5%-xXFSLl}(-qPrSNX5^x@ES-xL{7lsexB%MCK_h!lPXrrV-t_czc6K)O z+MXL?h1%iwxgVcK zFxmEl;~O2mnv7VYzDLR{R_bRa!Bada2LS~)HuAPck{WvNnksmZ{?7HH8GCv^wCKa- z-A3ZvVn~A;aGLq96=MLEeh%;-2Fg9iQp!nQc||SVdzZ*({hTggddGu-BFc*@|9AZ} zXHWcF68ca3BQ`dD2pRBQJiR@4248`SkF znFKA(1#YM?`)gx^k7Vr|=8t>X0cCrjO8=23_J^9J~LG;QihA^U%&qAvkYim%fbQbP#>gcy! zdURiYBqgg=QU&(jEx|;YL9JuF|K?2r#4`p8Zkw2l<*08(sr~#Lii8NDtt5(X?>Ce1 z+N(@sq*r$E6bsJaA-Z_3iKi@=#8sEM5z@Z)J+=tsaiQ z^x^uUvaMdqU`j(cNz=1unRH3a!=STiQ(79_O3zNnn2dW}t=FWoeX$$jIFu>=Mp>%9 zV<^==`^xi%YB||mW;dTD5zrh?{RP;a2Ych_;epbqKQgA z%~N5Y_$fryq3j1FDg0j0+yu&)6|H!it3mUebloTkkMWya=U$=J3)fKzWBBYs>fUzg zytrs1`0#pW1C%*dbaF&zJV!kH^AQO zkf}|Q6Cj=ixfZ6%r%qMx7!0M$T-L03@%by{UTGGLD~#U(o=t>Tgz~oiXTMt2p7)Tc zpKvsn$3L2WxRxaIRDhmDBf5&Kk@aRho2SCiNxNaVC&Aq>l`;$g# zI%UG58ub3IG44v=-rk0;(R_6tAG_+3S&jQ`IK^qS*0;ln}0q$>0`A8yjW zRpXfLAUB)wru)a9It2<^70>^sVEyn``=$cg&3cb}Vl@jC`C~0?4qh<|&Np+6R*Zux zP@?>5t*o+TiKj1NVirqmWV?+I!gQ{$+t-?(6- zqJ!;%j4@n%+sM`*p9sC#=N`f4)bgUtI248pG4IglfYqg#NA!KGn@ET5t*{5GXeiCN<&N*jd9|B!RI z2JOrT@pa0m^&(Hbn_cN}ECMl*#J%?)5 zS?fNIVm#J@PODW=`iPEJ+|aNcu~c9;_YLy&)oxvxpPH)GYO6$U5esdKMt-MN>Vr#E z`w85pU5_==0jJxdD*)u;V4xxcszn;)*y-T_oBHps(4m*Mf(F3 z;_d~4=EKToRP^iAQINl(=KANx)xgOD7!IA+CEyt2;v(yzU|+ll-6MbGKk*fOZ@56E zjbr(azLki$PfIW%eSMio@x@!dw<*_f+Y+Ge+M;nSzM@6$d(L~x`Q&C~bxjqE2gwzm z;<3)96BZlMH>yAwsLrO|mpWzN)BZU1(X;XS3!ZF^vY(|RxzQ0fZt4oYo>zq;Fp3ZB zKBy!qVVK*yeSU3IJFaLRkdaM1hd`%YuAFx;HnoIrS5-lj(%MIa2`PFt}^3@}OW$ zR|3p<7TK436=%t>3P<_Iv&dx2QVX}Y-?7IC%lfv6iQHqtZ;+LYGV72Ng0Bx=`^PP}Clk1u`~0&xSvJY1PKcJ4War_aZ)V((>qlIVY@C+kmzKNR&) zEwr$wz&@ALjk7Um1&)f_F96iQ?7Qkghe;J!O(1v`k#` z7~AF3Y>)AH=(USi&g=bpvbo@b&pZPKWupSTYoJIVG(a`ceW?0|y2TpGB#JWPO+e62 z=gHRM&V#1d8v`k4v$cgI^?v-}xGoW&Ei(h=?AtmhEWpc3a^HPrZY;Jk`@wC#e-$!G zW!$BhheN-wDfm&S?q~Vi*Gg}ZFi>Z>QXbhMVrS~EB+bv{jzDX9-)w3U)b+o>kct?v z0tY+(IcdBv@6Nv-t3eW3LO-wYE~dc}k!pL-4kNMi6|$@(0ViunY2b>ysatn3aw&Z< z6$u*B3O|5Y#k*FL4^#F!+Uiu&o^&LmF-L;Zn>(>kL`YIh*v{LgXDV^?bd_1tzRC>S zEujuw7`7#0&&d?K>-DF^Av0Qdf?%rih7`rQvAcNk?vSa3L)3eb`pDy`#ry}Crz2B` zcSuP2ty_hWoX02I-&)or{l_!5+uUsb2OFmI8_Cn$!RWRi4hcA)1-d^9)>MhF z-kygWeTQIg?-JCq)` zU~hJ1S_BdhS6kTzA!DVd$>WXrFGM4cgT&9Zu0MX%zG$(@Frd$-B^Jfq+m)1LWl&|6 z^rOs2wr!}2{sS^2lcVO<(#IDr&!S_+kM%0P#YcA{6cY74bLNbemKFyGhpMV7w43Uy z&{X;0s3gA{>>r;|(j3&@pAZ!q&m+i-$)m<{&og13BL!>#5qF}lT4-?(DRg$lmH9=m zDujmDWK|MpM?bl76^xE(11P8@vm)){K0)Gr#~0{X5*GY3ImARi6^qJ8hyY0|APMtv zT{#UFvKT8b8n>3`4?i&+W?B7K15wKxWd{0w4aiMh4M%?%U!TFTNUB1K_4QCC;Vs@1 zUEQL$cvsVox5Eq<;%&m)E{kD-^Y~SN>hL{!cSL)h;DbU2QI(4X6t#z**|K_+?>M)L zMla1m;RqB=s*t4`Mw#!AvZ1nA1bJfR+%R@IdgTnej+%Vni?hhWfR`AQbGaH#K=4xV zB6Gf$Ue;Yl4sPoCOOQNaH+O&bF0|oz0y8cVY|v!zw9fhF0RF`5?)!fML1)Gy8@ge^ zC-O~CDC5p+_K~g&hi+$AiPG|i-8m?Wiya}lWqDPV-+l*IS=g8|Z%Zn^ zI|+!Ae31YjY}I|gvQnUm?D>U22?D|+lK)x1Kg>}j?y6&;B0N>Z zwPdJmx)ZZdeQxLVnU)TAegD4hTqWQmfOGNbG_GjNvlmsyuO3ipr929cCcKpL&ii_2 zcq+Y=lT+E?0#AG^UxMuZx0V)sn<5M1kc-=Ul6BI{yaWhD9p|bEWh@n+GWPR3LP_nT zPh;-Ws!ExqhhD)F^v=9N&x9wMa9cIhZ%9Y7XoWxw*Z-aKr6)>CN`lwMFq73_fMYb< zWM~M4?qw(L&~Mjm>~85&N&Bys@mQ2rl*yq@bx`^7`4mvz?!FvB=--d|LNs29og^Zf zeRjinMd5yQRFugOJw@B{R;XX}FH3p^!cTv^N7VX7%KXYsRKU>@i>lN(JObr{zSzlV z^2sp#dGN*L0|ElZFIl1Zd^RX_5+G-Ne16uaCRnucyvN-$2oYwgJlZfiJ%-MSI5SSW z?=x!gt%7w&9y#QXYTroU*rELUVI`hA>@o52rpWHZRZV)M(@&o_-JUo5^ z{os+LDQg*?c$db9DT8jlF;pXIMz?YDuUvU^Uf!@vzGZ zTaE&69!SpFpUF=nyiATu_j%+20)f*~L~e%QN*{fb;iUYWLRX+j@Q#<`%E(cv8Z5l7 z6gvs@?hGHYfOe;aFU3SkM;!V#21`pxiMpHd#M|*D)ay32w4k3qt5%BMaUXsC zGmoVfv-|FWQ&&bX9{ye5&6&QX06%@9gY~rD9FN{&QwiK?t+=R2yWcCn(?-!Vv--$& z`GxlejCIjX2nYmhzq1^T`d0a%ZXN#eY3<>GHoTF)zhaw@N8?vdBnq-=mfJSuKS|?e zVXkSK4$+ofSa>&q9uc3wJ(ms*%{w|aB`hKG?5?lB^KxJ9Jtp5B_$-kdm(HIph@fw8 zXlrZRbgtr1m%`|aVKui}nYH?j!tEXz5HO~0QFiE!{sX$v-L`c+T1K@@7<`vD@^t`%L93MP#0vBgS=(op8Jp(K4#X_wZab^l zadCay1u6Z~+fmR+azVF+wQqdaHN4ZxdO@C-X*XB1Tw{NukDqLZO;!~bKFSZ>ON4@# zx6h!kUXy`CgE@i_Vk<(H!$sFTm~p$*OLKADhVT0B(+ycZ+^@yKm9D2hL?5W7kREjNH^z`%-$u<5JLN~>-Ii1%(1qE3^_cuQcw}l@r_0qTr{Mvf?>x9xm>m4k2 zU#!oJj8M}un$k0gn@J?RvM}GS_!=CoW!t&qi@o$}IQxe{=i5|?EtxnjjiT#DT45(9WUYoEMwd{8z>`HG*n`pFlZ^*Xm!*CVbHGUq2_23j2bzH&h=|`elQvpnT z&n=a*SC^`eESKK>a90qmVXM5RW_~S4NRZ##@1k>;h|kkc2qThX>vILmS27P1#V2Oj zQNSwU@wwuxG;AL0-&gk5w#@c?ru@H0>fWggQW3Gu6!o2I46V{FH*A+aTF)>P8)?v; zVJhcJVWZ~Lw5*yKsPXWb7JYP>v8Es|&zxmM-tOb@lSAo#<<>B5=RNeiTz~Rz&oe`% z7Mj;fM&9^n*=K%XtKU1^;}s=dbf|HWnwPbm8o%FJJXkp8Z_*MlI(RARcr7L-$V6|9 z6TLT2RBKQ#U94_?&#Jq(IpyD`RVNW>p|`D-LIzoKUxg1>VyBC{EjgWoNA>F;Wf`}a6Hi&k%kKDesrh{iJtSPw(MGXUNLwsy)p)4c+QPh-sU}&@#Bjvw z=I2#Av(QX1HwPe3^8Cf~XCa`(6VJlJBEv$1;&VG^u!oCuAg$_`w}-Z><3yx*CvM=P zx!C%L6y>INk6*p&Ih`)Si-KZX@hN+5qX!)(dqx{G1Gu819q}ln&(5KIYQn!~Z()(! zW70FXVNf_QV;gmRfh7C)bfZ@6^+=evw>dvPDnF{jA4`k5Tpcvs6hx{Y)kP()H$S*F z?tP(@Ma#9~^$o+ex}L15Zbc;lSI5#AoT_A0@|c3hwHiBqw>z2euk(oo3vk}G04_E! zN*$ez>Bj<9Z+2N}!E9yQtE>Ee3xU(UIcuOXxN=}tUvpT@?#e&f)tQ6ou{9ALEf=E` z+SS7&$20TwnzTl4Q|*hiz`PWh_!~zn!6|;hL_^4(A5G}7)3SNZ^}{V^mW}wJ;U|u& z95{}-HdBG=GKEA$tmq#1Q-ACPZPTGIxdzBwe)(d#!F5yhjljz0;ogFi)0U{m6YIjY zNg4HmaoJnKRH1=msC#K*3XO>0q9}v_pZ5bLG#^0w6eWjkG6#B~d&ULtfxV++abuwBj47@x(r)UNq zUacVZkJdHV<{Xua#1V|Ga_RGb)%E2$)-A)$`AaZPm|+4Aw_W-qOXK#$XsF;w#;vKJ zX`#)+B(yVrV8UkFnKB5iGl{CD5>9t5u717AQVwHWk;RZ3J{?vxuP0%PpO*HYElXLl zKVbNLDlwk2yr9$c-H6^gd2LHTFG z2ZE4JVx5D4BNy2zGRy3PzMA2n)0x{~W9t15myO@)WkXAZfzp?)`lNJF>=i4g zT}Ic;)?Tp%!P8ZfS)a>F{pmu&bBobSIcOQ%3|axPqb;IadZO5d(;Y%7*!J@(#?H>s z4#PXGE=%Ukjnhi1nyM=crRc0R`$tWK_1%TpDq7WVB!Z>8r|!@H*l>jDBo{1~yC2xr zKb2JTJ-I;F*0gpcXNES@q>;8YN%-J1YKC8QURBBLw;OUt^MzlIw_aRx03-xZ-6+hM zcM{J_a}V!y!C+&u7_L+i!d&_FxexP2ck_E^J^j`MIk2`zEM|(V-UogogmG)jXwFVrnAx&s7KCQ|Nj2Q*B^2Ap- z$l1M*gt68%=71Zz?2S{8uPYOm&rnp&6i)I~l1J{ex_|8Ibw8L=lRX*SgX(2>qhhzB z-Szp4l$56!f~JxGW&grvcekR?=U=vbfdK)GjEvw4JSbI5v=pX3%AM<#)1K2VPd3Vp z!C{N%$-_LKh(AQv+BK*4Wuf}6kT^%KaNIOTpCLzQ{lbsp-@GFPro+GDypP8irXN`Z+I8$wjdMHv9uU$FMVFYAkN-xC3l# zrh-R5EP~~5@nzz>A?yHoThikPx1R7oQ@W*JpR0tob!;>|idfX6dJXeqZW!5Yo?<(GYh6PEP(obsYpViRGm%{?P<1*(jbZ{O5G7>OrJ8smYQLuIT*vtJmQ<)B}JuGss~*+WcBOt-pMCQ#ni z_)<#S=?Ra)!9W?ZD6QA+oZCpTq;S3f4UpA%(Y7xEF4sDl3%sxt`iY4Ni_$@p;u7Po z20hMO39?D;U!9sZZbwH^%wqgO5q>PIJ>0PKxn`I_w;5Vpxr2@y|LBSZLts9HT3a z^fWxBN)h1D=AJHs=mUBA(Y2j!X_CT%f{*&(d&1ujwC8sUN-elZH=L__OeUWFA`({( zh1qTnIL2&}T<}b-FZ9mL0HM>rB27O+q$PyzknalCAH&6T)dsWvj;hs(5A!k+x}riC4ooUE7+U{!1ATTN zF?^3e^z3v#;9}`Kv$f;O8XfphH`{-rU@S$f zlubL4hF119{ajZ@;Go;rVBsjg==%CBwvWj}VCSvBM^=LW1C}oGQDh6NCM(G35;lz6 zo7~VS&v+M59=m*Dci2h3wYCyoC9Bd(N=#g|3wCb)x!@A{Sqfp+J|?gY@_pv8{qq(tS1S->JfB zF{9D3uk=4TI>eL+Uc|SlQ=6I5RLYJ~-AVHP>2*#WN|tcj8YNY`RRE6RcduMe32YN~ zb#%~wbXEQv#)zeCTh741@A?41MZu){i#>A%P2qWjS((LEelPq#?k!%@%@=YjuUu_L z%l~%buBfO!s0pw5fEPx8SiwRKWA%;f#||*Ra_$+ED}VMn61mcd(Hf2;}oBxK;i@Xw{{iBYvE@% z>SmH4OP3_@#ke9rJE~x;@ukgt`$*veb|RgWa=@h(PF=67{7S>NUF^36T^(x#T@oD+ zcj{BcW0|rRRpY{}l~h&TZB zVl13ioR^{Kl^aop%fE&rAN7g4@8`=|2?R%IFng{m1{X&D()1wRjF7s_C^(B1#gNT{5bXfu`Y`-{WDY*sMrzPf+{6%|#xg1~OB~z9gwmef?V95O9f7ks4~_ zE!n9&rJOhxUc#XdJ8Ky&{0YsOh1n$e0~gRO)IWAypYl7HoEX9v%t+Ui@E9H&>lr)O z>;V2hQ~*er)W4CknVfwQNrWKwgCEdSJ4@MD2y%1x=&*NK9h(E{bHx!x#cJ(BduC1w z*PQECa?=Y^9zQpqT`OF4-x+@YG$85NkNTFY8N4;`Gy}{CV-{LKlE-25T00y0t<~A) z_i96`h6(aYQEX9VfDm=t97Yy|rd~%J?D0Zb+hZ7d>mL|;M>PaO^HxqfC>%#8cyl>E zSxHHA4=V5$J^AYH{B0CqcGYys+TAziy7ik2?MO$LF^ly%!5(tm``Z#~+P=*@jfoB% z9gMun%H^!u8cqk6!n$P?P~6LR*#P4#?EzEkp6T>lUuRvaT#GlEQmlX@A+5@>cxodb zq>4}^!+qeMtCH{S+kOtY@_7E!JCl=>Kg@VkrM?|=PZ4(NZho0f1^`KJSm5~qe6ZSkz=r(>aeG%r3nni{6pYB`1=hk1d$aUf z4ug(lE#zpR`E)*joVst%(XZYx_(@^V?EUiuG3py^5(rlf?*NHHit!FOlqh~a+eTsi zWVpzmF42NTt9g(DrCGOzw)G=Nn5BAj}Le38b`Mb2b67#`_QuoccBMHSYnri0CH@fNz+O^TiW-Sgdj*#lRq!{X4#v?fjT||s%3EsM_iDsdA=_y*z;EJoTXF%Oh}e1t zaOz*)Jo04-6a0(kAsh)1V#s34xhlms%dVoM(u9F2pwZn)_4L*+*htvc*}m!$lg(l>#<#)*s|^g6*Rce zTYd@Ap#d9L#OxA^52Rw8z+ek%!?(x3z7v)vS~_T6tTtF;ZpuZf&k)*m-)o!j;`zG# zY&#}Ia@hl=^PVJW!|Sx5Jg1=%v*i>dhZXp933`eHeX?r%@E>E~RrhOa4}fcL4#IQt z4iLLzBJ3mt3HK|8aucf=!xVR4Bfk`fjh^Q0y)VC40PE$bpFy-f2QRq}e*^2*@m;FQ z90hT^PX3;gtJYI8rBDfiUr~4q_7#0|z$d~oirk3fwM=Z8_+C!<=L0L>XJ#(%xTa{w z#l@MhlN1ybIG#mCzRd_$IXx}SKQS&QIpQKUp;Nc%5E{aW73fb2T)$UYmCrcf%k~Mn zz$XDu1^JV(JM;u$?K$kEFP*D~O^U6xFH;!Dwi?uoLtUlkFm_4T<0yhp*9-w0)3NuAd?^>zvGZtcZ4F35HseO@{S;nBMa4Rs zPQ>fiPHU588m!0Lzz?>;uNu**TR0a#4p}(4=WfSFM}Jkgi+lz(M&}%^+m+6_e6<{a zW0CDIe$`1KWWGRbt4izdualiq3R&156AYPw5C8P0sKI?XGWfBG0NVMpu=~Mk@$<)H zVR_OT^2C=paeCH?BgbBaRa$JjQ{=&P1ZV*NkfYtc2kyQ_sY{jCE;B++93;z*H_=ZHYuP);uKky}8IhjzBoEyDQk4Y=!upq-Unh2A(QH!r-4Gjz?KY5+A zv9_+0=2-h_)85+L{E)8hn=iSuQDeyUgS{8|v z!w0YNnDuAMgN$XC+i7(ivf%oLUqF2n3{ozm>5ezL3Q&)r^IGrkmCOrY97fzq0dY#a zx^l~PdzmI$V|6AYNonr@gM?jY^viSj<~m$p^gRvU&<9KpgV_(Ao&yT0{*Lc8_;8WC z{)x}kq#}4FaH}w!i9{+x^y|S9P+^Y3r;~-CQ>EodwPn>t@8Q87D%KTxq6c(!oV-J} z;Topf%cCxv^9-!4iyd!jM{9kVvKZ|hep>m)I4)IfL66yW7>0qK9%yk(;XdEp8#&UF zR+Tp!9}8p?vY+oK)^_cLy7O{EJEE0#^@PfPEPf@$rhV|RyD5*AwcR#Ayk}}z3;&{* zvoe=g;|^^YQ-$X%oz{RddaYGr7R&5}R%p=L+}@tIxwo@eu_PMLz;xD+3c1Lwvg2E@ zoK?>_{&o3znlwF3X_){MQOB>Ur13$=OYKQK1QTX^vQk?Dum#s2z=EJE&GhCBLUiDf zhKB3GMqf)Y)-|EmQBn3#wg#2lQK$9=Wx`|?X8)7o((5=-`1_|5NCaTb;01w)A0VDTx6wQU|E!+Pni0*W&06gh zS4|0vG={XLB7tZqKH$*7$#v)CL+;LG&i4_LhG+b=LBuPQwc5%%lBo~HgNU0QVBr&@ zqlq<@G>VKmf0o(QmMUmyFfS=FLMLd={itbh9=Aq3@}dakUf=A@kV{J7cW5rqef;>1 zni2teV?j!J=vo`UoGiv=PSHx59|py06IU|2%AhX_-yns%0vIzH*OcX0#+AU9rL7iY z7$F`qeC^32%BrQPNWG-5$4&yF>94;>@x)=A6i$A!UL#8mWg4rUmmrRT)qGA8QIJjV zj&+r99ZoKMzOyH`#K*=mVC}=nR#F=3F0z?wOBH|lkd85n(aCLZ zv$e4?M$l!`AD<3Z)4S8?XQ`$3#8DLC^Go=&jRj-a!u#yD(;fLwzVcmpb=wD}3MRie zg0K|wXRXH@?*TglXLh#xoo;Y(=_r&KTNygHfSi7 z1QxkKKRgMnf?rP6-eN`MBW0E5Oa29vp4&)tgLAjLFj9s#Tr00sE$83%pH6Ok=a}MD z*V_hMMmgxfmmriBToH-LtP!E;fDz}Y%Tsj!ej)E6L zB(yT>dkq$DBf4gP0`@)?ZKBiUWf?Ur1m^LJQ%?;!Y#+jRWtGoD6=L9?_H@L(cwUb| zK7D;)V1TkVBjVyOYVa{@T(%bXwg7}*=U&}A0FX7Ogb*oA&Ay26vkORxh>8Lf>=aEM zSU?^oTNzlePwg~fCxN&!oQ82Dcxke6)7PYyhbeG#;|nALOeh?f zM~0LxAh`IEgM9`l2uhmeC}qKa+^XuDVP;|JIrc?FD#L=W1KBN}DnH&&+11w4@|)*6 z0_U?1`_p-jEpZ6ZQ^ZDgnu1dHjnF^Hia;Hdd7)C@Yf9z%PUFd3wY}1g^D> zO^-TFu}P2A{%UuLAfn?eGK7Yw%}%0GXz+5#qHk_PJhgMi*4hwx4k%tT)0AtlO8Fxm z$Uy)YlB^15tqNf{Z3JC_RpLs}5plRkQ-v2A&0apVxV~X}qx2>Cb(q3&KtVz3 zyyq?;Eh#xUVti2L?4#vm?^ZcaL@Ga2Iv?^0`Tnp7SIM9u3pp7XZW0XH425;n?uux5 z0^-uWMB^#zvTp{3YEt6{`nnZ7H+`gE-R zwAg~Cjm|q0(WXN<2%}5L`_BkeQ6E7C7~h)SH#hO}Es&{Hd76g!{NtliR&YPMLs{hsR(pdbOg zm$&!X_*Mwur$^!Wd`Wmmf4r zNNMjaPl71Mf0wQ%rD)kG2Off8=oTIe4t0^p!%+^N4VV8j zJ%*>zn+J_kYscpf0_Jdv8X3hB-@%5cySA#8a>4rpyJBw3rq^aITI(nI!YLrmZ#+bI%a&b zvV{LgnjQmr<+1If{0&mtCbn1Zavud*5d?ifG~JgcNNR^I4eq58ZBzJG*q8~)-|wA( z?^RSBe|j?r8Rq&hxNz*Tc|}Dp*D@XL_QhLPmz6}_YKb=FcK@aiv=N90_Ex^)ae!zw z0N5pm>5!xG2904ihbQz!Ih(iqNs(`)O^GndS3$l{$Ug$!*I*XXarWNxbL0qw`FWqu z;O4;r>YMB8>Pq-SgNPJJz8#J9yZKaS?qF-sWux5ecY_mm>%0B-m=+)pX=O--^dysj zmt#`;MzB_|3mTPiP5^h?E!tXsDk3j|GiA!1Cdi*_K6>4JwMu_wG5K~CP-tDHmVH2` z;<9<{4SIW>bGe`Bnp3_t{b!sa@&utX@6tq__Ry?_5iArOt6C3#l3<_iqvZVM$F#uK z6sw*3_aciLDerP3+jhpRlxF9c70ey1QIZmEhKEy@2N-bq&; zY94f4KLWN4mx|LQHe3e|NQfkX7*t$xs2Cy&gnayZJx+hywy8C%E*FHF|5jx*0Wa&{ zGg3$CbSZUkAu6L(otY^3D_AI`&Xi)z%`xrAP4`OMmZ&t436g^bZP1cfk$-BfT^nj3 z4in&m(&l&do`n_&l>G61lWxk>PQFbmj=syNtf{}dT-%D2(oID3zt84NhX@pF{{d99M)YAG*ws)I4sE zz`bGkBW0@J?C$F7BCbtCP!lLL5BGPvuDL!9aNPM}n$;$-X6y8Qt2xqP#C~wvGr!l%9JT;6WA4MxFFu?#0eM&HC0XIfTkZzaUXJdexC@b z)uadpq9w}#{=seF>3n^OMvX$!$m{KwGuQ}`pXlJ!SP21XWyZsw(DwcN!yK{;bzCo$ z{rWG7KlUVf8-)j&Xh2y=CX7P%KlcHJ6@9?i)?Q&@;R&D}Fgpdb6qD?gcj{!3^L;Ut z6X+Hvri-BxsyV%tl|We}!)=f({~u!>5_3XvfY+jx#~XWJHlu54%M?V+N~aGpqCXZD z`PNFBN zrSY&^Ma71ti$qS1uP1fN@DS5+90#KKCrnhw_rJeJzyjP0tc*7D`nyM(B;_Lj_y8Gt ztODL{VyOvH#4~W5Sa4{eCH^Tm)~Aqb9b9cO(Wd;jk^^yu10l-V9b&DB-sDvd9Gbwn+-OdG)$)IHoHUDg-k3pCXi z!>^~iu#hlJnhx`%9Kup}JcE-cV8saVfR4@=zD*TBJo2q=`p~+g_Iin%iX08>c67(&ZF|c zV&KtY=a!|_!mhMcorKFld!FF->bWYSAVtgTG^nB|n6t&MPQ4&r1i|xYgo5nOf+c#~ z6OG^7D-Z1)TB3%ypEO`HFc>B{&%Vp6-c?~S-I%qDRVhe!s5!9jLudOaW{Ph|@j{;D z7j3+kC;z0Q7Zvu~xZauk#>PHXg(~s0v z2Ewie)>o8TJZ*YT)u3y?;H**kt*MOLRipN|(=w1b0(03npu^Ql6P*wTQVs{-auum< ztJ#@U%h$88D2W*^(4Qexw&erK@+0L~p~LMAnwA#cMoeIVZkw^_I(Y2=zLBGTl&xOc zwyM8A3xFczH#bym&ncnl(aQN~HlPVH*Lr&p44aKq({UdW8{dbO?$)Nj3AyFZOoNox zOxYtcH8CcuUf$F1aok(*27z$jjGDINw<1~2ylgOu*2@|d532(5b zjm_h=ijc?3fU+|0FS8)hbN&k@_Gjeojys`>9QIp8UT5R4V*cIV) z+E91Vw4HA*2sHGMlp?q4Dk>cEaJz2ZDowO*W&lvta?NAjbZFNQsgxb5K?)5~uoqc| zkt#6h^z_5APzeLSmlD_q7}tj7zU_eOq$l@qbMV5LX1uwdkz6s0KPwYZrNTJ{Mx6_Q zeEKxHbvetyo!h@%&q+X(QU7}VzFGFc^LrHsH*<9tsCdb z+D)dVI=5cK6(X|PTX-T2=@25n$~c~I*0(GSD!+eJ4r&eP zZI+WspPOB--klbm?(gq|sty0wyEVMmbx~Gaywv?57Y9FBkViUu70^N5dK~u_a;mjm zSKj|@Yvt7@uC7>qlLQxRz^x?Lu`K%ysPM^R(5$ZbXfSNLK-YN2nS;aLZ1UArrU`~& ztE&LVJOAF1J1-jfL>J${-9&L%r!(w=_s&dI6^_pm@eOTPEB}~72tykt*flJV9(*Zm zUY^<2gZ{qN)_owJTlPXte{KKc9oVe3;%LH&I!%WmdIMHaMaK*y6|_j<0y?Wv2CLJc zd1^f}6#{_GI-r=I45$30gRoQl&lQkD58^kFxgn7(kilUPJs4s@r?>d&RuBtB%}Vq?%R7()txEh0R0dNkkz01mS_zvNoW8 zl2=ZwA5Tg)J*ON;VUcZ>t1&Wt$!11m!!BWbj(<2mb+ zk0=Q?;LfVv)18~Kc5{p2TQPWZ&D$WL z_=5U?G4+M};WFwHGIL_7TXA7_)JLeNDX*>R9-^b@?}Wu99eKtuggt`x^e$~T?>a0LEB7cWWy_(ct7^_> zMG-P(kz0- zgRcyzo2y!ZQUiRf)##+FZ2iuxYJPVju3mhQbR;4F<;>3>pKy*pbB zq=tr5+DdVs(vbb2QU0gN*1+0-PAlYM)cQJr(xz-IjhPARehi8QFp)09V&|2mYx^_@ zI3L_%tNth|q;6Fo5W8WlHE&pR$_5}>rR5{QtpwhOi}X*5ipH0V=E$4+?vq0%rV2s5 z{P9;wtMUBg&ib8WSDEVMI0W5?iq@Z})>X`g!8Qkl_f+1;lrAW>CFHbPoL5;eHYF?! z?fveT1q}(^VJ>uaiYo8T$`hg`UhK`Wf$Xg1g=b5$e{SgU9$)SpR^8ooc|OfKaMu-) zHf;s#*Zczi za&n9|NJ>D%hP>hLq8`ll*iaTU`^~^z3%xgf> z+sm~IbIeTDkVX?5wFP=_*8nohD=G9Yvt5<_?e6#f_!S5>*<-zAv0Bc5T#M&=1o3d` zzTC;nu3UZG($B46E+<=<6Hn_NV9Y`1N|7EqYAquKZODZ8vLCEr$A1EFi$@69+wTHL z{8otw<6sBHhTzO!4Bl}ZQWfUsAJpT7Fjd<>%vcDsuDxU0o0ZK9eO>`8!$~S$Of4b# z{ryLWL$jEvq?bScA_C_fZcT7IyZ4{a+QZ2dr97(60>*>b7OBBFBeEJWQCYh4aNq^X zJ4-5`Q|3zK$%#JrltX}U>^4=LIx}`&oup&cnq7JC!LH`Y&QzY!a7-O+++#O+z^r%1 z7K9pLASvuawtDF#-N-Kg-CFr~UP+(uwN9l)`}_OHK2nJ1AMfL~0G>y|31JG+@a=GO zq(a6Jqy)#J4aAYpf7}v3vHuuH3RI>OX9n+-Tk|6Z0zWz$LXKHIKT6EioqW!$e2uGV zgAB_5_+*|j>GV<*I8Q5vR(nmx3{QnKcU+%1KKDxizL7(ApRld9^^vqc@ZI2803@kH z{}0JWVL=~2$p+`;CFGQjK4mZP53l`06zYaB(|{@rxKf;@|GxGk!9RL%hyc*hWfT=D z&WT^Q_~Q!tkl-HYpMj%z5%yxEmFnm(%fq5tZ z#M3TVI{hw#T&r3jYiF~->USlL^rG30oO0I`Bdb>g*Nr?-J&iYX=#4Qg(X#Si9644$iL7cr}C^0IE_E1ADN9mOK z0Y19!$*^=3nZsYtA;c4CVbK1-#)6oUQdT60@MYIVk?EZBso$P_H3&$8F!UaX0xc`8 z{AxKGOs+0x2n$vK9kH?s$t)P8-E*B1g$ZriFG)hJ_8pD4L!jBk%vw_SRulrs2CNjx8c0 z5`uz)BCrT)0Rd4`L`u2@q)WO%1?iA32|;?%Ej3co-6`E5-LRhp>gddGpX)kjpFRIM z7c9Q@esA2*9nbqtC};p~At?c$E(Yexq7G^plaH?uF6sSx-m0vrX(`&!; zx?9O6u*bOaGuBBORS8_7VI-1}S12p{YxioA`%wH=5XnZ$_7-~VJuSLX+UVvff9Z{S zan+x*_AfNXBHSjMT4R5P`d>Ig;MJ`9wWOpZuudqI10n(N7k#I(-d!lTNv7=J1l=}cRC;BML5;?K^vP^F|Ag&`+(7*gM1YzvM$ZP)%zHAZ+`y} z!lm8z9juyrJKZLb1>OrTD@GkLRrIR0#l<#x5IJlf^A4fiumLpKS>hU;Kcs_$4Fz+y zrpV=b*HpJ);FZm*JqZD(mTdH;;8GPVEPst?4VNq4+8ExOR9S9YC?#dLv0Xb`3=N{Q z8aD?RC{zbsPvB)Hy?yIaSqXc2^5FB_VaYY-DxOOF+gyqmakO9Idc4zR@(Y?&uVP=io^LXh-Wd$$%I_%X zU5Te$9Om09Y&TFpP}r_yJ%dF@XTR5iG7CnX2nNWM4mbg*F(ILDk8PHr`{Ut;?%;4| zv)f688W@$|lk7MV?7FR52FciwlQdb^P_+|v@mv}qbx^7KuB)KGGy8S5O54^v<-O5) z@FEhHB(X2zBf9)ZpMck0cjvWnJ=PrG#RAu*5U)%Ag zWQLaXZdEC3V7$Tj*CFL6#>@Mzc}n6Rwk6~NBP}9LhK_-DiHKEWd9jF)*p5*?5<^iu z>j0*>Zp-U>XAF~xa*+okVGNq15ZLM0dkh5p!I;k-$Qbc?7t0l!HFNk!+8FqTKtgyO zN<6*(jl}ltlf&N}9RLm7v)M9c&vr15C0 zOwBj8!Z@P`j;A|19@~S+vsF$#0@UH|TWK-**U(SWc$P=A@&sII)L?+)@pbZlzntRF zmH>Qe{!Z`?p_n3OF=+8(xpa-G4m>G~hGm0h?ZOrDBE(Ek{{a!HL%_2I@hq2%GuU+B zjKBRYMb|zNX8ZMq8p=97bOSv053+YZ&+X7EOkc)E1*{pW{sO8QYyv_XU?0w^Z_Cq@ zC7@H*6P}kI6sr90q%{u91r;ps(@pQoU#ev_A*;(9d{yzy&u<+o+#1j^iX|0tQENcT zvUIMUm+htO1v?a1 zUI&&R&6SlJNVEwu&7faZ>T8E1%dG&Xa~&!mvH{(WX;hvHgazYZb6&dM1HKn7YO+Xs zHqDu~Yhn3!gB->1JN&muCB?=4{QaTV!l+1R1tu5Of!;Amg%Z)inQM$wFn)3hXwOOU zFM_ifHs}(i;RF$4fB}*>-JC`OC%vI74;`c#fO-cXQKzch>c-L5E|(~B8rQ)GJSS?P zdJdC9fQf<@?X@+@l78RK`c&qhq%4{!;X0oQ2*~N(@~K?_2aXaIE#w!|FK1$Vmfw^E zhULgKOyE^LKd(oGb`kA9>v#zd`Y+xcZ5Dxl(fR>V(ICxNtBE_F19mh-bUzqbip%H5 zj||yx^u8wJ`JDqri~a(PGBvds9Qh}tL7#x0lt*6M4!j?_TrCx-SNZ+W7@h}qkQS#u zPHr^DCr4J>pMLo79VrDZ7hLbi5nt94d-E_FE)&qmhWZv9&nR0QWJKHq=NQk{KAJn6 zL6f_L;@uzm`-)Wf1>bXzLP#OWk5c)Ge2At#GF70#1;a~1fWC4Jo@f0)K4iWCYlPg6%$jb)d!w6Ns3qw9=V0Vq{=*Nw zLS}i?v*J#y^TqV%O3Y@B1%=@}u5QV>xl8%jZXia2{ppEmp#mU8G&U^6UnBHcaK_M^c^d$6CXB*k4PG*t8QD5K&ODfz=>|o1i{hmDZI^@_Bg}gw8772 z=7nU$TuPK05SuONtJdYz^98Z&DHSZ@PoHkzy~`hTMVPD|wIb~qJdSAN*Ve7~mN1$v z!zq&+V4l)li83MBl#qSemhB&L zq%xU&TD(0uhwM7h>d`4H^#B@EEPHKl^eciu34MF=A3r_(UNJP6Hg^cx7=qHBfx*U^ zw(IsTNsOadh)nFknLHqV`*M6YlQy<(Ka7Lm#ZNFzA3l?8zF?eq|2h}u~mGICL5-emh zRW&9YoTWRsgP*iX5!-Jn27saV_#e)<2&`&3Fv|Ya$&0$WI-l_ zsnKqpU?f}eYTDs>MD8cQz4-SVj*X>ZQ6Vxaaem)x-oK)3fzr5qx6?+FCTrDGTl=4{ z%510g%!3xrOUP{SAt4ASmP>9+4vP@U+?_h%xVMpidbA#kxDeV{OeT??r+!WL6{Ie- z%Pc@#9vKGezgmM}tTmWe@O<}sor@!Xl9;R+Lt=8^0ei7AoQUIVK=Q$f7E@rk4m7Fd zyvz*19m>2tAk&w-fHU7&YY&D~DlTj99yOOsJ3vE7_Y-B_VEw!dJx!wQ+3t=`z}Nv{?6Fk?)YoAZ zZByxT^{trPl~X7zdGUJBwPV><)_hB&qRX zcY|~J3?&-XP%H(nE=={(tb!W~tK{d{duu9WGx}r3=>whpk=Z+3p7; zw?Ce4aYAXmXrn&=*qa?ukpnP%U%QZA#^xf;Y0-%7;T9!HN_Dup_!x7r4O&D3gW>w4 z<%5DkvGv;Yo0UP6CP80p=z(IjIq1c8ATw$#9F(A)uWd5Pbs%W#wFY z`eN^@I2|1XoS=FOAgqP*LMn)ugMS{FyY#dqjyHdspafR^u-uGSy+Foc(dzDWfPJ;k zq?tOapZ|k}Qh)w1%{f{`@~(Kj`{-B=>v21amym%2_28&l*f282( zzL6NN-5xDo?Mrr~*xfXgITrWp`Kv(M_aR5v@2@v8G(RYT&I}NF>q(W@j7VE2KHT!` z*=mbp80XtrFN#6Uzh_%SvHhfv#5Pd4wC9}y)u=kpb#^K1@89NT>tQfa5FDO)R!&D& zEP??P>YZE`kzaCLfMp9tK=OP{!ZZ9a1CsY=2HQGyC_=oQMw` zHjkEug42~arizx#bt7LfTl}iFZ*qfFt4JelZEX#qLZp-~oN7l=lgz|OP5nc)nY!tb zK*b`WrQX#x=$YdNUe*M=gKb3a#rU5wy5GAwK$ih1oyeYMB+8e9#spO{80cjp+4h?q z47fl}14TA=%4v!2Gl#0x_Vr-E(=7Pnq)L(Z#c=KG@?0TKzcJH3(Q?Z{z4Jw5y1iK= z_!i2JlXQXW2!uf&qq36i_Dlk;n~$JuCO4dK3=pw_qO2qt^k}r3y+8KCNvlSt{mOdK zF6@9J2i~whpKA#!=S1Y^95JN44T+txtnM0f=L0E>)({E_zJ?FlDRIceG@Ozz(+c?; zm{4^;Sf`se;OG<(D&BUx(EUM2c zdt0y;tPbtcW$ynnG$fJRjK6qdNhNRI4SVPnVWvFy+ZznfbX~vF6-AeGY<)!+)J2HT=wElK;#z-=AH<{%w5IMsAGY z#C%)^Gfkv}y^(q+{u6Lh0};*C7=D!>-Y_X-B(@-Uf$TkO9*TI#YZPa9Qe=Q{${))S z9qT<7cR)~Toc8J7Q@38fCN<(#x%U@VY$dg;+l83du7uTt1yzYg=iRYRzD>kBW#=Ee zEF8mNc%5-92;WQS9@Ss_93$0F;?UhSv~lMS*_%fO(n_Htx0+RCDW;NF%%G;S{OGCl^F_x`E^7y{7Hz8AsQfX3;&yStn7z3sidtZZyMyo#k?S1!b?pORAP z2nY!Hz-~c9M&@isihT4@2F&@;V_Du>34U4s%8C*X!>KEBXo6o(16n{so85}KJlmU{ zP*!#bs=^{bt+rJi`k!AadFSv0-0DGg=Z=-N_3ZR?%i)98)>gm$6=`s$k~tOQwO)MV zg!@qZ)fZF__U|Qn28ORb#Lf0_8)=E!S8w{zhK2^D#nO-?I1%8Fym|8l!YqW_SbPRA zNrLPB{r!Qp)tR&X_V*it{Pyc4BqTl03GbbyO1~*5B4Cc=jJUJ2yK6Dm<#mw44V4&p zGjE|rLx35EgY3s}u;*sQWMZbG5C|KaU66rWgTS(&SZExls<`~S zkB5v>uq_)5+?RR+_ry*{a@ZVAjg~9iLn+p#Ss57-cTO`U7ws)yg`ur@qytE-a^x*4 zVfQ|9Wn~47^@T-6{r&yv3Weg5HnP6Z5S>~k<245_yZ1GQ>bABlejg&3OMHBMb~|fe z{?~YS0v+80-Z5k>+G;Lcy43N4)Q55gmXL*V@B*7 zi-Wsx56zo5Z{V3{I+EJKta#=keuhegGlUH??BFV0@Y03*`>VZp@o+3L+3;;0E#gY( z(+xEQ1~QH^#t$SYw1_}h5VoHs^zleFH^6_~c68Th3^${fky+Ck#cyHN;RuPA66iE&@pA z6x7h?ptzHl$2|6Vuy{M^4Y$a0Y`X-!*7vnf$zfX(7uU!*xWSf_KvxL)j$36@(cBIe zW`T#h!)J9rL<+1>G(5-Bc>^(zPv4HG_(=id#gdz^3Z)mMpBZuU)v)CnJBd< z0c{-5S$yK-;o*T4g944i&YJe4N9E9D0WMj7zyVp6`I-6o>({S0;N0rqD>WM9gQ!Oo zhYg=iRaMn8=2u9pB|IU7`A1JrpLhcwKe2YMB}4?ltRic)IN)};m*7)Y=D#sC!etvW zHcKfjnVuaIdJW^cmV#VZf({&Xl9C@^jrreASX*DW-=Xm6Q&+d*p5K?YaG{hk--3^hUf%M~p+bL1efijIX7zl_);!yqap7H6LR8vS zPEYSk@pg&|dTMGjW}FV=HO6c-{l3gfpL66b71*>k7um$b)Y6r?M(6c}NNh;?^*LdM zfzpW9SQi?LQYu(Lo9gKy-#r1_Y8CnW3yZ$LD;yvF%@J3bPy0X;eeyIX zzSPu2Hxz9!z$=DK72IUeiu*ZiU|_&0pTM98!(E%!S_eW>Fm&@RA06S)?90_%Ut1$^ za8!lqpe1K!pC9wpWl0pt;UIIT#)jg}r%&}==4e7gX^KLt8$=p<$aEldy~f|yTjEmbZc}x2bw!2R zOj}%gf{2BQNqYv8e887OS3@HxDyq59$jInPTw80a5kv$eB_%;PgMAuu{)8+BuoB|( zBOr%o4?!6C2du-Mub1E1z|05_3p;b_l&i=(#2wAyN+vV2xG30EI>!g9>fH|su>O9R zyrUu7$-bXC+7j!sIQF$rQu1Aei@c_0nZsOHCbj%zyTruA_Rfy=ojaN)#$DG4HdGy4 zse{8Eb{D?tMU0MC4Q_1UViHfbY^Z&e^2S%;_hK2oUShs*UFYRXiT_BKnv^Rmy~kM8gF@)s+VB=J)JoU zaVg}&^n6!*4&Fo}5V^gDCcaNZxyLWn4TCLFKHyzfNPJh2t#K&jqubDq;3qz8IhCeR z81tNxX8~PE)0?MhMBs&g7833k;lTity0d6pMBCNM|1?b5K1z zaB2}%<|r}1qnZB}j8kY=7Atv~po!zB(;uQYRWPKgZjx{tq1uj3&4pMfYcZLl| z%PdG$Ajg~g$_cN|&CU`%ux#n=EtF1Inim&-^5iq1j12?Pi?D8mg>En9_Fre+)nc6E z^O~(2jbh6V7K*M#b3lPy(x>NJG(lIWK-X@we<@aK6`Tw>cC$2j&}y=?c6pt*!2o@NN)Ph_(kf7d+EIcMDzWgG#_UEMqV0+aE07E5;t%)02UNi0Q!<48+-nA^>n> z6sMh>q$Cqaa1sg6n7*B|}d&{`UUfd^Y^Cf-KYg>Z}+!KMK!2>w_&dvJ}#+f>$aoPc=yF zO>>)X(kNXoeeq?SjVo2aP50$^NS~5dpPxto4ihSrcKb9Q=q?a@gj>P4;_wI}qRG-L zgu?9PmJN(`ywrK^>`D?uniG?`hAd_~oVT`IPoo17kA1hxd}bw>kflwR3gJz>Bqk;X z5!!E_o|1E2jTjeJ#;ZSY9Y(<`>g`=B`dpWie8FLdLbvChP_7H(TNjtNL~S==&jT{r zw?qCeKvuWAb^ zC~P%`N3B>S^tPpZaag}Z{7j0RiioR8QZg9d(NS4QIHFNl+&^nHl4E&Zo^r->x-&>m zwsmljVU~S9rzPI4hLCDm^**d(jeSX6ChTBOhfxo6fGyq!$%pB&8w+Fb|gw zFRjmYe{IR=4rK^0p!0CQ)E?yS{x&4!a-6t^Cd>46D^8B$!bq{zt+X^_O-*@ola8#c z2W-g(v>U;fsh{Pp9x3o9?@?vFYq2I9P%N=r^7YrC!e zoz>*67P`ZxpI^3Ki5oEm1p^qGMbbm!)DJ@$z6J+RnfP~nLT;_iTq7nv8>S-@Qs&{Y zfTujKx2zs8sr+ulfG*js&p;HmU*qM`s-7Mh5fPCaH*N?C)lW}reO}bEv716w%c((@ zLq+5kn!^)nI!KCS%XhF9WPP^i-d^u+mW)y7wfCc-56gg?TD6CUDtaP>hV57a&fsye z73C&9)p$hr;vwwhcg1lji_cT4C+Or>wdhzaP6^phBg)`t)OL2*FaX5d=@wW8wX!gaSiaH(T%Y|Cd9x0%Q0Fd7cr zx3IB6cL$3}?v^b9abv~>lZm=OAT`?WR6KEmwg`HCK8L{QJWS~%v08$4U~PpV=>exV zb)?0uoU4#d{Yk56p{hZd&`gq{L1jK!|uic^v4jJ zGvC`Xg7=t~wRNS=-Bk)(xZvoqp?Z21SJ~3i0`CtO2ZyP13tn!^XzA%S7+iw98GP8QBQoi? zGyZ!F7@^=2O}K~3*xY=vBgr4y>_I;Q;26p!aWjkGYH{3Bcqa?hDjYJjO2sn$W#6I4 zN5+NEp`q7y-!DRFct*J(h?WXIT+8M^pH5N){rBYLdEP77h@XVdhl?-up zJ*TUyJMtByw`?4W5co;+*$%hBE4)v9-~-VpVbB||{PvRGI{}>mRXE%I`=$V%IqMP0 zZzPz{R4;st=35t_J}kYlOmaH^0&EnIVX0tZV(Mg2LuwGr*7GTkWhv+=D9(fQ41@aN z=8_}_o2*PvlOM1&ia*GU-|+EkJPAR;S|EY^{QO|{b;G1JPZzL;OB&?Uvs7OUL~>yc ztSt3o(TZnChJejgS9cD+3`+Dsi#`ts?cD-ZuS7H9e<_8(Jz&6V+uDpL8$w|ZlPrpR z`MBPl`q2Bv^62S(-3JZ_wtxlsJ@LOhaf90jkOzbFco^`=#acXd?#=thi+7p{Pr+LE`MNP9SiFOb*|-d`KkRj zRC||FI~(vAS`1lD0e$IJrh|G>A!i0o1=ichnt!&jxZbVV7$%!D0nl%L1bUX*L8cq7 z-n(WaBl`W#Qw?#su&+d)ix3<0Zg^x|xpzt!!*X-4buUadieOx{b{BDsobAaxc_W~_ zp@IC4`kA@mkB@B#{5@{4_z-naAl@SR7gk&FiMs-HfPVC)xEFRZZDS!uSr9L@8ns6 z?A#Cya)gv5&IJ}uE8{21%1a4RS&52LK~rO6uO=JF!aP$_iks}PFwX6C%N$|hdV`++ zA`3%OQc}Refe7g~zC{~c6sb$_P)2Ipb?DT3K6OCE?}fY@iL?~3siRznGh<6jm1dSx z5rRu4ETIHN=G3ExQ~dgZ=>oEudD<-!)zv$CV~%Hg+1MPQpuX)udKkUu70&Y2^SZWj z`Ul|56Go=~PzM9zbDVTI&7gjRCT` z{GDMz5#l-&$RCrH=3klT#ofThCY3D42_oeW5(yix@_u#_O7Cz!LM9zc%Yrg-3J(wT zC??&%I9|p9Q4Q@-7f$~5!R6vL{41l4oLY(IH)k2Fb39^Wal917jq@NWwI@+(#~#a% zIS9ws7U5T)&FLY54+JjBuJp1>(#kKrzQ|K@a5PmpJbL zXp*tf++;u8(BK-GOt+mxJ%dAm=wimO&qQH~kLFfZr9W$H=_p|t>T7A#RTC>CxXncJ zD!(6K>tVo2LF*q`n<{r4p&%-(4G5mfLEj3X{@{NnjAV?eS2=i{`}RoEjv;Y2;y^}g zx;^3SZodvDCa?L%E-ULC;PJ0e8|PH@_05MHZm6rrSzA*AFhn5G1D^D5ul6VTd{m*$ z3sx{VSjZ=qh&qqWmqk}n>>SBaw7s)V>g_Eh68T|vc=+4K!bRUK60fdwN^eN-7F@&k)#`J0hI^$BPQEHEi5nBr!H-44ST#Qi7#w$ZVz%` z7Cjj%P@NEXqk3x40_C>cl&m;Ume>*PQlC9aG0)7Oe9DTp{u#`r?sNwiBC^{&i+#DS z2m}#(o^jOy}xOy|NLT?T7BNsq(|A)c?F0b6tr(5BiZb#iIFvLV-JpH;vCu z7%Jq=Q`ebEGTo`jjVn;#^!%L>LzANwu5-r_)s$V*S_KzkYcbxq`TMs6L5}J;hK9!X-{NvU z2a3yjvE$;hJ`m^R$w6jA?~m2B{&VBTCI;z%jDW~|s{K;`Ybwm2hEfm>`uiMF+EGqyNajI>-)zpQ$>3RinfKG%veF^)NEwU zIx-VEJ+*102%_)%j*i~FC8@P_vxfTqte3UnLc!0}FJKswpUVbx<>K zK?s=CM=HJ%`<(JBf+8X#b?zN+0&xmk*VaB~Iu{kajf|NwQdYyhaibXMoRK$`Wf!3X z+c%&&2P)L-hr;O8sD2-<9(NouJ@h(vGiT?Ndjvz5w}PQ8G9EvNYF&c%x9fPxXF+?I zW3ntdEst|ADVRUu;@Z_~1PtGyv0i)@GpopS+v6QCm#!`Yyh)IF>{lB(t@B_!)iZ!z zx_@6roLPR%NLR?(VUNzg6H8X1AmcF^16GuCpXOye+DpnAFN`Ciq&}_Pr#F78Pb;KP zK~v{W@;ip;nuDGfnORws)YK?y)Xl9HXw~H8WDq`iTmUHIBDj2}k<|6W=qveJ}djaxvsEY4Na~2Nn zEM)&5CoR|0CYYI-%~BzmW<*?K!(D7Y*dNlrwNR<3nWtB7jAg7%u!ZN?3LDsT76-kF@))adBXXpF* z1)B_&t?p}h>OOk(X^U~I>d_MmMa78#SE%5q5cSy^)0^8<;m`j!Yc>}LAtfU(541;y z4`on5K#t*XD*Vh1DMq(V3cN{&BO1J*;@tI^UX%H}s zj7FZsHMF+sG<}FpPJVQ70i74vTX_`XwX(7YHAQ^9^NABpVF5T% zoE&3gV-F=T&{r*tGWSP4B93;&lYrU)dlD@dTZk<-mDYdlkdb)4JZrVNsBX5DwmO#{ zl@i&#>U{B-ekP^4;O+a zaH-B+?!Pkl#K#dz&qX4hPTPgZb6EsSz z%ogAJ4p-LG0tpITyc87`b>{o?MQrd;AChw(Tnn)D9L^tjD)}EByTwi*A_W|aNNBZI z=hb<6QqCjG;fG&u{9Ez9$@)W%Mp1QInEzI={Tz?#lK($``P_<9XRQA_r~)Vw#ReyQ z5`Ia-4)zz`Tt@%LSb%nW>dxz~PjR&8;F&B{N^X-QaPzWXif|b2Ek8%q8B2pJ>y$j< z=YIbc{*28dKPV%GTt(Fd z5BK%!*XQ`PHgj@bZUC!t-NZyq2O0$`!Cq+aHJpWu>E3kh`fv%mw~fwEFkgN(@%-6@ z1UB>eoBpAqAdtm^nB6|9qoGRc+XkeiB z)29OJ>Tg~(0m(^9@|RA-^rhj$>lBD&q|erxO>@(2Dc{v-=hIlHVtn67qC~ou zNH)-Z$0{tyyCS)eJF&LZX zT^E61?)h4kL>1r>cCYblVm*($LDV<`T0hB1Gy(5Pb>8mgw=&#DjhjV@t)ojlShPz2 zy!7I19Xz0!nWjfpkd_yU8hw*zq2u||+VRt~$#Ga2Cp$Pe_UAJ&ChdtGR#w)mL*3~Cs!y?&yG?Ly1#wd2P=7&$rMRo^xK*n0cA$LS@|G)Xf6QJDXp|k$T5(m&biwI zf+-3+?hVSy(%sdJ0GzsUOQ>*QuiucX)Koj)!$aRgGZ3CX+Un=JO)`}A&DX6j)8;RJ z@nfuse2cpcOvdM9TakoJ$pF%3|E9F+=qa`17I4iMm}qXv!0Rr9!@9q|$)E+0G9YTQ zYYG5tp2ShHSd@G(!YMoCj6ggTH-)`*XO47nnCr_8kG|irm&>ar1zEbAe~0QX19WK~ zhSt-+8Cv|>L$XNZJ&y$KMz7eIYAGq)j62SJrG!&HRKekQ?~0@+k|(CFzB%c}WYFBG zNQ{Lg+HVWWAfIPOk@Y%SF6JL=;9LZ+S4Gx|IU5>%-S4v5WU#A!@+~Vpb5=?Prq)9n zo)x9R+hr57%kXulKgyj=TalKO6zG+PasauqIp!0J?D#rb{_aLV>B??P(t47CtO*HkSbZzVp z+PaSTUFrhbhO{h*eqTL};%@s7JAmGE^2{2CDY;-Aw`}IJW6H!-0Is;blBTS$`s*l% zQnx-A%)5xIxIV;F$;m>V$G9fmcBR_O^k6YJ5QkQIs3O9H@sC}mwHPT7k$SYgy>aj) z-x|IYd=i%T;>*jtN>QaqE^WWyqN4JlA=Qbx{Fz>r3aaL);U;T2nKc9@TQSH=9bdkD z@gvaFS6$P>8dmSf{ENm=qRV{fc>rK%YiJbv)t zD$Gg_j<~F>zgse-jh3}%&3DcDe26~7*}p6jL>R<{V1E9uk{e`EFdF0x*4C1d*O-h8 z?7n@ASzy#R`VH3QT1q7)>JTe_QMo$ATgbjkLoA^tEVe#XM|^tXnZm?UUoNBJlFIyK z%u*>~-31o~hbNunEW0Q|I4lr{koEpyBr%LF2)HD>9vr#h(M_S)pMngdI7?cg_QeSF zqgVU>?SPs2Bo*~;^9R<*HodwE=#%_4%0yqC_eqAZFh(JJ?2~)H+0x!$_nintS8CGv z(!aEcbJ>6{reG__-@h?a4N*ux6hyr$+39d6H9cKg+}M0~<8*uczPVvseeh;MAx3T8 zlZbmNxBI*6b)nU3I7;)yUwawHITH1}{w3M4ZMH}Oo<-E15v* zqyewaU!0 z1w}N!XGX=3ft+2PGCjw0@{ieqg+P49JY3)J+>55(yELEvF*UW3eB5|-qCO!dQaU4& zgB=eWn;x7O4_})AO}se7p`>8j*CSJw!wBtz$(Dw07u_(&Ldv{k?S^4YUUTS8h3Wy6mxN+j~Qro&Fj_pF48X8VVXO`uc z9OtajAIvxUhQ$k4Jkmd2TV_^Fc18M9y8Y)@w>*aH+?y`#U1G8qMo)R6-%IZ>%0s)6 zYTq%nI5^e;1gC(IP*8m%;9Gy#Wbp8shJ76{@8tR#4hSp_DNX_9S6)&;8TQ?EFDoW_ z2VI+(7yPOsw_5A7sYg0eMS4W!>em!lIQFiizk_bHbye@&{+d5_W>(`I)rR6QA zcw~X4(Uwp&opqty?NB|8cJp zgm@~Md!a;;_cQaLni(kYeYSv(XMT}jZ8PJZgH9A%E;kJeEp2mx`LQV0B_&mCL0jJ` zS!z1dL9qE5N}^qzYe)0!5TWQo7+HRxu?d(EVC=coJmnj#NU z6xwoTT9EfGX4)2++3p*>y#Q;I_O@>^-W#pt-z=XGjVtTi9rcK*6s;{*#*6%iqoX&r zxtaGafw>3#7?sr;uU%YJXWEk56Ur7RnVnEpjDV=Jz}&IX^1vsdv9X;WSh-yo_QvY#R|{2`D4ICztgF9%edCRfJGnHf3XTKzC=T|oB^mmA2PD7THgoF5L?{x1gk{5b%Fa&;*eFtv)KahXr&jkmW`H_ zy1qENb&yjxGSJZ#I2@kJO%QP?p}zDCBeY?BD#}D|JoxP~$RhfmK7LseU4?NA!`#-^ z$*U2~e6y+RGHi~dd_9UK*3rw41A``k=`b)f)ci!^%k#Vw6O$JQp4>ZPkV5u#% zFMj;+sJn+^uKo2p@ci<_?|O6!|J!Z*{`!%J^aqv#sIJl4cGz|zdiy7G`M+9mtjj8i z{UcNSG?U7_Lov+@Nzy~Qc9jXkHs)lR?jO!h$vxnIl=JbA**2S7EDjRK!g_J*u~_jF z6Rs9_uV>$s5>;5;r_sr=^mfvE{G0vu^*AFExusBFX2@SG@Duv(1mRc}<=KWgX(9gj z(VTf0)wY)tSL9=-3<3)t_XW_t@vMrEu{HHYjO0g~7YS0IY9`1+51coq_TJcQ5^u{}792|ytEi~hr}*gpLwoo%@9QNZsP^zRL)`SOTSN0w z*GNo6>l?QO?{h8iXc7HBH@s_B+=uy19%GjJFPHVR;C&KzWE+;v^#CG;VC^1ADCy~> z>w8NMK0CYY6XQq*D3OD1Wo3!qeTrXOy}UwTy)i#2pYIK)rn01>az}SJf`Zj%rp*8k zub+TjxwxRew$?dZ@Lyo1KiEN3svw5i*}0@g*{XhG5}L*@e|#*+$RW9IEqZJmTNcS~ z#IQfu+kn;eywl-@{eoQBZ1bD~BuXE?)N4e5jV4$YS*v!JjK)| zEfdkxlQCW~T~q<>60BR<8=yUfgy0tx^oYyIhC;LeVB(RlEIk40QRB@;BZDuzJ=@^g z;O36pQycuZ&kJv=z|pvhmF-mj=-gZgoTrnae)Nv|VwCDSfo04r_e)G+S?)c~FnpMk z#~k~0Ks>zGOw%5Z$gphC!Rx7m39|s$>6KYPEhuVXOL}ZS1?&nTD_En7gKe zao$C2BM5MCPWuo=j7Q4l&8I?|V8z2^fV*(?dVW=RHyy2^5K5YG#t)2M>Bc>qo{1zc zM&J=jY-LH1l91e_rk15)0ZX>$_o^w8$NJ;oaw@Xuw%VBg@N3#hc5WIp*2wVcFOnY} zUavK7N=ccTZvnCv!J~P(EHB&I%l%+bE^E9oPADO315nOfcNr-a!u>Af>aIykeR}ZZ zV@6GrJxF%#?Ggo9BxWzvc?~Xuj}K-0i%uXPQ{DV5DjAFe6I^2I`c~n!I(IM3-zl?% zya}Axz_Ix2ADDKhi)UTqqt@Otuk60mEL)nTOi!lVn@K=Iy|<0H3Jg3<0f0qGH}LT6 zS0@u-6wz^?#=G2ohFoZypk*=mq+D9;LLG6AeRZkKbn8{tBbDVXqqOADqIlQ$fF!xjPpGYYG`|YY>x}(z z93|;n-PwB;Q$N`@E+ z?m|fBfAU{Z9C@Z{40e;c3ZWPVXuVsF=N=Gn4LUO?3{oFeo2A7MqWY!%M+e#yG(oVQ zc7-8=0!rC7a~0P8fLng*7bqq-O2NCDJj+E5ce_M9$w89a_V#AOItI;7cTYGe5QqJU zsSF7x+??yaOQB7$iy!lALY5s^r#m{vrKEhYUmPU#IRc(q%&O{D+}K5=IQmh&1G`i6_t>f}37sj+#z`ck~tLM}X63b{_9QGv_#?pI;!K_X?O zg7`x_f7rz6Q=qfO9dW6K8kvbH7F#%&L873L*pr!rmUg)VPpQvOwLNp1`~yw7Ql%Fd zhL5arSH=&F-}yQQ-o@}$3@hN_LCe&+uv;BnkT z`6bw@ZRGVso_cB6KUR_oVo`M(>`XaJ!1Wyb_ytCb;6uC$ZJ5*uK~XaBvtnNTW?06t zIOY?~tkJNwb&fFn#5{9qD)m2q*|zf$eqWQVoF?jzdWo>d{Q=0lEoH?X;G!7)=#SIG zm`7Y#bZ>EoE0j(xfAR~z1M#zvHTwUI;{T6+If2tEz3v#LJXOVE#cay`FM5tfx@vL! zq%j*;m42sYS+B%koc^78Llq>bxZP3tF)J@MzRnT(>qqp2yU0?Yw>Y+vLVYfDO#>D%w6!j@XMEpo@BW; zZQpDu56$MLTBX8tC5|f;q5hr#1a{1rCqi4$cxUljM68Pm*0hd%XG#Fn+ z8)<7tCd<~jGij77cq^*0Q5$#dZ2`5#b!h7^0A}f6feHzf6MS)?_M0Jcd5}B%j{zqw zExr;F+DA9kuT$|-=Uh~r#r)V;oBOQ2pk;c4$42z~34H}n`7JEoIXd29n!aUr>_gG> z(xn4WO)MVoPaKSdhX%Sc8(KJavL->KXSpy ze&ArwPFPnS173Z?4-xv>4V9GwIO#|M4hn?E;IvFI%>u6AM{V0D3cne0l*#E`0KFC# zVl00bZ4m`i_uloQYE!xU1cxsvwEngj#)yk`SZh^|n%RMBo(`;Q8PXD%z`J(|4L+<+<@?~PBqSd=PUv>G^;94068$}yi;S0c*>w%* zO%+Z$M(R~LVF<`y#IVUM!w5d&2h~P;b8E_-KK<06fTuo9tC{<4!9qz=f>OvT9bzPY z&7dvEiSx~n2kRJPsG!Cg1j>8*efT;GrNv(x_0QcXZ*85e^_SS+C4|h-Epe%BV@k7$76Gb033-iw_<0l|b^C$G?xx86aMWdj*_4Ft1<)jb zY=Xi|mN*S5a8^~`*6zmYYW{`hG+tb?+c-biia)8;@~lx*u)_yOydi?!2#EgjteAT& zlbRo^suHvtAW;ohR274l5P?WeuJ?k%4b+`JajiN%ia>Swm{`YdWau4Em)~Lc^XFF+ zCweHk*{J;%(q*ui;MnpaoEazuCQ>e*dgY4t3a+EW1%(IpA9jHg>Y3@@SVTJy_uo(- z`*m-!=2EBUDDU<84cs}OaK+xI{rnL=Y_YN5XhaOy92}ci?4uwSP_x+0^D&{@OCI}U z3EbeuWfX%4Af_*~RJ{ruR{TECj4^|$ZFH+WGxxGcEELj^H9s^Lk_Sb&vLWxE|72m1 z$m#j-bGs=~GzvAN`M|@TEPL#wUiBvgzF?we-z12Ih4nrt2xKQNJ8NeILi{hJs0?7& z<~ny&{6f~3Eh9@+pMp_6Z$8#Oc{XdbFV|%D!(&hs!4@BY+0JKwv{Cvk01I%P^TPvG zryCd$K)uu%Rqw77npY$AsaF1Cu0(+mX@vc0b92Y8)bL&KlSqR1_Myp7=2fD9#l5x8 zNa9jcn4PJT8Be>2wE*F^9XL6OxrQ85vrT7K@T{91W;@Ol4#54QQOs@OhoB{w0Ie!d z3<@6|C#UL{x3z2EE8TJ#x8Us%44n$xcUv2&sD(ryNO*d}x9@M|f|+RGB_jpm-xG)e z5t*N9!)CawR`%_#{g*ZWzMOZ%+#z-1mu5yjMvwc83qz<75LZ=Pn8M=_!j#Sm8igJ@ zt(M`6^1a?$JxW&r8EW%Gc8FWaMxUI?etRM(Ee-Bp`zNyjo*DP%k-y*YYy>(%HN;3i z+t{3``!s~gX2d|0kazd1%AcdAy#=p0Z=N=yY*Td|;k8P-a zFj`NxMtFi7Ki1wI_0Jv+)~u4KP;YRVkph&C$>gb4o84b- zlJf9q@w2{6zW-*2pYe|bhZFqXUoJdC!)19I9(Y%O1PvTzC{KI9aZN z;>;9z<59{_Sdy7Fdb9tmQ1DR1gYCs}7V<P_K+2D3&8ntrks7L+?$9yP;?TCw`j@ZAo)XM zo*?Og{KoIwH&rDJR{X|{z&)sQi`vrM~g8{P|u%hW`NI z(J@jzy)z1*bBd}=?%BU$H5U?&UKT6}8oHr{{|A1ul*M|Z)${`c{ogdH{2$q4I`01N zpts4ljd#+*{-c!n9R){P2Oc#FD2Zf|U?qJ$`Ppmn|tnmKaKIR z>@uv(9jpBv-(&6LDinJ#(-{Xij?(d%xn9Z+C7Tn$$_L~A>W%kI+<)=W_jJ3Q>~@3B z*%1B5=6B z!ZR~VCljRlS=~?Fcxz9qtzA10_lL5ABC~$uQ}rhLzZ@FBP;sZ!9pK-(TDRvQ07qE- z^q$3HrNcq{*Wux$YZ}jdVI`~b1|(ZgRF%gNH%1Mw0-k^@EZA47*tU)&cDFKikL2WX z4f!Um*q`J)3@_^_3wP(eFW4H+3WehewR3W1^`xFsw?B>P`)!a-ptM@nwo%fBVEDBl z)g3q6!=M*`r?nSzqf{^6l#(*H4g_Egf{pq&(IXK&r=bA^6c2fMb2-{HK0mu3Y6zwH z)oGnhQBd64D0tmbQQ_WH27G&qdFQk2x#sxZcb_d-Gtl&x*(Md#oI+KdfCLlvhDs|y zw__`&x=&wq7`0%%^L9#df$C~kIg}^j*VGHyp-DAGk9@NS9U;7Z z$FmmY{;u9IzHqmCTCud^`a`Y@OD}yRB1~vK;`S*k%P`Eu7YlonQc_N5$k%`p6G|Pf z5g6Dyq}0=6YYMwody=1opWhiK-NV9-X&~VNJSlpu_n0=5EzfLd%R?D^vO ze$eQsayISa(_#Ku^&q6#FhcFgb9Cchdy&|7cdPO`y%6a3p-|<5NP)f2$#M|_iX>m( z%$oLgU(%he8JSSOE`iX%#6%1vZ5Yh8({nB9~L zY4<5As<|43D;dwn2uTm9knWBU<^cHoqftlx-#*k%)L}N(v1#9OV73Ow( zY{Jki?&5ao*1boxjKQaIc~S$XgBTpWWW|mx3}t5zxo(yPQudX)-VhndpeIwTbu`Fu z*;}8mp0dA&3kC3~c4+wB3Rx2U=P;I;co&r@DrLXJ2%W>OzNs=*wmM$FZb%@Mw5jvrwqe+^CcNM^;kurMmp-$&Tfq~zw7rJ>2$dyir}!drdR}=+59Gb zKQNT<6es@_o?ihW!bcB?u_?q?#W<$Mg;l>PC)rqLh7*G~4@kYhIVz{d$a>P|%l7iK zZ-vRq)Jv@wq%?}IH~}f#K`bJIs5$${o@t8RA^T`|OloQw>GZTGG|Nk>r&m-$o_xkc z55b)8js+1(R06ZO_yW|s&rkQ7qtQtzjGXQd?Yx4CjChXMWGXi)Ds0`UVK>r!(l7uV zG6-r12DTx<1#}u*Wc1s;O3-^lt+_?bE^|iW7Qq+7#a6FMUTPHRO3E#ISK=kKRCFq6 zPn$3U<*~k6bAz)`f@}6w%5d<~BE2f(ofQ_XT55ZHY=;WX-MVM~jlwVgt_UvyP|K$F zBC#O>R03E6HIKfkC)v2ZGy`L^?oOP9h|M_S#?))*l#8IlUF|2t#$lk$8iv|(-+ZtL znGZ8`TGCCk1HbtSLi^49<&1vES&c~4c3KzJrj7+$);#jz4Gu~+e( zY>5w&Dpn9;=+^f?hD|$u^ruZLjHs00x?;54WZ!mNf`2!2?993p={zw2KvRL$n)sc8 zT%g;khQB~dhHT%y>i(j&vEf?B4zYc%3U1s0@))dqEpmoY+aH_%25`Jx2^kj)M-Y#K z`$gYohAJma*kyLz=8uhi08~)ozW=7W`3qLb{!?|cZ&-fS)Jy$$tyi3`D8GDVw|PZT zL0tcg!*f^)$*c9k)e1f(em8^r5A%1AunIC@{2P5ke9bT z`oO&~1V~!v2nneA7cyL@ZgIt3o*_!EXbTy}1z>(IUyhJVB*(80bT)^Mtvf2c;O9Be z?ez^l9~>)ymNdU&=8Y4=Ba+^b_mQ4n5(WU|nEO`%1UQGl*|H~$VFLbA?=R_NU}vlUC73>-c)2Zg2{}E0fz6JOi8%wA{4=9}DQR9l+zSEQ(Z$1d4sCMpxECmmD-pW=H z>bTB)a1=^0x7N*P%fqMWf;JObx1;3b$)b4JeJ$l3^JzxO0Zd5QRzt=`6W&1y%~`2W z34*R@ZS7bTTXk)9wQkKx{^jFRzFb)SvP0A8UV8DnxtQ-RTr^>I;(Oc?5LPr~M~;

    A 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 nubE?DSO9EB!|HX0hPM9 - - + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + - + - - + - - + - - + - - - - - - - - - + - + - - - - - - - - - - - - - - - - + - - + - - + - - - - - - - - - + @@ -146,174 +145,130 @@ - + - - - - - - - - - + - - - - - - - - - + - + + + + + + + + + - - + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - + + + + + + - - - - - - - - + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + accounts = new HashMap<>(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java similarity index 96% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java index fe17d8cdf..0d510b411 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/LotteryTicketInMemoryRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/InMemoryTicketRepository.java @@ -34,7 +34,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; * Mock database for lottery tickets. * */ -public class LotteryTicketInMemoryRepository implements LotteryTicketRepository { +public class InMemoryTicketRepository implements LotteryTicketRepository { private static Map tickets = new HashMap<>(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java similarity index 97% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java index c59a47970..f6bd3b546 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotificationsImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java @@ -24,7 +24,7 @@ package com.iluwatar.hexagonal.notifications; import com.iluwatar.hexagonal.domain.PlayerDetails; -public class LotteryNotificationsImpl implements LotteryNotifications { +public class StdOutNotifications implements LotteryNotifications { @Override public void notifyTicketSubmitted(PlayerDetails details) { diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java similarity index 94% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java index c912bb0b4..6804f21c5 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryServiceImpl.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java @@ -36,7 +36,7 @@ import java.util.Optional; * Implementation for lottery service * */ -public class LotteryServiceImpl implements LotteryService { +public class ConsoleService implements LotteryService { private final LotterySystem lotterySystem; @@ -44,7 +44,7 @@ public class LotteryServiceImpl implements LotteryService { * Constructor */ @Inject - public LotteryServiceImpl(LotterySystem lotterySystem) { + public ConsoleService(LotterySystem lotterySystem) { this.lotterySystem = lotterySystem; } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java index 5fbeb8240..c401467b5 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -23,18 +23,18 @@ package com.iluwatar.hexagonal; import com.google.inject.AbstractModule; +import com.iluwatar.hexagonal.administration.ConsoleAdministration; import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministrationImpl; +import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; +import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.domain.LotterySystem; import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.LotteryNotificationsImpl; +import com.iluwatar.hexagonal.notifications.StdOutNotifications; +import com.iluwatar.hexagonal.service.ConsoleService; import com.iluwatar.hexagonal.service.LotteryService; -import com.iluwatar.hexagonal.service.LotteryServiceImpl; /** * Guice module for testing dependencies @@ -42,11 +42,11 @@ import com.iluwatar.hexagonal.service.LotteryServiceImpl; public class LotteryTestingModule extends AbstractModule { @Override protected void configure() { - bind(LotteryTicketRepository.class).to(LotteryTicketInMemoryRepository.class); + bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); bind(LotterySystem.class).to(LotterySystemImpl.class); - bind(LotteryNotifications.class).to(LotteryNotificationsImpl.class); - bind(WireTransfers.class).to(WireTransfersImpl.class); - bind(LotteryAdministration.class).to(LotteryAdministrationImpl.class); - bind(LotteryService.class).to(LotteryServiceImpl.class); + bind(LotteryNotifications.class).to(StdOutNotifications.class); + bind(WireTransfers.class).to(InMemoryBank.class); + bind(LotteryAdministration.class).to(ConsoleAdministration.class); + bind(LotteryService.class).to(ConsoleService.class); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java index 0274feca3..25fbf460c 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java @@ -34,7 +34,7 @@ import org.junit.Test; */ public class WireTransfersTest { - private final WireTransfers bank = new WireTransfersImpl(); + private final WireTransfers bank = new InMemoryBank(); @Test public void testInit() { diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java index b20e928c8..31cb8f5f0 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java @@ -30,8 +30,6 @@ import java.util.Optional; import org.junit.Before; import org.junit.Test; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.test.LotteryTestUtils; @@ -43,7 +41,7 @@ import com.iluwatar.hexagonal.test.LotteryTestUtils; */ public class LotteryTicketRepositoryTest { - private final LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + private final LotteryTicketRepository repository = new InMemoryTicketRepository(); @Before public void clear() { @@ -52,7 +50,7 @@ public class LotteryTicketRepositoryTest { @Test public void testCrudOperations() { - LotteryTicketRepository repository = new LotteryTicketInMemoryRepository(); + LotteryTicketRepository repository = new InMemoryTicketRepository(); assertEquals(repository.findAll().size(), 0); LotteryTicket ticket = LotteryTestUtils.createLotteryTicket(); Optional id = repository.save(ticket); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 331cc0d66..732f98305 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -33,16 +33,11 @@ import java.util.Optional; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; -import com.iluwatar.hexagonal.LotteryModule; import com.iluwatar.hexagonal.LotteryTestingModule; -import com.iluwatar.hexagonal.domain.*; import org.junit.Before; import org.junit.Test; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.banking.WireTransfersImpl; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.database.LotteryTicketInMemoryRepository; import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult.CheckResult; import com.iluwatar.hexagonal.test.LotteryTestUtils; From adc6019c7e19a958fb775a073c61214f7ca01351 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 10 Sep 2016 07:14:24 +0300 Subject: [PATCH 051/145] Hexagonal pattern: Remove interfaces with only one implementation --- hexagonal/etc/hexagonal.ucls | 6 +- .../com/iluwatar/hexagonal/LotteryModule.java | 8 -- .../administration/ConsoleAdministration.java | 61 ---------- .../administration/LotteryAdministration.java | 26 +++- .../hexagonal/domain/LotterySystem.java | 80 +++++++++++-- .../hexagonal/domain/LotterySystemImpl.java | 112 ------------------ .../hexagonal/service/ConsoleService.java | 60 ---------- .../hexagonal/service/LotteryService.java | 28 +++-- .../hexagonal/LotteryTestingModule.java | 8 -- 9 files changed, 115 insertions(+), 274 deletions(-) delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls index 8f4481cbd..f698c0dd2 100644 --- a/hexagonal/etc/hexagonal.ucls +++ b/hexagonal/etc/hexagonal.ucls @@ -22,7 +22,7 @@ - @@ -62,7 +62,7 @@ - @@ -122,7 +122,7 @@ - diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java index 978d7ce9f..992e66357 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java @@ -23,17 +23,12 @@ package com.iluwatar.hexagonal; import com.google.inject.AbstractModule; -import com.iluwatar.hexagonal.administration.LotteryAdministration; -import com.iluwatar.hexagonal.administration.ConsoleAdministration; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.ConsoleService; import com.iluwatar.hexagonal.service.LotteryService; /** @@ -43,10 +38,7 @@ public class LotteryModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); - bind(LotterySystem.class).to(LotterySystemImpl.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); bind(WireTransfers.class).to(InMemoryBank.class); - bind(LotteryAdministration.class).to(ConsoleAdministration.class); - bind(LotteryService.class).to(ConsoleService.class); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java deleted file mode 100644 index 1526fc8d6..000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ /dev/null @@ -1,61 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.administration; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketId; - -import java.util.Map; - -/** - * - * Lottery administration implementation - * - */ -public class ConsoleAdministration implements LotteryAdministration { - - private final LotterySystem lotterySystem; - - @Inject - public ConsoleAdministration(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; - } - - @Override - public Map getAllSubmittedTickets() { - return lotterySystem.getAllSubmittedTickets(); - } - - @Override - public LotteryNumbers performLottery() { - return lotterySystem.performLottery(); - } - - @Override - public void resetLottery() { - lotterySystem.resetLottery(); - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java index c6c034ac9..a59461a48 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java @@ -22,7 +22,9 @@ */ package com.iluwatar.hexagonal.administration; +import com.google.inject.Inject; import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotterySystem; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; @@ -30,24 +32,36 @@ import java.util.Map; /** * - * Administrator interface for lottery service. + * Lottery administration implementation * */ -public interface LotteryAdministration { +public class LotteryAdministration { + + private final LotterySystem lotterySystem; + + @Inject + public LotteryAdministration(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; + } /** * Get all the lottery tickets submitted for lottery */ - Map getAllSubmittedTickets(); + public Map getAllSubmittedTickets() { + return lotterySystem.getAllSubmittedTickets(); + } /** * Draw lottery numbers */ - LotteryNumbers performLottery(); + public LotteryNumbers performLottery() { + return lotterySystem.performLottery(); + } /** * Begin new lottery round */ - void resetLottery(); - + public void resetLottery() { + lotterySystem.resetLottery(); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java index 2ee114556..ed58c82db 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java @@ -22,37 +22,101 @@ */ package com.iluwatar.hexagonal.domain; +import com.google.inject.Inject; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; + import java.util.Map; import java.util.Optional; /** - * Lottery system interface + * Lottery system */ -public interface LotterySystem { +public class LotterySystem { + + private final LotteryTicketRepository repository; + private final LotteryNotifications notifications; + private final WireTransfers wireTransfers; + + /** + * Constructor + */ + @Inject + public LotterySystem(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { + this.repository = repository; + this.notifications = notifications; + this.wireTransfers = wireTransfers; + } /** * Get all the lottery tickets submitted for lottery */ - Map getAllSubmittedTickets(); + public Map getAllSubmittedTickets() { + return repository.findAll(); + } /** * Draw lottery numbers */ - LotteryNumbers performLottery(); + public LotteryNumbers performLottery() { + LotteryNumbers numbers = LotteryNumbers.createRandom(); + Map tickets = getAllSubmittedTickets(); + for (LotteryTicketId id : tickets.keySet()) { + LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, + LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); + if (transferred) { + notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } else { + notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + } + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + } + } + return numbers; + } /** * Begin new lottery round */ - void resetLottery(); + public void resetLottery() { + repository.deleteAll(); + } /** * Submit lottery ticket to participate in the lottery */ - Optional submitTicket(LotteryTicket ticket); + public Optional submitTicket(LotteryTicket ticket) { + boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, + ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; + } /** * Check if lottery ticket has won */ - LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); - + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); + } + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java deleted file mode 100644 index e37185143..000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystemImpl.java +++ /dev/null @@ -1,112 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.domain; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; - -import java.util.Map; -import java.util.Optional; - -/** - * Lottery system implementation - */ -public class LotterySystemImpl implements LotterySystem { - - private final LotteryTicketRepository repository; - private final LotteryNotifications notifications; - private final WireTransfers wireTransfers; - - /** - * Constructor - */ - @Inject - public LotterySystemImpl(LotteryTicketRepository repository, LotteryNotifications notifications, - WireTransfers wireTransfers) { - this.repository = repository; - this.notifications = notifications; - this.wireTransfers = wireTransfers; - } - - @Override - public Map getAllSubmittedTickets() { - return repository.findAll(); - } - - @Override - public LotteryNumbers performLottery() { - LotteryNumbers numbers = LotteryNumbers.createRandom(); - Map tickets = getAllSubmittedTickets(); - for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); - if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, - LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); - if (transferred) { - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } else { - notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); - } - } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); - } - } - return numbers; - } - - @Override - public void resetLottery() { - repository.deleteAll(); - } - - @Override - public Optional submitTicket(LotteryTicket ticket) { - boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, - ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); - if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); - return Optional.empty(); - } - Optional optional = repository.save(ticket); - if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); - } - return optional; - } - - @Override - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - Optional optional = repository.findById(id); - if (optional.isPresent()) { - if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); - } - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); - } - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java deleted file mode 100644 index 6804f21c5..000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleService.java +++ /dev/null @@ -1,60 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.service; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; - -import java.util.Optional; - -/** - * - * Implementation for lottery service - * - */ -public class ConsoleService implements LotteryService { - - private final LotterySystem lotterySystem; - - /** - * Constructor - */ - @Inject - public ConsoleService(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; - } - - @Override - public Optional submitTicket(LotteryTicket ticket) { - return lotterySystem.submitTicket(ticket); - } - - @Override - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return lotterySystem.checkTicketForPrize(id, winningNumbers); - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java index ef2202968..80d306b04 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java @@ -22,27 +22,39 @@ */ package com.iluwatar.hexagonal.service; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; -import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.google.inject.Inject; +import com.iluwatar.hexagonal.domain.*; import java.util.Optional; /** * - * Interface for submitting and checking lottery tickets. + * Implementation for lottery service * */ -public interface LotteryService { +public class LotteryService { + + private final LotterySystem lotterySystem; + + /** + * Constructor + */ + @Inject + public LotteryService(LotterySystem lotterySystem) { + this.lotterySystem = lotterySystem; + } /** * Submit lottery ticket to participate in the lottery */ - Optional submitTicket(LotteryTicket ticket); + public Optional submitTicket(LotteryTicket ticket) { + return lotterySystem.submitTicket(ticket); + } /** * Check if lottery ticket has won */ - LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers); + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + return lotterySystem.checkTicketForPrize(id, winningNumbers); + } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java index c401467b5..252e3e66d 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -23,17 +23,12 @@ package com.iluwatar.hexagonal; import com.google.inject.AbstractModule; -import com.iluwatar.hexagonal.administration.ConsoleAdministration; -import com.iluwatar.hexagonal.administration.LotteryAdministration; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotterySystemImpl; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.ConsoleService; import com.iluwatar.hexagonal.service.LotteryService; /** @@ -43,10 +38,7 @@ public class LotteryTestingModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); - bind(LotterySystem.class).to(LotterySystemImpl.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); bind(WireTransfers.class).to(InMemoryBank.class); - bind(LotteryAdministration.class).to(ConsoleAdministration.class); - bind(LotteryService.class).to(ConsoleService.class); } } From 121ed3cca896d5870225565e50142c23f2cd4f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 10 Sep 2016 07:56:37 +0300 Subject: [PATCH 052/145] Hexagonal pattern: Move lottery administration and service to the core. Introduce console interfaces for players and administartors. --- hexagonal/etc/hexagonal.ucls | 8 +-- .../main/java/com/iluwatar/hexagonal/App.java | 4 +- .../com/iluwatar/hexagonal/LotteryModule.java | 1 - .../administration/ConsoleAdministration.java | 7 ++ .../administration/LotteryAdministration.java | 67 ------------------- ...System.java => LotteryAdministration.java} | 51 +++----------- .../{service => domain}/LotteryService.java | 33 +++++++-- .../domain/LotteryTicketChecker.java | 33 +++++++++ .../hexagonal/service/ConsoleLottery.java | 7 ++ .../hexagonal/LotteryTestingModule.java | 1 - .../hexagonal/domain/LotteryTest.java | 28 ++++---- 11 files changed, 103 insertions(+), 137 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java delete mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java rename hexagonal/src/main/java/com/iluwatar/hexagonal/domain/{LotterySystem.java => LotteryAdministration.java} (64%) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{service => domain}/LotteryService.java (55%) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls index f698c0dd2..0318576c2 100644 --- a/hexagonal/etc/hexagonal.ucls +++ b/hexagonal/etc/hexagonal.ucls @@ -62,7 +62,7 @@ - @@ -102,7 +102,7 @@ - @@ -122,7 +122,7 @@ - @@ -142,7 +142,7 @@ - diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index a1bb26454..83ae15032 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -28,13 +28,13 @@ import java.util.Random; import com.google.inject.Guice; import com.google.inject.Injector; -import com.iluwatar.hexagonal.administration.LotteryAdministration; +import com.iluwatar.hexagonal.domain.LotteryAdministration; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.PlayerDetails; -import com.iluwatar.hexagonal.service.LotteryService; +import com.iluwatar.hexagonal.domain.LotteryService; /** * diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java index 992e66357..dbaf494c2 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java @@ -29,7 +29,6 @@ import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.LotteryService; /** * Guice module for binding production dependencies diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java new file mode 100644 index 000000000..5238edd24 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -0,0 +1,7 @@ +package com.iluwatar.hexagonal.administration; + +/** + * Console interface for lottery administration + */ +public class ConsoleAdministration { +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java deleted file mode 100644 index a59461a48..000000000 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/LotteryAdministration.java +++ /dev/null @@ -1,67 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.hexagonal.administration; - -import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotterySystem; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.LotteryTicketId; - -import java.util.Map; - -/** - * - * Lottery administration implementation - * - */ -public class LotteryAdministration { - - private final LotterySystem lotterySystem; - - @Inject - public LotteryAdministration(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; - } - - /** - * Get all the lottery tickets submitted for lottery - */ - public Map getAllSubmittedTickets() { - return lotterySystem.getAllSubmittedTickets(); - } - - /** - * Draw lottery numbers - */ - public LotteryNumbers performLottery() { - return lotterySystem.performLottery(); - } - - /** - * Begin new lottery round - */ - public void resetLottery() { - lotterySystem.resetLottery(); - } -} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java similarity index 64% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index ed58c82db..7117397d3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotterySystem.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -28,26 +28,26 @@ import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import java.util.Map; -import java.util.Optional; /** - * Lottery system + * + * Lottery administration implementation + * */ -public class LotterySystem { +public class LotteryAdministration { private final LotteryTicketRepository repository; private final LotteryNotifications notifications; private final WireTransfers wireTransfers; + private final LotteryTicketChecker checker; - /** - * Constructor - */ @Inject - public LotterySystem(LotteryTicketRepository repository, LotteryNotifications notifications, - WireTransfers wireTransfers) { + public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { this.repository = repository; this.notifications = notifications; this.wireTransfers = wireTransfers; + this.checker = new LotteryTicketChecker(this.repository); } /** @@ -64,7 +64,7 @@ public class LotterySystem { LotteryNumbers numbers = LotteryNumbers.createRandom(); Map tickets = getAllSubmittedTickets(); for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = checkTicketForPrize(id, numbers); + LotteryTicketCheckResult result = checker.checkTicketForPrize(id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); @@ -86,37 +86,4 @@ public class LotterySystem { public void resetLottery() { repository.deleteAll(); } - - /** - * Submit lottery ticket to participate in the lottery - */ - public Optional submitTicket(LotteryTicket ticket) { - boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, - ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); - if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); - return Optional.empty(); - } - Optional optional = repository.save(ticket); - if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); - } - return optional; - } - - /** - * Check if lottery ticket has won - */ - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - Optional optional = repository.findById(id); - if (optional.isPresent()) { - if (optional.get().getNumbers().equals(winningNumbers)) { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); - } - } else { - return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); - } - } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java similarity index 55% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 80d306b04..76cd47d1d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -20,10 +20,12 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.service; +package com.iluwatar.hexagonal.domain; import com.google.inject.Inject; -import com.iluwatar.hexagonal.domain.*; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.notifications.LotteryNotifications; import java.util.Optional; @@ -34,27 +36,44 @@ import java.util.Optional; */ public class LotteryService { - private final LotterySystem lotterySystem; + private final LotteryTicketRepository repository; + private final LotteryNotifications notifications; + private final WireTransfers wireTransfers; + private final LotteryTicketChecker checker; /** * Constructor */ @Inject - public LotteryService(LotterySystem lotterySystem) { - this.lotterySystem = lotterySystem; + public LotteryService(LotteryTicketRepository repository, LotteryNotifications notifications, + WireTransfers wireTransfers) { + this.repository = repository; + this.notifications = notifications; + this.wireTransfers = wireTransfers; + this.checker = new LotteryTicketChecker(this.repository); } /** * Submit lottery ticket to participate in the lottery */ public Optional submitTicket(LotteryTicket ticket) { - return lotterySystem.submitTicket(ticket); + boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, + ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); + if (result == false) { + notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + return Optional.empty(); + } + Optional optional = repository.save(ticket); + if (optional.isPresent()) { + notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + } + return optional; } /** * Check if lottery ticket has won */ public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return lotterySystem.checkTicketForPrize(id, winningNumbers); + return checker.checkTicketForPrize(id, winningNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java new file mode 100644 index 000000000..10042528b --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java @@ -0,0 +1,33 @@ +package com.iluwatar.hexagonal.domain; + +import com.iluwatar.hexagonal.database.LotteryTicketRepository; + +import java.util.Optional; + +/** + * Lottery ticket checker + */ +public class LotteryTicketChecker { + + private final LotteryTicketRepository repository; + + public LotteryTicketChecker(LotteryTicketRepository repository) { + this.repository = repository; + } + + /** + * Check if lottery ticket has won + */ + public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + Optional optional = repository.findById(id); + if (optional.isPresent()) { + if (optional.get().getNumbers().equals(winningNumbers)) { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.WIN_PRIZE, 1000); + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.NO_PRIZE); + } + } else { + return new LotteryTicketCheckResult(LotteryTicketCheckResult.CheckResult.TICKET_NOT_SUBMITTED); + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java new file mode 100644 index 000000000..e48410982 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -0,0 +1,7 @@ +package com.iluwatar.hexagonal.service; + +/** + * Console interface for lottery players + */ +public class ConsoleLottery { +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java index 252e3e66d..22bfa8f01 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java @@ -29,7 +29,6 @@ import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; -import com.iluwatar.hexagonal.service.LotteryService; /** * Guice module for testing dependencies diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index 732f98305..ed6f8d180 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -50,7 +50,9 @@ public class LotteryTest { private Injector injector; @Inject - private LotterySystem lotterySystem; + private LotteryAdministration administration; + @Inject + private LotteryService service; @Inject private WireTransfers wireTransfers; @@ -68,34 +70,34 @@ public class LotteryTest { @Test public void testLottery() { // admin resets the lottery - lotterySystem.resetLottery(); - assertEquals(lotterySystem.getAllSubmittedTickets().size(), 0); + administration.resetLottery(); + assertEquals(administration.getAllSubmittedTickets().size(), 0); // players submit the lottery tickets - Optional ticket1 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", + Optional ticket1 = service.submitTicket(LotteryTestUtils.createLotteryTicket("cvt@bbb.com", "123-12312", "+32425255", new HashSet<>(Arrays.asList(1, 2, 3, 4)))); assertTrue(ticket1.isPresent()); - Optional ticket2 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", + Optional ticket2 = service.submitTicket(LotteryTestUtils.createLotteryTicket("ant@bac.com", "123-12312", "+32423455", new HashSet<>(Arrays.asList(11, 12, 13, 14)))); assertTrue(ticket2.isPresent()); - Optional ticket3 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", + Optional ticket3 = service.submitTicket(LotteryTestUtils.createLotteryTicket("arg@boo.com", "123-12312", "+32421255", new HashSet<>(Arrays.asList(6, 8, 13, 19)))); assertTrue(ticket3.isPresent()); - assertEquals(lotterySystem.getAllSubmittedTickets().size(), 3); + assertEquals(administration.getAllSubmittedTickets().size(), 3); // perform lottery - LotteryNumbers winningNumbers = lotterySystem.performLottery(); + LotteryNumbers winningNumbers = administration.performLottery(); // cheat a bit for testing sake, use winning numbers to submit another ticket - Optional ticket4 = lotterySystem.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", + Optional ticket4 = service.submitTicket(LotteryTestUtils.createLotteryTicket("lucky@orb.com", "123-12312", "+12421255", winningNumbers.getNumbers())); assertTrue(ticket4.isPresent()); - assertEquals(lotterySystem.getAllSubmittedTickets().size(), 4); + assertEquals(administration.getAllSubmittedTickets().size(), 4); // check winners - Map tickets = lotterySystem.getAllSubmittedTickets(); + Map tickets = administration.getAllSubmittedTickets(); for (LotteryTicketId id: tickets.keySet()) { - LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(id, winningNumbers); + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(id, winningNumbers); assertTrue(checkResult.getResult() != CheckResult.TICKET_NOT_SUBMITTED); if (checkResult.getResult().equals(CheckResult.WIN_PRIZE)) { assertTrue(checkResult.getPrizeAmount() > 0); @@ -105,7 +107,7 @@ public class LotteryTest { } // check another ticket that has not been submitted - LotteryTicketCheckResult checkResult = lotterySystem.checkTicketForPrize(new LotteryTicketId(), winningNumbers); + LotteryTicketCheckResult checkResult = service.checkTicketForPrize(new LotteryTicketId(), winningNumbers); assertTrue(checkResult.getResult() == CheckResult.TICKET_NOT_SUBMITTED); assertEquals(checkResult.getPrizeAmount(), 0); } From e17d72bca81523ae4521cc4b198e72980aad8b38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 10 Sep 2016 14:28:05 +0300 Subject: [PATCH 053/145] Hexagonal pattern: Added console interfaces for players and administration. --- .../main/java/com/iluwatar/hexagonal/App.java | 81 +---------- .../administration/ConsoleAdministration.java | 77 +++++++++++ .../domain/LotteryAdministration.java | 3 + .../hexagonal/domain/LotteryNumbers.java | 7 +- .../hexagonal/domain/LotteryTicket.java | 5 + .../domain/LotteryTicketChecker.java | 22 +++ .../hexagonal/domain/LotteryTicketId.java | 9 ++ .../hexagonal/domain/PlayerDetails.java | 7 + .../hexagonal/{ => module}/LotteryModule.java | 2 +- .../module}/LotteryTestingModule.java | 2 +- .../hexagonal/sampledata/SampleData.java | 107 ++++++++++++++ .../hexagonal/service/ConsoleLottery.java | 130 ++++++++++++++++++ .../hexagonal/domain/LotteryTest.java | 2 +- 13 files changed, 374 insertions(+), 80 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{ => module}/LotteryModule.java (97%) rename hexagonal/src/{test/java/com/iluwatar/hexagonal => main/java/com/iluwatar/hexagonal/module}/LotteryTestingModule.java (97%) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 83ae15032..a7d31446b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -22,19 +22,12 @@ */ package com.iluwatar.hexagonal; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - import com.google.inject.Guice; import com.google.inject.Injector; import com.iluwatar.hexagonal.domain.LotteryAdministration; -import com.iluwatar.hexagonal.banking.InMemoryBank; -import com.iluwatar.hexagonal.domain.LotteryConstants; -import com.iluwatar.hexagonal.domain.LotteryNumbers; -import com.iluwatar.hexagonal.domain.LotteryTicket; -import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.module.LotteryTestingModule; +import com.iluwatar.hexagonal.sampledata.SampleData; /** * @@ -68,64 +61,13 @@ import com.iluwatar.hexagonal.domain.LotteryService; * */ public class App { - - private static final List PLAYERS; - - static { - PLAYERS = new ArrayList<>(); - PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); - PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); - PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); - PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); - PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); - PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); - PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); - PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); - PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); - PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); - PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); - PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); - PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); - PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); - PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); - PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); - PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); - PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); - PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); - PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); - PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); - PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); - PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); - PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); - PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); - PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); - PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); - PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); - PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); - PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); - PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); - PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); - PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); - PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); - PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); - PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); - PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); - InMemoryBank wireTransfers = new InMemoryBank(); - Random random = new Random(); - for (int i = 0; i < PLAYERS.size(); i++) { - wireTransfers.setFunds(PLAYERS.get(i).getBankAccount(), - random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); - } - } - + /** * Program entry point */ public static void main(String[] args) { - Injector injector = Guice.createInjector(new LotteryModule()); + Injector injector = Guice.createInjector(new LotteryTestingModule()); // start new lottery round LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); @@ -133,22 +75,9 @@ public class App { // submit some lottery tickets LotteryService service = injector.getInstance(LotteryService.class); - submitTickets(service, 20); + SampleData.submitTickets(service, 20); // perform lottery administartion.performLottery(); } - - private static void submitTickets(LotteryService lotteryService, int numTickets) { - for (int i = 0; i < numTickets; i++) { - LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); - lotteryService.submitTicket(ticket); - } - } - - private static PlayerDetails getRandomPlayerDetails() { - Random random = new Random(); - int idx = random.nextInt(PLAYERS.size()); - return PLAYERS.get(idx); - } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index 5238edd24..4301c07e3 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -1,7 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.administration; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.iluwatar.hexagonal.domain.LotteryAdministration; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.sampledata.SampleData; + +import java.util.Scanner; + /** * Console interface for lottery administration */ public class ConsoleAdministration { + + /** + * Program entry point + */ + public static void main(String[] args) { + Injector injector = Guice.createInjector(new LotteryModule()); + LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); + LotteryService service = injector.getInstance(LotteryService.class); + SampleData.submitTickets(service, 20); + Scanner scanner = new Scanner(System.in); + boolean exit = false; + while (!exit) { + printMainMenu(); + String cmd = readString(scanner); + if (cmd.equals("1")) { + administartion.getAllSubmittedTickets().forEach((k,v)->System.out.println("Key: " + k + " Value: " + v)); + } else if (cmd.equals("2")) { + LotteryNumbers numbers = administartion.performLottery(); + System.out.println("The winning numbers: " + numbers); + System.out.println("Time to reset the database for next round, eh?"); + } else if (cmd.equals("3")) { + administartion.resetLottery(); + System.out.println("The lottery ticket database was cleared."); + } else if (cmd.equals("4")) { + exit = true; + } else { + System.out.println("Unknown command: " + cmd); + } + } + } + + private static void printMainMenu() { + System.out.println(""); + System.out.println("### Lottery Administration Console ###"); + System.out.println("(1) Show all submitted tickets"); + System.out.println("(2) Perform lottery draw"); + System.out.println("(3) Reset lottery ticket database"); + System.out.println("(4) Exit"); + } + + private static String readString(Scanner scanner) { + System.out.print("> "); + String cmd = scanner.next(); + return cmd; + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 7117397d3..f73390863 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -41,6 +41,9 @@ public class LotteryAdministration { private final WireTransfers wireTransfers; private final LotteryTicketChecker checker; + /** + * Constructor + */ @Inject public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications, WireTransfers wireTransfers) { diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java index 69d654657..9ce8d61bd 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -92,7 +92,12 @@ public class LotteryNumbers { } } } - + + @Override + public String toString() { + return "LotteryNumbers{" + "numbers=" + numbers + '}'; + } + /** * * Helper class for generating random numbers. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java index e5828cfbf..08064f46c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -61,6 +61,11 @@ public class LotteryTicket { return lotteryNumbers; } + @Override + public String toString() { + return playerDetails.toString() + " " + lotteryNumbers.toString(); + } + @Override public int hashCode() { final int prime = 31; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java index 10042528b..ce193386b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java @@ -1,3 +1,25 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.domain; import com.iluwatar.hexagonal.database.LotteryTicketRepository; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java index 710091222..e2aa51792 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -34,8 +34,17 @@ public class LotteryTicketId { public LotteryTicketId() { id = UUID.randomUUID(); } + + public LotteryTicketId(String str) { + id = UUID.fromString(str); + } public UUID getId() { return id; } + + @Override + public String toString() { + return id.toString(); + } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java index 2fcdb6eb3..1061ad553 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -70,6 +70,13 @@ public class PlayerDetails { return phoneNumber; } + @Override + public String toString() { + return "PlayerDetails{" + "emailAddress='" + emailAddress + '\'' + + ", bankAccountNumber='" + bankAccountNumber + '\'' + + ", phoneNumber='" + phoneNumber + '\'' + '}'; + } + @Override public int hashCode() { final int prime = 31; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java similarity index 97% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index dbaf494c2..c62dbfa54 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal; +package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; import com.iluwatar.hexagonal.banking.InMemoryBank; diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java similarity index 97% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java index 22bfa8f01..c934ed43c 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/LotteryTestingModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java @@ -20,7 +20,7 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal; +package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; import com.iluwatar.hexagonal.banking.InMemoryBank; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java new file mode 100644 index 000000000..1af18118d --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java @@ -0,0 +1,107 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.sampledata; + +import com.iluwatar.hexagonal.banking.InMemoryBank; +import com.iluwatar.hexagonal.domain.LotteryConstants; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.PlayerDetails; + +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +/** + * Utilities for creating sample lottery tickets + */ +public class SampleData { + + private static final List PLAYERS; + + static { + PLAYERS = new ArrayList<>(); + PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); + PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); + PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); + PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); + PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); + PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); + PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); + PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); + PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); + PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); + PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); + PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); + PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); + PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); + PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); + PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); + PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); + PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); + PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); + PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); + PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); + PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); + PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); + PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); + PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); + PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); + PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); + PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); + PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); + PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); + PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); + PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); + PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); + PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); + PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); + PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); + PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); + PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + InMemoryBank wireTransfers = new InMemoryBank(); + Random random = new Random(); + for (int i = 0; i < PLAYERS.size(); i++) { + wireTransfers.setFunds(PLAYERS.get(i).getBankAccount(), + random.nextInt(LotteryConstants.PLAYER_MAX_SALDO)); + } + } + + /** + * Inserts lottery tickets into the database based on the sample data + */ + public static void submitTickets(LotteryService lotteryService, int numTickets) { + for (int i = 0; i < numTickets; i++) { + LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); + lotteryService.submitTicket(ticket); + } + } + + private static PlayerDetails getRandomPlayerDetails() { + Random random = new Random(); + int idx = random.nextInt(PLAYERS.size()); + return PLAYERS.get(idx); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index e48410982..aade24a91 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -1,7 +1,137 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ package com.iluwatar.hexagonal.service; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.iluwatar.hexagonal.banking.WireTransfers; +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryService; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.sampledata.SampleData; + +import java.util.HashSet; +import java.util.Optional; +import java.util.Scanner; +import java.util.Set; + /** * Console interface for lottery players */ public class ConsoleLottery { + + + /** + * Program entry point + */ + public static void main(String[] args) { + Injector injector = Guice.createInjector(new LotteryModule()); + LotteryService service = injector.getInstance(LotteryService.class); + WireTransfers bank = injector.getInstance(WireTransfers.class); + SampleData.submitTickets(service, 20); + Scanner scanner = new Scanner(System.in); + boolean exit = false; + while (!exit) { + printMainMenu(); + String cmd = readString(scanner); + if (cmd.equals("1")) { + System.out.println("What is the account number?"); + String account = readString(scanner); + System.out.println(String.format("The account %s has %d credits.", account, bank.getFunds(account))); + } else if (cmd.equals("2")) { + System.out.println("What is the account number?"); + String account = readString(scanner); + System.out.println("How many credits do you want to deposit?"); + String amount = readString(scanner); + bank.setFunds(account, Integer.parseInt(amount)); + System.out.println(String.format("The account %s now has %d credits.", account, bank.getFunds(account))); + } else if (cmd.equals("3")) { + System.out.println("What is your email address?"); + String email = readString(scanner); + System.out.println("What is your bank account number?"); + String account = readString(scanner); + System.out.println("What is your phone number?"); + String phone = readString(scanner); + PlayerDetails details = PlayerDetails.create(email, account, phone); + System.out.println("Give 4 comma separated lottery numbers?"); + String numbers = readString(scanner); + String[] parts = numbers.split(","); + Set chosen = new HashSet<>(); + for (int i = 0; i < 4; i++) { + chosen.add(Integer.parseInt(parts[i])); + } + LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); + LotteryTicket lotteryTicket = LotteryTicket.create(details, lotteryNumbers); + Optional id = service.submitTicket(lotteryTicket); + if (id.isPresent()) { + System.out.println("Submitted lottery ticket with id: " + id.get()); + } else { + System.out.println("Failed submitting lottery ticket - please try again."); + } + } else if (cmd.equals("4")) { + System.out.println("What is the ID of the lottery ticket?"); + String id = readString(scanner); + System.out.println("Give the 4 comma separated winning numbers?"); + String numbers = readString(scanner); + String[] parts = numbers.split(","); + Set winningNumbers = new HashSet<>(); + for (int i = 0; i < 4; i++) { + winningNumbers.add(Integer.parseInt(parts[i])); + } + LotteryTicketCheckResult result = service.checkTicketForPrize( + new LotteryTicketId(id), LotteryNumbers.create(winningNumbers)); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + System.out.println("Congratulations! The lottery ticket has won!"); + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + System.out.println("Unfortunately the lottery ticket did not win."); + } else { + System.out.println("Such lottery ticket has not been submitted."); + } + } else if (cmd.equals("5")) { + exit = true; + } else { + System.out.println("Unknown command"); + } + } + } + + private static void printMainMenu() { + System.out.println(""); + System.out.println("### Lottery Service Console ###"); + System.out.println("(1) Query lottery account funds"); + System.out.println("(2) Add funds to lottery account"); + System.out.println("(3) Submit ticket"); + System.out.println("(4) Check ticket"); + System.out.println("(5) Exit"); + } + + private static String readString(Scanner scanner) { + System.out.print("> "); + String cmd = scanner.next(); + return cmd; + } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java index ed6f8d180..943440f07 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTest.java @@ -33,7 +33,7 @@ import java.util.Optional; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; -import com.iluwatar.hexagonal.LotteryTestingModule; +import com.iluwatar.hexagonal.module.LotteryTestingModule; import org.junit.Before; import org.junit.Test; From 4410419914ee89c7eea2382ae49b056451b420cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 10:34:12 +0300 Subject: [PATCH 054/145] Hexagonal pattern: Simplified lottery ticket ids --- .../hexagonal/domain/LotteryTicketId.java | 38 ++++++++++++---- .../hexagonal/service/ConsoleLottery.java | 4 +- .../hexagonal/domain/LotteryTicketIdTest.java | 45 +++++++++++++++++++ 3 files changed, 75 insertions(+), 12 deletions(-) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java index e2aa51792..2f8a59bba 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketId.java @@ -22,29 +22,49 @@ */ package com.iluwatar.hexagonal.domain; -import java.util.UUID; - /** * Lottery ticked id */ public class LotteryTicketId { - - private final UUID id; + + private static volatile int numAllocated; + private final int id; public LotteryTicketId() { - id = UUID.randomUUID(); + this.id = numAllocated + 1; + numAllocated++; } - public LotteryTicketId(String str) { - id = UUID.fromString(str); + public LotteryTicketId(int id) { + this.id = id; } - public UUID getId() { + public int getId() { return id; } @Override public String toString() { - return id.toString(); + return String.format("%d", id); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + LotteryTicketId that = (LotteryTicketId) o; + + return id == that.id; + + } + + @Override + public int hashCode() { + return id; } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index aade24a91..5463eca7c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -32,7 +32,6 @@ import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.module.LotteryModule; -import com.iluwatar.hexagonal.sampledata.SampleData; import java.util.HashSet; import java.util.Optional; @@ -52,7 +51,6 @@ public class ConsoleLottery { Injector injector = Guice.createInjector(new LotteryModule()); LotteryService service = injector.getInstance(LotteryService.class); WireTransfers bank = injector.getInstance(WireTransfers.class); - SampleData.submitTickets(service, 20); Scanner scanner = new Scanner(System.in); boolean exit = false; while (!exit) { @@ -103,7 +101,7 @@ public class ConsoleLottery { winningNumbers.add(Integer.parseInt(parts[i])); } LotteryTicketCheckResult result = service.checkTicketForPrize( - new LotteryTicketId(id), LotteryNumbers.create(winningNumbers)); + new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { System.out.println("Congratulations! The lottery ticket has won!"); } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java new file mode 100644 index 000000000..c5e4c1541 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketIdTest.java @@ -0,0 +1,45 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.domain; + +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Tests for lottery ticket id + */ +public class LotteryTicketIdTest { + + @Test + public void testEquals() { + LotteryTicketId ticketId1 = new LotteryTicketId(); + LotteryTicketId ticketId2 = new LotteryTicketId(); + LotteryTicketId ticketId3 = new LotteryTicketId(); + assertFalse(ticketId1.equals(ticketId2)); + assertFalse(ticketId2.equals(ticketId3)); + LotteryTicketId ticketId4 = new LotteryTicketId(ticketId1.getId()); + assertTrue(ticketId1.equals(ticketId4)); + } +} From a85463470ea3a679ac69e6e1c43c2cb7ae512139 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 13:53:00 +0300 Subject: [PATCH 055/145] Hexagonal pattern: Add mongo driver dependency --- hexagonal/pom.xml | 4 ++++ pom.xml | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index a73fea48d..ce1a7049a 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -43,5 +43,9 @@ com.google.inject guice + + org.mongodb + mongo-java-driver + diff --git a/pom.xml b/pom.xml index 023579e97..b1ae7d811 100644 --- a/pom.xml +++ b/pom.xml @@ -48,6 +48,7 @@ 2.22 1.4.1 4.0 + 3.3.0 abstract-factory @@ -242,6 +243,11 @@ guice ${guice.version} + + org.mongodb + mongo-java-driver + ${mongo-java-driver.version} + From 626c56730c7dac32eb33bd414ad4a9436736a097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 20:02:34 +0300 Subject: [PATCH 056/145] Hexagonal pattern: Added Mongo based ticket repository and set production configuration to use that --- .../administration/ConsoleAdministration.java | 2 +- .../database/MongoTicketRepository.java | 194 ++++++++++++++++++ .../hexagonal/domain/LotteryNumbers.java | 18 ++ .../hexagonal/domain/LotteryTicket.java | 24 ++- .../hexagonal/module/LotteryModule.java | 4 +- .../hexagonal/sampledata/SampleData.java | 4 +- .../hexagonal/service/ConsoleLottery.java | 2 +- ...java => InMemoryTicketRepositoryTest.java} | 2 +- .../database/MongoTicketRepositoryTest.java | 94 +++++++++ .../hexagonal/domain/LotteryTicketTest.java | 6 +- .../hexagonal/test/LotteryTestUtils.java | 3 +- 11 files changed, 339 insertions(+), 14 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/database/{LotteryTicketRepositoryTest.java => InMemoryTicketRepositoryTest.java} (98%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index 4301c07e3..6a846280c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -54,7 +54,7 @@ public class ConsoleAdministration { administartion.getAllSubmittedTickets().forEach((k,v)->System.out.println("Key: " + k + " Value: " + v)); } else if (cmd.equals("2")) { LotteryNumbers numbers = administartion.performLottery(); - System.out.println("The winning numbers: " + numbers); + System.out.println("The winning numbers: " + numbers.getNumbersAsString()); System.out.println("Time to reset the database for next round, eh?"); } else if (cmd.equals("3")) { administartion.resetLottery(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java new file mode 100644 index 000000000..ff0439af8 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -0,0 +1,194 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.database; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.bson.Document; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Optional; + +/** + * Mongo lottery ticket database + */ +public class MongoTicketRepository implements LotteryTicketRepository { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 27017; + private static final String DEFAULT_DB = "lotteryDB"; + private static final String DEFAULT_TICKETS_COLLECTION = "lotteryTickets"; + private static final String DEFAULT_COUNTERS_COLLECTION = "counters"; + + private MongoClient mongoClient; + private MongoDatabase database; + private MongoCollection ticketsCollection; + private MongoCollection countersCollection; + + /** + * Constructor + */ + public MongoTicketRepository() { + connect(); + } + + /** + * Constructor accepting parameters + */ + public MongoTicketRepository(String host, int port, String dbName, String ticketsCollectionName, + String countersCollectionName) { + connect(host, port, dbName, ticketsCollectionName, countersCollectionName); + } + + /** + * Connect to database with default parameters + */ + public void connect() { + connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); + } + + /** + * Connect to database with given parameters + */ + public void connect(String host, int port, String dbName, String ticketsCollectionName, + String countersCollectionName) { + if (mongoClient != null) { + mongoClient.close(); + } + mongoClient = new MongoClient(host , port); + database = mongoClient.getDatabase(dbName); + ticketsCollection = database.getCollection(ticketsCollectionName); + countersCollection = database.getCollection(countersCollectionName); + if (countersCollection.count() <= 0) { + initCounters(); + } + } + + private void initCounters() { + Document doc = new Document("_id", "ticketId").append("seq", 1); + countersCollection.insertOne(doc); + } + + /** + * @return next ticket id + */ + public int getNextId() { + Document find = new Document("_id", "ticketId"); + Document increase = new Document("seq", 1); + Document update = new Document("$inc", increase); + Document result = countersCollection.findOneAndUpdate(find, update); + return result.getInteger("seq"); + } + + /** + * @return mongo client + */ + public MongoClient getMongoClient() { + return mongoClient; + } + + /** + * + * @return mongo database + */ + public MongoDatabase getMongoDatabase() { + return database; + } + + /** + * + * @return tickets collection + */ + public MongoCollection getTicketsCollection() { + return ticketsCollection; + } + + /** + * + * @return counters collection + */ + public MongoCollection getCountersCollection() { + return countersCollection; + } + + @Override + public Optional findById(LotteryTicketId id) { + Document find = new Document("ticketId", id.getId()); + ArrayList results = ticketsCollection.find(find).limit(1).into(new ArrayList()); + if (results.size() > 0) { + LotteryTicket lotteryTicket = docToTicket(results.get(0)); + return Optional.of(lotteryTicket); + } else { + return Optional.empty(); + } + } + + @Override + public Optional save(LotteryTicket ticket) { + int ticketId = getNextId(); + Document doc = new Document("ticketId", ticketId); + doc.put("email", ticket.getPlayerDetails().getEmail()); + doc.put("bank", ticket.getPlayerDetails().getBankAccount()); + doc.put("phone", ticket.getPlayerDetails().getPhoneNumber()); + doc.put("numbers", ticket.getNumbers().getNumbersAsString()); + ticketsCollection.insertOne(doc); + return Optional.of(new LotteryTicketId(ticketId)); + } + + @Override + public Map findAll() { + Map map = new HashMap<>(); + ArrayList docs = ticketsCollection.find(new Document()).into(new ArrayList()); + for (Document doc: docs) { + LotteryTicket lotteryTicket = docToTicket(doc); + map.put(lotteryTicket.getId(), lotteryTicket); + } + return map; + } + + @Override + public void deleteAll() { + ticketsCollection.deleteMany(new Document()); + } + + private LotteryTicket docToTicket(Document doc) { + PlayerDetails playerDetails = PlayerDetails.create(doc.getString("email"), doc.getString("bank"), + doc.getString("phone")); + int[] numArray = Arrays.asList(doc.getString("numbers").split(",")).stream().mapToInt(Integer::parseInt).toArray(); + HashSet numbers = new HashSet<>(); + for (int num: numArray) { + numbers.add(num); + } + LotteryNumbers lotteryNumbers = LotteryNumbers.create(numbers); + return LotteryTicket.create(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java index 9ce8d61bd..930c919da 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryNumbers.java @@ -22,8 +22,10 @@ */ package com.iluwatar.hexagonal.domain; +import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.PrimitiveIterator; import java.util.Random; import java.util.Set; @@ -78,6 +80,22 @@ public class LotteryNumbers { public Set getNumbers() { return Collections.unmodifiableSet(numbers); } + + /** + * @return numbers as comma separated string + */ + public String getNumbersAsString() { + List list = new ArrayList<>(); + list.addAll(numbers); + StringBuilder builder = new StringBuilder(); + for (int i = 0; i < NUM_NUMBERS; i++) { + builder.append(list.get(i)); + if (i < NUM_NUMBERS - 1) { + builder.append(","); + } + } + return builder.toString(); + } /** * Generates 4 unique random numbers between 1-20 into numbers set. diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java index 08064f46c..3e9ebdae4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -29,13 +29,15 @@ package com.iluwatar.hexagonal.domain; */ public class LotteryTicket { + private LotteryTicketId id; private final PlayerDetails playerDetails; private final LotteryNumbers lotteryNumbers; - + /** * Constructor. */ - private LotteryTicket(PlayerDetails details, LotteryNumbers numbers) { + private LotteryTicket(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { + this.id = id; playerDetails = details; lotteryNumbers = numbers; } @@ -43,8 +45,8 @@ public class LotteryTicket { /** * Factory for creating lottery tickets; */ - public static LotteryTicket create(PlayerDetails details, LotteryNumbers numbers) { - return new LotteryTicket(details, numbers); + public static LotteryTicket create(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { + return new LotteryTicket(id, details, numbers); } /** @@ -61,6 +63,20 @@ public class LotteryTicket { return lotteryNumbers; } + /** + * @return id + */ + public LotteryTicketId getId() { + return id; + } + + /** + * set id + */ + public void setId(LotteryTicketId id) { + this.id = id; + } + @Override public String toString() { return playerDetails.toString() + " " + lotteryNumbers.toString(); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index c62dbfa54..0a0177f25 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -25,8 +25,8 @@ package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; -import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; +import com.iluwatar.hexagonal.database.MongoTicketRepository; import com.iluwatar.hexagonal.notifications.LotteryNotifications; import com.iluwatar.hexagonal.notifications.StdOutNotifications; @@ -36,7 +36,7 @@ import com.iluwatar.hexagonal.notifications.StdOutNotifications; public class LotteryModule extends AbstractModule { @Override protected void configure() { - bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); + bind(LotteryTicketRepository.class).to(MongoTicketRepository.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); bind(WireTransfers.class).to(InMemoryBank.class); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java index 1af18118d..b9c779c34 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java @@ -27,6 +27,7 @@ import com.iluwatar.hexagonal.domain.LotteryConstants; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import java.util.ArrayList; @@ -94,7 +95,8 @@ public class SampleData { */ public static void submitTickets(LotteryService lotteryService, int numTickets) { for (int i = 0; i < numTickets; i++) { - LotteryTicket ticket = LotteryTicket.create(getRandomPlayerDetails(), LotteryNumbers.createRandom()); + LotteryTicket ticket = LotteryTicket.create(new LotteryTicketId(), + getRandomPlayerDetails(), LotteryNumbers.createRandom()); lotteryService.submitTicket(ticket); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index 5463eca7c..afacc35cc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -83,7 +83,7 @@ public class ConsoleLottery { chosen.add(Integer.parseInt(parts[i])); } LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); - LotteryTicket lotteryTicket = LotteryTicket.create(details, lotteryNumbers); + LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); Optional id = service.submitTicket(lotteryTicket); if (id.isPresent()) { System.out.println("Submitted lottery ticket with id: " + id.get()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/InMemoryTicketRepositoryTest.java similarity index 98% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/database/InMemoryTicketRepositoryTest.java index 31cb8f5f0..d32e594a8 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/LotteryTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/InMemoryTicketRepositoryTest.java @@ -39,7 +39,7 @@ import com.iluwatar.hexagonal.test.LotteryTestUtils; * Tests for {@link LotteryTicketRepository} * */ -public class LotteryTicketRepositoryTest { +public class InMemoryTicketRepositoryTest { private final LotteryTicketRepository repository = new InMemoryTicketRepository(); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java new file mode 100644 index 000000000..dc0d5c7fd --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -0,0 +1,94 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.database; + +import com.iluwatar.hexagonal.domain.LotteryNumbers; +import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.mongodb.MongoClient; +import org.junit.Before; +import org.junit.Test; + +import java.util.Optional; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +/** + * Tests for Mongo based ticket repository + */ +public class MongoTicketRepositoryTest { + + private static final String TEST_HOST = "localhost"; + private static final int TEST_PORT = 27017; + private static final String TEST_DB = "lotteryDB"; + private static final String TEST_TICKETS_COLLECTION = "lotteryTickets"; + private static final String TEST_COUNTERS_COLLECTION = "counters"; + + private MongoTicketRepository repository; + + @Before + public void init() { + MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + mongoClient.dropDatabase(TEST_DB); + mongoClient.close(); + repository = new MongoTicketRepository(TEST_HOST, TEST_PORT, TEST_DB, TEST_TICKETS_COLLECTION, + TEST_COUNTERS_COLLECTION); + } + + @Test + public void testSetup() { + assertTrue(repository.getCountersCollection().count() == 1); + assertTrue(repository.getTicketsCollection().count() == 0); + } + + @Test + public void testNextId() { + assertEquals(1, repository.getNextId()); + assertEquals(2, repository.getNextId()); + assertEquals(3, repository.getNextId()); + } + + @Test + public void testCrudOperations() { + // create new lottery ticket and save it + PlayerDetails details = PlayerDetails.create("foo@bar.com", "123-123", "07001234"); + LotteryNumbers random = LotteryNumbers.createRandom(); + LotteryTicket original = LotteryTicket.create(new LotteryTicketId(), details, random); + Optional saved = repository.save(original); + assertEquals(1, repository.getTicketsCollection().count()); + assertTrue(saved.isPresent()); + // fetch the saved lottery ticket from database and check its contents + Optional found = repository.findById(saved.get()); + assertTrue(found.isPresent()); + LotteryTicket ticket = found.get(); + assertEquals("foo@bar.com", ticket.getPlayerDetails().getEmail()); + assertEquals("123-123", ticket.getPlayerDetails().getBankAccount()); + assertEquals("07001234", ticket.getPlayerDetails().getPhoneNumber()); + assertEquals(original.getNumbers(), ticket.getNumbers()); + // clear the collection + repository.deleteAll(); + assertEquals(0, repository.getTicketsCollection().count()); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java index e1918686a..4840dc897 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -36,14 +36,14 @@ public class LotteryTicketTest { public void testEquals() { PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket1 = LotteryTicket.create(details1, numbers1); + LotteryTicket ticket1 = LotteryTicket.create(new LotteryTicketId(), details1, numbers1); PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket2 = LotteryTicket.create(details2, numbers2); + LotteryTicket ticket2 = LotteryTicket.create(new LotteryTicketId(), details2, numbers2); assertEquals(ticket1, ticket2); PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); - LotteryTicket ticket3 = LotteryTicket.create(details3, numbers3); + LotteryTicket ticket3 = LotteryTicket.create(new LotteryTicketId(), details3, numbers3); assertFalse(ticket1.equals(ticket3)); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java index 883c8127f..02304296f 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -28,6 +28,7 @@ import java.util.Set; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; +import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; /** @@ -51,6 +52,6 @@ public class LotteryTestUtils { Set givenNumbers) { PlayerDetails details = PlayerDetails.create(email, account, phone); LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); - return LotteryTicket.create(details, numbers); + return LotteryTicket.create(new LotteryTicketId(), details, numbers); } } From 9a90f2de1f50557e584d3a0ced4d134c8c702090 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Sun, 11 Sep 2016 18:45:51 +0100 Subject: [PATCH 057/145] Changes based on code review --- event-asynchronous/README.md | 2 + .../etc/event-asynchronous.ucls | 30 +++++---- .../com/iluwatar/event/asynchronous/App.java | 63 ++++++++++--------- .../iluwatar/event/asynchronous/Event.java | 14 ++--- .../event/asynchronous/EventManager.java | 59 ++++++++++++----- .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 37 ++++++----- 7 files changed, 125 insertions(+), 82 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index 59e6e8b33..dde434aba 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -5,6 +5,8 @@ folder: event-asynchronous permalink: /patterns/event-asynchronous/ categories: Other tags: + - difficulty-intermediate + - performance - Java --- diff --git a/event-asynchronous/etc/event-asynchronous.ucls b/event-asynchronous/etc/event-asynchronous.ucls index cc7241044..df09fc28d 100644 --- a/event-asynchronous/etc/event-asynchronous.ucls +++ b/event-asynchronous/etc/event-asynchronous.ucls @@ -65,12 +65,15 @@ - - - - + + + + + + + - + @@ -78,19 +81,24 @@ - + + + - - - - + + + + + + + - + diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index fa6116b46..f951af07c 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -48,6 +48,8 @@ import java.util.Scanner; */ public class App { + public static final String PROP_FILE_NAME = "config.properties"; + boolean interactiveMode = false; /** @@ -68,15 +70,14 @@ public class App { */ public void setUp() { Properties prop = new Properties(); - String propFileName = "config.properties"; - InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME); if (inputStream != null) { try { prop.load(inputStream); } catch (IOException e) { - System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); + System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -104,23 +105,23 @@ public class App { try { // Create an Asynchronous event. - int aEventId = eventManager.createAsyncEvent(60); + int aEventId = eventManager.createAsync(60); System.out.println("Event [" + aEventId + "] has been created."); - eventManager.startEvent(aEventId); + eventManager.start(aEventId); System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventId = eventManager.createSyncEvent(60); + int sEventId = eventManager.create(60); System.out.println("Event [" + sEventId + "] has been created."); - eventManager.startEvent(sEventId); + eventManager.start(sEventId); System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventId); - eventManager.getStatus(sEventId); + eventManager.status(aEventId); + eventManager.status(sEventId); - eventManager.stopEvent(aEventId); + eventManager.cancel(aEventId); System.out.println("Event [" + aEventId + "] has been stopped."); - eventManager.stopEvent(sEventId); + eventManager.cancel(sEventId); System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException @@ -136,35 +137,33 @@ public class App { EventManager eventManager = new EventManager(); Scanner s = new Scanner(System.in); - int option = 0; - option = -1; + int option = -1; while (option != 5) { - System.out - .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + System.out.println("Hello. Would you like to boil some eggs?"); + System.out.println( + "(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); System.out.print("Choose [1,2,3,4,5]: "); option = s.nextInt(); if (option == 1) { s.nextLine(); - System.out.print("(A)sync or (S)ync event?: "); + System.out.print("Boil multiple eggs at once (A) or boil them one-by-one (S)?: "); String eventType = s.nextLine(); - System.out.print("How long should this event run for (in seconds)?: "); + System.out.print("How long should this egg be boiled for (in seconds)?: "); int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { - int eventId = eventManager.createAsyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + int eventId = eventManager.createAsync(eventTime); + eventManager.start(eventId); + System.out.println("Egg [" + eventId + "] is being boiled."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { - int eventId = eventManager.createSyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + int eventId = eventManager.create(eventTime); + eventManager.start(eventId); + System.out.println("Egg [" + eventId + "] is being boiled."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -173,24 +172,26 @@ public class App { System.out.println("Unknown event type."); } } else if (option == 2) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.stopEvent(eventId); - System.out.println("Event [" + eventId + "] has been stopped."); + eventManager.cancel(eventId); + System.out.println("Egg [" + eventId + "] is removed from boiler."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.getStatus(eventId); + eventManager.status(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 4) { - eventManager.getStatusOfAllEvents(); + eventManager.statusOfAllEvents(); + } else if (option == 5) { + eventManager.shutdown(); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 4b4fe1d94..1cb04acdc 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -26,11 +26,10 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; private Thread thread; - private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventId, int eventTime) { + public Event(final int eventId, final int eventTime) { this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +48,9 @@ public class Event implements IEvent, Runnable { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] is not done."); } else { - System.out.println("[" + eventId + "] I am done."); + System.out.println("[" + eventId + "] is done."); } } @@ -61,14 +60,13 @@ public class Event implements IEvent, Runnable { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - counter += 1; Thread.sleep(5000); // Sleep for 5 seconds. } catch (InterruptedException e) { return; } } isComplete = true; - notifyListener(); + completed(); } public final void addListener(final ThreadCompleteListener listener) { @@ -79,9 +77,9 @@ public class Event implements IEvent, Runnable { this.eventListener = null; } - private final void notifyListener() { + private final void completed() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventId); + eventListener.completedEventHandler(eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index d3278594f..e65816cec 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,10 +32,10 @@ import java.util.concurrent.ConcurrentHashMap; */ public class EventManager implements ThreadCompleteListener { - private int minId = 1; - private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. - private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) - private int maxEventTime = 1800; // in seconds / 30 minutes. + public static final int MAX_RUNNING_EVENTS = 1000; // Just don't wanna have too many running events. :) + public static final int MIN_ID = 1; + public static final int MAX_ID = MAX_RUNNING_EVENTS; + public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; @@ -46,7 +46,7 @@ public class EventManager implements ThreadCompleteListener { */ public EventManager() { rand = new Random(1); - eventPool = new ConcurrentHashMap(maxRunningEvents); + eventPool = new ConcurrentHashMap(MAX_RUNNING_EVENTS); } @@ -59,7 +59,7 @@ public class EventManager implements ThreadCompleteListener { * @throws InvalidOperationException No new synchronous events can be created when one is already running. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createSyncEvent(int eventTime) + public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { @@ -79,18 +79,18 @@ public class EventManager implements ThreadCompleteListener { * @throws MaxNumOfEventsAllowedException When too many events are running at a time. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - if (eventPool.size() == maxRunningEvents) { + if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } - if (eventTime >= maxEventTime) { + if (eventTime >= MAX_EVENT_TIME) { throw new LongRunningEventException( - "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again."); } int newEventId = generateId(); @@ -108,7 +108,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event that needs to be started. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void startEvent(int eventId) throws EventDoesNotExistException { + public void start(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -122,7 +122,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event that needs to be stopped. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void stopEvent(int eventId) throws EventDoesNotExistException { + public void cancel(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -141,7 +141,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event to inquire status of. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void getStatus(int eventId) throws EventDoesNotExistException { + public void status(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -153,7 +153,7 @@ public class EventManager implements ThreadCompleteListener { * Gets status of all running events. */ @SuppressWarnings("rawtypes") - public void getStatusOfAllEvents() { + public void statusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); @@ -161,6 +161,18 @@ public class EventManager implements ThreadCompleteListener { } } + /** + * Stop all running events. + */ + @SuppressWarnings("rawtypes") + public void shutdown() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).stop(); + } + } + /** * Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most * Integer.MAX_VALUE - 1. @@ -168,9 +180,9 @@ public class EventManager implements ThreadCompleteListener { private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxId - minId) + 1) + minId; + int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxId - minId) + 1) + minId; + randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; } return randomNum; @@ -180,9 +192,22 @@ public class EventManager implements ThreadCompleteListener { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventId) { + public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); eventPool.remove(eventId); } + /** + * Getter method for event pool. + */ + public Map getEventPool() { + return eventPool; + } + + /** + * Get number of currently running Synchronous events. + */ + public int numOfCurrentlyRunningSyncEvent() { + return currentlyRunningSyncEvent; + } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index 88f300634..fd62a3e80 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventId); + void completedEventHandler(final int eventId); } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 392c7fba6..6565d5bad 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -16,6 +16,8 @@ */ package com.iluwatar.event.asynchronous; +import static org.junit.Assert.assertTrue; + import org.junit.Before; import org.junit.Test; @@ -36,9 +38,13 @@ public class EventAsynchronousTest { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventId = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventId); - eventManager.stopEvent(aEventId); + int aEventId = eventManager.createAsync(60); + eventManager.start(aEventId); + assertTrue(eventManager.getEventPool().size() == 1); + assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); + assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() == -1); + eventManager.cancel(aEventId); + assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,25 +54,28 @@ public class EventAsynchronousTest { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - eventManager.stopEvent(sEventId); + int sEventId = eventManager.create(60); + eventManager.start(sEventId); + assertTrue(eventManager.getEventPool().size() == 1); + assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS); + assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() != -1); + eventManager.cancel(sEventId); + assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); } } - @Test - public void testUnsuccessfulSynchronousEvent() { + @Test(expected = InvalidOperationException.class) + public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException - | InvalidOperationException e) { + int sEventId = eventManager.create(60); + eventManager.start(sEventId); + sEventId = eventManager.create(60); + eventManager.start(sEventId); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } } From 59e6a0af853c8064735302bf8eae6847bb54f6f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 22:16:50 +0300 Subject: [PATCH 058/145] Hexagonal pattern: Ignore Mongo repository test --- .../iluwatar/hexagonal/database/MongoTicketRepositoryTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index dc0d5c7fd..09a2772bd 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.mongodb.MongoClient; import org.junit.Before; +import org.junit.Ignore; import org.junit.Test; import java.util.Optional; @@ -38,6 +39,7 @@ import static org.junit.Assert.assertTrue; /** * Tests for Mongo based ticket repository */ +@Ignore public class MongoTicketRepositoryTest { private static final String TEST_HOST = "localhost"; From e685512ed59a46f02e3b7670b8ee6731fc3279e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 11 Sep 2016 23:19:02 +0300 Subject: [PATCH 059/145] Hexagonal pattern: Added Mongo based banking adapter and bound it in Guice production module --- .../iluwatar/hexagonal/banking/MongoBank.java | 134 ++++++++++++++++++ .../hexagonal/module/LotteryModule.java | 4 +- ...ansfersTest.java => InMemoryBankTest.java} | 2 +- .../hexagonal/banking/MongoBankTest.java | 68 +++++++++ .../database/MongoTicketRepositoryTest.java | 6 +- 5 files changed, 208 insertions(+), 6 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java rename hexagonal/src/test/java/com/iluwatar/hexagonal/banking/{WireTransfersTest.java => InMemoryBankTest.java} (98%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java new file mode 100644 index 000000000..3da65e156 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java @@ -0,0 +1,134 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.UpdateOptions; +import org.bson.Document; + +import java.util.ArrayList; + +/** + * Mongo based banking adapter + */ +public class MongoBank implements WireTransfers { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 27017; + private static final String DEFAULT_DB = "lotteryDB"; + private static final String DEFAULT_ACCOUNTS_COLLECTION = "accounts"; + + private MongoClient mongoClient; + private MongoDatabase database; + private MongoCollection accountsCollection; + + /** + * Constructor + */ + public MongoBank() { + connect(); + } + + /** + * Constructor accepting parameters + */ + public MongoBank(String host, int port, String dbName, String accountsCollectionName) { + connect(host, port, dbName, accountsCollectionName); + } + + /** + * Connect to database with default parameters + */ + public void connect() { + connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); + } + + /** + * Connect to database with given parameters + */ + public void connect(String host, int port, String dbName, String accountsCollectionName) { + if (mongoClient != null) { + mongoClient.close(); + } + mongoClient = new MongoClient(host , port); + database = mongoClient.getDatabase(dbName); + accountsCollection = database.getCollection(accountsCollectionName); + } + + /** + * @return mongo client + */ + public MongoClient getMongoClient() { + return mongoClient; + } + + /** + * + * @return mongo database + */ + public MongoDatabase getMongoDatabase() { + return database; + } + + /** + * + * @return accounts collection + */ + public MongoCollection getAccountsCollection() { + return accountsCollection; + } + + + @Override + public void setFunds(String bankAccount, int amount) { + Document search = new Document("_id", bankAccount); + Document update = new Document("_id", bankAccount).append("funds", amount); + accountsCollection.updateOne(search, new Document("$set", update), new UpdateOptions().upsert(true)); + } + + @Override + public int getFunds(String bankAccount) { + Document search = new Document("_id", bankAccount); + ArrayList results = accountsCollection.find(search).limit(1).into(new ArrayList()); + if (results.size() > 0) { + return results.get(0).getInteger("funds"); + } else { + return 0; + } + } + + @Override + public boolean transferFunds(int amount, String sourceBackAccount, String destinationBankAccount) { + int sourceFunds = getFunds(sourceBackAccount); + if (sourceFunds < amount) { + return false; + } else { + int destFunds = getFunds(destinationBankAccount); + setFunds(sourceBackAccount, sourceFunds - amount); + setFunds(destinationBankAccount, destFunds + amount); + return true; + } + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index 0a0177f25..c9bc301fc 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -23,7 +23,7 @@ package com.iluwatar.hexagonal.module; import com.google.inject.AbstractModule; -import com.iluwatar.hexagonal.banking.InMemoryBank; +import com.iluwatar.hexagonal.banking.MongoBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.MongoTicketRepository; @@ -38,6 +38,6 @@ public class LotteryModule extends AbstractModule { protected void configure() { bind(LotteryTicketRepository.class).to(MongoTicketRepository.class); bind(LotteryNotifications.class).to(StdOutNotifications.class); - bind(WireTransfers.class).to(InMemoryBank.class); + bind(WireTransfers.class).to(MongoBank.class); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/InMemoryBankTest.java similarity index 98% rename from hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java rename to hexagonal/src/test/java/com/iluwatar/hexagonal/banking/InMemoryBankTest.java index 25fbf460c..c5efda240 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/WireTransfersTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/InMemoryBankTest.java @@ -32,7 +32,7 @@ import org.junit.Test; * Tests for banking * */ -public class WireTransfersTest { +public class InMemoryBankTest { private final WireTransfers bank = new InMemoryBank(); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java new file mode 100644 index 000000000..ad142f028 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java @@ -0,0 +1,68 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.banking; + +import com.mongodb.MongoClient; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for Mongo banking adapter + */ +@Ignore +public class MongoBankTest { + + private static final String TEST_HOST = "localhost"; + private static final int TEST_PORT = 27017; + private static final String TEST_DB = "lotteryDBTest"; + private static final String TEST_ACCOUNTS_COLLECTION = "testAccounts"; + + private MongoBank mongoBank; + + @Before + public void init() { + MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + mongoClient.dropDatabase(TEST_DB); + mongoClient.close(); + mongoBank = new MongoBank(TEST_HOST, TEST_PORT, TEST_DB, TEST_ACCOUNTS_COLLECTION); + } + + @Test + public void testSetup() { + assertEquals(0, mongoBank.getAccountsCollection().count()); + } + + @Test + public void testFundTransfers() { + assertEquals(0, mongoBank.getFunds("000-000")); + mongoBank.setFunds("000-000", 10); + assertEquals(10, mongoBank.getFunds("000-000")); + assertEquals(0, mongoBank.getFunds("111-111")); + mongoBank.transferFunds(9, "000-000", "111-111"); + assertEquals(1, mongoBank.getFunds("000-000")); + assertEquals(9, mongoBank.getFunds("111-111")); + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index 09a2772bd..45fa1cc67 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -44,9 +44,9 @@ public class MongoTicketRepositoryTest { private static final String TEST_HOST = "localhost"; private static final int TEST_PORT = 27017; - private static final String TEST_DB = "lotteryDB"; - private static final String TEST_TICKETS_COLLECTION = "lotteryTickets"; - private static final String TEST_COUNTERS_COLLECTION = "counters"; + private static final String TEST_DB = "lotteryTestDB"; + private static final String TEST_TICKETS_COLLECTION = "lotteryTestTickets"; + private static final String TEST_COUNTERS_COLLECTION = "testCounters"; private MongoTicketRepository repository; From 27e8cb7f2d28bcc09c3cd0babe8ed901e1bd3804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 14 Sep 2016 19:25:26 +0300 Subject: [PATCH 060/145] Hexagonal pattern: Add separate class for Mongo connection properties --- .../iluwatar/hexagonal/banking/MongoBank.java | 14 ++-- .../database/MongoTicketRepository.java | 14 ++-- .../mongo/MongoConnectionProperties.java | 80 +++++++++++++++++++ .../hexagonal/banking/MongoBankTest.java | 8 +- .../database/MongoTicketRepositoryTest.java | 8 +- 5 files changed, 102 insertions(+), 22 deletions(-) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java index 3da65e156..2a18d8986 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java @@ -22,6 +22,7 @@ */ package com.iluwatar.hexagonal.banking; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -35,8 +36,6 @@ import java.util.ArrayList; */ public class MongoBank implements WireTransfers { - private static final String DEFAULT_HOST = "localhost"; - private static final int DEFAULT_PORT = 27017; private static final String DEFAULT_DB = "lotteryDB"; private static final String DEFAULT_ACCOUNTS_COLLECTION = "accounts"; @@ -54,25 +53,26 @@ public class MongoBank implements WireTransfers { /** * Constructor accepting parameters */ - public MongoBank(String host, int port, String dbName, String accountsCollectionName) { - connect(host, port, dbName, accountsCollectionName); + public MongoBank(String dbName, String accountsCollectionName) { + connect(dbName, accountsCollectionName); } /** * Connect to database with default parameters */ public void connect() { - connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); + connect(DEFAULT_DB, DEFAULT_ACCOUNTS_COLLECTION); } /** * Connect to database with given parameters */ - public void connect(String host, int port, String dbName, String accountsCollectionName) { + public void connect(String dbName, String accountsCollectionName) { if (mongoClient != null) { mongoClient.close(); } - mongoClient = new MongoClient(host , port); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + mongoClient = new MongoClient(properties.getHost(), properties.getPort()); database = mongoClient.getDatabase(dbName); accountsCollection = database.getCollection(accountsCollectionName); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java index ff0439af8..73ff40b78 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -26,6 +26,7 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -43,8 +44,6 @@ import java.util.Optional; */ public class MongoTicketRepository implements LotteryTicketRepository { - private static final String DEFAULT_HOST = "localhost"; - private static final int DEFAULT_PORT = 27017; private static final String DEFAULT_DB = "lotteryDB"; private static final String DEFAULT_TICKETS_COLLECTION = "lotteryTickets"; private static final String DEFAULT_COUNTERS_COLLECTION = "counters"; @@ -64,27 +63,28 @@ public class MongoTicketRepository implements LotteryTicketRepository { /** * Constructor accepting parameters */ - public MongoTicketRepository(String host, int port, String dbName, String ticketsCollectionName, + public MongoTicketRepository(String dbName, String ticketsCollectionName, String countersCollectionName) { - connect(host, port, dbName, ticketsCollectionName, countersCollectionName); + connect(dbName, ticketsCollectionName, countersCollectionName); } /** * Connect to database with default parameters */ public void connect() { - connect(DEFAULT_HOST, DEFAULT_PORT, DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); + connect(DEFAULT_DB, DEFAULT_TICKETS_COLLECTION, DEFAULT_COUNTERS_COLLECTION); } /** * Connect to database with given parameters */ - public void connect(String host, int port, String dbName, String ticketsCollectionName, + public void connect(String dbName, String ticketsCollectionName, String countersCollectionName) { if (mongoClient != null) { mongoClient.close(); } - mongoClient = new MongoClient(host , port); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + mongoClient = new MongoClient(properties.getHost(), properties.getPort()); database = mongoClient.getDatabase(dbName); ticketsCollection = database.getCollection(ticketsCollectionName); countersCollection = database.getCollection(countersCollectionName); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java new file mode 100644 index 000000000..20b1876f6 --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java @@ -0,0 +1,80 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.mongo; + +import java.io.FileInputStream; +import java.util.Properties; + +/** + * Mongo connection properties + */ +public class MongoConnectionProperties { + + private static final String DEFAULT_HOST = "localhost"; + private static final int DEFAULT_PORT = 27017; + + private String host; + private int port; + + /** + * Constructor + */ + public MongoConnectionProperties() { + this.host = DEFAULT_HOST; + this.port = DEFAULT_PORT; + } + + /** + * @return host name + */ + public String getHost() { + return host; + } + + /** + * @return port number + */ + public int getPort() { + return port; + } + + /** + * Try to load connection properties from file. + * Fall back to default connection properties. + */ + public MongoConnectionProperties load() { + String path = System.getProperty("hexagonal.properties.path"); + Properties properties = new Properties(); + if (path != null) { + try (FileInputStream fin = new FileInputStream(path)) { + properties.load(fin); + this.host = properties.getProperty("host"); + this.port = Integer.parseInt(properties.getProperty("port")); + } catch (Exception e) { + // error occurred, use default properties + e.printStackTrace(); + } + } + return this; + } +} diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java index ad142f028..26041b174 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java @@ -22,6 +22,7 @@ */ package com.iluwatar.hexagonal.banking; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -35,8 +36,6 @@ import static org.junit.Assert.assertEquals; @Ignore public class MongoBankTest { - private static final String TEST_HOST = "localhost"; - private static final int TEST_PORT = 27017; private static final String TEST_DB = "lotteryDBTest"; private static final String TEST_ACCOUNTS_COLLECTION = "testAccounts"; @@ -44,10 +43,11 @@ public class MongoBankTest { @Before public void init() { - MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); - mongoBank = new MongoBank(TEST_HOST, TEST_PORT, TEST_DB, TEST_ACCOUNTS_COLLECTION); + mongoBank = new MongoBank(TEST_DB, TEST_ACCOUNTS_COLLECTION); } @Test diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index 45fa1cc67..a29b535f6 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -26,6 +26,7 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -42,8 +43,6 @@ import static org.junit.Assert.assertTrue; @Ignore public class MongoTicketRepositoryTest { - private static final String TEST_HOST = "localhost"; - private static final int TEST_PORT = 27017; private static final String TEST_DB = "lotteryTestDB"; private static final String TEST_TICKETS_COLLECTION = "lotteryTestTickets"; private static final String TEST_COUNTERS_COLLECTION = "testCounters"; @@ -52,10 +51,11 @@ public class MongoTicketRepositoryTest { @Before public void init() { - MongoClient mongoClient = new MongoClient(TEST_HOST, TEST_PORT); + MongoConnectionProperties properties = new MongoConnectionProperties().load(); + MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); - repository = new MongoTicketRepository(TEST_HOST, TEST_PORT, TEST_DB, TEST_TICKETS_COLLECTION, + repository = new MongoTicketRepository(TEST_DB, TEST_TICKETS_COLLECTION, TEST_COUNTERS_COLLECTION); } From 3cf2b34d2a1f20a2606d515ad145bf0fb018b44f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 14 Sep 2016 22:01:41 +0300 Subject: [PATCH 061/145] Hexagonal pattern: Improve connection properties handling --- .../administration/ConsoleAdministration.java | 2 + .../iluwatar/hexagonal/banking/MongoBank.java | 5 +-- .../database/MongoTicketRepository.java | 5 +-- ...a => MongoConnectionPropertiesLoader.java} | 40 +++++-------------- .../hexagonal/service/ConsoleLottery.java | 2 + .../hexagonal/banking/MongoBankTest.java | 7 ++-- .../database/MongoTicketRepositoryTest.java | 7 ++-- 7 files changed, 25 insertions(+), 43 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/{MongoConnectionProperties.java => MongoConnectionPropertiesLoader.java} (75%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index 6a846280c..ea2f33699 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -28,6 +28,7 @@ import com.iluwatar.hexagonal.domain.LotteryAdministration; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.iluwatar.hexagonal.sampledata.SampleData; import java.util.Scanner; @@ -41,6 +42,7 @@ public class ConsoleAdministration { * Program entry point */ public static void main(String[] args) { + MongoConnectionPropertiesLoader.load(); Injector injector = Guice.createInjector(new LotteryModule()); LotteryAdministration administartion = injector.getInstance(LotteryAdministration.class); LotteryService service = injector.getInstance(LotteryService.class); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java index 2a18d8986..23a0e376a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/banking/MongoBank.java @@ -22,7 +22,6 @@ */ package com.iluwatar.hexagonal.banking; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -71,8 +70,8 @@ public class MongoBank implements WireTransfers { if (mongoClient != null) { mongoClient.close(); } - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); database = mongoClient.getDatabase(dbName); accountsCollection = database.getCollection(accountsCollectionName); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java index 73ff40b78..4cfa83649 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -26,7 +26,6 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; import com.mongodb.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; @@ -83,8 +82,8 @@ public class MongoTicketRepository implements LotteryTicketRepository { if (mongoClient != null) { mongoClient.close(); } - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); database = mongoClient.getDatabase(dbName); ticketsCollection = database.getCollection(ticketsCollectionName); countersCollection = database.getCollection(countersCollectionName); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionPropertiesLoader.java similarity index 75% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionPropertiesLoader.java index 20b1876f6..a9bb39803 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionProperties.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/mongo/MongoConnectionPropertiesLoader.java @@ -26,55 +26,33 @@ import java.io.FileInputStream; import java.util.Properties; /** - * Mongo connection properties + * Mongo connection properties loader */ -public class MongoConnectionProperties { +public class MongoConnectionPropertiesLoader { private static final String DEFAULT_HOST = "localhost"; private static final int DEFAULT_PORT = 27017; - private String host; - private int port; - - /** - * Constructor - */ - public MongoConnectionProperties() { - this.host = DEFAULT_HOST; - this.port = DEFAULT_PORT; - } - - /** - * @return host name - */ - public String getHost() { - return host; - } - - /** - * @return port number - */ - public int getPort() { - return port; - } - /** * Try to load connection properties from file. * Fall back to default connection properties. */ - public MongoConnectionProperties load() { + public static void load() { + String host = DEFAULT_HOST; + int port = DEFAULT_PORT; String path = System.getProperty("hexagonal.properties.path"); Properties properties = new Properties(); if (path != null) { try (FileInputStream fin = new FileInputStream(path)) { properties.load(fin); - this.host = properties.getProperty("host"); - this.port = Integer.parseInt(properties.getProperty("port")); + host = properties.getProperty("mongo-host"); + port = Integer.parseInt(properties.getProperty("mongo-port")); } catch (Exception e) { // error occurred, use default properties e.printStackTrace(); } } - return this; + System.setProperty("mongo-host", host); + System.setProperty("mongo-port", String.format("%d", port)); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index afacc35cc..b0d660023 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -32,6 +32,7 @@ import com.iluwatar.hexagonal.domain.LotteryTicketCheckResult; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.module.LotteryModule; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import java.util.HashSet; import java.util.Optional; @@ -48,6 +49,7 @@ public class ConsoleLottery { * Program entry point */ public static void main(String[] args) { + MongoConnectionPropertiesLoader.load(); Injector injector = Guice.createInjector(new LotteryModule()); LotteryService service = injector.getInstance(LotteryService.class); WireTransfers bank = injector.getInstance(WireTransfers.class); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java index 26041b174..ce5c9ff1a 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/banking/MongoBankTest.java @@ -22,7 +22,7 @@ */ package com.iluwatar.hexagonal.banking; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -43,8 +43,9 @@ public class MongoBankTest { @Before public void init() { - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + MongoConnectionPropertiesLoader.load(); + MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); mongoBank = new MongoBank(TEST_DB, TEST_ACCOUNTS_COLLECTION); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index a29b535f6..bbd95b38d 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -26,7 +26,7 @@ import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryTicket; import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; -import com.iluwatar.hexagonal.mongo.MongoConnectionProperties; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.mongodb.MongoClient; import org.junit.Before; import org.junit.Ignore; @@ -51,8 +51,9 @@ public class MongoTicketRepositoryTest { @Before public void init() { - MongoConnectionProperties properties = new MongoConnectionProperties().load(); - MongoClient mongoClient = new MongoClient(properties.getHost(), properties.getPort()); + MongoConnectionPropertiesLoader.load(); + MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); mongoClient.dropDatabase(TEST_DB); mongoClient.close(); repository = new MongoTicketRepository(TEST_DB, TEST_TICKETS_COLLECTION, From c4c5e78e50f6ccfe73e5fafaf4019cfb3dc68fa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 14 Sep 2016 22:18:42 +0300 Subject: [PATCH 062/145] Hexagonal pattern: Improve error handling in console lottery --- .../hexagonal/service/ConsoleLottery.java | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index b0d660023..eb791ca55 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -79,17 +79,21 @@ public class ConsoleLottery { PlayerDetails details = PlayerDetails.create(email, account, phone); System.out.println("Give 4 comma separated lottery numbers?"); String numbers = readString(scanner); - String[] parts = numbers.split(","); - Set chosen = new HashSet<>(); - for (int i = 0; i < 4; i++) { - chosen.add(Integer.parseInt(parts[i])); - } - LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); - LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); - Optional id = service.submitTicket(lotteryTicket); - if (id.isPresent()) { - System.out.println("Submitted lottery ticket with id: " + id.get()); - } else { + try { + String[] parts = numbers.split(","); + Set chosen = new HashSet<>(); + for (int i = 0; i < 4; i++) { + chosen.add(Integer.parseInt(parts[i])); + } + LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); + LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); + Optional id = service.submitTicket(lotteryTicket); + if (id.isPresent()) { + System.out.println("Submitted lottery ticket with id: " + id.get()); + } else { + System.out.println("Failed submitting lottery ticket - please try again."); + } + } catch (Exception e) { System.out.println("Failed submitting lottery ticket - please try again."); } } else if (cmd.equals("4")) { @@ -97,19 +101,23 @@ public class ConsoleLottery { String id = readString(scanner); System.out.println("Give the 4 comma separated winning numbers?"); String numbers = readString(scanner); - String[] parts = numbers.split(","); - Set winningNumbers = new HashSet<>(); - for (int i = 0; i < 4; i++) { - winningNumbers.add(Integer.parseInt(parts[i])); - } - LotteryTicketCheckResult result = service.checkTicketForPrize( - new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); - if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - System.out.println("Congratulations! The lottery ticket has won!"); - } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - System.out.println("Unfortunately the lottery ticket did not win."); - } else { - System.out.println("Such lottery ticket has not been submitted."); + try { + String[] parts = numbers.split(","); + Set winningNumbers = new HashSet<>(); + for (int i = 0; i < 4; i++) { + winningNumbers.add(Integer.parseInt(parts[i])); + } + LotteryTicketCheckResult result = service.checkTicketForPrize( + new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); + if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { + System.out.println("Congratulations! The lottery ticket has won!"); + } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { + System.out.println("Unfortunately the lottery ticket did not win."); + } else { + System.out.println("Such lottery ticket has not been submitted."); + } + } catch (Exception e) { + System.out.println("Failed checking the lottery ticket - please try again."); } } else if (cmd.equals("5")) { exit = true; From df32a7b893fb0120708bfde3d7c0122264033f69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 15 Sep 2016 21:45:09 +0300 Subject: [PATCH 063/145] Hexagonal pattern: Introduced lottery events port with two adapters --- hexagonal/etc/hexagonal.ucls | 4 +- hexagonal/etc/hexagonal.urm.puml | 2 +- .../main/java/com/iluwatar/hexagonal/App.java | 2 +- .../domain/LotteryAdministration.java | 12 +- .../hexagonal/domain/LotteryService.java | 10 +- .../LotteryEventLog.java} | 26 +-- .../hexagonal/eventlog/MongoEventLog.java | 154 ++++++++++++++++++ .../StdOutEventLog.java} | 17 +- .../hexagonal/module/LotteryModule.java | 6 +- .../module/LotteryTestingModule.java | 6 +- .../hexagonal/eventlog/MongoEventLogTest.java | 84 ++++++++++ 11 files changed, 282 insertions(+), 41 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/{notifications/LotteryNotifications.java => eventlog/LotteryEventLog.java} (66%) create mode 100644 hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java rename hexagonal/src/main/java/com/iluwatar/hexagonal/{notifications/StdOutNotifications.java => eventlog/StdOutEventLog.java} (83%) create mode 100644 hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java diff --git a/hexagonal/etc/hexagonal.ucls b/hexagonal/etc/hexagonal.ucls index 0318576c2..d3b46b23d 100644 --- a/hexagonal/etc/hexagonal.ucls +++ b/hexagonal/etc/hexagonal.ucls @@ -2,7 +2,7 @@ - @@ -161,7 +161,7 @@ - diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml index 4102c5863..c4b0d0a73 100644 --- a/hexagonal/etc/hexagonal.urm.puml +++ b/hexagonal/etc/hexagonal.urm.puml @@ -116,7 +116,7 @@ package com.iluwatar.hexagonal.database { + save(LotteryTicket) : Optional {abstract} } } -package com.iluwatar.hexagonal.notifications { +package com.iluwatar.hexagonal.eventlog { interface LotteryNotifications { + notifyNoWin(PlayerDetails) {abstract} + notifyPrize(PlayerDetails, int) {abstract} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index a7d31446b..7ec974c3b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -56,7 +56,7 @@ import com.iluwatar.hexagonal.sampledata.SampleData; * * The secondary ports that application core uses are {@link WireTransfers} * which is a banking service, {@link LotteryNotifications} that delivers - * notifications as lottery events occur and {@link LotteryTicketRepository} + * eventlog as lottery events occur and {@link LotteryTicketRepository} * that is the storage for the lottery tickets. * */ diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index f73390863..3e01b6e03 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -25,7 +25,7 @@ package com.iluwatar.hexagonal.domain; import com.google.inject.Inject; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; import java.util.Map; @@ -37,7 +37,7 @@ import java.util.Map; public class LotteryAdministration { private final LotteryTicketRepository repository; - private final LotteryNotifications notifications; + private final LotteryEventLog notifications; private final WireTransfers wireTransfers; private final LotteryTicketChecker checker; @@ -45,7 +45,7 @@ public class LotteryAdministration { * Constructor */ @Inject - public LotteryAdministration(LotteryTicketRepository repository, LotteryNotifications notifications, + public LotteryAdministration(LotteryTicketRepository repository, LotteryEventLog notifications, WireTransfers wireTransfers) { this.repository = repository; this.notifications = notifications; @@ -72,12 +72,12 @@ public class LotteryAdministration { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); if (transferred) { - notifications.notifyPrize(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + notifications.ticketWon(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); } else { - notifications.notifyPrizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); + notifications.prizeError(tickets.get(id).getPlayerDetails(), LotteryConstants.PRIZE_AMOUNT); } } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - notifications.notifyNoWin(tickets.get(id).getPlayerDetails()); + notifications.ticketDidNotWin(tickets.get(id).getPlayerDetails()); } } return numbers; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 76cd47d1d..a9dff7fd0 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -25,7 +25,7 @@ package com.iluwatar.hexagonal.domain; import com.google.inject.Inject; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; import java.util.Optional; @@ -37,7 +37,7 @@ import java.util.Optional; public class LotteryService { private final LotteryTicketRepository repository; - private final LotteryNotifications notifications; + private final LotteryEventLog notifications; private final WireTransfers wireTransfers; private final LotteryTicketChecker checker; @@ -45,7 +45,7 @@ public class LotteryService { * Constructor */ @Inject - public LotteryService(LotteryTicketRepository repository, LotteryNotifications notifications, + public LotteryService(LotteryTicketRepository repository, LotteryEventLog notifications, WireTransfers wireTransfers) { this.repository = repository; this.notifications = notifications; @@ -60,12 +60,12 @@ public class LotteryService { boolean result = wireTransfers.transferFunds(LotteryConstants.TICKET_PRIZE, ticket.getPlayerDetails().getBankAccount(), LotteryConstants.SERVICE_BANK_ACCOUNT); if (result == false) { - notifications.notifyTicketSubmitError(ticket.getPlayerDetails()); + notifications.ticketSubmitError(ticket.getPlayerDetails()); return Optional.empty(); } Optional optional = repository.save(ticket); if (optional.isPresent()) { - notifications.notifyTicketSubmitted(ticket.getPlayerDetails()); + notifications.ticketSubmitted(ticket.getPlayerDetails()); } return optional; } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java similarity index 66% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java index d7a0cc870..e47640e27 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/LotteryNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/LotteryEventLog.java @@ -20,40 +20,40 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.notifications; +package com.iluwatar.hexagonal.eventlog; import com.iluwatar.hexagonal.domain.PlayerDetails; /** * - * Provides notifications for lottery events. + * Event log for lottery events * */ -public interface LotteryNotifications { +public interface LotteryEventLog { /** - * Notify lottery ticket was submitted + * lottery ticket submitted */ - void notifyTicketSubmitted(PlayerDetails details); + void ticketSubmitted(PlayerDetails details); /** - * Notify there was an error submitting lottery ticket + * error submitting lottery ticket */ - void notifyTicketSubmitError(PlayerDetails details); + void ticketSubmitError(PlayerDetails details); /** - * Notify lottery ticket did not win + * lottery ticket did not win */ - void notifyNoWin(PlayerDetails details); + void ticketDidNotWin(PlayerDetails details); /** - * Notify that prize has been paid + * lottery ticket won */ - void notifyPrize(PlayerDetails details, int prizeAmount); + void ticketWon(PlayerDetails details, int prizeAmount); /** - * Notify that there was an error paying the prize + * error paying the prize */ - void notifyPrizeError(PlayerDetails details, int prizeAmount); + void prizeError(PlayerDetails details, int prizeAmount); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java new file mode 100644 index 000000000..7f9b4da5e --- /dev/null +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/MongoEventLog.java @@ -0,0 +1,154 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.eventlog; + +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import org.bson.Document; + +/** + * Mongo based event log + */ +public class MongoEventLog implements LotteryEventLog { + + private static final String DEFAULT_DB = "lotteryDB"; + private static final String DEFAULT_EVENTS_COLLECTION = "events"; + + private MongoClient mongoClient; + private MongoDatabase database; + private MongoCollection eventsCollection; + + private StdOutEventLog stdOutEventLog = new StdOutEventLog(); + + /** + * Constructor + */ + public MongoEventLog() { + connect(); + } + + /** + * Constructor accepting parameters + */ + public MongoEventLog(String dbName, String eventsCollectionName) { + connect(dbName, eventsCollectionName); + } + + /** + * Connect to database with default parameters + */ + public void connect() { + connect(DEFAULT_DB, DEFAULT_EVENTS_COLLECTION); + } + + /** + * Connect to database with given parameters + */ + public void connect(String dbName, String eventsCollectionName) { + if (mongoClient != null) { + mongoClient.close(); + } + mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); + database = mongoClient.getDatabase(dbName); + eventsCollection = database.getCollection(eventsCollectionName); + } + + /** + * @return mongo client + */ + public MongoClient getMongoClient() { + return mongoClient; + } + + /** + * + * @return mongo database + */ + public MongoDatabase getMongoDatabase() { + return database; + } + + /** + * + * @return accounts collection + */ + public MongoCollection getEventsCollection() { + return eventsCollection; + } + + + @Override + public void ticketSubmitted(PlayerDetails details) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket was submitted and bank account was charged for 3 credits.")); + eventsCollection.insertOne(document); + stdOutEventLog.ticketSubmitted(details); + } + + @Override + public void ticketSubmitError(PlayerDetails details) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket could not be submitted because lack of funds.")); + eventsCollection.insertOne(document); + stdOutEventLog.ticketSubmitError(details); + } + + @Override + public void ticketDidNotWin(PlayerDetails details) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket was checked and unfortunately did not win this time.")); + eventsCollection.insertOne(document); + stdOutEventLog.ticketDidNotWin(details); + } + + @Override + public void ticketWon(PlayerDetails details, int prizeAmount) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket won! The bank account was deposited with %d credits.", + prizeAmount)); + eventsCollection.insertOne(document); + stdOutEventLog.ticketWon(details, prizeAmount); + } + + @Override + public void prizeError(PlayerDetails details, int prizeAmount) { + Document document = new Document("email", details.getEmail()); + document.put("phone", details.getPhoneNumber()); + document.put("bank", details.getBankAccount()); + document.put("message", String.format("Lottery ticket won! Unfortunately the bank credit transfer of %d failed.", + prizeAmount)); + eventsCollection.insertOne(document); + stdOutEventLog.prizeError(details, prizeAmount); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java similarity index 83% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java index f6bd3b546..4150dd401 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/notifications/StdOutNotifications.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java @@ -20,40 +20,43 @@ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ -package com.iluwatar.hexagonal.notifications; +package com.iluwatar.hexagonal.eventlog; import com.iluwatar.hexagonal.domain.PlayerDetails; -public class StdOutNotifications implements LotteryNotifications { +/** + * Standard output event log + */ +public class StdOutEventLog implements LotteryEventLog { @Override - public void notifyTicketSubmitted(PlayerDetails details) { + public void ticketSubmitted(PlayerDetails details) { System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.", details.getEmail(), details.getBankAccount())); } @Override - public void notifyNoWin(PlayerDetails details) { + public void ticketDidNotWin(PlayerDetails details) { System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.", details.getEmail())); } @Override - public void notifyPrize(PlayerDetails details, int prizeAmount) { + public void ticketWon(PlayerDetails details, int prizeAmount) { System.out .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", details.getEmail(), details.getBankAccount(), prizeAmount)); } @Override - public void notifyPrizeError(PlayerDetails details, int prizeAmount) { + public void prizeError(PlayerDetails details, int prizeAmount) { System.out .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", details.getEmail(), prizeAmount)); } @Override - public void notifyTicketSubmitError(PlayerDetails details) { + public void ticketSubmitError(PlayerDetails details) { System.out.println( String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", details.getEmail())); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java index c9bc301fc..7e784548d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryModule.java @@ -27,8 +27,8 @@ import com.iluwatar.hexagonal.banking.MongoBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.LotteryTicketRepository; import com.iluwatar.hexagonal.database.MongoTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.StdOutNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; +import com.iluwatar.hexagonal.eventlog.MongoEventLog; /** * Guice module for binding production dependencies @@ -37,7 +37,7 @@ public class LotteryModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(MongoTicketRepository.class); - bind(LotteryNotifications.class).to(StdOutNotifications.class); + bind(LotteryEventLog.class).to(MongoEventLog.class); bind(WireTransfers.class).to(MongoBank.class); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java index c934ed43c..2a1ad1155 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/module/LotteryTestingModule.java @@ -27,8 +27,8 @@ import com.iluwatar.hexagonal.banking.InMemoryBank; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.database.InMemoryTicketRepository; import com.iluwatar.hexagonal.database.LotteryTicketRepository; -import com.iluwatar.hexagonal.notifications.LotteryNotifications; -import com.iluwatar.hexagonal.notifications.StdOutNotifications; +import com.iluwatar.hexagonal.eventlog.LotteryEventLog; +import com.iluwatar.hexagonal.eventlog.StdOutEventLog; /** * Guice module for testing dependencies @@ -37,7 +37,7 @@ public class LotteryTestingModule extends AbstractModule { @Override protected void configure() { bind(LotteryTicketRepository.class).to(InMemoryTicketRepository.class); - bind(LotteryNotifications.class).to(StdOutNotifications.class); + bind(LotteryEventLog.class).to(StdOutEventLog.class); bind(WireTransfers.class).to(InMemoryBank.class); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java new file mode 100644 index 000000000..9b9f14c78 --- /dev/null +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java @@ -0,0 +1,84 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.hexagonal.eventlog; + +import com.iluwatar.hexagonal.domain.PlayerDetails; +import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; +import com.mongodb.MongoClient; +import org.junit.Before; +import org.junit.Ignore; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Tests for Mongo event log + */ +@Ignore +public class MongoEventLogTest { + + private static final String TEST_DB = "lotteryDBTest"; + private static final String TEST_EVENTS_COLLECTION = "testEvents"; + + private MongoEventLog mongoEventLog; + + @Before + public void init() { + MongoConnectionPropertiesLoader.load(); + MongoClient mongoClient = new MongoClient(System.getProperty("mongo-host"), + Integer.parseInt(System.getProperty("mongo-port"))); + mongoClient.dropDatabase(TEST_DB); + mongoClient.close(); + mongoEventLog = new MongoEventLog(TEST_DB, TEST_EVENTS_COLLECTION); + } + + @Test + public void testSetup() { + assertEquals(0, mongoEventLog.getEventsCollection().count()); + } + + @Test + public void testFundTransfers() { + PlayerDetails playerDetails = PlayerDetails.create("john@wayne.com", "000-000", "03432534543"); + mongoEventLog.prizeError(playerDetails, 1000); + assertEquals(1, mongoEventLog.getEventsCollection().count()); + mongoEventLog.prizeError(playerDetails, 1000); + assertEquals(2, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketDidNotWin(playerDetails); + assertEquals(3, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketDidNotWin(playerDetails); + assertEquals(4, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitError(playerDetails); + assertEquals(5, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitError(playerDetails); + assertEquals(6, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitted(playerDetails); + assertEquals(7, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketSubmitted(playerDetails); + assertEquals(8, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketWon(playerDetails, 1000); + assertEquals(9, mongoEventLog.getEventsCollection().count()); + mongoEventLog.ticketWon(playerDetails, 1000); + assertEquals(10, mongoEventLog.getEventsCollection().count()); + } +} From 914d1353a18fbbec8c7036a131f64638da7ebeb0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Thu, 15 Sep 2016 21:56:15 +0300 Subject: [PATCH 064/145] Hexagonal pattern: Update test application description --- .../src/main/java/com/iluwatar/hexagonal/App.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java index 7ec974c3b..97190937c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/App.java @@ -49,13 +49,13 @@ import com.iluwatar.hexagonal.sampledata.SampleData; * The application core is separate from the services that drive it and * from the services it uses.

    * - * The primary ports for the application are {@link LotteryAdministration} - * through which the lottery round is initiated and run and - * {@link LotteryService} that allows players to submit lottery tickets for - * the draw.

    + * The primary ports for the application are console interfaces + * {@link ConsoleAdministration} through which the lottery round is + * initiated and run and {@link ConsoleLottery} that allows players to + * submit lottery tickets for the draw.

    * * The secondary ports that application core uses are {@link WireTransfers} - * which is a banking service, {@link LotteryNotifications} that delivers + * which is a banking service, {@link LotteryEventLog} that delivers * eventlog as lottery events occur and {@link LotteryTicketRepository} * that is the storage for the lottery tickets. * From 6aa58e8ae658cfc66e4421e9b8c92bb017b46906 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 17 Sep 2016 09:07:06 +0300 Subject: [PATCH 065/145] Hexagonal pattern: Remove unnecessary factories --- .../database/MongoTicketRepository.java | 4 +- .../domain/LotteryAdministration.java | 4 +- .../hexagonal/domain/LotteryConstants.java | 3 + .../hexagonal/domain/LotteryService.java | 4 +- .../hexagonal/domain/LotteryTicket.java | 9 +-- .../hexagonal/domain/PlayerDetails.java | 11 +-- .../hexagonal/sampledata/SampleData.java | 80 +++++++++---------- .../hexagonal/service/ConsoleLottery.java | 4 +- .../database/MongoTicketRepositoryTest.java | 4 +- .../hexagonal/domain/LotteryTicketTest.java | 12 +-- .../hexagonal/domain/PlayerDetailsTest.java | 6 +- .../hexagonal/eventlog/MongoEventLogTest.java | 2 +- .../hexagonal/test/LotteryTestUtils.java | 4 +- 13 files changed, 66 insertions(+), 81 deletions(-) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java index 4cfa83649..13a937a1b 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/database/MongoTicketRepository.java @@ -180,7 +180,7 @@ public class MongoTicketRepository implements LotteryTicketRepository { } private LotteryTicket docToTicket(Document doc) { - PlayerDetails playerDetails = PlayerDetails.create(doc.getString("email"), doc.getString("bank"), + PlayerDetails playerDetails = new PlayerDetails(doc.getString("email"), doc.getString("bank"), doc.getString("phone")); int[] numArray = Arrays.asList(doc.getString("numbers").split(",")).stream().mapToInt(Integer::parseInt).toArray(); HashSet numbers = new HashSet<>(); @@ -188,6 +188,6 @@ public class MongoTicketRepository implements LotteryTicketRepository { numbers.add(num); } LotteryNumbers lotteryNumbers = LotteryNumbers.create(numbers); - return LotteryTicket.create(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); + return new LotteryTicket(new LotteryTicketId(doc.getInteger("ticketId")), playerDetails, lotteryNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index 3e01b6e03..d544c84a1 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -39,7 +39,6 @@ public class LotteryAdministration { private final LotteryTicketRepository repository; private final LotteryEventLog notifications; private final WireTransfers wireTransfers; - private final LotteryTicketChecker checker; /** * Constructor @@ -50,7 +49,6 @@ public class LotteryAdministration { this.repository = repository; this.notifications = notifications; this.wireTransfers = wireTransfers; - this.checker = new LotteryTicketChecker(this.repository); } /** @@ -67,7 +65,7 @@ public class LotteryAdministration { LotteryNumbers numbers = LotteryNumbers.createRandom(); Map tickets = getAllSubmittedTickets(); for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = checker.checkTicketForPrize(id, numbers); + LotteryTicketCheckResult result = new LotteryTicketChecker(repository).checkTicketForPrize(id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java index fb4c8025f..28fd4e2f4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryConstants.java @@ -29,6 +29,9 @@ package com.iluwatar.hexagonal.domain; */ public class LotteryConstants { + private LotteryConstants() { + } + public static final int PRIZE_AMOUNT = 100000; public static final String SERVICE_BANK_ACCOUNT = "123-123"; public static final int TICKET_PRIZE = 3; diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index a9dff7fd0..6a032462d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -39,7 +39,6 @@ public class LotteryService { private final LotteryTicketRepository repository; private final LotteryEventLog notifications; private final WireTransfers wireTransfers; - private final LotteryTicketChecker checker; /** * Constructor @@ -50,7 +49,6 @@ public class LotteryService { this.repository = repository; this.notifications = notifications; this.wireTransfers = wireTransfers; - this.checker = new LotteryTicketChecker(this.repository); } /** @@ -74,6 +72,6 @@ public class LotteryService { * Check if lottery ticket has won */ public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return checker.checkTicketForPrize(id, winningNumbers); + return new LotteryTicketChecker(repository).checkTicketForPrize(id, winningNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java index 3e9ebdae4..9fa318e4c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicket.java @@ -36,19 +36,12 @@ public class LotteryTicket { /** * Constructor. */ - private LotteryTicket(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { + public LotteryTicket(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { this.id = id; playerDetails = details; lotteryNumbers = numbers; } - /** - * Factory for creating lottery tickets; - */ - public static LotteryTicket create(LotteryTicketId id, PlayerDetails details, LotteryNumbers numbers) { - return new LotteryTicket(id, details, numbers); - } - /** * @return player details */ diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java index 1061ad553..7af115a7c 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/PlayerDetails.java @@ -36,19 +36,12 @@ public class PlayerDetails { /** * Constructor. */ - private PlayerDetails(String email, String bankAccount, String phone) { + public PlayerDetails(String email, String bankAccount, String phone) { emailAddress = email; bankAccountNumber = bankAccount; phoneNumber = phone; } - - /** - * Factory for creating new objects. - */ - public static PlayerDetails create(String email, String bankAccount, String phone) { - return new PlayerDetails(email, bankAccount, phone); - } - + /** * @return email */ diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java index b9c779c34..13458d02a 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/sampledata/SampleData.java @@ -43,45 +43,45 @@ public class SampleData { static { PLAYERS = new ArrayList<>(); - PLAYERS.add(PlayerDetails.create("john@google.com", "312-342", "+3242434242")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "234-987", "+23452346")); - PLAYERS.add(PlayerDetails.create("steve@google.com", "833-836", "+63457543")); - PLAYERS.add(PlayerDetails.create("wayne@google.com", "319-826", "+24626")); - PLAYERS.add(PlayerDetails.create("johnie@google.com", "983-322", "+3635635")); - PLAYERS.add(PlayerDetails.create("andy@google.com", "934-734", "+0898245")); - PLAYERS.add(PlayerDetails.create("richard@google.com", "536-738", "+09845325")); - PLAYERS.add(PlayerDetails.create("kevin@google.com", "453-936", "+2423532")); - PLAYERS.add(PlayerDetails.create("arnold@google.com", "114-988", "+5646346524")); - PLAYERS.add(PlayerDetails.create("ian@google.com", "663-765", "+928394235")); - PLAYERS.add(PlayerDetails.create("robin@google.com", "334-763", "+35448")); - PLAYERS.add(PlayerDetails.create("ted@google.com", "735-964", "+98752345")); - PLAYERS.add(PlayerDetails.create("larry@google.com", "734-853", "+043842423")); - PLAYERS.add(PlayerDetails.create("calvin@google.com", "334-746", "+73294135")); - PLAYERS.add(PlayerDetails.create("jacob@google.com", "444-766", "+358042354")); - PLAYERS.add(PlayerDetails.create("edwin@google.com", "895-345", "+9752435")); - PLAYERS.add(PlayerDetails.create("mary@google.com", "760-009", "+34203542")); - PLAYERS.add(PlayerDetails.create("lolita@google.com", "425-907", "+9872342")); - PLAYERS.add(PlayerDetails.create("bruno@google.com", "023-638", "+673824122")); - PLAYERS.add(PlayerDetails.create("peter@google.com", "335-886", "+5432503945")); - PLAYERS.add(PlayerDetails.create("warren@google.com", "225-946", "+9872341324")); - PLAYERS.add(PlayerDetails.create("monica@google.com", "265-748", "+134124")); - PLAYERS.add(PlayerDetails.create("ollie@google.com", "190-045", "+34453452")); - PLAYERS.add(PlayerDetails.create("yngwie@google.com", "241-465", "+9897641231")); - PLAYERS.add(PlayerDetails.create("lars@google.com", "746-936", "+42345298345")); - PLAYERS.add(PlayerDetails.create("bobbie@google.com", "946-384", "+79831742")); - PLAYERS.add(PlayerDetails.create("tyron@google.com", "310-992", "+0498837412")); - PLAYERS.add(PlayerDetails.create("tyrell@google.com", "032-045", "+67834134")); - PLAYERS.add(PlayerDetails.create("nadja@google.com", "000-346", "+498723")); - PLAYERS.add(PlayerDetails.create("wendy@google.com", "994-989", "+987324454")); - PLAYERS.add(PlayerDetails.create("luke@google.com", "546-634", "+987642435")); - PLAYERS.add(PlayerDetails.create("bjorn@google.com", "342-874", "+7834325")); - PLAYERS.add(PlayerDetails.create("lisa@google.com", "024-653", "+980742154")); - PLAYERS.add(PlayerDetails.create("anton@google.com", "834-935", "+876423145")); - PLAYERS.add(PlayerDetails.create("bruce@google.com", "284-936", "+09843212345")); - PLAYERS.add(PlayerDetails.create("ray@google.com", "843-073", "+678324123")); - PLAYERS.add(PlayerDetails.create("ron@google.com", "637-738", "+09842354")); - PLAYERS.add(PlayerDetails.create("xavier@google.com", "143-947", "+375245")); - PLAYERS.add(PlayerDetails.create("harriet@google.com", "842-404", "+131243252")); + PLAYERS.add(new PlayerDetails("john@google.com", "312-342", "+3242434242")); + PLAYERS.add(new PlayerDetails("mary@google.com", "234-987", "+23452346")); + PLAYERS.add(new PlayerDetails("steve@google.com", "833-836", "+63457543")); + PLAYERS.add(new PlayerDetails("wayne@google.com", "319-826", "+24626")); + PLAYERS.add(new PlayerDetails("johnie@google.com", "983-322", "+3635635")); + PLAYERS.add(new PlayerDetails("andy@google.com", "934-734", "+0898245")); + PLAYERS.add(new PlayerDetails("richard@google.com", "536-738", "+09845325")); + PLAYERS.add(new PlayerDetails("kevin@google.com", "453-936", "+2423532")); + PLAYERS.add(new PlayerDetails("arnold@google.com", "114-988", "+5646346524")); + PLAYERS.add(new PlayerDetails("ian@google.com", "663-765", "+928394235")); + PLAYERS.add(new PlayerDetails("robin@google.com", "334-763", "+35448")); + PLAYERS.add(new PlayerDetails("ted@google.com", "735-964", "+98752345")); + PLAYERS.add(new PlayerDetails("larry@google.com", "734-853", "+043842423")); + PLAYERS.add(new PlayerDetails("calvin@google.com", "334-746", "+73294135")); + PLAYERS.add(new PlayerDetails("jacob@google.com", "444-766", "+358042354")); + PLAYERS.add(new PlayerDetails("edwin@google.com", "895-345", "+9752435")); + PLAYERS.add(new PlayerDetails("mary@google.com", "760-009", "+34203542")); + PLAYERS.add(new PlayerDetails("lolita@google.com", "425-907", "+9872342")); + PLAYERS.add(new PlayerDetails("bruno@google.com", "023-638", "+673824122")); + PLAYERS.add(new PlayerDetails("peter@google.com", "335-886", "+5432503945")); + PLAYERS.add(new PlayerDetails("warren@google.com", "225-946", "+9872341324")); + PLAYERS.add(new PlayerDetails("monica@google.com", "265-748", "+134124")); + PLAYERS.add(new PlayerDetails("ollie@google.com", "190-045", "+34453452")); + PLAYERS.add(new PlayerDetails("yngwie@google.com", "241-465", "+9897641231")); + PLAYERS.add(new PlayerDetails("lars@google.com", "746-936", "+42345298345")); + PLAYERS.add(new PlayerDetails("bobbie@google.com", "946-384", "+79831742")); + PLAYERS.add(new PlayerDetails("tyron@google.com", "310-992", "+0498837412")); + PLAYERS.add(new PlayerDetails("tyrell@google.com", "032-045", "+67834134")); + PLAYERS.add(new PlayerDetails("nadja@google.com", "000-346", "+498723")); + PLAYERS.add(new PlayerDetails("wendy@google.com", "994-989", "+987324454")); + PLAYERS.add(new PlayerDetails("luke@google.com", "546-634", "+987642435")); + PLAYERS.add(new PlayerDetails("bjorn@google.com", "342-874", "+7834325")); + PLAYERS.add(new PlayerDetails("lisa@google.com", "024-653", "+980742154")); + PLAYERS.add(new PlayerDetails("anton@google.com", "834-935", "+876423145")); + PLAYERS.add(new PlayerDetails("bruce@google.com", "284-936", "+09843212345")); + PLAYERS.add(new PlayerDetails("ray@google.com", "843-073", "+678324123")); + PLAYERS.add(new PlayerDetails("ron@google.com", "637-738", "+09842354")); + PLAYERS.add(new PlayerDetails("xavier@google.com", "143-947", "+375245")); + PLAYERS.add(new PlayerDetails("harriet@google.com", "842-404", "+131243252")); InMemoryBank wireTransfers = new InMemoryBank(); Random random = new Random(); for (int i = 0; i < PLAYERS.size(); i++) { @@ -95,7 +95,7 @@ public class SampleData { */ public static void submitTickets(LotteryService lotteryService, int numTickets) { for (int i = 0; i < numTickets; i++) { - LotteryTicket ticket = LotteryTicket.create(new LotteryTicketId(), + LotteryTicket ticket = new LotteryTicket(new LotteryTicketId(), getRandomPlayerDetails(), LotteryNumbers.createRandom()); lotteryService.submitTicket(ticket); } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index eb791ca55..cc13d389d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -76,7 +76,7 @@ public class ConsoleLottery { String account = readString(scanner); System.out.println("What is your phone number?"); String phone = readString(scanner); - PlayerDetails details = PlayerDetails.create(email, account, phone); + PlayerDetails details = new PlayerDetails(email, account, phone); System.out.println("Give 4 comma separated lottery numbers?"); String numbers = readString(scanner); try { @@ -86,7 +86,7 @@ public class ConsoleLottery { chosen.add(Integer.parseInt(parts[i])); } LotteryNumbers lotteryNumbers = LotteryNumbers.create(chosen); - LotteryTicket lotteryTicket = LotteryTicket.create(new LotteryTicketId(), details, lotteryNumbers); + LotteryTicket lotteryTicket = new LotteryTicket(new LotteryTicketId(), details, lotteryNumbers); Optional id = service.submitTicket(lotteryTicket); if (id.isPresent()) { System.out.println("Submitted lottery ticket with id: " + id.get()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java index bbd95b38d..e30468f99 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/database/MongoTicketRepositoryTest.java @@ -76,9 +76,9 @@ public class MongoTicketRepositoryTest { @Test public void testCrudOperations() { // create new lottery ticket and save it - PlayerDetails details = PlayerDetails.create("foo@bar.com", "123-123", "07001234"); + PlayerDetails details = new PlayerDetails("foo@bar.com", "123-123", "07001234"); LotteryNumbers random = LotteryNumbers.createRandom(); - LotteryTicket original = LotteryTicket.create(new LotteryTicketId(), details, random); + LotteryTicket original = new LotteryTicket(new LotteryTicketId(), details, random); Optional saved = repository.save(original); assertEquals(1, repository.getTicketsCollection().count()); assertTrue(saved.isPresent()); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java index 4840dc897..ce1e6b4b0 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/LotteryTicketTest.java @@ -34,16 +34,16 @@ public class LotteryTicketTest { @Test public void testEquals() { - PlayerDetails details1 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + PlayerDetails details1 = new PlayerDetails("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers1 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket1 = LotteryTicket.create(new LotteryTicketId(), details1, numbers1); - PlayerDetails details2 = PlayerDetails.create("bob@foo.bar", "1212-121212", "+34332322"); + LotteryTicket ticket1 = new LotteryTicket(new LotteryTicketId(), details1, numbers1); + PlayerDetails details2 = new PlayerDetails("bob@foo.bar", "1212-121212", "+34332322"); LotteryNumbers numbers2 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 4))); - LotteryTicket ticket2 = LotteryTicket.create(new LotteryTicketId(), details2, numbers2); + LotteryTicket ticket2 = new LotteryTicket(new LotteryTicketId(), details2, numbers2); assertEquals(ticket1, ticket2); - PlayerDetails details3 = PlayerDetails.create("elsa@foo.bar", "1223-121212", "+49332322"); + PlayerDetails details3 = new PlayerDetails("elsa@foo.bar", "1223-121212", "+49332322"); LotteryNumbers numbers3 = LotteryNumbers.create(new HashSet(Arrays.asList(1, 2, 3, 8))); - LotteryTicket ticket3 = LotteryTicket.create(new LotteryTicketId(), details3, numbers3); + LotteryTicket ticket3 = new LotteryTicket(new LotteryTicketId(), details3, numbers3); assertFalse(ticket1.equals(ticket3)); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java index 813b035a2..53aa2d9d5 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/domain/PlayerDetailsTest.java @@ -36,10 +36,10 @@ public class PlayerDetailsTest { @Test public void testEquals() { - PlayerDetails details1 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); - PlayerDetails details2 = PlayerDetails.create("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details1 = new PlayerDetails("tom@foo.bar", "11212-123434", "+12323425"); + PlayerDetails details2 = new PlayerDetails("tom@foo.bar", "11212-123434", "+12323425"); assertEquals(details1, details2); - PlayerDetails details3 = PlayerDetails.create("john@foo.bar", "16412-123439", "+34323432"); + PlayerDetails details3 = new PlayerDetails("john@foo.bar", "16412-123439", "+34323432"); assertFalse(details1.equals(details3)); } } diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java index 9b9f14c78..d02694826 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/eventlog/MongoEventLogTest.java @@ -59,7 +59,7 @@ public class MongoEventLogTest { @Test public void testFundTransfers() { - PlayerDetails playerDetails = PlayerDetails.create("john@wayne.com", "000-000", "03432534543"); + PlayerDetails playerDetails = new PlayerDetails("john@wayne.com", "000-000", "03432534543"); mongoEventLog.prizeError(playerDetails, 1000); assertEquals(1, mongoEventLog.getEventsCollection().count()); mongoEventLog.prizeError(playerDetails, 1000); diff --git a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java index 02304296f..ae15cd3d7 100644 --- a/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java +++ b/hexagonal/src/test/java/com/iluwatar/hexagonal/test/LotteryTestUtils.java @@ -50,8 +50,8 @@ public class LotteryTestUtils { */ public static LotteryTicket createLotteryTicket(String email, String account, String phone, Set givenNumbers) { - PlayerDetails details = PlayerDetails.create(email, account, phone); + PlayerDetails details = new PlayerDetails(email, account, phone); LotteryNumbers numbers = LotteryNumbers.create(givenNumbers); - return LotteryTicket.create(new LotteryTicketId(), details, numbers); + return new LotteryTicket(new LotteryTicketId(), details, numbers); } } From b030cd4ebafcdcb017cbec979ec58b0de663ceba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 17 Sep 2016 09:20:33 +0300 Subject: [PATCH 066/145] Hexagonal pattern: Introduce lottery utils class --- .../hexagonal/domain/LotteryAdministration.java | 2 +- .../iluwatar/hexagonal/domain/LotteryService.java | 2 +- ...LotteryTicketChecker.java => LotteryUtils.java} | 14 ++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) rename hexagonal/src/main/java/com/iluwatar/hexagonal/domain/{LotteryTicketChecker.java => LotteryUtils.java} (83%) diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java index d544c84a1..bc264a9ef 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryAdministration.java @@ -65,7 +65,7 @@ public class LotteryAdministration { LotteryNumbers numbers = LotteryNumbers.createRandom(); Map tickets = getAllSubmittedTickets(); for (LotteryTicketId id : tickets.keySet()) { - LotteryTicketCheckResult result = new LotteryTicketChecker(repository).checkTicketForPrize(id, numbers); + LotteryTicketCheckResult result = LotteryUtils.checkTicketForPrize(repository, id, numbers); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { boolean transferred = wireTransfers.transferFunds(LotteryConstants.PRIZE_AMOUNT, LotteryConstants.SERVICE_BANK_ACCOUNT, tickets.get(id).getPlayerDetails().getBankAccount()); diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java index 6a032462d..dceac26e4 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryService.java @@ -72,6 +72,6 @@ public class LotteryService { * Check if lottery ticket has won */ public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { - return new LotteryTicketChecker(repository).checkTicketForPrize(id, winningNumbers); + return LotteryUtils.checkTicketForPrize(repository, id, winningNumbers); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryUtils.java similarity index 83% rename from hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java rename to hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryUtils.java index ce193386b..dc34c7f7d 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryTicketChecker.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/domain/LotteryUtils.java @@ -27,20 +27,18 @@ import com.iluwatar.hexagonal.database.LotteryTicketRepository; import java.util.Optional; /** - * Lottery ticket checker + * Lottery utilities */ -public class LotteryTicketChecker { +public class LotteryUtils { - private final LotteryTicketRepository repository; - - public LotteryTicketChecker(LotteryTicketRepository repository) { - this.repository = repository; + private LotteryUtils() { } /** - * Check if lottery ticket has won + * Checks if lottery ticket has won */ - public LotteryTicketCheckResult checkTicketForPrize(LotteryTicketId id, LotteryNumbers winningNumbers) { + public static LotteryTicketCheckResult checkTicketForPrize(LotteryTicketRepository repository, LotteryTicketId id, + LotteryNumbers winningNumbers) { Optional optional = repository.findById(id); if (optional.isPresent()) { if (optional.get().getNumbers().equals(winningNumbers)) { From 6026eedd51b9753250051b620312bbedd2ccd6ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 18 Sep 2016 17:51:09 +0300 Subject: [PATCH 067/145] UML generation: Mark the urm-maven-plugin execution to be ignored in Eclipse and recreate all .puml files --- .../etc/abstract-document.urm.puml | 36 +-- .../etc/abstract-factory.urm.puml | 94 +++---- adapter/etc/adapter.urm.puml | 22 +- .../etc/aggregator-service.urm.puml | 42 +-- .../etc/inventory-microservice.urm.puml | 8 +- api-gateway/etc/api-gateway-service.urm.puml | 28 +- .../etc/async-method-invocation.urm.puml | 26 +- bridge/etc/bridge.urm.puml | 64 ++--- builder/etc/builder.urm.puml | 90 +++--- .../etc/business-delegate.urm.puml | 22 +- caching/etc/caching.urm.puml | 50 ++-- callback/etc/callback.urm.puml | 26 +- chain/etc/chain.urm.puml | 40 +-- command/etc/command.urm.puml | 68 ++--- composite/etc/composite.urm.puml | 30 +- dao/etc/dao.urm.puml | 90 +++--- decorator/etc/decorator.urm.puml | 10 +- delegation/etc/delegation.urm.puml | 20 +- .../etc/dependency-injection.urm.puml | 56 ++-- double-dispatch/etc/double-dispatch.urm.puml | 28 +- .../etc/event-aggregator.urm.puml | 54 ++-- .../etc/event-driven-architecture.urm.puml | 30 +- execute-around/etc/execute-around.urm.puml | 6 +- facade/etc/facade.urm.puml | 18 +- factory-kit/etc/factory-kit.urm.puml | 36 +-- factory-method/etc/factory-method.urm.puml | 6 +- feature-toggle/etc/feature-toggle.urm.puml | 16 +- fluentinterface/etc/fluentinterface.urm.puml | 16 +- flux/etc/flux.urm.puml | 88 +++--- flyweight/etc/flyweight.urm.puml | 46 +-- .../etc/front-controller.urm.puml | 46 +-- .../etc/half-sync-half-async.urm.puml | 26 +- hexagonal/etc/hexagonal.urm.puml | 265 ++++++++++++------ .../etc/intercepting-filter.urm.puml | 72 ++--- interpreter/etc/interpreter.urm.puml | 54 ++-- iterator/etc/iterator.urm.puml | 22 +- layers/etc/layers.urm.puml | 172 ++++++------ lazy-loading/etc/lazy-loading.urm.puml | 20 +- mediator/etc/mediator.urm.puml | 38 +-- memento/etc/memento.urm.puml | 24 +- .../etc/model-view-controller.urm.puml | 34 +-- .../etc/model-view-presenter.urm.puml | 78 +++--- monostate/etc/monostate.urm.puml | 16 +- mute-idiom/etc/mute-idiom.urm.puml | 6 +- mutex/etc/mutex.urm.puml | 12 +- naked-objects/etc/naked-objects-dom.urm.puml | 47 +--- .../etc/naked-objects-fixture.urm.puml | 10 +- .../etc/naked-objects-integtests.urm.puml | 26 +- null-object/etc/null-object.urm.puml | 36 +-- object-pool/etc/object-pool.urm.puml | 16 +- observer/etc/observer.urm.puml | 36 +-- poison-pill/etc/poison-pill.urm.puml | 46 +-- pom.xml | 19 ++ .../etc/private-class-data.urm.puml | 26 +- .../etc/producer-consumer.urm.puml | 34 +-- promise/etc/promise.urm.puml | 36 +-- property/etc/property.urm.puml | 32 +-- prototype/etc/prototype.urm.puml | 76 ++--- proxy/etc/proxy.urm.puml | 12 +- reactor/etc/reactor.urm.puml | 140 ++++----- .../etc/reader-writer-lock.urm.puml | 36 +-- repository/etc/repository.urm.puml | 50 ++-- ...rce-acquisition-is-initialization.urm.puml | 8 +- semaphore/etc/semaphore.urm.puml | 42 +-- servant/etc/servant.urm.puml | 40 +-- service-layer/etc/service-layer.urm.puml | 76 ++--- service-locator/etc/service-locator.urm.puml | 30 +- singleton/etc/singleton.urm.puml | 22 +- specification/etc/specification.urm.puml | 66 ++--- state/etc/state.urm.puml | 32 +-- step-builder/etc/step-builder.urm.puml | 36 +-- strategy/etc/strategy.urm.puml | 26 +- template-method/etc/template-method.urm.puml | 26 +- thread-pool/etc/thread-pool.urm.puml | 30 +- tolerant-reader/etc/tolerant-reader.urm.puml | 18 +- twin/etc/twin.urm.puml | 18 +- value-object/etc/value-object.urm.puml | 8 +- visitor/etc/visitor.urm.puml | 78 +++--- 78 files changed, 1676 insertions(+), 1609 deletions(-) diff --git a/abstract-document/etc/abstract-document.urm.puml b/abstract-document/etc/abstract-document.urm.puml index c738b50ce..fa15eb3c7 100644 --- a/abstract-document/etc/abstract-document.urm.puml +++ b/abstract-document/etc/abstract-document.urm.puml @@ -3,31 +3,30 @@ package com.iluwatar.abstractdocument.domain { class Part { + Part(properties : Map) } - class Car { - + Car(properties : Map) - } - interface HasModel { + interface HasPrice { + PROPERTY : String {static} - + getModel() : Optional + + getPrice() : Optional } interface HasParts { + PROPERTY : String {static} + getParts() : Stream } + class Car { + + Car(properties : Map) + } interface HasType { + PROPERTY : String {static} + getType() : Optional } - interface HasPrice { + interface HasModel { + PROPERTY : String {static} - + getPrice() : Optional + + getModel() : Optional } } package com.iluwatar.abstractdocument { - interface Document { - + children(String, Function, T>) : Stream {abstract} - + get(String) : Object {abstract} - + put(String, Object) {abstract} + class App { + + App() + + main(args : String[]) {static} } abstract class AbstractDocument { - properties : Map @@ -37,9 +36,10 @@ package com.iluwatar.abstractdocument { + put(key : String, value : Object) + toString() : String } - class App { - + App() - + main(args : String[]) {static} + interface Document { + + children(String, Function, T>) : Stream {abstract} + + get(String) : Object {abstract} + + put(String, Object) {abstract} } } AbstractDocument --+ Map @@ -47,13 +47,13 @@ Part ..|> HasType Part ..|> HasModel Part ..|> HasPrice Part --|> AbstractDocument +AbstractDocument ..|> Document +HasPrice --|> Document +HasParts --|> Document Car ..|> HasModel Car ..|> HasPrice Car ..|> HasParts Car --|> AbstractDocument -HasModel --|> Document -HasParts --|> Document -AbstractDocument ..|> Document HasType --|> Document -HasPrice --|> Document +HasModel --|> Document @enduml \ No newline at end of file diff --git a/abstract-factory/etc/abstract-factory.urm.puml b/abstract-factory/etc/abstract-factory.urm.puml index 88402c6d7..9648a6a96 100644 --- a/abstract-factory/etc/abstract-factory.urm.puml +++ b/abstract-factory/etc/abstract-factory.urm.puml @@ -1,22 +1,5 @@ @startuml package com.iluwatar.abstractfactory { - interface Castle { - + getDescription() : String {abstract} - } - class OrcKingdomFactory { - + OrcKingdomFactory() - + createArmy() : Army - + createCastle() : Castle - + createKing() : King - } - class ElfKing { - ~ DESCRIPTION : String {static} - + ElfKing() - + getDescription() : String - } - interface King { - + getDescription() : String {abstract} - } class App { - army : Army - castle : Castle @@ -34,35 +17,12 @@ package com.iluwatar.abstractfactory { - setCastle(castle : Castle) - setKing(king : King) } - class OrcKing { - ~ DESCRIPTION : String {static} - + OrcKing() - + getDescription() : String - } - class ElfKingdomFactory { - + ElfKingdomFactory() + class OrcKingdomFactory { + + OrcKingdomFactory() + createArmy() : Army + createCastle() : Castle + createKing() : King } - interface Army { - + getDescription() : String {abstract} - } - class OrcArmy { - ~ DESCRIPTION : String {static} - + OrcArmy() - + getDescription() : String - } - interface KingdomFactory { - + createArmy() : Army {abstract} - + createCastle() : Castle {abstract} - + createKing() : King {abstract} - } - class ElfArmy { - ~ DESCRIPTION : String {static} - + ElfArmy() - + getDescription() : String - } class ElfCastle { ~ DESCRIPTION : String {static} + ElfCastle() @@ -73,16 +33,56 @@ package com.iluwatar.abstractfactory { + OrcCastle() + getDescription() : String } + interface KingdomFactory { + + createArmy() : Army {abstract} + + createCastle() : Castle {abstract} + + createKing() : King {abstract} + } + class ElfKing { + ~ DESCRIPTION : String {static} + + ElfKing() + + getDescription() : String + } + class ElfArmy { + ~ DESCRIPTION : String {static} + + ElfArmy() + + getDescription() : String + } + interface Castle { + + getDescription() : String {abstract} + } + interface Army { + + getDescription() : String {abstract} + } + class OrcKing { + ~ DESCRIPTION : String {static} + + OrcKing() + + getDescription() : String + } + class OrcArmy { + ~ DESCRIPTION : String {static} + + OrcArmy() + + getDescription() : String + } + interface King { + + getDescription() : String {abstract} + } + class ElfKingdomFactory { + + ElfKingdomFactory() + + createArmy() : Army + + createCastle() : Castle + + createKing() : King + } } App --> "-castle" Castle App --> "-king" King App --> "-army" Army OrcKingdomFactory ..|> KingdomFactory -ElfKing ..|> King -OrcKing ..|> King -ElfKingdomFactory ..|> KingdomFactory -OrcArmy ..|> Army -ElfArmy ..|> Army ElfCastle ..|> Castle OrcCastle ..|> Castle +ElfKing ..|> King +ElfArmy ..|> Army +OrcKing ..|> King +OrcArmy ..|> Army +ElfKingdomFactory ..|> KingdomFactory @enduml \ No newline at end of file diff --git a/adapter/etc/adapter.urm.puml b/adapter/etc/adapter.urm.puml index 2cee13dc4..a7c962a3b 100644 --- a/adapter/etc/adapter.urm.puml +++ b/adapter/etc/adapter.urm.puml @@ -1,13 +1,19 @@ @startuml package com.iluwatar.adapter { - class App { - + App() - + main(args : String[]) {static} - } interface BattleShip { + fire() {abstract} + move() {abstract} } + class BattleFishingBoat { + - boat : FishingBoat + + BattleFishingBoat() + + fire() + + move() + } + class App { + + App() + + main(args : String[]) {static} + } class Captain { - battleship : BattleShip + Captain() @@ -16,12 +22,6 @@ package com.iluwatar.adapter { + move() + setBattleship(battleship : BattleShip) } - class BattleFishingBoat { - - boat : FishingBoat - + BattleFishingBoat() - + fire() - + move() - } class FishingBoat { + FishingBoat() + fish() @@ -30,6 +30,6 @@ package com.iluwatar.adapter { } BattleFishingBoat --> "-boat" FishingBoat Captain --> "-battleship" BattleShip -Captain ..|> BattleShip BattleFishingBoat ..|> BattleShip +Captain ..|> BattleShip @enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/aggregator-service.urm.puml b/aggregator-microservices/etc/aggregator-service.urm.puml index 5c2e1167a..2a6600531 100644 --- a/aggregator-microservices/etc/aggregator-service.urm.puml +++ b/aggregator-microservices/etc/aggregator-service.urm.puml @@ -1,5 +1,25 @@ @startuml package com.iluwatar.aggregator.microservices { + class ProductInventoryClientImpl { + + ProductInventoryClientImpl() + + getProductInventories() : int + } + class App { + + App() + + main(args : String[]) {static} + } + interface ProductInventoryClient { + + getProductInventories() : int {abstract} + } + class Product { + - productInventories : int + - title : String + + Product() + + getProductInventories() : int + + getTitle() : String + + setProductInventories(productInventories : int) + + setTitle(title : String) + } class Aggregator { - informationClient : ProductInformationClient - inventoryClient : ProductInventoryClient @@ -13,29 +33,9 @@ package com.iluwatar.aggregator.microservices { interface ProductInformationClient { + getProductTitle() : String {abstract} } - class Product { - - productInventories : int - - title : String - + Product() - + getProductInventories() : int - + getTitle() : String - + setProductInventories(productInventories : int) - + setTitle(title : String) - } - class ProductInventoryClientImpl { - + ProductInventoryClientImpl() - + getProductInventories() : int - } - class App { - + App() - + main(args : String[]) {static} - } - interface ProductInventoryClient { - + getProductInventories() : int {abstract} - } } Aggregator --> "-inventoryClient" ProductInventoryClient Aggregator --> "-informationClient" ProductInformationClient -ProductInformationClientImpl ..|> ProductInformationClient ProductInventoryClientImpl ..|> ProductInventoryClient +ProductInformationClientImpl ..|> ProductInformationClient @enduml \ No newline at end of file diff --git a/aggregator-microservices/etc/inventory-microservice.urm.puml b/aggregator-microservices/etc/inventory-microservice.urm.puml index 90f327e07..a07a36306 100644 --- a/aggregator-microservices/etc/inventory-microservice.urm.puml +++ b/aggregator-microservices/etc/inventory-microservice.urm.puml @@ -1,12 +1,12 @@ @startuml package com.iluwatar.inventory.microservice { - class InventoryApplication { - + InventoryApplication() - + main(args : String[]) {static} - } class InventoryController { + InventoryController() + getProductInventories() : int } + class InventoryApplication { + + InventoryApplication() + + main(args : String[]) {static} + } } @enduml \ No newline at end of file diff --git a/api-gateway/etc/api-gateway-service.urm.puml b/api-gateway/etc/api-gateway-service.urm.puml index 3313f7059..409e7ee91 100644 --- a/api-gateway/etc/api-gateway-service.urm.puml +++ b/api-gateway/etc/api-gateway-service.urm.puml @@ -1,7 +1,16 @@ @startuml package com.iluwatar.api.gateway { - interface ImageClient { - + getImagePath() : String {abstract} + class App { + + App() + + main(args : String[]) {static} + } + class PriceClientImpl { + + PriceClientImpl() + + getPrice() : String + } + class ImageClientImpl { + + ImageClientImpl() + + getImagePath() : String } class MobileProduct { - price : String @@ -9,6 +18,9 @@ package com.iluwatar.api.gateway { + getPrice() : String + setPrice(price : String) } + interface ImageClient { + + getImagePath() : String {abstract} + } class ApiGateway { - imageClient : ImageClient - priceClient : PriceClient @@ -28,18 +40,6 @@ package com.iluwatar.api.gateway { interface PriceClient { + getPrice() : String {abstract} } - class PriceClientImpl { - + PriceClientImpl() - + getPrice() : String - } - class ImageClientImpl { - + ImageClientImpl() - + getImagePath() : String - } - class App { - + App() - + main(args : String[]) {static} - } } ApiGateway --> "-imageClient" ImageClient ApiGateway --> "-priceClient" PriceClient diff --git a/async-method-invocation/etc/async-method-invocation.urm.puml b/async-method-invocation/etc/async-method-invocation.urm.puml index 9a90d307e..b96e843d3 100644 --- a/async-method-invocation/etc/async-method-invocation.urm.puml +++ b/async-method-invocation/etc/async-method-invocation.urm.puml @@ -1,13 +1,22 @@ @startuml package com.iluwatar.async.method.invocation { - interface AsyncCallback { - + onComplete(T, Optional) {abstract} + class App { + + App() + - callback(name : String) : AsyncCallback {static} + - lazyval(value : T, delayMillis : long) : Callable {static} + - log(msg : String) {static} + + main(args : String[]) {static} } interface AsyncResult { + await() {abstract} + getValue() : T {abstract} + isCompleted() : boolean {abstract} } + interface AsyncExecutor { + + endProcess(AsyncResult) : T {abstract} + + startProcess(Callable) : AsyncResult {abstract} + + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} + } class ThreadAsyncExecutor { - idx : AtomicInteger + ThreadAsyncExecutor() @@ -15,12 +24,8 @@ package com.iluwatar.async.method.invocation { + startProcess(task : Callable) : AsyncResult + startProcess(task : Callable, callback : AsyncCallback) : AsyncResult } - class App { - + App() - - callback(name : String) : AsyncCallback {static} - - lazyval(value : T, delayMillis : long) : Callable {static} - - log(msg : String) {static} - + main(args : String[]) {static} + interface AsyncCallback { + + onComplete(T, Optional) {abstract} } -class CompletableResult { ~ COMPLETED : int {static} @@ -38,11 +43,6 @@ package com.iluwatar.async.method.invocation { ~ setException(exception : Exception) ~ setValue(value : T) } - interface AsyncExecutor { - + endProcess(AsyncResult) : T {abstract} - + startProcess(Callable) : AsyncResult {abstract} - + startProcess(Callable, AsyncCallback) : AsyncResult {abstract} - } } CompletableResult ..+ ThreadAsyncExecutor ThreadAsyncExecutor ..|> AsyncExecutor diff --git a/bridge/etc/bridge.urm.puml b/bridge/etc/bridge.urm.puml index d9d7a4145..84e250a06 100644 --- a/bridge/etc/bridge.urm.puml +++ b/bridge/etc/bridge.urm.puml @@ -8,26 +8,6 @@ package com.iluwatar.bridge { + unwield() + wield() } - abstract class MagicWeapon { - # imp : MagicWeaponImpl - + MagicWeapon(imp : MagicWeaponImpl) - + getImp() : MagicWeaponImpl - + swing() {abstract} - + unwield() {abstract} - + wield() {abstract} - } - abstract class SoulEatingMagicWeaponImpl { - + SoulEatingMagicWeaponImpl() - + eatSoulImp() {abstract} - } - class BlindingMagicWeapon { - + BlindingMagicWeapon(imp : BlindingMagicWeaponImpl) - + blind() - + getImp() : BlindingMagicWeaponImpl - + swing() - + unwield() - + wield() - } class Stormbringer { + Stormbringer() + eatSoulImp() @@ -35,9 +15,9 @@ package com.iluwatar.bridge { + unwieldImp() + wieldImp() } - abstract class BlindingMagicWeaponImpl { - + BlindingMagicWeaponImpl() - + blindImp() {abstract} + abstract class FlyingMagicWeaponImpl { + + FlyingMagicWeaponImpl() + + flyImp() {abstract} } class SoulEatingMagicWeapon { + SoulEatingMagicWeapon(imp : SoulEatingMagicWeaponImpl) @@ -53,6 +33,10 @@ package com.iluwatar.bridge { + unwieldImp() {abstract} + wieldImp() {abstract} } + abstract class SoulEatingMagicWeaponImpl { + + SoulEatingMagicWeaponImpl() + + eatSoulImp() {abstract} + } class Excalibur { + Excalibur() + blindImp() @@ -60,10 +44,6 @@ package com.iluwatar.bridge { + unwieldImp() + wieldImp() } - abstract class FlyingMagicWeaponImpl { - + FlyingMagicWeaponImpl() - + flyImp() {abstract} - } class Mjollnir { + Mjollnir() + flyImp() @@ -75,15 +55,35 @@ package com.iluwatar.bridge { + App() + main(args : String[]) {static} } + abstract class MagicWeapon { + # imp : MagicWeaponImpl + + MagicWeapon(imp : MagicWeaponImpl) + + getImp() : MagicWeaponImpl + + swing() {abstract} + + unwield() {abstract} + + wield() {abstract} + } + abstract class BlindingMagicWeaponImpl { + + BlindingMagicWeaponImpl() + + blindImp() {abstract} + } + class BlindingMagicWeapon { + + BlindingMagicWeapon(imp : BlindingMagicWeaponImpl) + + blind() + + getImp() : BlindingMagicWeaponImpl + + swing() + + unwield() + + wield() + } } MagicWeapon --> "-imp" MagicWeaponImpl FlyingMagicWeapon --|> MagicWeapon -SoulEatingMagicWeaponImpl --|> MagicWeaponImpl -BlindingMagicWeapon --|> MagicWeapon Stormbringer --|> SoulEatingMagicWeaponImpl -BlindingMagicWeaponImpl --|> MagicWeaponImpl -SoulEatingMagicWeapon --|> MagicWeapon -Excalibur --|> BlindingMagicWeaponImpl FlyingMagicWeaponImpl --|> MagicWeaponImpl +SoulEatingMagicWeapon --|> MagicWeapon +SoulEatingMagicWeaponImpl --|> MagicWeaponImpl +Excalibur --|> BlindingMagicWeaponImpl Mjollnir --|> FlyingMagicWeaponImpl +BlindingMagicWeaponImpl --|> MagicWeaponImpl +BlindingMagicWeapon --|> MagicWeapon @enduml \ No newline at end of file diff --git a/builder/etc/builder.urm.puml b/builder/etc/builder.urm.puml index 262476329..0c98f3b4b 100644 --- a/builder/etc/builder.urm.puml +++ b/builder/etc/builder.urm.puml @@ -1,5 +1,23 @@ @startuml package com.iluwatar.builder { + class Builder { + - armor : Armor + - hairColor : HairColor + - hairType : HairType + - name : String + - profession : Profession + - weapon : Weapon + + Builder(profession : Profession, name : String) + + build() : Hero + + withArmor(armor : Armor) : Builder + + withHairColor(hairColor : HairColor) : Builder + + withHairType(hairType : HairType) : Builder + + withWeapon(weapon : Weapon) : Builder + } + class App { + + App() + + main(args : String[]) {static} + } class Hero { - armor : Armor - hairColor : HairColor @@ -16,33 +34,25 @@ package com.iluwatar.builder { + getWeapon() : Weapon + toString() : String } - class App { - + App() - + main(args : String[]) {static} - } - class Builder { - - armor : Armor - - hairColor : HairColor - - hairType : HairType - - name : String - - profession : Profession - - weapon : Weapon - + Builder(profession : Profession, name : String) - + build() : Hero - + withArmor(armor : Armor) : Builder - + withHairColor(hairColor : HairColor) : Builder - + withHairType(hairType : HairType) : Builder - + withWeapon(weapon : Weapon) : Builder - } - enum Armor { - + CHAIN_MAIL {static} - + CLOTHES {static} - + LEATHER {static} - + PLATE_MAIL {static} - - title : String + enum Weapon { + + AXE {static} + + BOW {static} + + DAGGER {static} + + SWORD {static} + + WARHAMMER {static} + toString() : String - + valueOf(name : String) : Armor {static} - + values() : Armor[] {static} + + valueOf(name : String) : Weapon {static} + + values() : Weapon[] {static} + } + enum HairColor { + + BLACK {static} + + BLOND {static} + + BROWN {static} + + RED {static} + + WHITE {static} + + toString() : String + + valueOf(name : String) : HairColor {static} + + values() : HairColor[] {static} } enum Profession { + MAGE {static} @@ -53,15 +63,15 @@ package com.iluwatar.builder { + valueOf(name : String) : Profession {static} + values() : Profession[] {static} } - enum Weapon { - + AXE {static} - + BOW {static} - + DAGGER {static} - + SWORD {static} - + WARHAMMER {static} + enum Armor { + + CHAIN_MAIL {static} + + CLOTHES {static} + + LEATHER {static} + + PLATE_MAIL {static} + - title : String + toString() : String - + valueOf(name : String) : Weapon {static} - + values() : Weapon[] {static} + + valueOf(name : String) : Armor {static} + + values() : Armor[] {static} } enum HairType { + BALD {static} @@ -74,19 +84,9 @@ package com.iluwatar.builder { + valueOf(name : String) : HairType {static} + values() : HairType[] {static} } - enum HairColor { - + BLACK {static} - + BLOND {static} - + BROWN {static} - + RED {static} - + WHITE {static} - + toString() : String - + valueOf(name : String) : HairColor {static} - + values() : HairColor[] {static} - } } -Builder ..+ Hero Hero --> "-profession" Profession +Builder ..+ Hero Hero --> "-armor" Armor App --+ Hero Builder --> "-weapon" Weapon diff --git a/business-delegate/etc/business-delegate.urm.puml b/business-delegate/etc/business-delegate.urm.puml index a8f31700b..a58136c8c 100644 --- a/business-delegate/etc/business-delegate.urm.puml +++ b/business-delegate/etc/business-delegate.urm.puml @@ -1,5 +1,9 @@ @startuml package com.iluwatar.business.delegate { + class EjbService { + + EjbService() + + doProcessing() + } class BusinessLookup { - ejbService : EjbService - jmsService : JmsService @@ -8,15 +12,18 @@ package com.iluwatar.business.delegate { + setEjbService(ejbService : EjbService) + setJmsService(jmsService : JmsService) } + class App { + + App() + + main(args : String[]) {static} + } + interface BusinessService { + + doProcessing() {abstract} + } class Client { - businessDelegate : BusinessDelegate + Client(businessDelegate : BusinessDelegate) + doTask() } - class EjbService { - + EjbService() - + doProcessing() - } class BusinessDelegate { - businessService : BusinessService - lookupService : BusinessLookup @@ -26,17 +33,10 @@ package com.iluwatar.business.delegate { + setLookupService(businessLookup : BusinessLookup) + setServiceType(serviceType : ServiceType) } - interface BusinessService { - + doProcessing() {abstract} - } class JmsService { + JmsService() + doProcessing() } - class App { - + App() - + main(args : String[]) {static} - } enum ServiceType { + EJB {static} + JMS {static} diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml index 273c9911c..b8f4fb49b 100644 --- a/caching/etc/caching.urm.puml +++ b/caching/etc/caching.urm.puml @@ -1,17 +1,18 @@ @startuml package com.iluwatar.caching { - class UserAccount { - - additionalInfo : String - - userId : String - - userName : String - + UserAccount(userId : String, userName : String, additionalInfo : String) - + getAdditionalInfo() : String - + getUserId() : String - + getUserName() : String - + setAdditionalInfo(additionalInfo : String) - + setUserId(userId : String) - + setUserName(userName : String) - + toString() : String + class App { + + App() + + main(args : String[]) {static} + + useReadAndWriteThroughStrategy() + + useReadThroughAndWriteAroundStrategy() + + useReadThroughAndWriteBehindStrategy() + } + ~class Node { + ~ next : Node + ~ previous : Node + ~ userAccount : UserAccount + ~ userId : String + + Node(this$0 : String, userId : UserAccount) } class CacheStore { ~ cache : LruCache {static} @@ -36,12 +37,18 @@ package com.iluwatar.caching { + printCacheContent() : String {static} + save(userAccount : UserAccount) {static} } - ~class Node { - ~ next : Node - ~ previous : Node - ~ userAccount : UserAccount - ~ userId : String - + Node(this$0 : LruCache, userId : String, userAccount : UserAccount) + class UserAccount { + - additionalInfo : String + - userId : String + - userName : String + + UserAccount(userId : String, userName : String, additionalInfo : String) + + getAdditionalInfo() : String + + getUserId() : String + + getUserName() : String + + setAdditionalInfo(additionalInfo : String) + + setUserId(userId : String) + + setUserName(userName : String) + + toString() : String } class LruCache { ~ cache : Map @@ -74,13 +81,6 @@ package com.iluwatar.caching { + upsertDb(userAccount : UserAccount) {static} + writeToDb(userAccount : UserAccount) {static} } - class App { - + App() - + main(args : String[]) {static} - + useReadAndWriteThroughStrategy() - + useReadThroughAndWriteAroundStrategy() - + useReadThroughAndWriteBehindStrategy() - } enum CachingPolicy { + AROUND {static} + BEHIND {static} diff --git a/callback/etc/callback.urm.puml b/callback/etc/callback.urm.puml index 8b27ee8a8..b9e1ca694 100644 --- a/callback/etc/callback.urm.puml +++ b/callback/etc/callback.urm.puml @@ -1,24 +1,24 @@ @startuml package com.iluwatar.callback { - class LambdasApp { - + LambdasApp() - + main(args : String[]) {static} - } - class SimpleTask { - + SimpleTask() - + execute() - } - class App { - + App() - + main(args : String[]) {static} + interface Callback { + + call() {abstract} } abstract class Task { + Task() + execute() {abstract} + executeWith(callback : Callback) } - interface Callback { - + call() {abstract} + class App { + + App() + + main(args : String[]) {static} + } + class SimpleTask { + + SimpleTask() + + execute() + } + class LambdasApp { + + LambdasApp() + + main(args : String[]) {static} } } SimpleTask --|> Task diff --git a/chain/etc/chain.urm.puml b/chain/etc/chain.urm.puml index c75cbc8d1..21365765d 100644 --- a/chain/etc/chain.urm.puml +++ b/chain/etc/chain.urm.puml @@ -1,13 +1,20 @@ @startuml package com.iluwatar.chain { + class OrcSoldier { + + OrcSoldier(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } class OrcCommander { + OrcCommander(handler : RequestHandler) + handleRequest(req : Request) + toString() : String } - class App { - + App() - + main(args : String[]) {static} + class OrcKing { + ~ chain : RequestHandler + + OrcKing() + - buildChain() + + makeRequest(req : Request) } class Request { - handled : boolean @@ -20,21 +27,9 @@ package com.iluwatar.chain { + markHandled() + toString() : String } - class OrcOfficer { - + OrcOfficer(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String - } - class OrcKing { - ~ chain : RequestHandler - + OrcKing() - - buildChain() - + makeRequest(req : Request) - } - class OrcSoldier { - + OrcSoldier(handler : RequestHandler) - + handleRequest(req : Request) - + toString() : String + class App { + + App() + + main(args : String[]) {static} } abstract class RequestHandler { - next : RequestHandler @@ -43,6 +38,11 @@ package com.iluwatar.chain { # printHandling(req : Request) + toString() : String {abstract} } + class OrcOfficer { + + OrcOfficer(handler : RequestHandler) + + handleRequest(req : Request) + + toString() : String + } enum RequestType { + COLLECT_TAX {static} + DEFEND_CASTLE {static} @@ -52,9 +52,9 @@ package com.iluwatar.chain { } } RequestHandler --> "-next" RequestHandler -OrcKing --> "-chain" RequestHandler Request --> "-requestType" RequestType +OrcKing --> "-chain" RequestHandler +OrcSoldier --|> RequestHandler OrcCommander --|> RequestHandler OrcOfficer --|> RequestHandler -OrcSoldier --|> RequestHandler @enduml \ No newline at end of file diff --git a/command/etc/command.urm.puml b/command/etc/command.urm.puml index 015fb30be..27bff7330 100644 --- a/command/etc/command.urm.puml +++ b/command/etc/command.urm.puml @@ -1,29 +1,5 @@ @startuml package com.iluwatar.command { - abstract class Target { - - size : Size - - visibility : Visibility - + Target() - + getSize() : Size - + getVisibility() : Visibility - + printStatus() - + setSize(size : Size) - + setVisibility(visibility : Visibility) - + toString() : String {abstract} - } - class Goblin { - + Goblin() - + toString() : String - } - class ShrinkSpell { - - oldSize : Size - - target : Target - + ShrinkSpell() - + execute(target : Target) - + redo() - + toString() : String - + undo() - } class InvisibilitySpell { - target : Target + InvisibilitySpell() @@ -32,15 +8,6 @@ package com.iluwatar.command { + toString() : String + undo() } - class Wizard { - - redoStack : Deque - - undoStack : Deque - + Wizard() - + castSpell(command : Command, target : Target) - + redoLastSpell() - + toString() : String - + undoLastSpell() - } class App { + App() + main(args : String[]) {static} @@ -52,6 +19,39 @@ package com.iluwatar.command { + toString() : String {abstract} + undo() {abstract} } + class Goblin { + + Goblin() + + toString() : String + } + abstract class Target { + - size : Size + - visibility : Visibility + + Target() + + getSize() : Size + + getVisibility() : Visibility + + printStatus() + + setSize(size : Size) + + setVisibility(visibility : Visibility) + + toString() : String {abstract} + } + class Wizard { + - redoStack : Deque + - undoStack : Deque + + Wizard() + + castSpell(command : Command, target : Target) + + redoLastSpell() + + toString() : String + + undoLastSpell() + } + class ShrinkSpell { + - oldSize : Size + - target : Target + + ShrinkSpell() + + execute(target : Target) + + redo() + + toString() : String + + undo() + } enum Size { + LARGE {static} + NORMAL {static} @@ -78,7 +78,7 @@ ShrinkSpell --> "-oldSize" Size InvisibilitySpell --> "-target" Target ShrinkSpell --> "-target" Target Target --> "-visibility" Visibility +InvisibilitySpell --|> Command Goblin --|> Target ShrinkSpell --|> Command -InvisibilitySpell --|> Command @enduml \ No newline at end of file diff --git a/composite/etc/composite.urm.puml b/composite/etc/composite.urm.puml index 6f6e93c98..82e9b65eb 100644 --- a/composite/etc/composite.urm.puml +++ b/composite/etc/composite.urm.puml @@ -1,5 +1,19 @@ @startuml package com.iluwatar.composite { + class Word { + + Word(letters : List) + # printThisAfter() + # printThisBefore() + } + class App { + + App() + + main(args : String[]) {static} + } + class Messenger { + + Messenger() + ~ messageFromElves() : LetterComposite + ~ messageFromOrcs() : LetterComposite + } class Letter { - c : char + Letter(c : char) @@ -11,20 +25,6 @@ package com.iluwatar.composite { # printThisAfter() # printThisBefore() } - class Word { - + Word(letters : List) - # printThisAfter() - # printThisBefore() - } - class Messenger { - + Messenger() - ~ messageFromElves() : LetterComposite - ~ messageFromOrcs() : LetterComposite - } - class App { - + App() - + main(args : String[]) {static} - } abstract class LetterComposite { - children : List + LetterComposite() @@ -36,7 +36,7 @@ package com.iluwatar.composite { } } LetterComposite --> "-children" LetterComposite +Word --|> LetterComposite Letter --|> LetterComposite Sentence --|> LetterComposite -Word --|> LetterComposite @enduml \ No newline at end of file diff --git a/dao/etc/dao.urm.puml b/dao/etc/dao.urm.puml index f751b967c..90a9c9725 100644 --- a/dao/etc/dao.urm.puml +++ b/dao/etc/dao.urm.puml @@ -1,5 +1,49 @@ @startuml package com.iluwatar.dao { + class InMemoryCustomerDao { + - idToCustomer : Map + + InMemoryCustomerDao() + + add(customer : Customer) : boolean + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + + update(customer : Customer) : boolean + } + interface CustomerSchemaSql { + + CREATE_SCHEMA_SQL : String {static} + + DELETE_SCHEMA_SQL : String {static} + } + interface CustomerDao { + + add(Customer) : boolean {abstract} + + delete(Customer) : boolean {abstract} + + getAll() : Stream {abstract} + + getById(int) : Optional {abstract} + + update(Customer) : boolean {abstract} + } + class App { + - DB_URL : String {static} + - log : Logger {static} + + App() + - addCustomers(customerDao : CustomerDao) {static} + - createDataSource() : DataSource {static} + - createSchema(dataSource : DataSource) {static} + - deleteSchema(dataSource : DataSource) {static} + + generateSampleCustomers() : List {static} + + main(args : String[]) {static} + - performOperationsUsing(customerDao : CustomerDao) {static} + } + class DbCustomerDao { + - dataSource : DataSource + + DbCustomerDao(dataSource : DataSource) + + add(customer : Customer) : boolean + - createCustomer(resultSet : ResultSet) : Customer + + delete(customer : Customer) : boolean + + getAll() : Stream + + getById(id : int) : Optional + - getConnection() : Connection + - mutedClose(connection : Connection) + + update(customer : Customer) : boolean + } class Customer { - firstName : String - id : int @@ -15,51 +59,7 @@ package com.iluwatar.dao { + setLastName(lastName : String) + toString() : String } - interface CustomerDao { - + add(Customer) : boolean {abstract} - + delete(Customer) : boolean {abstract} - + getAll() : Stream {abstract} - + getById(int) : Optional {abstract} - + update(Customer) : boolean {abstract} - } - class DbCustomerDao { - - dataSource : DataSource - + DbCustomerDao(dataSource : DataSource) - + add(customer : Customer) : boolean - - createCustomer(resultSet : ResultSet) : Customer - + delete(customer : Customer) : boolean - + getAll() : Stream - + getById(id : int) : Optional - - getConnection() : Connection - - mutedClose(connection : Connection) - + update(customer : Customer) : boolean - } - class InMemoryCustomerDao { - - idToCustomer : Map - + InMemoryCustomerDao() - + add(customer : Customer) : boolean - + delete(customer : Customer) : boolean - + getAll() : Stream - + getById(id : int) : Optional - + update(customer : Customer) : boolean - } - interface CustomerSchemaSql { - + CREATE_SCHEMA_SQL : String {static} - + DELETE_SCHEMA_SQL : String {static} - } - class App { - - DB_URL : String {static} - - log : Logger {static} - + App() - - addCustomers(customerDao : CustomerDao) {static} - - createDataSource() : DataSource {static} - - createSchema(dataSource : DataSource) {static} - - deleteSchema(dataSource : DataSource) {static} - + generateSampleCustomers() : List {static} - + main(args : String[]) {static} - - performOperationsUsing(customerDao : CustomerDao) {static} - } } -DbCustomerDao ..|> CustomerDao InMemoryCustomerDao ..|> CustomerDao +DbCustomerDao ..|> CustomerDao @enduml \ No newline at end of file diff --git a/decorator/etc/decorator.urm.puml b/decorator/etc/decorator.urm.puml index 6c44e6cc9..4862ddfeb 100644 --- a/decorator/etc/decorator.urm.puml +++ b/decorator/etc/decorator.urm.puml @@ -1,5 +1,10 @@ @startuml package com.iluwatar.decorator { + interface Hostile { + + attack() {abstract} + + fleeBattle() {abstract} + + getAttackPower() : int {abstract} + } class App { + App() + main(args : String[]) {static} @@ -10,11 +15,6 @@ package com.iluwatar.decorator { + fleeBattle() + getAttackPower() : int } - interface Hostile { - + attack() {abstract} - + fleeBattle() {abstract} - + getAttackPower() : int {abstract} - } class SmartHostile { - decorated : Hostile + SmartHostile(decorated : Hostile) diff --git a/delegation/etc/delegation.urm.puml b/delegation/etc/delegation.urm.puml index c143a6ba0..378587019 100644 --- a/delegation/etc/delegation.urm.puml +++ b/delegation/etc/delegation.urm.puml @@ -1,19 +1,24 @@ @startuml package com.iluwatar.delegation.simple.printers { - class EpsonPrinter { - + EpsonPrinter() - + print(message : String) - } class HpPrinter { + HpPrinter() + print(message : String) } + class EpsonPrinter { + + EpsonPrinter() + + print(message : String) + } class CanonPrinter { + CanonPrinter() + print(message : String) } } package com.iluwatar.delegation.simple { + class App { + + MESSAGE_TO_PRINT : String {static} + + App() + + main(args : String[]) {static} + } class PrinterController { - printer : Printer + PrinterController(printer : Printer) @@ -22,15 +27,10 @@ package com.iluwatar.delegation.simple { interface Printer { + print(String) {abstract} } - class App { - + MESSAGE_TO_PRINT : String {static} - + App() - + main(args : String[]) {static} - } } PrinterController --> "-printer" Printer +HpPrinter ..|> Printer PrinterController ..|> Printer EpsonPrinter ..|> Printer -HpPrinter ..|> Printer CanonPrinter ..|> Printer @enduml \ No newline at end of file diff --git a/dependency-injection/etc/dependency-injection.urm.puml b/dependency-injection/etc/dependency-injection.urm.puml index c22c658ad..bf4d10599 100644 --- a/dependency-injection/etc/dependency-injection.urm.puml +++ b/dependency-injection/etc/dependency-injection.urm.puml @@ -1,48 +1,48 @@ @startuml package com.iluwatar.dependency.injection { - interface Wizard { - + smoke() {abstract} - } - class GuiceWizard { - - tobacco : Tobacco - + GuiceWizard(tobacco : Tobacco) - + smoke() - } - class OldTobyTobacco { - + OldTobyTobacco() - } - abstract class Tobacco { - + Tobacco() - + smoke(wizard : Wizard) - } - class App { - + App() - + main(args : String[]) {static} - } - class RivendellTobacco { - + RivendellTobacco() - } class AdvancedWizard { - tobacco : Tobacco + AdvancedWizard(tobacco : Tobacco) + smoke() } - class SecondBreakfastTobacco { - + SecondBreakfastTobacco() + interface Wizard { + + smoke() {abstract} + } + class RivendellTobacco { + + RivendellTobacco() } class SimpleWizard { - tobacco : OldTobyTobacco + SimpleWizard() + smoke() } + class OldTobyTobacco { + + OldTobyTobacco() + } + class SecondBreakfastTobacco { + + SecondBreakfastTobacco() + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Tobacco { + + Tobacco() + + smoke(wizard : Wizard) + } + class GuiceWizard { + - tobacco : Tobacco + + GuiceWizard(tobacco : Tobacco) + + smoke() + } } SimpleWizard --> "-tobacco" OldTobyTobacco AdvancedWizard --> "-tobacco" Tobacco GuiceWizard --> "-tobacco" Tobacco -GuiceWizard ..|> Wizard -OldTobyTobacco --|> Tobacco -RivendellTobacco --|> Tobacco AdvancedWizard ..|> Wizard -SecondBreakfastTobacco --|> Tobacco +RivendellTobacco --|> Tobacco SimpleWizard ..|> Wizard +OldTobyTobacco --|> Tobacco +SecondBreakfastTobacco --|> Tobacco +GuiceWizard ..|> Wizard @enduml \ No newline at end of file diff --git a/double-dispatch/etc/double-dispatch.urm.puml b/double-dispatch/etc/double-dispatch.urm.puml index 725f009c0..78d361326 100644 --- a/double-dispatch/etc/double-dispatch.urm.puml +++ b/double-dispatch/etc/double-dispatch.urm.puml @@ -1,17 +1,5 @@ @startuml package com.iluwatar.doubledispatch { - class App { - + App() - + main(args : String[]) {static} - } - class FlamingAsteroid { - + FlamingAsteroid(left : int, top : int, right : int, bottom : int) - + collision(gameObject : GameObject) - } - class SpaceStationIss { - + SpaceStationIss(left : int, top : int, right : int, bottom : int) - + collision(gameObject : GameObject) - } abstract class GameObject { - damaged : boolean - onFire : boolean @@ -27,6 +15,14 @@ package com.iluwatar.doubledispatch { + setOnFire(onFire : boolean) + toString() : String } + class SpaceStationIss { + + SpaceStationIss(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } + class FlamingAsteroid { + + FlamingAsteroid(left : int, top : int, right : int, bottom : int) + + collision(gameObject : GameObject) + } class SpaceStationMir { + SpaceStationMir(left : int, top : int, right : int, bottom : int) + collision(gameObject : GameObject) @@ -56,10 +52,14 @@ package com.iluwatar.doubledispatch { ~ intersectsWith(r : Rectangle) : boolean + toString() : String } + class App { + + App() + + main(args : String[]) {static} + } } -FlamingAsteroid --|> Meteoroid -SpaceStationIss --|> SpaceStationMir GameObject --|> Rectangle +SpaceStationIss --|> SpaceStationMir +FlamingAsteroid --|> Meteoroid SpaceStationMir --|> GameObject Meteoroid --|> GameObject @enduml \ No newline at end of file diff --git a/event-aggregator/etc/event-aggregator.urm.puml b/event-aggregator/etc/event-aggregator.urm.puml index 18b7621c9..3ccfea536 100644 --- a/event-aggregator/etc/event-aggregator.urm.puml +++ b/event-aggregator/etc/event-aggregator.urm.puml @@ -1,34 +1,10 @@ @startuml package com.iluwatar.event.aggregator { - class KingsHand { - + KingsHand() - + KingsHand(obs : EventObserver) - + onEvent(e : Event) - + timePasses(day : Weekday) - } - class KingJoffrey { - + KingJoffrey() - + onEvent(e : Event) - } - class Scout { - + Scout() - + Scout(obs : EventObserver) - + timePasses(day : Weekday) - } class LordVarys { + LordVarys() + LordVarys(obs : EventObserver) + timePasses(day : Weekday) } - class App { - + App() - + main(args : String[]) {static} - } - class LordBaelish { - + LordBaelish() - + LordBaelish(obs : EventObserver) - + timePasses(day : Weekday) - } abstract class EventEmitter { - observers : List + EventEmitter() @@ -37,9 +13,33 @@ package com.iluwatar.event.aggregator { + registerObserver(obs : EventObserver) + timePasses(Weekday) {abstract} } + class KingJoffrey { + + KingJoffrey() + + onEvent(e : Event) + } + class LordBaelish { + + LordBaelish() + + LordBaelish(obs : EventObserver) + + timePasses(day : Weekday) + } interface EventObserver { + onEvent(Event) {abstract} } + class KingsHand { + + KingsHand() + + KingsHand(obs : EventObserver) + + onEvent(e : Event) + + timePasses(day : Weekday) + } + class Scout { + + Scout() + + Scout(obs : EventObserver) + + timePasses(day : Weekday) + } + class App { + + App() + + main(args : String[]) {static} + } enum Weekday { + FRIDAY {static} + MONDAY {static} @@ -64,10 +64,10 @@ package com.iluwatar.event.aggregator { } } EventEmitter --> "-observers" EventObserver +LordVarys --|> EventEmitter +KingJoffrey ..|> EventObserver +LordBaelish --|> EventEmitter KingsHand ..|> EventObserver KingsHand --|> EventEmitter -KingJoffrey ..|> EventObserver Scout --|> EventEmitter -LordVarys --|> EventEmitter -LordBaelish --|> EventEmitter @enduml \ No newline at end of file diff --git a/event-driven-architecture/etc/event-driven-architecture.urm.puml b/event-driven-architecture/etc/event-driven-architecture.urm.puml index 55039190f..8314913a6 100644 --- a/event-driven-architecture/etc/event-driven-architecture.urm.puml +++ b/event-driven-architecture/etc/event-driven-architecture.urm.puml @@ -1,31 +1,34 @@ @startuml package com.iluwatar.eda.handler { - class UserUpdatedEventHandler { - + UserUpdatedEventHandler() - + onEvent(event : UserUpdatedEvent) - } class UserCreatedEventHandler { + UserCreatedEventHandler() + onEvent(event : UserCreatedEvent) } + class UserUpdatedEventHandler { + + UserUpdatedEventHandler() + + onEvent(event : UserUpdatedEvent) + } } package com.iluwatar.eda.event { abstract class AbstractEvent { + AbstractEvent() + getType() : Class } - class UserUpdatedEvent { - - user : User - + UserUpdatedEvent(user : User) - + getUser() : User - } class UserCreatedEvent { - user : User + UserCreatedEvent(user : User) + getUser() : User } + class UserUpdatedEvent { + - user : User + + UserUpdatedEvent(user : User) + + getUser() : User + } } package com.iluwatar.eda.framework { + interface Handler { + + onEvent(E extends Event) {abstract} + } class EventDispatcher { - handlers : Map, Handler> + EventDispatcher() @@ -35,9 +38,6 @@ package com.iluwatar.eda.framework { interface Event { + getType() : Class {abstract} } - interface Handler { - + onEvent(E extends Event) {abstract} - } } package com.iluwatar.eda.model { class User { @@ -52,11 +52,11 @@ package com.iluwatar.eda { + main(args : String[]) {static} } } -UserCreatedEvent --> "-user" User UserUpdatedEvent --> "-user" User +UserCreatedEvent --> "-user" User AbstractEvent ..|> Event -UserUpdatedEventHandler ..|> Handler +UserCreatedEvent --|> AbstractEvent UserCreatedEventHandler ..|> Handler UserUpdatedEvent --|> AbstractEvent -UserCreatedEvent --|> AbstractEvent +UserUpdatedEventHandler ..|> Handler @enduml \ No newline at end of file diff --git a/execute-around/etc/execute-around.urm.puml b/execute-around/etc/execute-around.urm.puml index 66d23ce7a..4fdc4bffb 100644 --- a/execute-around/etc/execute-around.urm.puml +++ b/execute-around/etc/execute-around.urm.puml @@ -1,8 +1,5 @@ @startuml package com.iluwatar.execute.around { - interface FileWriterAction { - + writeFile(FileWriter) {abstract} - } class SimpleFileWriter { + SimpleFileWriter(filename : String, action : FileWriterAction) } @@ -10,5 +7,8 @@ package com.iluwatar.execute.around { + App() + main(args : String[]) {static} } + interface FileWriterAction { + + writeFile(FileWriter) {abstract} + } } @enduml \ No newline at end of file diff --git a/facade/etc/facade.urm.puml b/facade/etc/facade.urm.puml index f72bc2b62..8c53cb728 100644 --- a/facade/etc/facade.urm.puml +++ b/facade/etc/facade.urm.puml @@ -1,7 +1,7 @@ @startuml package com.iluwatar.facade { - class DwarvenTunnelDigger { - + DwarvenTunnelDigger() + class DwarvenGoldDigger { + + DwarvenGoldDigger() + name() : String + work() } @@ -13,15 +13,11 @@ package com.iluwatar.facade { - makeActions(workers : Collection, actions : Action[]) {static} + startNewDay() } - class DwarvenGoldDigger { - + DwarvenGoldDigger() + class DwarvenTunnelDigger { + + DwarvenTunnelDigger() + name() : String + work() } - class App { - + App() - + main(args : String[]) {static} - } abstract class DwarvenMineWorker { + DwarvenMineWorker() - action(action : Action) @@ -33,6 +29,10 @@ package com.iluwatar.facade { + wakeUp() + work() {abstract} } + class App { + + App() + + main(args : String[]) {static} + } class DwarvenCartOperator { + DwarvenCartOperator() + name() : String @@ -51,7 +51,7 @@ package com.iluwatar.facade { DwarvenGoldmineFacade --+ DwarvenMineWorker DwarvenGoldmineFacade --> "-workers" DwarvenMineWorker Action ..+ DwarvenMineWorker -DwarvenTunnelDigger --|> DwarvenMineWorker DwarvenGoldDigger --|> DwarvenMineWorker +DwarvenTunnelDigger --|> DwarvenMineWorker DwarvenCartOperator --|> DwarvenMineWorker @enduml \ No newline at end of file diff --git a/factory-kit/etc/factory-kit.urm.puml b/factory-kit/etc/factory-kit.urm.puml index faf5727eb..fdee9a01c 100644 --- a/factory-kit/etc/factory-kit.urm.puml +++ b/factory-kit/etc/factory-kit.urm.puml @@ -1,33 +1,33 @@ @startuml package com.iluwatar.factorykit { - interface Builder { - + add(WeaponType, Supplier) {abstract} - } class Spear { + Spear() + toString() : String } - class Bow { - + Bow() + class App { + + App() + + main(args : String[]) {static} + } + interface Weapon { + } + interface WeaponFactory { + + create(WeaponType) : Weapon {abstract} + + factory(consumer : Consumer) : WeaponFactory {static} + } + class Axe { + + Axe() + toString() : String } class Sword { + Sword() + toString() : String } - interface Weapon { - } - class App { - + App() - + main(args : String[]) {static} - } - class Axe { - + Axe() + class Bow { + + Bow() + toString() : String } - interface WeaponFactory { - + create(WeaponType) : Weapon {abstract} - + factory(consumer : Consumer) : WeaponFactory {static} + interface Builder { + + add(WeaponType, Supplier) {abstract} } enum WeaponType { + AXE {static} @@ -39,7 +39,7 @@ package com.iluwatar.factorykit { } } Spear ..|> Weapon -Bow ..|> Weapon -Sword ..|> Weapon Axe ..|> Weapon +Sword ..|> Weapon +Bow ..|> Weapon @enduml \ No newline at end of file diff --git a/factory-method/etc/factory-method.urm.puml b/factory-method/etc/factory-method.urm.puml index ebc9d2ff7..d61984a85 100644 --- a/factory-method/etc/factory-method.urm.puml +++ b/factory-method/etc/factory-method.urm.puml @@ -17,9 +17,6 @@ package com.iluwatar.factory.method { interface Blacksmith { + manufactureWeapon(WeaponType) : Weapon {abstract} } - interface Weapon { - + getWeaponType() : WeaponType {abstract} - } class ElfWeapon { - weaponType : WeaponType + ElfWeapon(weaponType : WeaponType) @@ -32,6 +29,9 @@ package com.iluwatar.factory.method { + main(args : String[]) {static} - manufactureWeapons() } + interface Weapon { + + getWeaponType() : WeaponType {abstract} + } enum WeaponType { + AXE {static} + SHORT_SWORD {static} diff --git a/feature-toggle/etc/feature-toggle.urm.puml b/feature-toggle/etc/feature-toggle.urm.puml index 762d49cb3..6c935f3e6 100644 --- a/feature-toggle/etc/feature-toggle.urm.puml +++ b/feature-toggle/etc/feature-toggle.urm.puml @@ -20,17 +20,17 @@ package com.iluwatar.featuretoggle.user { + isPaid(user : User) : boolean {static} } } -package com.iluwatar.featuretoggle.pattern.propertiesversion { - class PropertiesFeatureToggleVersion { - - isEnhanced : boolean - + PropertiesFeatureToggleVersion(properties : Properties) +package com.iluwatar.featuretoggle.pattern.tieredversion { + class TieredFeatureToggleVersion { + + TieredFeatureToggleVersion() + getWelcomeMessage(user : User) : String + isEnhanced() : boolean } } -package com.iluwatar.featuretoggle.pattern.tieredversion { - class TieredFeatureToggleVersion { - + TieredFeatureToggleVersion() +package com.iluwatar.featuretoggle.pattern.propertiesversion { + class PropertiesFeatureToggleVersion { + - isEnhanced : boolean + + PropertiesFeatureToggleVersion(properties : Properties) + getWelcomeMessage(user : User) : String + isEnhanced() : boolean } @@ -42,6 +42,6 @@ package com.iluwatar.featuretoggle { } } UserGroup --> "-freeGroup" User -TieredFeatureToggleVersion ..|> Service PropertiesFeatureToggleVersion ..|> Service +TieredFeatureToggleVersion ..|> Service @enduml \ No newline at end of file diff --git a/fluentinterface/etc/fluentinterface.urm.puml b/fluentinterface/etc/fluentinterface.urm.puml index 436fcb2e8..0283e20b5 100644 --- a/fluentinterface/etc/fluentinterface.urm.puml +++ b/fluentinterface/etc/fluentinterface.urm.puml @@ -31,6 +31,14 @@ package com.iluwatar.fluentinterface.app { } } package com.iluwatar.fluentinterface.fluentiterable.lazy { + abstract class DecoratingIterator { + # fromIterator : Iterator + - next : E + + DecoratingIterator(fromIterator : Iterator) + + computeNext() : E {abstract} + + hasNext() : boolean + + next() : E + } class LazyFluentIterable { - iterable : Iterable # LazyFluentIterable() @@ -45,14 +53,6 @@ package com.iluwatar.fluentinterface.fluentiterable.lazy { + last(count : int) : FluentIterable + map(function : Function) : FluentIterable } - abstract class DecoratingIterator { - # fromIterator : Iterator - - next : E - + DecoratingIterator(fromIterator : Iterator) - + computeNext() : E {abstract} - + hasNext() : boolean - + next() : E - } } package com.iluwatar.fluentinterface.fluentiterable { interface FluentIterable { diff --git a/flux/etc/flux.urm.puml b/flux/etc/flux.urm.puml index e4bece2fc..a4e0b0622 100644 --- a/flux/etc/flux.urm.puml +++ b/flux/etc/flux.urm.puml @@ -1,10 +1,8 @@ @startuml package com.iluwatar.flux.view { - class ContentView { - - content : Content - + ContentView() - + render() - + storeChanged(store : Store) + interface View { + + render() {abstract} + + storeChanged(Store) {abstract} } class MenuView { - selected : MenuItem @@ -13,27 +11,35 @@ package com.iluwatar.flux.view { + render() + storeChanged(store : Store) } - interface View { - + render() {abstract} - + storeChanged(Store) {abstract} + class ContentView { + - content : Content + + ContentView() + + render() + + storeChanged(store : Store) } } package com.iluwatar.flux.action { - class ContentAction { - - content : Content - + ContentAction(content : Content) - + getContent() : Content - } class MenuAction { - menuItem : MenuItem + MenuAction(menuItem : MenuItem) + getMenuItem() : MenuItem } + class ContentAction { + - content : Content + + ContentAction(content : Content) + + getContent() : Content + } abstract class Action { - type : ActionType + Action(type : ActionType) + getType() : ActionType } + enum ActionType { + + CONTENT_CHANGED {static} + + MENU_ITEM_SELECTED {static} + + valueOf(name : String) : ActionType {static} + + values() : ActionType[] {static} + } enum MenuItem { + COMPANY {static} + HOME {static} @@ -51,12 +57,6 @@ package com.iluwatar.flux.action { + valueOf(name : String) : Content {static} + values() : Content[] {static} } - enum ActionType { - + CONTENT_CHANGED {static} - + MENU_ITEM_SELECTED {static} - + valueOf(name : String) : ActionType {static} - + values() : ActionType[] {static} - } } package com.iluwatar.flux.app { class App { @@ -64,27 +64,6 @@ package com.iluwatar.flux.app { + main(args : String[]) {static} } } -package com.iluwatar.flux.store { - abstract class Store { - - views : List - + Store() - # notifyChange() - + onAction(Action) {abstract} - + registerView(view : View) - } - class ContentStore { - - content : Content - + ContentStore() - + getContent() : Content - + onAction(action : Action) - } - class MenuStore { - - selected : MenuItem - + MenuStore() - + getSelected() : MenuItem - + onAction(action : Action) - } -} package com.iluwatar.flux.dispatcher { class Dispatcher { - instance : Dispatcher {static} @@ -96,6 +75,27 @@ package com.iluwatar.flux.dispatcher { + registerStore(store : Store) } } +package com.iluwatar.flux.store { + class ContentStore { + - content : Content + + ContentStore() + + getContent() : Content + + onAction(action : Action) + } + class MenuStore { + - selected : MenuItem + + MenuStore() + + getSelected() : MenuItem + + onAction(action : Action) + } + abstract class Store { + - views : List + + Store() + # notifyChange() + + onAction(Action) {abstract} + + registerView(view : View) + } +} MenuAction --> "-menuItem" MenuItem Action --> "-type" ActionType MenuStore --> "-selected" MenuItem @@ -104,12 +104,12 @@ ContentView --> "-content" Content Dispatcher --> "-stores" Store MenuView --> "-selected" MenuItem Store --> "-views" View -ContentStore --> "-content" Content ContentAction --> "-content" Content -ContentAction --|> Action +ContentStore --> "-content" Content ContentStore --|> Store -ContentView ..|> View MenuAction --|> Action -MenuView ..|> View MenuStore --|> Store +ContentAction --|> Action +MenuView ..|> View +ContentView ..|> View @enduml \ No newline at end of file diff --git a/flyweight/etc/flyweight.urm.puml b/flyweight/etc/flyweight.urm.puml index 98a2b4721..3f2203712 100644 --- a/flyweight/etc/flyweight.urm.puml +++ b/flyweight/etc/flyweight.urm.puml @@ -1,28 +1,17 @@ @startuml package com.iluwatar.flyweight { - class PoisonPotion { - + PoisonPotion() - + drink() - } - class StrengthPotion { - + StrengthPotion() - + drink() - } - class HealingPotion { - + HealingPotion() - + drink() - } class PotionFactory { - potions : Map + PotionFactory() ~ createPotion(type : PotionType) : Potion } - interface Potion { - + drink() {abstract} + class HealingPotion { + + HealingPotion() + + drink() } - class App { - + App() - + main(args : String[]) {static} + class InvisibilityPotion { + + InvisibilityPotion() + + drink() } class AlchemistShop { - bottomShelf : List @@ -33,12 +22,23 @@ package com.iluwatar.flyweight { + getBottomShelf() : List + getTopShelf() : List } - class HolyWaterPotion { - + HolyWaterPotion() + class App { + + App() + + main(args : String[]) {static} + } + interface Potion { + + drink() {abstract} + } + class PoisonPotion { + + PoisonPotion() + drink() } - class InvisibilityPotion { - + InvisibilityPotion() + class StrengthPotion { + + StrengthPotion() + + drink() + } + class HolyWaterPotion { + + HolyWaterPotion() + drink() } enum PotionType { @@ -52,9 +52,9 @@ package com.iluwatar.flyweight { } } AlchemistShop --> "-topShelf" Potion +HealingPotion ..|> Potion +InvisibilityPotion ..|> Potion PoisonPotion ..|> Potion StrengthPotion ..|> Potion -HealingPotion ..|> Potion HolyWaterPotion ..|> Potion -InvisibilityPotion ..|> Potion @enduml \ No newline at end of file diff --git a/front-controller/etc/front-controller.urm.puml b/front-controller/etc/front-controller.urm.puml index 17ccebae1..8fb9be184 100644 --- a/front-controller/etc/front-controller.urm.puml +++ b/front-controller/etc/front-controller.urm.puml @@ -1,5 +1,20 @@ @startuml package com.iluwatar.front.controller { + class ArcherView { + + ArcherView() + + display() + } + interface View { + + display() {abstract} + } + class CatapultView { + + CatapultView() + + display() + } + class ArcherCommand { + + ArcherCommand() + + process() + } class App { + App() + main(args : String[]) {static} @@ -10,41 +25,26 @@ package com.iluwatar.front.controller { - getCommandClass(request : String) : Class {static} + handleRequest(request : String) } - class ArcherView { - + ArcherView() - + display() - } - interface View { - + display() {abstract} - } - interface Command { - + process() {abstract} + class UnknownCommand { + + UnknownCommand() + + process() } class ErrorView { + ErrorView() + display() } - class ArcherCommand { - + ArcherCommand() - + process() - } - class CatapultView { - + CatapultView() - + display() - } class CatapultCommand { + CatapultCommand() + process() } - class UnknownCommand { - + UnknownCommand() - + process() + interface Command { + + process() {abstract} } } ArcherView ..|> View -ErrorView ..|> View -ArcherCommand ..|> Command CatapultView ..|> View -CatapultCommand ..|> Command +ArcherCommand ..|> Command UnknownCommand ..|> Command +ErrorView ..|> View +CatapultCommand ..|> Command @enduml \ No newline at end of file diff --git a/half-sync-half-async/etc/half-sync-half-async.urm.puml b/half-sync-half-async/etc/half-sync-half-async.urm.puml index e733dd586..88f49eb4d 100644 --- a/half-sync-half-async/etc/half-sync-half-async.urm.puml +++ b/half-sync-half-async/etc/half-sync-half-async.urm.puml @@ -1,5 +1,18 @@ @startuml package com.iluwatar.halfsynchalfasync { + class AsynchronousService { + - service : ExecutorService + + AsynchronousService(workQueue : BlockingQueue) + + execute(task : AsyncTask) + } + ~class ArithmeticSumTask { + - n : long + + ArithmeticSumTask(n : long) + + call() : Long + + onError(throwable : Throwable) + + onPostCall(result : Long) + + onPreCall() + } class App { + App() - ap(i : long) : long {static} @@ -11,19 +24,6 @@ package com.iluwatar.halfsynchalfasync { + onPostCall(O) {abstract} + onPreCall() {abstract} } - ~class ArithmeticSumTask { - - n : long - + ArithmeticSumTask(n : long) - + call() : Long - + onError(throwable : Throwable) - + onPostCall(result : Long) - + onPreCall() - } - class AsynchronousService { - - service : ExecutorService - + AsynchronousService(workQueue : BlockingQueue) - + execute(task : AsyncTask) - } } ArithmeticSumTask ..+ App ArithmeticSumTask ..|> AsyncTask diff --git a/hexagonal/etc/hexagonal.urm.puml b/hexagonal/etc/hexagonal.urm.puml index c4b0d0a73..a2f5d0469 100644 --- a/hexagonal/etc/hexagonal.urm.puml +++ b/hexagonal/etc/hexagonal.urm.puml @@ -1,31 +1,47 @@ @startuml -package com.iluwatar.hexagonal.service { - class LotteryServiceImpl { - - bank : WireTransfers - - notifications : LotteryNotifications - - repository : LotteryTicketRepository - + LotteryServiceImpl() - + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult - + submitTicket(ticket : LotteryTicket) : Optional +package com.iluwatar.hexagonal.sampledata { + class SampleData { + - PLAYERS : List {static} + + SampleData() + - getRandomPlayerDetails() : PlayerDetails {static} + + submitTickets(lotteryService : LotteryService, numTickets : int) {static} } - interface LotteryService { - + checkTicketForPrize(LotteryTicketId, LotteryNumbers) : LotteryTicketCheckResult {abstract} - + submitTicket(LotteryTicket) : Optional {abstract} +} +package com.iluwatar.hexagonal.service { + class ConsoleLottery { + + ConsoleLottery() + + main(args : String[]) {static} + - printMainMenu() {static} + - readString(scanner : Scanner) : String {static} + } +} +package com.iluwatar.hexagonal.mongo { + class MongoConnectionPropertiesLoader { + - DEFAULT_HOST : String {static} + - DEFAULT_PORT : int {static} + + MongoConnectionPropertiesLoader() + + load() {static} } } package com.iluwatar.hexagonal.domain { class LotteryTicketId { - - id : UUID + - id : int + - numAllocated : int {static} + LotteryTicketId() - + getId() : UUID + + LotteryTicketId(id : int) + + equals(o : Object) : boolean + + getId() : int + + hashCode() : int + + toString() : String } - class LotteryConstants { - + PLAYER_MAX_SALDO : int {static} - + PRIZE_AMOUNT : int {static} - + SERVICE_BANK_ACCOUNT : String {static} - + SERVICE_BANK_ACCOUNT_SALDO : int {static} - + TICKET_PRIZE : int {static} - + LotteryConstants() + class LotteryAdministration { + - notifications : LotteryEventLog + - repository : LotteryTicketRepository + - wireTransfers : WireTransfers + + LotteryAdministration(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers) + + getAllSubmittedTickets() : Map + + performLottery() : LotteryNumbers + + resetLottery() } class LotteryNumbers { + MAX_NUMBER : int {static} @@ -39,19 +55,34 @@ package com.iluwatar.hexagonal.domain { + equals(obj : Object) : boolean - generateRandomNumbers() + getNumbers() : Set + + getNumbersAsString() : String + hashCode() : int + + toString() : String + } + class LotteryService { + - notifications : LotteryEventLog + - repository : LotteryTicketRepository + - wireTransfers : WireTransfers + + LotteryService(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers) + + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult + + submitTicket(ticket : LotteryTicket) : Optional + } + -class RandomNumberGenerator { + - randomIterator : OfInt + + RandomNumberGenerator(min : int, max : int) + + nextInt() : int } class PlayerDetails { - bankAccountNumber : String - emailAddress : String - phoneNumber : String - - PlayerDetails(email : String, bankAccount : String, phone : String) - + create(email : String, bankAccount : String, phone : String) : PlayerDetails {static} + + PlayerDetails(email : String, bankAccount : String, phone : String) + equals(obj : Object) : boolean + getBankAccount() : String + getEmail() : String + getPhoneNumber() : String + hashCode() : int + + toString() : String } class LotteryTicketCheckResult { - checkResult : CheckResult @@ -63,20 +94,30 @@ package com.iluwatar.hexagonal.domain { + getResult() : CheckResult + hashCode() : int } + class LotteryConstants { + + PLAYER_MAX_SALDO : int {static} + + PRIZE_AMOUNT : int {static} + + SERVICE_BANK_ACCOUNT : String {static} + + SERVICE_BANK_ACCOUNT_SALDO : int {static} + + TICKET_PRIZE : int {static} + - LotteryConstants() + } class LotteryTicket { + - id : LotteryTicketId - lotteryNumbers : LotteryNumbers - playerDetails : PlayerDetails - - LotteryTicket(details : PlayerDetails, numbers : LotteryNumbers) - + create(details : PlayerDetails, numbers : LotteryNumbers) : LotteryTicket {static} + + LotteryTicket(id : LotteryTicketId, details : PlayerDetails, numbers : LotteryNumbers) + equals(obj : Object) : boolean + + getId() : LotteryTicketId + getNumbers() : LotteryNumbers + getPlayerDetails() : PlayerDetails + hashCode() : int + + setId(id : LotteryTicketId) + + toString() : String } - -class RandomNumberGenerator { - - randomIterator : OfInt - + RandomNumberGenerator(min : int, max : int) - + nextInt() : int + class LotteryUtils { + - LotteryUtils() + + checkTicketForPrize(repository : LotteryTicketRepository, id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult {static} } enum CheckResult { + NO_PRIZE {static} @@ -87,23 +128,64 @@ package com.iluwatar.hexagonal.domain { } } package com.iluwatar.hexagonal.banking { - class WireTransfersImpl { - - accounts : Map {static} - + WireTransfersImpl() - + getFunds(bankAccount : String) : int - + setFunds(bankAccount : String, amount : int) - + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean - } interface WireTransfers { + getFunds(String) : int {abstract} + setFunds(String, int) {abstract} + transferFunds(int, String, String) : boolean {abstract} } + class MongoBank { + - DEFAULT_ACCOUNTS_COLLECTION : String {static} + - DEFAULT_DB : String {static} + - accountsCollection : MongoCollection + - database : MongoDatabase + - mongoClient : MongoClient + + MongoBank() + + MongoBank(dbName : String, accountsCollectionName : String) + + connect() + + connect(dbName : String, accountsCollectionName : String) + + getAccountsCollection() : MongoCollection + + getFunds(bankAccount : String) : int + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean + } + class InMemoryBank { + - accounts : Map {static} + + InMemoryBank() + + getFunds(bankAccount : String) : int + + setFunds(bankAccount : String, amount : int) + + transferFunds(amount : int, sourceBackAccount : String, destinationBankAccount : String) : boolean + } } package com.iluwatar.hexagonal.database { - class LotteryTicketInMemoryRepository { + class MongoTicketRepository { + - DEFAULT_COUNTERS_COLLECTION : String {static} + - DEFAULT_DB : String {static} + - DEFAULT_TICKETS_COLLECTION : String {static} + - countersCollection : MongoCollection + - database : MongoDatabase + - mongoClient : MongoClient + - ticketsCollection : MongoCollection + + MongoTicketRepository() + + MongoTicketRepository(dbName : String, ticketsCollectionName : String, countersCollectionName : String) + + connect() + + connect(dbName : String, ticketsCollectionName : String, countersCollectionName : String) + + deleteAll() + - docToTicket(doc : Document) : LotteryTicket + + findAll() : Map + + findById(id : LotteryTicketId) : Optional + + getCountersCollection() : MongoCollection + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + getNextId() : int + + getTicketsCollection() : MongoCollection + - initCounters() + + save(ticket : LotteryTicket) : Optional + } + class InMemoryTicketRepository { - tickets : Map {static} - + LotteryTicketInMemoryRepository() + + InMemoryTicketRepository() + deleteAll() + findAll() : Map + findById(id : LotteryTicketId) : Optional @@ -116,68 +198,79 @@ package com.iluwatar.hexagonal.database { + save(LotteryTicket) : Optional {abstract} } } -package com.iluwatar.hexagonal.eventlog { - interface LotteryNotifications { - + notifyNoWin(PlayerDetails) {abstract} - + notifyPrize(PlayerDetails, int) {abstract} - + notifyPrizeError(PlayerDetails, int) {abstract} - + notifyTicketSubmitError(PlayerDetails) {abstract} - + notifyTicketSubmitted(PlayerDetails) {abstract} - } - class LotteryNotificationsImpl { - + LotteryNotificationsImpl() - + notifyNoWin(details : PlayerDetails) - + notifyPrize(details : PlayerDetails, prizeAmount : int) - + notifyPrizeError(details : PlayerDetails, prizeAmount : int) - + notifyTicketSubmitError(details : PlayerDetails) - + notifyTicketSubmitted(details : PlayerDetails) - } -} package com.iluwatar.hexagonal { class App { - - allPlayerDetails : List {static} + App() - - getRandomPlayerDetails() : PlayerDetails {static} + main(args : String[]) {static} - - submitTickets(lotteryService : LotteryService, numTickets : int) {static} } } package com.iluwatar.hexagonal.administration { - interface LotteryAdministration { - + getAllSubmittedTickets() : Map {abstract} - + performLottery() : LotteryNumbers {abstract} - + resetLottery() {abstract} - } - class LotteryAdministrationImpl { - - bank : WireTransfers - - notifications : LotteryNotifications - - repository : LotteryTicketRepository - - service : LotteryService - + LotteryAdministrationImpl() - + getAllSubmittedTickets() : Map - + performLottery() : LotteryNumbers - + resetLottery() + class ConsoleAdministration { + + ConsoleAdministration() + + main(args : String[]) {static} + - printMainMenu() {static} + - readString(scanner : Scanner) : String {static} } } +package com.iluwatar.hexagonal.eventlog { + interface LotteryEventLog { + + prizeError(PlayerDetails, int) {abstract} + + ticketDidNotWin(PlayerDetails) {abstract} + + ticketSubmitError(PlayerDetails) {abstract} + + ticketSubmitted(PlayerDetails) {abstract} + + ticketWon(PlayerDetails, int) {abstract} + } + class StdOutEventLog { + + StdOutEventLog() + + prizeError(details : PlayerDetails, prizeAmount : int) + + ticketDidNotWin(details : PlayerDetails) + + ticketSubmitError(details : PlayerDetails) + + ticketSubmitted(details : PlayerDetails) + + ticketWon(details : PlayerDetails, prizeAmount : int) + } + class MongoEventLog { + - DEFAULT_DB : String {static} + - DEFAULT_EVENTS_COLLECTION : String {static} + - database : MongoDatabase + - eventsCollection : MongoCollection + - mongoClient : MongoClient + - stdOutEventLog : StdOutEventLog + + MongoEventLog() + + MongoEventLog(dbName : String, eventsCollectionName : String) + + connect() + + connect(dbName : String, eventsCollectionName : String) + + getEventsCollection() : MongoCollection + + getMongoClient() : MongoClient + + getMongoDatabase() : MongoDatabase + + prizeError(details : PlayerDetails, prizeAmount : int) + + ticketDidNotWin(details : PlayerDetails) + + ticketSubmitError(details : PlayerDetails) + + ticketSubmitted(details : PlayerDetails) + + ticketWon(details : PlayerDetails, prizeAmount : int) + } +} +LotteryAdministration --+ LotteryTicketCheckResult LotteryTicket --> "-playerDetails" PlayerDetails -LotteryAdministrationImpl --> "-bank" WireTransfers -App --> "-allPlayerDetails" PlayerDetails +MongoEventLog --> "-stdOutEventLog" StdOutEventLog +LotteryService --> "-wireTransfers" WireTransfers +LotteryAdministration --> "-notifications" LotteryEventLog RandomNumberGenerator ..+ PrimitiveIterator -LotteryAdministrationImpl --> "-repository" LotteryTicketRepository -LotteryAdministrationImpl --+ LotteryTicketCheckResult -LotteryServiceImpl --> "-notifications" LotteryNotifications +LotteryAdministration --> "-wireTransfers" WireTransfers +LotteryTicket --> "-id" LotteryTicketId +LotteryService --> "-notifications" LotteryEventLog +LotteryAdministration --> "-repository" LotteryTicketRepository LotteryTicket --> "-lotteryNumbers" LotteryNumbers -LotteryAdministrationImpl --> "-notifications" LotteryNotifications -LotteryServiceImpl --> "-repository" LotteryTicketRepository -LotteryServiceImpl --+ LotteryTicketCheckResult -LotteryServiceImpl --> "-bank" WireTransfers +SampleData --> "-PLAYERS" PlayerDetails +ConsoleLottery --+ LotteryTicketCheckResult RandomNumberGenerator ..+ LotteryNumbers -LotteryAdministrationImpl --> "-service" LotteryService +LotteryService --> "-repository" LotteryTicketRepository +LotteryUtils --+ LotteryTicketCheckResult LotteryTicketCheckResult --> "-checkResult" CheckResult CheckResult ..+ LotteryTicketCheckResult -LotteryTicketInMemoryRepository ..|> LotteryTicketRepository -WireTransfersImpl ..|> WireTransfers -LotteryServiceImpl ..|> LotteryService -LotteryNotificationsImpl ..|> LotteryNotifications -LotteryAdministrationImpl ..|> LotteryAdministration +MongoTicketRepository ..|> LotteryTicketRepository +MongoBank ..|> WireTransfers +InMemoryBank ..|> WireTransfers +StdOutEventLog ..|> LotteryEventLog +InMemoryTicketRepository ..|> LotteryTicketRepository +MongoEventLog ..|> LotteryEventLog @enduml \ No newline at end of file diff --git a/intercepting-filter/etc/intercepting-filter.urm.puml b/intercepting-filter/etc/intercepting-filter.urm.puml index f5bfb54e4..e3616c3ab 100644 --- a/intercepting-filter/etc/intercepting-filter.urm.puml +++ b/intercepting-filter/etc/intercepting-filter.urm.puml @@ -1,10 +1,16 @@ @startuml package com.iluwatar.intercepting.filter { - interface Filter { - + execute(Order) : String {abstract} - + getLast() : Filter {abstract} - + getNext() : Filter {abstract} - + setNext(Filter) {abstract} + class DepositFilter { + + DepositFilter() + + execute(order : Order) : String + } + class AddressFilter { + + AddressFilter() + + execute(order : Order) : String + } + class App { + + App() + + main(args : String[]) {static} } abstract class AbstractFilter { - next : Filter @@ -15,14 +21,6 @@ package com.iluwatar.intercepting.filter { + getNext() : Filter + setNext(filter : Filter) } - class ContactFilter { - + ContactFilter() - + execute(order : Order) : String - } - class OrderFilter { - + OrderFilter() - + execute(order : Order) : String - } class Order { - address : String - contactNumber : String @@ -42,47 +40,49 @@ package com.iluwatar.intercepting.filter { + setName(name : String) + setOrder(order : String) } - class AddressFilter { - + AddressFilter() - + execute(order : Order) : String - } - ~class DListener { - ~ DListener(this$0 : Target) - + actionPerformed(e : ActionEvent) - } class FilterManager { - filterChain : FilterChain + FilterManager() + addFilter(filter : Filter) + filterRequest(order : Order) : String } + class NameFilter { + + NameFilter() + + execute(order : Order) : String + } + class ContactFilter { + + ContactFilter() + + execute(order : Order) : String + } + interface Filter { + + execute(Order) : String {abstract} + + getLast() : Filter {abstract} + + getNext() : Filter {abstract} + + setNext(Filter) {abstract} + } + ~class DListener { + ~ DListener() + + actionPerformed(e : ActionEvent) + } + class OrderFilter { + + OrderFilter() + + execute(order : Order) : String + } class FilterChain { - chain : Filter + FilterChain() + addFilter(filter : Filter) + execute(order : Order) : String } - class DepositFilter { - + DepositFilter() - + execute(order : Order) : String - } - class App { - + App() - + main(args : String[]) {static} - } - class NameFilter { - + NameFilter() - + execute(order : Order) : String - } } AbstractFilter --> "-next" Filter DListener --+ Target FilterChain --> "-chain" Filter FilterManager --> "-filterChain" FilterChain +DepositFilter --|> AbstractFilter +AddressFilter --|> AbstractFilter AbstractFilter ..|> Filter +NameFilter --|> AbstractFilter ContactFilter --|> AbstractFilter OrderFilter --|> AbstractFilter -AddressFilter --|> AbstractFilter -DepositFilter --|> AbstractFilter -NameFilter --|> AbstractFilter @enduml \ No newline at end of file diff --git a/interpreter/etc/interpreter.urm.puml b/interpreter/etc/interpreter.urm.puml index bdbd369d6..dc0d83bf2 100644 --- a/interpreter/etc/interpreter.urm.puml +++ b/interpreter/etc/interpreter.urm.puml @@ -1,23 +1,5 @@ @startuml package com.iluwatar.interpreter { - abstract class Expression { - + Expression() - + interpret() : int {abstract} - + toString() : String {abstract} - } - class PlusExpression { - - leftExpression : Expression - - rightExpression : Expression - + PlusExpression(leftExpression : Expression, rightExpression : Expression) - + interpret() : int - + toString() : String - } - class App { - + App() - + getOperatorInstance(s : String, left : Expression, right : Expression) : Expression {static} - + isOperator(s : String) : boolean {static} - + main(args : String[]) {static} - } class NumberExpression { - number : int + NumberExpression(number : int) @@ -25,13 +7,6 @@ package com.iluwatar.interpreter { + interpret() : int + toString() : String } - class MultiplyExpression { - - leftExpression : Expression - - rightExpression : Expression - + MultiplyExpression(leftExpression : Expression, rightExpression : Expression) - + interpret() : int - + toString() : String - } class MinusExpression { - leftExpression : Expression - rightExpression : Expression @@ -39,12 +14,37 @@ package com.iluwatar.interpreter { + interpret() : int + toString() : String } + class App { + + App() + + getOperatorInstance(s : String, left : Expression, right : Expression) : Expression {static} + + isOperator(s : String) : boolean {static} + + main(args : String[]) {static} + } + abstract class Expression { + + Expression() + + interpret() : int {abstract} + + toString() : String {abstract} + } + class MultiplyExpression { + - leftExpression : Expression + - rightExpression : Expression + + MultiplyExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } + class PlusExpression { + - leftExpression : Expression + - rightExpression : Expression + + PlusExpression(leftExpression : Expression, rightExpression : Expression) + + interpret() : int + + toString() : String + } } MultiplyExpression --> "-leftExpression" Expression MinusExpression --> "-leftExpression" Expression PlusExpression --> "-leftExpression" Expression -PlusExpression --|> Expression NumberExpression --|> Expression -MultiplyExpression --|> Expression MinusExpression --|> Expression +MultiplyExpression --|> Expression +PlusExpression --|> Expression @enduml \ No newline at end of file diff --git a/iterator/etc/iterator.urm.puml b/iterator/etc/iterator.urm.puml index cbafd6595..032cd4c16 100644 --- a/iterator/etc/iterator.urm.puml +++ b/iterator/etc/iterator.urm.puml @@ -1,13 +1,17 @@ @startuml package com.iluwatar.iterator { - interface ItemIterator { - + hasNext() : boolean {abstract} - + next() : Item {abstract} - } class App { + App() + main(args : String[]) {static} } + class Item { + - name : String + - type : ItemType + + Item(type : ItemType, name : String) + + getType() : ItemType + + setType(type : ItemType) + + toString() : String + } class TreasureChestItemIterator { - chest : TreasureChest - idx : int @@ -23,13 +27,9 @@ package com.iluwatar.iterator { + getItems() : List ~ iterator(itemType : ItemType) : ItemIterator } - class Item { - - name : String - - type : ItemType - + Item(type : ItemType, name : String) - + getType() : ItemType - + setType(type : ItemType) - + toString() : String + interface ItemIterator { + + hasNext() : boolean {abstract} + + next() : Item {abstract} } enum ItemType { + ANY {static} diff --git a/layers/etc/layers.urm.puml b/layers/etc/layers.urm.puml index d67216ff8..2ec6e142e 100644 --- a/layers/etc/layers.urm.puml +++ b/layers/etc/layers.urm.puml @@ -3,62 +3,6 @@ package com.iluwatar.layers { interface View { + render() {abstract} } - class CakeBakingServiceImpl { - - context : AbstractApplicationContext - + CakeBakingServiceImpl() - + bakeNewCake(cakeInfo : CakeInfo) - + getAllCakes() : List - - getAvailableLayerEntities() : List - + getAvailableLayers() : List - - getAvailableToppingEntities() : List - + getAvailableToppings() : List - + saveNewLayer(layerInfo : CakeLayerInfo) - + saveNewTopping(toppingInfo : CakeToppingInfo) - } - interface CakeDao { - } - class CakeTopping { - - cake : Cake - - calories : int - - id : Long - - name : String - + CakeTopping() - + CakeTopping(name : String, calories : int) - + getCake() : Cake - + getCalories() : int - + getId() : Long - + getName() : String - + setCake(cake : Cake) - + setCalories(calories : int) - + setId(id : Long) - + setName(name : String) - + toString() : String - } - class CakeLayerInfo { - + calories : int - + id : Optional - + name : String - + CakeLayerInfo(id : Long, name : String, calories : int) - + CakeLayerInfo(name : String, calories : int) - + toString() : String - } - interface CakeLayerDao { - } - interface CakeToppingDao { - } - interface CakeBakingService { - + bakeNewCake(CakeInfo) {abstract} - + getAllCakes() : List {abstract} - + getAvailableLayers() : List {abstract} - + getAvailableToppings() : List {abstract} - + saveNewLayer(CakeLayerInfo) {abstract} - + saveNewTopping(CakeToppingInfo) {abstract} - } - class CakeViewImpl { - - cakeBakingService : CakeBakingService - + CakeViewImpl(cakeBakingService : CakeBakingService) - + render() - } class CakeToppingInfo { + calories : int + id : Optional @@ -67,35 +11,6 @@ package com.iluwatar.layers { + CakeToppingInfo(name : String, calories : int) + toString() : String } - class CakeInfo { - + cakeLayerInfos : List - + cakeToppingInfo : CakeToppingInfo - + id : Optional - + CakeInfo(cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) - + CakeInfo(id : Long, cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) - + calculateTotalCalories() : int - + toString() : String - } - class App { - - cakeBakingService : CakeBakingService {static} - + App() - - initializeData(cakeBakingService : CakeBakingService) {static} - + main(args : String[]) {static} - } - class Cake { - - id : Long - - layers : Set - - topping : CakeTopping - + Cake() - + addLayer(layer : CakeLayer) - + getId() : Long - + getLayers() : Set - + getTopping() : CakeTopping - + setId(id : Long) - + setLayers(layers : Set) - + setTopping(topping : CakeTopping) - + toString() : String - } class CakeLayer { - cake : Cake - calories : int @@ -113,6 +28,91 @@ package com.iluwatar.layers { + setName(name : String) + toString() : String } + class CakeLayerInfo { + + calories : int + + id : Optional + + name : String + + CakeLayerInfo(id : Long, name : String, calories : int) + + CakeLayerInfo(name : String, calories : int) + + toString() : String + } + class CakeViewImpl { + - cakeBakingService : CakeBakingService + + CakeViewImpl(cakeBakingService : CakeBakingService) + + render() + } + class CakeBakingServiceImpl { + - context : AbstractApplicationContext + + CakeBakingServiceImpl() + + bakeNewCake(cakeInfo : CakeInfo) + + getAllCakes() : List + - getAvailableLayerEntities() : List + + getAvailableLayers() : List + - getAvailableToppingEntities() : List + + getAvailableToppings() : List + + saveNewLayer(layerInfo : CakeLayerInfo) + + saveNewTopping(toppingInfo : CakeToppingInfo) + } + class CakeTopping { + - cake : Cake + - calories : int + - id : Long + - name : String + + CakeTopping() + + CakeTopping(name : String, calories : int) + + getCake() : Cake + + getCalories() : int + + getId() : Long + + getName() : String + + setCake(cake : Cake) + + setCalories(calories : int) + + setId(id : Long) + + setName(name : String) + + toString() : String + } + class Cake { + - id : Long + - layers : Set + - topping : CakeTopping + + Cake() + + addLayer(layer : CakeLayer) + + getId() : Long + + getLayers() : Set + + getTopping() : CakeTopping + + setId(id : Long) + + setLayers(layers : Set) + + setTopping(topping : CakeTopping) + + toString() : String + } + interface CakeToppingDao { + } + interface CakeBakingService { + + bakeNewCake(CakeInfo) {abstract} + + getAllCakes() : List {abstract} + + getAvailableLayers() : List {abstract} + + getAvailableToppings() : List {abstract} + + saveNewLayer(CakeLayerInfo) {abstract} + + saveNewTopping(CakeToppingInfo) {abstract} + } + class App { + - cakeBakingService : CakeBakingService {static} + + App() + - initializeData(cakeBakingService : CakeBakingService) {static} + + main(args : String[]) {static} + } + interface CakeDao { + } + interface CakeLayerDao { + } + class CakeInfo { + + cakeLayerInfos : List + + cakeToppingInfo : CakeToppingInfo + + id : Optional + + CakeInfo(cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + CakeInfo(id : Long, cakeToppingInfo : CakeToppingInfo, cakeLayerInfos : List) + + calculateTotalCalories() : int + + toString() : String + } } CakeViewImpl --> "-cakeBakingService" CakeBakingService CakeInfo --> "-cakeToppingInfo" CakeToppingInfo @@ -120,6 +120,6 @@ CakeInfo --> "-cakeLayerInfos" CakeLayerInfo App --> "-cakeBakingService" CakeBakingService CakeLayer --> "-cake" Cake Cake --> "-topping" CakeTopping -CakeBakingServiceImpl ..|> CakeBakingService CakeViewImpl ..|> View +CakeBakingServiceImpl ..|> CakeBakingService @enduml \ No newline at end of file diff --git a/lazy-loading/etc/lazy-loading.urm.puml b/lazy-loading/etc/lazy-loading.urm.puml index 96c427553..80192160a 100644 --- a/lazy-loading/etc/lazy-loading.urm.puml +++ b/lazy-loading/etc/lazy-loading.urm.puml @@ -2,20 +2,24 @@ package com.iluwatar.lazy.loading { ~class HeavyFactory { - heavyInstance : Heavy - ~ HeavyFactory(this$0 : Java8Holder) + ~ HeavyFactory() + get() : Heavy } - class HolderNaive { + class App { + + App() + + main(args : String[]) {static} + } + class HolderThreadSafe { - heavy : Heavy - + HolderNaive() + + HolderThreadSafe() + getHeavy() : Heavy } class Heavy { + Heavy() } - class HolderThreadSafe { + class HolderNaive { - heavy : Heavy - + HolderThreadSafe() + + HolderNaive() + getHeavy() : Heavy } class Java8Holder { @@ -24,12 +28,8 @@ package com.iluwatar.lazy.loading { - createAndCacheHeavy() : Heavy + getHeavy() : Heavy } - class App { - + App() - + main(args : String[]) {static} - } } -HolderThreadSafe --> "-heavy" Heavy HolderNaive --> "-heavy" Heavy +HolderThreadSafe --> "-heavy" Heavy HeavyFactory --> "-heavyInstance" Heavy @enduml \ No newline at end of file diff --git a/mediator/etc/mediator.urm.puml b/mediator/etc/mediator.urm.puml index 0b3baab5a..330e024ab 100644 --- a/mediator/etc/mediator.urm.puml +++ b/mediator/etc/mediator.urm.puml @@ -1,24 +1,20 @@ @startuml package com.iluwatar.mediator { - class App { - + App() - + main(args : String[]) {static} - } - class Hobbit { - + Hobbit() - + toString() : String + interface Party { + + act(PartyMember, Action) {abstract} + + addMember(PartyMember) {abstract} } interface PartyMember { + act(Action) {abstract} + joinedParty(Party) {abstract} + partyAction(Action) {abstract} } - interface Party { - + act(PartyMember, Action) {abstract} - + addMember(PartyMember) {abstract} + class Rogue { + + Rogue() + + toString() : String } - class Wizard { - + Wizard() + class Hunter { + + Hunter() + toString() : String } class PartyImpl { @@ -27,12 +23,12 @@ package com.iluwatar.mediator { + act(actor : PartyMember, action : Action) + addMember(member : PartyMember) } - class Hunter { - + Hunter() + class Hobbit { + + Hobbit() + toString() : String } - class Rogue { - + Rogue() + class Wizard { + + Wizard() + toString() : String } abstract class PartyMemberBase { @@ -43,6 +39,10 @@ package com.iluwatar.mediator { + partyAction(action : Action) + toString() : String {abstract} } + class App { + + App() + + main(args : String[]) {static} + } enum Action { + ENEMY {static} + GOLD {static} @@ -59,10 +59,10 @@ package com.iluwatar.mediator { } PartyImpl --> "-members" PartyMember PartyMemberBase --> "-party" Party +Rogue --|> PartyMemberBase +Hunter --|> PartyMemberBase +PartyImpl ..|> Party Hobbit --|> PartyMemberBase Wizard --|> PartyMemberBase -PartyImpl ..|> Party -Hunter --|> PartyMemberBase -Rogue --|> PartyMemberBase PartyMemberBase ..|> PartyMember @enduml \ No newline at end of file diff --git a/memento/etc/memento.urm.puml b/memento/etc/memento.urm.puml index aa63b4ebe..316d4047d 100644 --- a/memento/etc/memento.urm.puml +++ b/memento/etc/memento.urm.puml @@ -1,17 +1,5 @@ @startuml package com.iluwatar.memento { - class Star { - - ageYears : int - - massTons : int - - type : StarType - + Star(startType : StarType, startAge : int, startMass : int) - ~ getMemento() : StarMemento - ~ setMemento(memento : StarMemento) - + timePasses() - + toString() : String - } - interface StarMemento { - } -class StarMementoInternal { - ageYears : int - massTons : int @@ -28,6 +16,18 @@ package com.iluwatar.memento { + App() + main(args : String[]) {static} } + interface StarMemento { + } + class Star { + - ageYears : int + - massTons : int + - type : StarType + + Star(startType : StarType, startAge : int, startMass : int) + ~ getMemento() : StarMemento + ~ setMemento(memento : StarMemento) + + timePasses() + + toString() : String + } enum StarType { + DEAD {static} + RED_GIANT {static} diff --git a/model-view-controller/etc/model-view-controller.urm.puml b/model-view-controller/etc/model-view-controller.urm.puml index f8137bdae..ad979fe0e 100644 --- a/model-view-controller/etc/model-view-controller.urm.puml +++ b/model-view-controller/etc/model-view-controller.urm.puml @@ -13,14 +13,6 @@ package com.iluwatar.model.view.controller { + setNourishment(nourishment : Nourishment) + toString() : String } - class App { - + App() - + main(args : String[]) {static} - } - class GiantView { - + GiantView() - + displayGiant(giant : GiantModel) - } class GiantController { - giant : GiantModel - view : GiantView @@ -33,6 +25,23 @@ package com.iluwatar.model.view.controller { + setNourishment(nourishment : Nourishment) + updateView() } + class GiantView { + + GiantView() + + displayGiant(giant : GiantModel) + } + class App { + + App() + + main(args : String[]) {static} + } + enum Health { + + DEAD {static} + + HEALTHY {static} + + WOUNDED {static} + - title : String + + toString() : String + + valueOf(name : String) : Health {static} + + values() : Health[] {static} + } enum Nourishment { + HUNGRY {static} + SATURATED {static} @@ -51,15 +60,6 @@ package com.iluwatar.model.view.controller { + valueOf(name : String) : Fatigue {static} + values() : Fatigue[] {static} } - enum Health { - + DEAD {static} - + HEALTHY {static} - + WOUNDED {static} - - title : String - + toString() : String - + valueOf(name : String) : Health {static} - + values() : Health[] {static} - } } GiantModel --> "-nourishment" Nourishment GiantController --> "-giant" GiantModel diff --git a/model-view-presenter/etc/model-view-presenter.urm.puml b/model-view-presenter/etc/model-view-presenter.urm.puml index 64bcfba32..0cec653e4 100644 --- a/model-view-presenter/etc/model-view-presenter.urm.puml +++ b/model-view-presenter/etc/model-view-presenter.urm.puml @@ -1,42 +1,5 @@ @startuml package com.iluwatar.model.view.presenter { - class FileLoader { - - fileName : String - - loaded : boolean - + FileLoader() - + fileExists() : boolean - + getFileName() : String - + isLoaded() : boolean - + loadData() : String - + setFileName(fileName : String) - } - class FileSelectorJFrame { - - area : JTextArea - - cancel : JButton - - contents : JLabel - - fileName : String - - info : JLabel - - input : JTextField - - ok : JButton - - panel : JPanel - - presenter : FileSelectorPresenter - - serialVersionUID : long {static} - + FileSelectorJFrame() - + actionPerformed(e : ActionEvent) - + close() - + displayData(data : String) - + getFileName() : String - + getPresenter() : FileSelectorPresenter - + isOpened() : boolean - + open() - + setFileName(name : String) - + setPresenter(presenter : FileSelectorPresenter) - + showMessage(message : String) - } - class App { - + App() - + main(args : String[]) {static} - } interface FileSelectorView { + close() {abstract} + displayData(String) {abstract} @@ -67,6 +30,16 @@ package com.iluwatar.model.view.presenter { + setPresenter(presenter : FileSelectorPresenter) + showMessage(message : String) } + class FileLoader { + - fileName : String + - loaded : boolean + + FileLoader() + + fileExists() : boolean + + getFileName() : String + + isLoaded() : boolean + + loadData() : String + + setFileName(fileName : String) + } class FileSelectorPresenter { - loader : FileLoader - view : FileSelectorView @@ -77,11 +50,38 @@ package com.iluwatar.model.view.presenter { + setLoader(loader : FileLoader) + start() } + class App { + + App() + + main(args : String[]) {static} + } + class FileSelectorJFrame { + - area : JTextArea + - cancel : JButton + - contents : JLabel + - fileName : String + - info : JLabel + - input : JTextField + - ok : JButton + - panel : JPanel + - presenter : FileSelectorPresenter + - serialVersionUID : long {static} + + FileSelectorJFrame() + + actionPerformed(e : ActionEvent) + + close() + + displayData(data : String) + + getFileName() : String + + getPresenter() : FileSelectorPresenter + + isOpened() : boolean + + open() + + setFileName(name : String) + + setPresenter(presenter : FileSelectorPresenter) + + showMessage(message : String) + } } -FileSelectorStub --> "-presenter" FileSelectorPresenter FileSelectorJFrame --> "-presenter" FileSelectorPresenter +FileSelectorStub --> "-presenter" FileSelectorPresenter FileSelectorPresenter --> "-loader" FileLoader FileSelectorPresenter --> "-view" FileSelectorView -FileSelectorJFrame ..|> FileSelectorView FileSelectorStub ..|> FileSelectorView +FileSelectorJFrame ..|> FileSelectorView @enduml \ No newline at end of file diff --git a/monostate/etc/monostate.urm.puml b/monostate/etc/monostate.urm.puml index 3c09bb4ed..6574bcad1 100644 --- a/monostate/etc/monostate.urm.puml +++ b/monostate/etc/monostate.urm.puml @@ -1,5 +1,13 @@ @startuml package com.iluwatar.monostate { + class App { + + App() + + main(args : String[]) {static} + } + class Request { + + value : String + + Request(value : String) + } class LoadBalancer { - id : int {static} - lastServedId : int {static} @@ -10,14 +18,6 @@ package com.iluwatar.monostate { + getNoOfServers() : int + serverRequest(request : Request) } - class App { - + App() - + main(args : String[]) {static} - } - class Request { - + value : String - + Request(value : String) - } class Server { + host : String + id : int diff --git a/mute-idiom/etc/mute-idiom.urm.puml b/mute-idiom/etc/mute-idiom.urm.puml index d4efc2db1..c992773f5 100644 --- a/mute-idiom/etc/mute-idiom.urm.puml +++ b/mute-idiom/etc/mute-idiom.urm.puml @@ -1,6 +1,7 @@ @startuml package com.iluwatar.mute { - interface Resource { + interface CheckedRunnable { + + run() {abstract} } class App { + App() @@ -16,8 +17,7 @@ package com.iluwatar.mute { + loggedMute(runnable : CheckedRunnable) {static} + mute(runnable : CheckedRunnable) {static} } - interface CheckedRunnable { - + run() {abstract} + interface Resource { } } @enduml \ No newline at end of file diff --git a/mutex/etc/mutex.urm.puml b/mutex/etc/mutex.urm.puml index 24ea83630..68b96d668 100644 --- a/mutex/etc/mutex.urm.puml +++ b/mutex/etc/mutex.urm.puml @@ -1,5 +1,11 @@ @startuml package com.iluwatar.mutex { + class Jar { + - beans : int + - lock : Lock + + Jar(beans : int, lock : Lock) + + takeBean() : boolean + } interface Lock { + acquire() {abstract} + release() {abstract} @@ -11,12 +17,6 @@ package com.iluwatar.mutex { + getOwner() : Object + release() } - class Jar { - - beans : int - - lock : Lock - + Jar(beans : int, lock : Lock) - + takeBean() : boolean - } class App { + App() + main(args : String[]) {static} diff --git a/naked-objects/etc/naked-objects-dom.urm.puml b/naked-objects/etc/naked-objects-dom.urm.puml index ea32b5787..6662a560a 100644 --- a/naked-objects/etc/naked-objects-dom.urm.puml +++ b/naked-objects/etc/naked-objects-dom.urm.puml @@ -23,58 +23,13 @@ package domainapp.dom.modules.simple { } class SimpleObject { - container : DomainObjectContainer - - dnFieldFlags : byte[] {static} - - dnFieldNames : String[] {static} - - dnFieldTypes : Class[] {static} - # dnFlags : byte - - dnInheritedFieldCount : int {static} - - dnPersistableSuperclass : Class {static} - # dnStateManager : StateManager - name : String + SimpleObject() - + ___dn$loadClass(className : String) : Class {static} - - __dnFieldFlagsInit() : byte[] {static} - - __dnFieldNamesInit() : String[] {static} - - __dnFieldTypesInit() : Class[] {static} - # __dnGetInheritedFieldCount() : int {static} - - __dnPersistableSuperclassInit() : Class {static} + compareTo(other : SimpleObject) : int + default0UpdateName() : String - # dnCopyField(obj : SimpleObject, index : int) - + dnCopyFields(obj : Object, indices : int[]) - + dnCopyKeyFieldsFromObjectId(fc : ObjectIdFieldConsumer, oid : Object) - # dnCopyKeyFieldsFromObjectId(oid : Object) - + dnCopyKeyFieldsToObjectId(fs : ObjectIdFieldSupplier, oid : Object) - + dnCopyKeyFieldsToObjectId(oid : Object) - + dnGetExecutionContext() : ExecutionContextReference - # dnGetManagedFieldCount() : int {static} - + dnGetObjectId() : Object - + dnGetTransactionalObjectId() : Object - + dnGetVersion() : Object - + dnGetname() : String - + dnIsDeleted() : boolean - + dnIsDetached() : boolean - + dnIsDirty() : boolean - + dnIsNew() : boolean - + dnIsPersistent() : boolean - + dnIsTransactional() : boolean - + dnMakeDirty(fieldName : String) - + dnNewInstance(sm : StateManager) : Persistable - + dnNewInstance(sm : StateManager, obj : Object) : Persistable - + dnNewObjectIdInstance() : Object - + dnNewObjectIdInstance(key : Object) : Object - # dnPreSerialize() - + dnProvideField(index : int) - + dnProvideFields(indices : int[]) - + dnReplaceField(index : int) - + dnReplaceFields(indices : int[]) - + dnReplaceFlags() - + dnReplaceStateManager(sm : StateManager) - + dnSetname(name : String) - - dnSuperClone() : Object + getName() : String + getVersionSequence() : Long - + setName(val : String) + + setName(name : String) + title() : TranslatableString + updateName(name : String) : SimpleObject + validateUpdateName(name : String) : TranslatableString diff --git a/naked-objects/etc/naked-objects-fixture.urm.puml b/naked-objects/etc/naked-objects-fixture.urm.puml index 65c44410a..21e38d710 100644 --- a/naked-objects/etc/naked-objects-fixture.urm.puml +++ b/naked-objects/etc/naked-objects-fixture.urm.puml @@ -1,16 +1,16 @@ @startuml package domainapp.dom.app.homepage { + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } class HomePageViewModel { ~ simpleObjects : SimpleObjects + HomePageViewModel() + getObjects() : List + title() : String } - class HomePageService { - ~ container : DomainObjectContainer - + HomePageService() - + homePage() : HomePageViewModel - } } package domainapp.dom.modules.simple { class SimpleObject { diff --git a/naked-objects/etc/naked-objects-integtests.urm.puml b/naked-objects/etc/naked-objects-integtests.urm.puml index 1f2dfc4c2..65c44410a 100644 --- a/naked-objects/etc/naked-objects-integtests.urm.puml +++ b/naked-objects/etc/naked-objects-integtests.urm.puml @@ -1,26 +1,18 @@ @startuml package domainapp.dom.app.homepage { - class HomePageService { - ~ container : DomainObjectContainer - + HomePageService() - + homePage() : HomePageViewModel - } class HomePageViewModel { ~ simpleObjects : SimpleObjects + HomePageViewModel() + getObjects() : List + title() : String } + class HomePageService { + ~ container : DomainObjectContainer + + HomePageService() + + homePage() : HomePageViewModel + } } package domainapp.dom.modules.simple { - class SimpleObjects { - ~ container : DomainObjectContainer - + SimpleObjects() - + create(name : String) : SimpleObject - + findByName(name : String) : List - + listAll() : List - + title() : TranslatableString - } class SimpleObject { - container : DomainObjectContainer - dnFieldFlags : byte[] {static} @@ -79,6 +71,14 @@ package domainapp.dom.modules.simple { + updateName(name : String) : SimpleObject + validateUpdateName(name : String) : TranslatableString } + class SimpleObjects { + ~ container : DomainObjectContainer + + SimpleObjects() + + create(name : String) : SimpleObject + + findByName(name : String) : List + + listAll() : List + + title() : TranslatableString + } } package domainapp.fixture { class DomainAppFixturesProvider { diff --git a/null-object/etc/null-object.urm.puml b/null-object/etc/null-object.urm.puml index d0b2936c5..bd4f8446e 100644 --- a/null-object/etc/null-object.urm.puml +++ b/null-object/etc/null-object.urm.puml @@ -1,22 +1,5 @@ @startuml package com.iluwatar.nullobject { - class NullNode { - - instance : NullNode {static} - - NullNode() - + getInstance() : NullNode {static} - + getLeft() : Node - + getName() : String - + getRight() : Node - + getTreeSize() : int - + walk() - } - interface Node { - + getLeft() : Node {abstract} - + getName() : String {abstract} - + getRight() : Node {abstract} - + getTreeSize() : int {abstract} - + walk() {abstract} - } class App { + App() + main(args : String[]) {static} @@ -32,9 +15,26 @@ package com.iluwatar.nullobject { + getTreeSize() : int + walk() } + interface Node { + + getLeft() : Node {abstract} + + getName() : String {abstract} + + getRight() : Node {abstract} + + getTreeSize() : int {abstract} + + walk() {abstract} + } + class NullNode { + - instance : NullNode {static} + - NullNode() + + getInstance() : NullNode {static} + + getLeft() : Node + + getName() : String + + getRight() : Node + + getTreeSize() : int + + walk() + } } NullNode --> "-instance" NullNode NodeImpl --> "-left" Node -NullNode ..|> Node NodeImpl ..|> Node +NullNode ..|> Node @enduml \ No newline at end of file diff --git a/object-pool/etc/object-pool.urm.puml b/object-pool/etc/object-pool.urm.puml index 9df1081d3..caf0b6d7e 100644 --- a/object-pool/etc/object-pool.urm.puml +++ b/object-pool/etc/object-pool.urm.puml @@ -1,5 +1,9 @@ @startuml package com.iluwatar.object.pool { + class OliphauntPool { + + OliphauntPool() + # create() : Oliphaunt + } class Oliphaunt { - counter : int {static} - id : int @@ -7,14 +11,6 @@ package com.iluwatar.object.pool { + getId() : int + toString() : String } - class OliphauntPool { - + OliphauntPool() - # create() : Oliphaunt - } - class App { - + App() - + main(args : String[]) {static} - } abstract class ObjectPool { - available : HashSet - inUse : HashSet @@ -24,6 +20,10 @@ package com.iluwatar.object.pool { # create() : T {abstract} + toString() : String } + class App { + + App() + + main(args : String[]) {static} + } } OliphauntPool --|> ObjectPool @enduml \ No newline at end of file diff --git a/observer/etc/observer.urm.puml b/observer/etc/observer.urm.puml index 33485d731..e9a8f7b9b 100644 --- a/observer/etc/observer.urm.puml +++ b/observer/etc/observer.urm.puml @@ -4,9 +4,12 @@ package com.iluwatar.observer { + Orcs() + update(currentWeather : WeatherType) } - class Hobbits { - + Hobbits() - + update(currentWeather : WeatherType) + interface WeatherObserver { + + update(WeatherType) {abstract} + } + class App { + + App() + + main(args : String[]) {static} } class Weather { - currentWeather : WeatherType @@ -17,12 +20,9 @@ package com.iluwatar.observer { + removeObserver(obs : WeatherObserver) + timePasses() } - class App { - + App() - + main(args : String[]) {static} - } - interface WeatherObserver { - + update(WeatherType) {abstract} + class Hobbits { + + Hobbits() + + update(currentWeather : WeatherType) } enum WeatherType { + COLD {static} @@ -41,6 +41,11 @@ package com.iluwatar.observer.generic { } interface Race { } + class GWeather { + - currentWeather : WeatherType + + GWeather() + + timePasses() + } abstract class Observable, A> { # observers : List> + Observable, A>() @@ -48,26 +53,21 @@ package com.iluwatar.observer.generic { + notifyObservers(argument : A) + removeObserver(observer : O extends Observer) } - class GWeather { - - currentWeather : WeatherType - + GWeather() - + timePasses() - } - interface Observer, O extends Observer, A> { - + update(S extends Observable, A) {abstract} - } class GHobbits { + GHobbits() + update(weather : GWeather, weatherType : WeatherType) } + interface Observer, O extends Observer, A> { + + update(S extends Observable, A) {abstract} + } } Weather --> "-currentWeather" WeatherType GWeather --> "-currentWeather" WeatherType Weather --> "-observers" WeatherObserver GOrcs ..|> Race Orcs ..|> WeatherObserver -Hobbits ..|> WeatherObserver Race --|> Observer GWeather --|> Observable GHobbits ..|> Race +Hobbits ..|> WeatherObserver @enduml \ No newline at end of file diff --git a/poison-pill/etc/poison-pill.urm.puml b/poison-pill/etc/poison-pill.urm.puml index 58f9eb937..f05c40692 100644 --- a/poison-pill/etc/poison-pill.urm.puml +++ b/poison-pill/etc/poison-pill.urm.puml @@ -1,5 +1,8 @@ @startuml package com.iluwatar.poison.pill { + interface MqPublishPoint { + + put(Message) {abstract} + } interface Message { + POISON_PILL : Message {static} + addHeader(Headers, String) {abstract} @@ -12,21 +15,7 @@ package com.iluwatar.poison.pill { + App() + main(args : String[]) {static} } - class SimpleMessage { - - body : String - - headers : Map - + SimpleMessage() - + addHeader(header : Headers, value : String) - + getBody() : String - + getHeader(header : Headers) : String - + getHeaders() : Map - + setBody(body : String) - } - class SimpleMessageQueue { - - queue : BlockingQueue - + SimpleMessageQueue(bound : int) - + put(msg : Message) - + take() : Message + interface MessageQueue { } class Producer { - isStopped : boolean @@ -36,19 +25,30 @@ package com.iluwatar.poison.pill { + send(body : String) + stop() } - interface MqSubscribePoint { - + take() : Message {abstract} - } class Consumer { - name : String - queue : MqSubscribePoint + Consumer(name : String, queue : MqSubscribePoint) + consume() } - interface MessageQueue { + class SimpleMessageQueue { + - queue : BlockingQueue + + SimpleMessageQueue(bound : int) + + put(msg : Message) + + take() : Message } - interface MqPublishPoint { - + put(Message) {abstract} + interface MqSubscribePoint { + + take() : Message {abstract} + } + class SimpleMessage { + - body : String + - headers : Map + + SimpleMessage() + + addHeader(header : Headers, value : String) + + getBody() : String + + getHeader(header : Headers) : String + + getHeaders() : Map + + setBody(body : String) } enum Headers { + DATE {static} @@ -65,8 +65,8 @@ SimpleMessage --+ Message Producer --+ Message Message --> "-POISON_PILL" Message Consumer --+ Message -SimpleMessage ..|> Message -SimpleMessageQueue ..|> MessageQueue MessageQueue --|> MqPublishPoint MessageQueue --|> MqSubscribePoint +SimpleMessageQueue ..|> MessageQueue +SimpleMessage ..|> Message @enduml \ No newline at end of file diff --git a/pom.xml b/pom.xml index b1ae7d811..fcf1671c8 100644 --- a/pom.xml +++ b/pom.xml @@ -282,6 +282,25 @@ + + + + com.github.markusmo3.urm + + + urm-maven-plugin + + + [1.4.1,) + + + map + + + + + + diff --git a/private-class-data/etc/private-class-data.urm.puml b/private-class-data/etc/private-class-data.urm.puml index 0edc2c1a8..ad35c0bdd 100644 --- a/private-class-data/etc/private-class-data.urm.puml +++ b/private-class-data/etc/private-class-data.urm.puml @@ -1,18 +1,5 @@ @startuml package com.iluwatar.privateclassdata { - class Stew { - - numCarrots : int - - numMeat : int - - numPeppers : int - - numPotatoes : int - + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) - + mix() - + taste() - } - class App { - + App() - + main(args : String[]) {static} - } class StewData { - numCarrots : int - numMeat : int @@ -24,6 +11,19 @@ package com.iluwatar.privateclassdata { + getNumPeppers() : int + getNumPotatoes() : int } + class App { + + App() + + main(args : String[]) {static} + } + class Stew { + - numCarrots : int + - numMeat : int + - numPeppers : int + - numPotatoes : int + + Stew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) + + mix() + + taste() + } class ImmutableStew { - data : StewData + ImmutableStew(numPotatoes : int, numCarrots : int, numMeat : int, numPeppers : int) diff --git a/producer-consumer/etc/producer-consumer.urm.puml b/producer-consumer/etc/producer-consumer.urm.puml index 9a6748552..8b793d75b 100644 --- a/producer-consumer/etc/producer-consumer.urm.puml +++ b/producer-consumer/etc/producer-consumer.urm.puml @@ -1,5 +1,22 @@ @startuml package com.iluwatar.producer.consumer { + class Consumer { + - name : String + - queue : ItemQueue + + Consumer(name : String, queue : ItemQueue) + + consume() + } + class Item { + - id : int + - producer : String + + Item(producer : String, id : int) + + getId() : int + + getProducer() : String + } + class App { + + App() + + main(args : String[]) {static} + } class Producer { - itemId : int - name : String @@ -13,23 +30,6 @@ package com.iluwatar.producer.consumer { + put(item : Item) + take() : Item } - class Item { - - id : int - - producer : String - + Item(producer : String, id : int) - + getId() : int - + getProducer() : String - } - class App { - + App() - + main(args : String[]) {static} - } - class Consumer { - - name : String - - queue : ItemQueue - + Consumer(name : String, queue : ItemQueue) - + consume() - } } Consumer --> "-queue" ItemQueue Producer --> "-queue" ItemQueue diff --git a/promise/etc/promise.urm.puml b/promise/etc/promise.urm.puml index d97871411..cd6e73252 100644 --- a/promise/etc/promise.urm.puml +++ b/promise/etc/promise.urm.puml @@ -24,12 +24,18 @@ package com.iluwatar.promise { - ConsumeAction(src : Promise, dest : Promise, action : Consumer) + run() } - -class TransformAction { - - dest : Promise - - func : Function - - src : Promise - - TransformAction(src : Promise, dest : Promise, func : Function) - + run() + class Promise { + - exceptionHandler : Consumer + - fulfillmentAction : Runnable + + Promise() + + fulfill(value : T) + + fulfillExceptionally(exception : Exception) + + fulfillInAsync(task : Callable, executor : Executor) : Promise + - handleException(exception : Exception) + + onError(exceptionHandler : Consumer) : Promise + - postFulfillment() + + thenAccept(action : Consumer) : Promise + + thenApply(func : Function) : Promise } class App { - DEFAULT_URL : String {static} @@ -47,18 +53,12 @@ package com.iluwatar.promise { - stop() - taskCompleted() } - class Promise { - - exceptionHandler : Consumer - - fulfillmentAction : Runnable - + Promise() - + fulfill(value : T) - + fulfillExceptionally(exception : Exception) - + fulfillInAsync(task : Callable, executor : Executor) : Promise - - handleException(exception : Exception) - + onError(exceptionHandler : Consumer) : Promise - - postFulfillment() - + thenAccept(action : Consumer) : Promise - + thenApply(func : Function) : Promise + -class TransformAction { + - dest : Promise + - func : Function + - src : Promise + - TransformAction(src : Promise, dest : Promise, func : Function) + + run() } class Utility { + Utility() diff --git a/property/etc/property.urm.puml b/property/etc/property.urm.puml index 7c90edccc..6ff0e5c52 100644 --- a/property/etc/property.urm.puml +++ b/property/etc/property.urm.puml @@ -1,5 +1,15 @@ @startuml package com.iluwatar.property { + class App { + + App() + + main(args : String[]) {static} + } + interface Prototype { + + get(Stats) : Integer {abstract} + + has(Stats) : boolean {abstract} + + remove(Stats) {abstract} + + set(Stats, Integer) {abstract} + } class Character { - name : String - properties : Map @@ -16,15 +26,12 @@ package com.iluwatar.property { + toString() : String + type() : Type } - interface Prototype { - + get(Stats) : Integer {abstract} - + has(Stats) : boolean {abstract} - + remove(Stats) {abstract} - + set(Stats, Integer) {abstract} - } - class App { - + App() - + main(args : String[]) {static} + enum Type { + + MAGE {static} + + ROGUE {static} + + WARRIOR {static} + + valueOf(name : String) : Type {static} + + values() : Type[] {static} } enum Stats { + AGILITY {static} @@ -38,13 +45,6 @@ package com.iluwatar.property { + valueOf(name : String) : Stats {static} + values() : Stats[] {static} } - enum Type { - + MAGE {static} - + ROGUE {static} - + WARRIOR {static} - + valueOf(name : String) : Type {static} - + values() : Type[] {static} - } } App --+ Character Character --> "-prototype" Prototype diff --git a/prototype/etc/prototype.urm.puml b/prototype/etc/prototype.urm.puml index 7bfc00e15..7ab26dd1b 100644 --- a/prototype/etc/prototype.urm.puml +++ b/prototype/etc/prototype.urm.puml @@ -1,59 +1,42 @@ @startuml package com.iluwatar.prototype { - interface HeroFactory { - + createBeast() : Beast {abstract} - + createMage() : Mage {abstract} - + createWarlord() : Warlord {abstract} + class OrcWarlord { + + OrcWarlord() + + clone() : Warlord + + toString() : String } class OrcBeast { + OrcBeast() + clone() : Beast + toString() : String } - abstract class Mage { - + Mage() - + clone() : Mage {abstract} - } - class HeroFactoryImpl { - - beast : Beast - - mage : Mage - - warlord : Warlord - + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) - + createBeast() : Beast - + createMage() : Mage - + createWarlord() : Warlord + abstract class Beast { + + Beast() + + clone() : Beast {abstract} } class ElfMage { + ElfMage() + clone() : Mage + toString() : String } + abstract class Mage { + + Mage() + + clone() : Mage {abstract} + } abstract class Prototype { + Prototype() + clone() : Object {abstract} } - class App { - + App() - + main(args : String[]) {static} - } - abstract class Warlord { - + Warlord() - + clone() : Warlord {abstract} - } - class OrcWarlord { - + OrcWarlord() - + clone() : Warlord - + toString() : String + interface HeroFactory { + + createBeast() : Beast {abstract} + + createMage() : Mage {abstract} + + createWarlord() : Warlord {abstract} } class ElfWarlord { + ElfWarlord() + clone() : Warlord + toString() : String } - abstract class Beast { - + Beast() - + clone() : Beast {abstract} - } class OrcMage { + OrcMage() + clone() : Mage @@ -64,18 +47,35 @@ package com.iluwatar.prototype { + clone() : Beast + toString() : String } + abstract class Warlord { + + Warlord() + + clone() : Warlord {abstract} + } + class HeroFactoryImpl { + - beast : Beast + - mage : Mage + - warlord : Warlord + + HeroFactoryImpl(mage : Mage, warlord : Warlord, beast : Beast) + + createBeast() : Beast + + createMage() : Mage + + createWarlord() : Warlord + } + class App { + + App() + + main(args : String[]) {static} + } } HeroFactoryImpl --> "-beast" Beast HeroFactoryImpl --> "-warlord" Warlord HeroFactoryImpl --> "-mage" Mage -OrcBeast --|> Beast -Mage --|> Prototype -HeroFactoryImpl ..|> HeroFactory -ElfMage --|> Mage -Warlord --|> Prototype OrcWarlord --|> Warlord -ElfWarlord --|> Warlord +OrcBeast --|> Beast Beast --|> Prototype +ElfMage --|> Mage +Mage --|> Prototype +ElfWarlord --|> Warlord OrcMage --|> Mage ElfBeast --|> Beast +Warlord --|> Prototype +HeroFactoryImpl ..|> HeroFactory @enduml \ No newline at end of file diff --git a/proxy/etc/proxy.urm.puml b/proxy/etc/proxy.urm.puml index 4203174de..1dc58087c 100644 --- a/proxy/etc/proxy.urm.puml +++ b/proxy/etc/proxy.urm.puml @@ -1,5 +1,11 @@ @startuml package com.iluwatar.proxy { + class WizardTowerProxy { + - NUM_WIZARDS_ALLOWED : int {static} + - numWizards : int + + WizardTowerProxy() + + enter(wizard : Wizard) + } class WizardTower { + WizardTower() + enter(wizard : Wizard) @@ -8,12 +14,6 @@ package com.iluwatar.proxy { + App() + main(args : String[]) {static} } - class WizardTowerProxy { - - NUM_WIZARDS_ALLOWED : int {static} - - numWizards : int - + WizardTowerProxy() - + enter(wizard : Wizard) - } class Wizard { - name : String + Wizard(name : String) diff --git a/reactor/etc/reactor.urm.puml b/reactor/etc/reactor.urm.puml index 302f2663c..ecf2f40f7 100644 --- a/reactor/etc/reactor.urm.puml +++ b/reactor/etc/reactor.urm.puml @@ -1,5 +1,24 @@ @startuml package com.iluwatar.reactor.app { + class App { + - channels : List + - dispatcher : Dispatcher + - reactor : NioReactor + + App(dispatcher : Dispatcher) + + main(args : String[]) {static} + + start() + + stop() + - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel + } + class LoggingHandler { + - ACK : byte[] {static} + + LoggingHandler() + - doLogging(data : ByteBuffer) {static} + + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} + - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} + } ~class TcpLoggingClient { - clientName : String - serverPort : int @@ -13,14 +32,6 @@ package com.iluwatar.reactor.app { + UdpLoggingClient(clientName : String, port : int) + run() } - class LoggingHandler { - - ACK : byte[] {static} - + LoggingHandler() - - doLogging(data : ByteBuffer) {static} - + handleChannelRead(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) - - sendReply(channel : AbstractNioChannel, incomingPacket : DatagramPacket, key : SelectionKey) {static} - - sendReply(channel : AbstractNioChannel, key : SelectionKey) {static} - } class AppClient { - service : ExecutorService + AppClient() @@ -29,58 +40,8 @@ package com.iluwatar.reactor.app { + start() + stop() } - class App { - - channels : List - - dispatcher : Dispatcher - - reactor : NioReactor - + App(dispatcher : Dispatcher) - + main(args : String[]) {static} - + start() - + stop() - - tcpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel - - udpChannel(port : int, handler : ChannelHandler) : AbstractNioChannel - } } package com.iluwatar.reactor.framework { - interface Dispatcher { - + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} - + stop() {abstract} - } - class SameThreadDispatcher { - + SameThreadDispatcher() - + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) - + stop() - } - class ThreadPoolDispatcher { - - executorService : ExecutorService - + ThreadPoolDispatcher(poolSize : int) - + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) - + stop() - } - interface ChannelHandler { - + handleChannelRead(AbstractNioChannel, Object, SelectionKey) {abstract} - } - class NioDatagramChannel { - - port : int - + NioDatagramChannel(port : int, handler : ChannelHandler) - + bind() - # doWrite(pendingWrite : Object, key : SelectionKey) - + getInterestedOps() : int - + getJavaChannel() : DatagramChannel - + read(key : SelectionKey) : DatagramPacket - + write(data : Object, key : SelectionKey) - } - class DatagramPacket { - - data : ByteBuffer - - receiver : SocketAddress - - sender : SocketAddress - + DatagramPacket(data : ByteBuffer) - + getData() : ByteBuffer - + getReceiver() : SocketAddress - + getSender() : SocketAddress - + setReceiver(receiver : SocketAddress) - + setSender(sender : SocketAddress) - } abstract class AbstractNioChannel { - channel : SelectableChannel - channelToPendingWrites : Map> @@ -97,6 +58,32 @@ package com.iluwatar.reactor.framework { ~ setReactor(reactor : NioReactor) + write(data : Object, key : SelectionKey) } + ~class ChangeKeyOpsCommand { + - interestedOps : int + - key : SelectionKey + + ChangeKeyOpsCommand(this$0 : SelectionKey, key : int) + + run() + + toString() : String + } + interface ChannelHandler { + + handleChannelRead(AbstractNioChannel, Object, SelectionKey) {abstract} + } + class NioDatagramChannel { + - port : int + + NioDatagramChannel(port : int, handler : ChannelHandler) + + bind() + # doWrite(pendingWrite : Object, key : SelectionKey) + + getInterestedOps() : int + + getJavaChannel() : DatagramChannel + + read(key : SelectionKey) : DatagramPacket + + write(data : Object, key : SelectionKey) + } + class ThreadPoolDispatcher { + - executorService : ExecutorService + + ThreadPoolDispatcher(poolSize : int) + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } class NioServerSocketChannel { - port : int + NioServerSocketChannel(port : int, handler : ChannelHandler) @@ -106,6 +93,26 @@ package com.iluwatar.reactor.framework { + getJavaChannel() : ServerSocketChannel + read(key : SelectionKey) : ByteBuffer } + class SameThreadDispatcher { + + SameThreadDispatcher() + + onChannelReadEvent(channel : AbstractNioChannel, readObject : Object, key : SelectionKey) + + stop() + } + interface Dispatcher { + + onChannelReadEvent(AbstractNioChannel, Object, SelectionKey) {abstract} + + stop() {abstract} + } + class DatagramPacket { + - data : ByteBuffer + - receiver : SocketAddress + - sender : SocketAddress + + DatagramPacket(data : ByteBuffer) + + getData() : ByteBuffer + + getReceiver() : SocketAddress + + getSender() : SocketAddress + + setReceiver(receiver : SocketAddress) + + setSender(sender : SocketAddress) + } class NioReactor { - dispatcher : Dispatcher - pendingCommands : Queue @@ -124,28 +131,21 @@ package com.iluwatar.reactor.framework { + start() + stop() } - ~class ChangeKeyOpsCommand { - - interestedOps : int - - key : SelectionKey - + ChangeKeyOpsCommand(this$0 : NioReactor, key : SelectionKey, interestedOps : int) - + run() - + toString() : String - } } AbstractNioChannel --> "-handler" ChannelHandler UdpLoggingClient ..+ AppClient -AbstractNioChannel --> "-reactor" NioReactor TcpLoggingClient ..+ AppClient +AbstractNioChannel --> "-reactor" NioReactor NioReactor --> "-dispatcher" Dispatcher App --> "-reactor" NioReactor App --> "-channels" AbstractNioChannel DatagramPacket ..+ NioDatagramChannel -ChangeKeyOpsCommand --+ NioReactor -App --> "-dispatcher" Dispatcher LoggingHandler --+ NioDatagramChannel -SameThreadDispatcher ..|> Dispatcher +App --> "-dispatcher" Dispatcher +ChangeKeyOpsCommand --+ NioReactor +NioDatagramChannel --|> AbstractNioChannel LoggingHandler ..|> ChannelHandler ThreadPoolDispatcher ..|> Dispatcher -NioDatagramChannel --|> AbstractNioChannel NioServerSocketChannel --|> AbstractNioChannel +SameThreadDispatcher ..|> Dispatcher @enduml \ No newline at end of file diff --git a/reader-writer-lock/etc/reader-writer-lock.urm.puml b/reader-writer-lock/etc/reader-writer-lock.urm.puml index 22b303cf5..1760cf3e4 100644 --- a/reader-writer-lock/etc/reader-writer-lock.urm.puml +++ b/reader-writer-lock/etc/reader-writer-lock.urm.puml @@ -1,21 +1,5 @@ @startuml package com.iluwatar.reader.writer.lock { - -class ReadLock { - - ReadLock(ReaderWriterLock) - + lock() - + lockInterruptibly() - + newCondition() : Condition - + tryLock() : boolean - + tryLock(time : long, unit : TimeUnit) : boolean - + unlock() - } - class Writer { - - name : String - - writeLock : Lock - + Writer(name : String, writeLock : Lock) - + run() - + write() - } class ReaderWriterLock { - currentReaderCount : int - globalMutex : Set @@ -31,7 +15,7 @@ package com.iluwatar.reader.writer.lock { + writeLock() : Lock } -class WriteLock { - - WriteLock(ReaderWriterLock) + - WriteLock() + lock() + lockInterruptibly() + newCondition() : Condition @@ -43,6 +27,22 @@ package com.iluwatar.reader.writer.lock { + App() + main(args : String[]) {static} } + -class ReadLock { + - ReadLock() + + lock() + + lockInterruptibly() + + newCondition() : Condition + + tryLock() : boolean + + tryLock(time : long, unit : TimeUnit) : boolean + + unlock() + } + class Writer { + - name : String + - writeLock : Lock + + Writer(name : String, writeLock : Lock) + + run() + + write() + } class Reader { - name : String - readLock : Lock @@ -51,8 +51,8 @@ package com.iluwatar.reader.writer.lock { + run() } } -ReadLock --+ ReaderWriterLock ReaderWriterLock --> "-readerLock" ReadLock +ReadLock --+ ReaderWriterLock ReaderWriterLock --> "-writerLock" WriteLock WriteLock --+ ReaderWriterLock @enduml \ No newline at end of file diff --git a/repository/etc/repository.urm.puml b/repository/etc/repository.urm.puml index 49a2c8fdc..ad1759033 100644 --- a/repository/etc/repository.urm.puml +++ b/repository/etc/repository.urm.puml @@ -1,9 +1,34 @@ @startuml package com.iluwatar.repository { + class PersonSpecifications { + + PersonSpecifications() + } + class NameEqualSpec { + + name : String + + NameEqualSpec(name : String) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } class App { + App() + main(args : String[]) {static} } + interface PersonRepository { + + findByName(String) : Person {abstract} + } + class AgeBetweenSpec { + - from : int + - to : int + + AgeBetweenSpec(from : int, to : int) + + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate + } + class AppConfig { + + AppConfig() + + dataSource() : DataSource + + entityManagerFactory() : LocalContainerEntityManagerFactoryBean + - jpaProperties() : Properties {static} + + main(args : String[]) {static} + + transactionManager() : JpaTransactionManager + } class Person { - age : int - id : Long @@ -23,31 +48,6 @@ package com.iluwatar.repository { + setSurname(surname : String) + toString() : String } - class AgeBetweenSpec { - - from : int - - to : int - + AgeBetweenSpec(from : int, to : int) - + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate - } - class AppConfig { - + AppConfig() - + dataSource() : DataSource - + entityManagerFactory() : LocalContainerEntityManagerFactoryBean - - jpaProperties() : Properties {static} - + main(args : String[]) {static} - + transactionManager() : JpaTransactionManager - } - interface PersonRepository { - + findByName(String) : Person {abstract} - } - class NameEqualSpec { - + name : String - + NameEqualSpec(name : String) - + toPredicate(root : Root, query : CriteriaQuery, cb : CriteriaBuilder) : Predicate - } - class PersonSpecifications { - + PersonSpecifications() - } } App --+ PersonSpecifications AppConfig --+ PersonSpecifications diff --git a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml index 847b716a0..81560c7f5 100644 --- a/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml +++ b/resource-acquisition-is-initialization/etc/resource-acquisition-is-initialization.urm.puml @@ -4,13 +4,13 @@ package com.iluwatar.resource.acquisition.is.initialization { + App() + main(args : String[]) {static} } - class TreasureChest { - + TreasureChest() - + close() - } class SlidingDoor { + SlidingDoor() + close() } + class TreasureChest { + + TreasureChest() + + close() + } } @enduml \ No newline at end of file diff --git a/semaphore/etc/semaphore.urm.puml b/semaphore/etc/semaphore.urm.puml index f85fff921..a94ff5a7c 100644 --- a/semaphore/etc/semaphore.urm.puml +++ b/semaphore/etc/semaphore.urm.puml @@ -1,32 +1,11 @@ @startuml package com.iluwatar.semaphore { - class FruitShop { - - available : boolean[] - - bowls : FruitBowl[] - - semaphore : Semaphore - + FruitShop() - + countFruit() : int - + returnBowl(bowl : FruitBowl) - + takeBowl() : FruitBowl - } - class FruitBowl { - - fruit : ArrayList - + FruitBowl() - + countFruit() : int - + put(f : Fruit) - + take() : Fruit - + toString() : String - } class Fruit { - type : FruitType + Fruit(type : FruitType) + getType() : FruitType + toString() : String } - interface Lock { - + acquire() {abstract} - + release() {abstract} - } class App { + App() + main(args : String[]) {static} @@ -40,6 +19,27 @@ package com.iluwatar.semaphore { + getNumLicenses() : int + release() } + class FruitShop { + - available : boolean[] + - bowls : FruitBowl[] + - semaphore : Semaphore + + FruitShop() + + countFruit() : int + + returnBowl(bowl : FruitBowl) + + takeBowl() : FruitBowl + } + interface Lock { + + acquire() {abstract} + + release() {abstract} + } + class FruitBowl { + - fruit : ArrayList + + FruitBowl() + + countFruit() : int + + put(f : Fruit) + + take() : Fruit + + toString() : String + } enum FruitType { + APPLE {static} + LEMON {static} diff --git a/servant/etc/servant.urm.puml b/servant/etc/servant.urm.puml index 48d48bd84..b7391f5dd 100644 --- a/servant/etc/servant.urm.puml +++ b/servant/etc/servant.urm.puml @@ -1,24 +1,5 @@ @startuml package com.iluwatar.servant { - class King { - - complimentReceived : boolean - - isDrunk : boolean - - isHappy : boolean - - isHungry : boolean - + King() - + changeMood() - + getDrink() - + getFed() - + getMood() : boolean - + receiveCompliments() - } - ~interface Royalty { - + changeMood() {abstract} - + getDrink() {abstract} - + getFed() {abstract} - + getMood() : boolean {abstract} - + receiveCompliments() {abstract} - } class Servant { + name : String + Servant(name : String) @@ -48,8 +29,27 @@ package com.iluwatar.servant { + main(args : String[]) {static} + scenario(servant : Servant, compliment : int) {static} } + class King { + - complimentReceived : boolean + - isDrunk : boolean + - isHappy : boolean + - isHungry : boolean + + King() + + changeMood() + + getDrink() + + getFed() + + getMood() : boolean + + receiveCompliments() + } + ~interface Royalty { + + changeMood() {abstract} + + getDrink() {abstract} + + getFed() {abstract} + + getMood() : boolean {abstract} + + receiveCompliments() {abstract} + } } App --> "-jenkins" Servant -King ..|> Royalty Queen ..|> Royalty +King ..|> Royalty @enduml \ No newline at end of file diff --git a/service-layer/etc/service-layer.urm.puml b/service-layer/etc/service-layer.urm.puml index c67ffa645..880384b9f 100644 --- a/service-layer/etc/service-layer.urm.puml +++ b/service-layer/etc/service-layer.urm.puml @@ -55,6 +55,9 @@ package com.iluwatar.servicelayer.magic { } } package com.iluwatar.servicelayer.wizard { + interface WizardDao { + + findByName(String) : Wizard {abstract} + } class Wizard { - id : Long - name : String @@ -74,9 +77,6 @@ package com.iluwatar.servicelayer.wizard { + WizardDaoImpl() + findByName(name : String) : Wizard } - interface WizardDao { - + findByName(String) : Wizard {abstract} - } } package com.iluwatar.servicelayer.app { class App { @@ -86,33 +86,14 @@ package com.iluwatar.servicelayer.app { + queryData() {static} } } -package com.iluwatar.servicelayer.spell { - class SpellDaoImpl { - + SpellDaoImpl() - + findByName(name : String) : Spell - } - class Spell { - - id : Long - - name : String - - spellbook : Spellbook - + Spell() - + Spell(name : String) - + getId() : Long - + getName() : String - + getSpellbook() : Spellbook - + setId(id : Long) - + setName(name : String) - + setSpellbook(spellbook : Spellbook) - + toString() : String - } - interface SpellDao { - + findByName(String) : Spell {abstract} - } -} package com.iluwatar.servicelayer.spellbook { interface SpellbookDao { + findByName(String) : Spellbook {abstract} } + class SpellbookDaoImpl { + + SpellbookDaoImpl() + + findByName(name : String) : Spellbook + } class Spellbook { - id : Long - name : String @@ -131,9 +112,28 @@ package com.iluwatar.servicelayer.spellbook { + setWizards(wizards : Set) + toString() : String } - class SpellbookDaoImpl { - + SpellbookDaoImpl() - + findByName(name : String) : Spellbook +} +package com.iluwatar.servicelayer.spell { + class SpellDaoImpl { + + SpellDaoImpl() + + findByName(name : String) : Spell + } + interface SpellDao { + + findByName(String) : Spell {abstract} + } + class Spell { + - id : Long + - name : String + - spellbook : Spellbook + + Spell() + + Spell(name : String) + + getId() : Long + + getName() : String + + getSpellbook() : Spellbook + + setId(id : Long) + + setName(name : String) + + setSpellbook(spellbook : Spellbook) + + toString() : String } } MagicServiceImpl --> "-wizardDao" WizardDao @@ -141,18 +141,18 @@ MagicServiceImpl --> "-spellbookDao" SpellbookDao MagicServiceImpl --> "-spellDao" SpellDao Spellbook --> "-spells" Spell Spellbook --> "-wizards" Wizard -Wizard --|> BaseEntity -SpellbookDao --|> Dao SpellDaoImpl ..|> SpellDao SpellDaoImpl --|> DaoBaseImpl -MagicServiceImpl ..|> MagicService -DaoBaseImpl ..|> Dao -WizardDaoImpl ..|> WizardDao -WizardDaoImpl --|> DaoBaseImpl -Spellbook --|> BaseEntity +SpellbookDao --|> Dao +WizardDao --|> Dao SpellbookDaoImpl ..|> SpellbookDao SpellbookDaoImpl --|> DaoBaseImpl -Spell --|> BaseEntity -WizardDao --|> Dao +MagicServiceImpl ..|> MagicService SpellDao --|> Dao +Spell --|> BaseEntity +Spellbook --|> BaseEntity +Wizard --|> BaseEntity +WizardDaoImpl ..|> WizardDao +WizardDaoImpl --|> DaoBaseImpl +DaoBaseImpl ..|> Dao @enduml \ No newline at end of file diff --git a/service-locator/etc/service-locator.urm.puml b/service-locator/etc/service-locator.urm.puml index 085b05b28..9e41e7f40 100644 --- a/service-locator/etc/service-locator.urm.puml +++ b/service-locator/etc/service-locator.urm.puml @@ -1,25 +1,10 @@ @startuml package com.iluwatar.servicelocator { - interface Service { - + execute() {abstract} - + getId() : int {abstract} - + getName() : String {abstract} - } - class InitContext { - + InitContext() - + lookup(serviceName : String) : Object - } class ServiceLocator { - serviceCache : ServiceCache {static} - ServiceLocator() + getService(serviceJndiName : String) : Service {static} } - class ServiceCache { - - serviceCache : Map - + ServiceCache() - + addService(newService : Service) - + getService(serviceName : String) : Service - } class App { + App() + main(args : String[]) {static} @@ -32,6 +17,21 @@ package com.iluwatar.servicelocator { + getId() : int + getName() : String } + class InitContext { + + InitContext() + + lookup(serviceName : String) : Object + } + class ServiceCache { + - serviceCache : Map + + ServiceCache() + + addService(newService : Service) + + getService(serviceName : String) : Service + } + interface Service { + + execute() {abstract} + + getId() : int {abstract} + + getName() : String {abstract} + } } ServiceLocator --> "-serviceCache" ServiceCache ServiceImpl ..|> Service diff --git a/singleton/etc/singleton.urm.puml b/singleton/etc/singleton.urm.puml index f5ec19879..a13091772 100644 --- a/singleton/etc/singleton.urm.puml +++ b/singleton/etc/singleton.urm.puml @@ -1,32 +1,32 @@ @startuml package com.iluwatar.singleton { + class App { + + App() + + main(args : String[]) {static} + } class ThreadSafeLazyLoadedIvoryTower { - instance : ThreadSafeLazyLoadedIvoryTower {static} - ThreadSafeLazyLoadedIvoryTower() + getInstance() : ThreadSafeLazyLoadedIvoryTower {static} } - -class HelperHolder { - + INSTANCE : InitializingOnDemandHolderIdiom {static} - - HelperHolder() - } - class App { - + App() - + main(args : String[]) {static} + class InitializingOnDemandHolderIdiom { + - InitializingOnDemandHolderIdiom() + + getInstance() : InitializingOnDemandHolderIdiom {static} } class ThreadSafeDoubleCheckLocking { - instance : ThreadSafeDoubleCheckLocking {static} - ThreadSafeDoubleCheckLocking() + getInstance() : ThreadSafeDoubleCheckLocking {static} } - class InitializingOnDemandHolderIdiom { - - InitializingOnDemandHolderIdiom() - + getInstance() : InitializingOnDemandHolderIdiom {static} - } class IvoryTower { - INSTANCE : IvoryTower {static} - IvoryTower() + getInstance() : IvoryTower {static} } + -class HelperHolder { + - INSTANCE : InitializingOnDemandHolderIdiom {static} + - HelperHolder() + } enum EnumIvoryTower { + INSTANCE {static} + toString() : String diff --git a/specification/etc/specification.urm.puml b/specification/etc/specification.urm.puml index 0009a1bcd..2f194d340 100644 --- a/specification/etc/specification.urm.puml +++ b/specification/etc/specification.urm.puml @@ -1,17 +1,5 @@ @startuml package com.iluwatar.specification.creature { - class Goblin { - + Goblin() - } - interface Creature { - + getColor() : Color {abstract} - + getMovement() : Movement {abstract} - + getName() : String {abstract} - + getSize() : Size {abstract} - } - class Troll { - + Troll() - } abstract class AbstractCreature { - color : Color - movement : Movement @@ -24,20 +12,41 @@ package com.iluwatar.specification.creature { + getSize() : Size + toString() : String } + class Troll { + + Troll() + } + class Octopus { + + Octopus() + } class Shark { + Shark() } + class Goblin { + + Goblin() + } class KillerBee { + KillerBee() } - class Octopus { - + Octopus() + interface Creature { + + getColor() : Color {abstract} + + getMovement() : Movement {abstract} + + getName() : String {abstract} + + getSize() : Size {abstract} } class Dragon { + Dragon() } } package com.iluwatar.specification.property { + enum Size { + + LARGE {static} + + NORMAL {static} + + SMALL {static} + - title : String + + toString() : String + + valueOf(name : String) : Size {static} + + values() : Size[] {static} + } enum Color { + DARK {static} + GREEN {static} @@ -57,15 +66,6 @@ package com.iluwatar.specification.property { + valueOf(name : String) : Movement {static} + values() : Movement[] {static} } - enum Size { - + LARGE {static} - + NORMAL {static} - + SMALL {static} - - title : String - + toString() : String - + valueOf(name : String) : Size {static} - + values() : Size[] {static} - } } package com.iluwatar.specification.app { class App { @@ -74,9 +74,9 @@ package com.iluwatar.specification.app { } } package com.iluwatar.specification.selector { - class SizeSelector { - - s : Size - + SizeSelector(s : Size) + class MovementSelector { + - m : Movement + + MovementSelector(m : Movement) + test(t : Creature) : boolean } class ColorSelector { @@ -84,9 +84,9 @@ package com.iluwatar.specification.selector { + ColorSelector(c : Color) + test(t : Creature) : boolean } - class MovementSelector { - - m : Movement - + MovementSelector(m : Movement) + class SizeSelector { + - s : Size + + SizeSelector(s : Size) + test(t : Creature) : boolean } } @@ -96,11 +96,11 @@ MovementSelector --> "-m" Movement AbstractCreature --> "-movement" Movement AbstractCreature --> "-size" Size ColorSelector --> "-c" Color -Goblin --|> AbstractCreature -Troll --|> AbstractCreature AbstractCreature ..|> Creature -Shark --|> AbstractCreature -KillerBee --|> AbstractCreature +Troll --|> AbstractCreature Octopus --|> AbstractCreature +Shark --|> AbstractCreature +Goblin --|> AbstractCreature +KillerBee --|> AbstractCreature Dragon --|> AbstractCreature @enduml \ No newline at end of file diff --git a/state/etc/state.urm.puml b/state/etc/state.urm.puml index a0951ff6e..378c11cd1 100644 --- a/state/etc/state.urm.puml +++ b/state/etc/state.urm.puml @@ -1,5 +1,19 @@ @startuml package com.iluwatar.state { + class PeacefulState { + - mammoth : Mammoth + + PeacefulState(mammoth : Mammoth) + + observe() + + onEnterState() + } + interface State { + + observe() {abstract} + + onEnterState() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } class AngryState { - mammoth : Mammoth + AngryState(mammoth : Mammoth) @@ -14,24 +28,10 @@ package com.iluwatar.state { + timePasses() + toString() : String } - interface State { - + observe() {abstract} - + onEnterState() {abstract} - } - class PeacefulState { - - mammoth : Mammoth - + PeacefulState(mammoth : Mammoth) - + observe() - + onEnterState() - } - class App { - + App() - + main(args : String[]) {static} - } } -PeacefulState --> "-mammoth" Mammoth AngryState --> "-mammoth" Mammoth +PeacefulState --> "-mammoth" Mammoth Mammoth --> "-state" State -AngryState ..|> State PeacefulState ..|> State +AngryState ..|> State @enduml \ No newline at end of file diff --git a/step-builder/etc/step-builder.urm.puml b/step-builder/etc/step-builder.urm.puml index cc1f88ef0..129ecbf30 100644 --- a/step-builder/etc/step-builder.urm.puml +++ b/step-builder/etc/step-builder.urm.puml @@ -1,8 +1,16 @@ @startuml package com.iluwatar.stepbuilder { + interface AbilityStep { + + noAbilities() : BuildStep {abstract} + + noMoreAbilities() : BuildStep {abstract} + + withAbility(String) : AbilityStep {abstract} + } interface BuildStep { + build() : Character {abstract} } + interface NameStep { + + name(String) : ClassStep {abstract} + } -class CharacterSteps { - abilities : List - fighterClass : String @@ -27,22 +35,6 @@ package com.iluwatar.stepbuilder { + App() + main(args : String[]) {static} } - interface ClassStep { - + fighterClass(String) : WeaponStep {abstract} - + wizardClass(String) : SpellStep {abstract} - } - interface WeaponStep { - + noWeapon() : BuildStep {abstract} - + withWeapon(String) : AbilityStep {abstract} - } - interface AbilityStep { - + noAbilities() : BuildStep {abstract} - + noMoreAbilities() : BuildStep {abstract} - + withAbility(String) : AbilityStep {abstract} - } - interface NameStep { - + name(String) : ClassStep {abstract} - } class CharacterStepBuilder { - CharacterStepBuilder() + newBuilder() : NameStep {static} @@ -73,13 +65,21 @@ package com.iluwatar.stepbuilder { + noSpell() : BuildStep {abstract} + withSpell(String) : AbilityStep {abstract} } + interface ClassStep { + + fighterClass(String) : WeaponStep {abstract} + + wizardClass(String) : SpellStep {abstract} + } + interface WeaponStep { + + noWeapon() : BuildStep {abstract} + + withWeapon(String) : AbilityStep {abstract} + } } App --+ CharacterStepBuilder WeaponStep ..+ CharacterStepBuilder -SpellStep ..+ CharacterStepBuilder -AbilityStep ..+ CharacterStepBuilder ClassStep ..+ CharacterStepBuilder +SpellStep ..+ CharacterStepBuilder CharacterSteps ..+ CharacterStepBuilder +AbilityStep ..+ CharacterStepBuilder NameStep ..+ CharacterStepBuilder BuildStep ..+ CharacterStepBuilder CharacterSteps ..|> NameStep diff --git a/strategy/etc/strategy.urm.puml b/strategy/etc/strategy.urm.puml index 2cc072863..db86b9ed6 100644 --- a/strategy/etc/strategy.urm.puml +++ b/strategy/etc/strategy.urm.puml @@ -6,28 +6,28 @@ package com.iluwatar.strategy { + changeStrategy(strategy : DragonSlayingStrategy) + goToBattle() } - class SpellStrategy { - + SpellStrategy() + interface DragonSlayingStrategy { + + execute() {abstract} + } + class App { + + App() + + main(args : String[]) {static} + } + class MeleeStrategy { + + MeleeStrategy() + execute() } class ProjectileStrategy { + ProjectileStrategy() + execute() } - interface DragonSlayingStrategy { - + execute() {abstract} - } - class MeleeStrategy { - + MeleeStrategy() + class SpellStrategy { + + SpellStrategy() + execute() } - class App { - + App() - + main(args : String[]) {static} - } } DragonSlayer --> "-strategy" DragonSlayingStrategy -SpellStrategy ..|> DragonSlayingStrategy -ProjectileStrategy ..|> DragonSlayingStrategy MeleeStrategy ..|> DragonSlayingStrategy +ProjectileStrategy ..|> DragonSlayingStrategy +SpellStrategy ..|> DragonSlayingStrategy @enduml \ No newline at end of file diff --git a/template-method/etc/template-method.urm.puml b/template-method/etc/template-method.urm.puml index c98287bc6..c2c6045fe 100644 --- a/template-method/etc/template-method.urm.puml +++ b/template-method/etc/template-method.urm.puml @@ -1,17 +1,17 @@ @startuml package com.iluwatar.templatemethod { - class SubtleMethod { - + SubtleMethod() - # confuseTarget(target : String) - # pickTarget() : String - # stealTheItem(target : String) - } class HitAndRunMethod { + HitAndRunMethod() # confuseTarget(target : String) # pickTarget() : String # stealTheItem(target : String) } + class HalflingThief { + - method : StealingMethod + + HalflingThief(method : StealingMethod) + + changeMethod(method : StealingMethod) + + steal() + } abstract class StealingMethod { + StealingMethod() # confuseTarget(String) {abstract} @@ -19,18 +19,18 @@ package com.iluwatar.templatemethod { + steal() # stealTheItem(String) {abstract} } + class SubtleMethod { + + SubtleMethod() + # confuseTarget(target : String) + # pickTarget() : String + # stealTheItem(target : String) + } class App { + App() + main(args : String[]) {static} } - class HalflingThief { - - method : StealingMethod - + HalflingThief(method : StealingMethod) - + changeMethod(method : StealingMethod) - + steal() - } } HalflingThief --> "-method" StealingMethod -SubtleMethod --|> StealingMethod HitAndRunMethod --|> StealingMethod +SubtleMethod --|> StealingMethod @enduml \ No newline at end of file diff --git a/thread-pool/etc/thread-pool.urm.puml b/thread-pool/etc/thread-pool.urm.puml index 2b73e2d53..421ea1dce 100644 --- a/thread-pool/etc/thread-pool.urm.puml +++ b/thread-pool/etc/thread-pool.urm.puml @@ -1,14 +1,5 @@ @startuml package com.iluwatar.threadpool { - class Worker { - - task : Task - + Worker(task : Task) - + run() - } - class App { - + App() - + main(args : String[]) {static} - } abstract class Task { - ID_GENERATOR : AtomicInteger {static} - id : int @@ -18,18 +9,27 @@ package com.iluwatar.threadpool { + getTimeMs() : int + toString() : String } - class PotatoPeelingTask { - - TIME_PER_POTATO : int {static} - + PotatoPeelingTask(numPotatoes : int) - + toString() : String - } class CoffeeMakingTask { - TIME_PER_CUP : int {static} + CoffeeMakingTask(numCups : int) + toString() : String } + class Worker { + - task : Task + + Worker(task : Task) + + run() + } + class PotatoPeelingTask { + - TIME_PER_POTATO : int {static} + + PotatoPeelingTask(numPotatoes : int) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } } Worker --> "-task" Task -PotatoPeelingTask --|> Task CoffeeMakingTask --|> Task +PotatoPeelingTask --|> Task @enduml \ No newline at end of file diff --git a/tolerant-reader/etc/tolerant-reader.urm.puml b/tolerant-reader/etc/tolerant-reader.urm.puml index 9e2bc83b3..f203f8257 100644 --- a/tolerant-reader/etc/tolerant-reader.urm.puml +++ b/tolerant-reader/etc/tolerant-reader.urm.puml @@ -1,11 +1,5 @@ @startuml package com.iluwatar.tolerantreader { - class RainbowFishSerializer { - - RainbowFishSerializer() - + readV1(filename : String) : RainbowFish {static} - + writeV1(rainbowFish : RainbowFish, filename : String) {static} - + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} - } class RainbowFish { - age : int - lengthMeters : int @@ -18,6 +12,10 @@ package com.iluwatar.tolerantreader { + getName() : String + getWeightTons() : int } + class App { + + App() + + main(args : String[]) {static} + } class RainbowFishV2 { - angry : boolean - hungry : boolean @@ -29,9 +27,11 @@ package com.iluwatar.tolerantreader { + getHungry() : boolean + getSleeping() : boolean } - class App { - + App() - + main(args : String[]) {static} + class RainbowFishSerializer { + - RainbowFishSerializer() + + readV1(filename : String) : RainbowFish {static} + + writeV1(rainbowFish : RainbowFish, filename : String) {static} + + writeV2(rainbowFish : RainbowFishV2, filename : String) {static} } } RainbowFishV2 --|> RainbowFish diff --git a/twin/etc/twin.urm.puml b/twin/etc/twin.urm.puml index b95325abb..b4bd52468 100644 --- a/twin/etc/twin.urm.puml +++ b/twin/etc/twin.urm.puml @@ -1,9 +1,10 @@ @startuml package com.iluwatar.twin { - class App { - + App() - + main(args : String[]) {static} - - waiting() {static} + abstract class GameItem { + + GameItem() + + click() {abstract} + + doDraw() {abstract} + + draw() } class BallItem { - isSuspended : boolean @@ -14,11 +15,10 @@ package com.iluwatar.twin { + move() + setTwin(twin : BallThread) } - abstract class GameItem { - + GameItem() - + click() {abstract} - + doDraw() {abstract} - + draw() + class App { + + App() + + main(args : String[]) {static} + - waiting() {static} } } BallItem --|> GameItem diff --git a/value-object/etc/value-object.urm.puml b/value-object/etc/value-object.urm.puml index 223f91957..6c81de017 100644 --- a/value-object/etc/value-object.urm.puml +++ b/value-object/etc/value-object.urm.puml @@ -1,9 +1,5 @@ @startuml package com.iluwatar.value.object { - class App { - + App() - + main(args : String[]) {static} - } class HeroStat { - intelligence : int - luck : int @@ -17,5 +13,9 @@ package com.iluwatar.value.object { + toString() : String + valueOf(strength : int, intelligence : int, luck : int) : HeroStat {static} } + class App { + + App() + + main(args : String[]) {static} + } } @enduml \ No newline at end of file diff --git a/visitor/etc/visitor.urm.puml b/visitor/etc/visitor.urm.puml index 3f5689f71..1f28e4e2f 100644 --- a/visitor/etc/visitor.urm.puml +++ b/visitor/etc/visitor.urm.puml @@ -1,57 +1,57 @@ @startuml package com.iluwatar.visitor { + class Commander { + + Commander(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + interface UnitVisitor { + + visitCommander(Commander) {abstract} + + visitSergeant(Sergeant) {abstract} + + visitSoldier(Soldier) {abstract} + } class CommanderVisitor { + CommanderVisitor() + visitCommander(commander : Commander) + visitSergeant(sergeant : Sergeant) + visitSoldier(soldier : Soldier) } + class Soldier { + + Soldier(children : Unit[]) + + accept(visitor : UnitVisitor) + + toString() : String + } + class App { + + App() + + main(args : String[]) {static} + } + abstract class Unit { + - children : Unit[] + + Unit(children : Unit[]) + + accept(visitor : UnitVisitor) + } + class SoldierVisitor { + + SoldierVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } + class SergeantVisitor { + + SergeantVisitor() + + visitCommander(commander : Commander) + + visitSergeant(sergeant : Sergeant) + + visitSoldier(soldier : Soldier) + } class Sergeant { + Sergeant(children : Unit[]) + accept(visitor : UnitVisitor) + toString() : String } - class Commander { - + Commander(children : Unit[]) - + accept(visitor : UnitVisitor) - + toString() : String - } - abstract class Unit { - - children : Unit[] - + Unit(children : Unit[]) - + accept(visitor : UnitVisitor) - } - class Soldier { - + Soldier(children : Unit[]) - + accept(visitor : UnitVisitor) - + toString() : String - } - class SergeantVisitor { - + SergeantVisitor() - + visitCommander(commander : Commander) - + visitSergeant(sergeant : Sergeant) - + visitSoldier(soldier : Soldier) - } - interface UnitVisitor { - + visitCommander(Commander) {abstract} - + visitSergeant(Sergeant) {abstract} - + visitSoldier(Soldier) {abstract} - } - class App { - + App() - + main(args : String[]) {static} - } - class SoldierVisitor { - + SoldierVisitor() - + visitCommander(commander : Commander) - + visitSergeant(sergeant : Sergeant) - + visitSoldier(soldier : Soldier) - } } -CommanderVisitor ..|> UnitVisitor -Sergeant --|> Unit Commander --|> Unit +CommanderVisitor ..|> UnitVisitor Soldier --|> Unit -SergeantVisitor ..|> UnitVisitor SoldierVisitor ..|> UnitVisitor +SergeantVisitor ..|> UnitVisitor +Sergeant --|> Unit @enduml \ No newline at end of file From dbd605e3786eb0d90ded55436976784865328d53 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:04 +0100 Subject: [PATCH 068/145] Changes based on latest code review --- .../com/iluwatar/event/asynchronous/App.java | 29 +++++++++++-------- .../iluwatar/event/asynchronous/Event.java | 17 ++++++++++- .../event/asynchronous/EventManager.java | 13 ++++++--- .../src/main/java/config.properties | 2 +- 4 files changed, 43 insertions(+), 18 deletions(-) diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index f951af07c..65ae02e56 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -138,11 +138,10 @@ public class App { Scanner s = new Scanner(System.in); int option = -1; - while (option != 5) { + while (option != 4) { System.out.println("Hello. Would you like to boil some eggs?"); - System.out.println( - "(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); - System.out.print("Choose [1,2,3,4,5]: "); + System.out.println("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT"); + System.out.print("Choose [1,2,3,4]: "); option = s.nextInt(); if (option == 1) { @@ -181,16 +180,22 @@ public class App { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Which egg?: "); - int eventId = s.nextInt(); - try { - eventManager.status(eventId); - } catch (EventDoesNotExistException e) { - System.out.println(e.getMessage()); + s.nextLine(); + System.out.print("Just one egg (O) OR all of them (A) ?: "); + String eggChoice = s.nextLine(); + + if (eggChoice.equalsIgnoreCase("O")) { + System.out.print("Which egg?: "); + int eventId = s.nextInt(); + try { + eventManager.status(eventId); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (eggChoice.equalsIgnoreCase("A")) { + eventManager.statusOfAllEvents(); } } else if (option == 4) { - eventManager.statusOfAllEvents(); - } else if (option == 5) { eventManager.shutdown(); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 1cb04acdc..c2e14ad68 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -25,13 +25,25 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; + private boolean isSynchronous; private Thread thread; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(final int eventId, final int eventTime) { + /** + * + * @param eventId event ID + * @param eventTime event time + * @param isSynchronous is of synchronous type + */ + public Event(final int eventId, final int eventTime, final boolean isSynchronous) { this.eventId = eventId; this.eventTime = eventTime; + this.isSynchronous = isSynchronous; + } + + public boolean isSynchronous() { + return isSynchronous; } @Override @@ -42,6 +54,9 @@ public class Event implements IEvent, Runnable { @Override public void stop() { + if (null == thread) { + return; + } thread.interrupt(); } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index e65816cec..dae995e38 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -61,11 +61,12 @@ public class EventManager implements ThreadCompleteListener { */ public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } + + int eventId = createEvent(eventTime, true); currentlyRunningSyncEvent = eventId; return eventId; @@ -80,10 +81,11 @@ public class EventManager implements ThreadCompleteListener { * @throws LongRunningEventException Long running events are not allowed in the app. */ public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - return createEvent(eventTime); + return createEvent(eventTime, false); } - private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + private int createEvent(int eventTime, boolean isSynchronous) + throws MaxNumOfEventsAllowedException, LongRunningEventException { if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } @@ -95,7 +97,7 @@ public class EventManager implements ThreadCompleteListener { int newEventId = generateId(); - Event newEvent = new Event(newEventId, eventTime); + Event newEvent = new Event(newEventId, eventTime, isSynchronous); newEvent.addListener(this); eventPool.put(newEventId, newEvent); @@ -194,6 +196,9 @@ public class EventManager implements ThreadCompleteListener { @Override public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); + if (eventPool.get(eventId).isSynchronous()) { + currentlyRunningSyncEvent = -1; + } eventPool.remove(eventId); } diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index edbe90e05..7216f665d 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=NO \ No newline at end of file +INTERACTIVE_MODE=YES \ No newline at end of file From 371b262a5135484d83efb0666269af0a55c6f185 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:23 +0100 Subject: [PATCH 069/145] Changes based on latest code review --- event-asynchronous/src/main/java/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index 7216f665d..edbe90e05 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 6ed842e58bab97bc7eebe47575838a0b1f0910d7 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 09:35:17 +0200 Subject: [PATCH 070/145] Caching pattern: Refactor shutdown hook to use method reference --- caching/src/main/java/com/iluwatar/caching/AppManager.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 2967c759f..13ef4d4dd 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -64,12 +64,7 @@ public final class AppManager { public static void initCachingPolicy(CachingPolicy policy) { cachingPolicy = policy; if (cachingPolicy == CachingPolicy.BEHIND) { - Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() { - @Override - public void run() { - CacheStore.flushCache(); - } - })); + Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache)); } CacheStore.clearCache(); } From 865f788612b4c5fd7fd978e346c47174750f8af4 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 09:38:23 +0200 Subject: [PATCH 071/145] Caching pattern: Refactor LRU cache to avoid NPE and unnecessary cache lookup --- caching/src/main/java/com/iluwatar/caching/LruCache.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 5c5549afd..4527b5548 100644 --- a/caching/src/main/java/com/iluwatar/caching/LruCache.java +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -136,10 +136,11 @@ public class LruCache { * Invalidate cache for user */ public void invalidate(String userId) { - System.out.println("# " + userId + " has been updated! Removing older version from cache..."); - Node toBeRemoved = cache.get(userId); - remove(toBeRemoved); - cache.remove(userId); + Node toBeRemoved = cache.remove(userId); + if (toBeRemoved != null) { + System.out.println("# " + userId + " has been updated! Removing older version from cache..."); + remove(toBeRemoved); + } } public boolean isFull() { From e3355d76d1930f935d902f54d4cf7fae4f94bd24 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 20:33:24 +0200 Subject: [PATCH 072/145] Caching pattern: Style fix for null check --- .../src/main/java/com/iluwatar/caching/CacheStore.java | 4 ++-- caching/src/main/java/com/iluwatar/caching/DbManager.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index 5903f8219..dd425645e 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -40,7 +40,7 @@ public class CacheStore { * Init cache capacity */ public static void initCapacity(int capacity) { - if (null == cache) { + if (cache == null) { cache = new LruCache(capacity); } else { cache.setCapacity(capacity); @@ -121,7 +121,7 @@ public class CacheStore { * Clears cache */ public static void clearCache() { - if (null != cache) { + if (cache != null) { cache.clear(); } } diff --git a/caching/src/main/java/com/iluwatar/caching/DbManager.java b/caching/src/main/java/com/iluwatar/caching/DbManager.java index c12461d0c..455302106 100644 --- a/caching/src/main/java/com/iluwatar/caching/DbManager.java +++ b/caching/src/main/java/com/iluwatar/caching/DbManager.java @@ -82,7 +82,7 @@ public final class DbManager { } return null; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { @@ -106,7 +106,7 @@ public final class DbManager { virtualDB.put(userAccount.getUserId(), userAccount); return; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { @@ -126,7 +126,7 @@ public final class DbManager { virtualDB.put(userAccount.getUserId(), userAccount); return; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { @@ -148,7 +148,7 @@ public final class DbManager { virtualDB.put(userAccount.getUserId(), userAccount); return; } - if (null == db) { + if (db == null) { try { connect(); } catch (ParseException e) { From b31edda3cf9b376423e1a6cec0b7803a9597e522 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 20:41:26 +0200 Subject: [PATCH 073/145] Caching pattern: Implementation of Cache-Aside pattern --- .../main/java/com/iluwatar/caching/App.java | 23 +++++++++++++++ .../java/com/iluwatar/caching/AppManager.java | 29 +++++++++++++++++++ .../java/com/iluwatar/caching/CacheStore.java | 21 ++++++++++++++ .../com/iluwatar/caching/CachingPolicy.java | 2 +- .../com/iluwatar/caching/CachingTest.java | 5 ++++ 5 files changed, 79 insertions(+), 1 deletion(-) diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 8e5a84085..4a3a9ab42 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -74,6 +74,7 @@ public class App { app.useReadAndWriteThroughStrategy(); app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteBehindStrategy(); + app.useCacheAsideStategy(); } /** @@ -136,4 +137,26 @@ public class App { AppManager.find("004"); System.out.println(AppManager.printCacheContent()); } + + /** + * Cache-Aside + */ + public void useCacheAsideStategy() { + System.out.println("# CachingPolicy.ASIDE"); + AppManager.initCachingPolicy(CachingPolicy.ASIDE); + System.out.println(AppManager.printCacheContent()); + + UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food."); + UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats."); + UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); + AppManager.save(userAccount3); + AppManager.save(userAccount4); + AppManager.save(userAccount5); + + System.out.println(AppManager.printCacheContent()); + AppManager.find("003"); + System.out.println(AppManager.printCacheContent()); + AppManager.find("004"); + System.out.println(AppManager.printCacheContent()); + } } diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 13ef4d4dd..2d6a424a3 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -81,6 +81,8 @@ public final class AppManager { return CacheStore.readThrough(userId); } else if (cachingPolicy == CachingPolicy.BEHIND) { return CacheStore.readThroughWithWriteBackPolicy(userId); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + return findAside(userId); } return null; } @@ -95,10 +97,37 @@ public final class AppManager { CacheStore.writeAround(userAccount); } else if (cachingPolicy == CachingPolicy.BEHIND) { CacheStore.writeBehind(userAccount); + } else if (cachingPolicy == CachingPolicy.ASIDE) { + saveAside(userAccount); } } public static String printCacheContent() { return CacheStore.print(); } + + /** + * Cache-Aside save user account helper + */ + private static void saveAside(UserAccount userAccount) { + DbManager.updateDb(userAccount); + CacheStore.invalidate(userAccount.getUserId()); + } + + /** + * Cache-Aside find user account helper + */ + private static UserAccount findAside(String userId) { + UserAccount userAccount = CacheStore.get(userId); + if (userAccount != null) { + return userAccount; + } + + userAccount = DbManager.readFromDb(userId); + if (userAccount != null) { + CacheStore.set(userId, userAccount); + } + + return userAccount; + } } diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index dd425645e..c2b95a1bd 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -153,4 +153,25 @@ public class CacheStore { sb.append("----\n"); return sb.toString(); } + + /** + * Delegate to backing cache store + */ + public static UserAccount get(String userId) { + return cache.get(userId); + } + + /** + * Delegate to backing cache store + */ + public static void set(String userId, UserAccount userAccount) { + cache.set(userId, userAccount); + } + + /** + * Delegate to backing cache store + */ + public static void invalidate(String userId) { + cache.invalidate(userId); + } } diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java index 490113baa..0c907fe88 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -28,7 +28,7 @@ package com.iluwatar.caching; * */ public enum CachingPolicy { - THROUGH("through"), AROUND("around"), BEHIND("behind"); + THROUGH("through"), AROUND("around"), BEHIND("behind"), ASIDE("aside"); private String policy; diff --git a/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java index 19262a3b6..9d2299902 100644 --- a/caching/src/test/java/com/iluwatar/caching/CachingTest.java +++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java @@ -60,4 +60,9 @@ public class CachingTest { public void testReadThroughAndWriteBehindStrategy() { app.useReadThroughAndWriteBehindStrategy(); } + + @Test + public void testCacheAsideStrategy() { + app.useCacheAsideStategy(); + } } From c5e6dcc11b59db979751c07316fda6daccb9730d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 30 Sep 2016 19:38:28 +0300 Subject: [PATCH 074/145] Reached milestone 1.13.0 --- abstract-document/pom.xml | 2 +- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- aggregator-microservices/aggregator-service/pom.xml | 2 +- aggregator-microservices/information-microservice/pom.xml | 2 +- aggregator-microservices/inventory-microservice/pom.xml | 2 +- aggregator-microservices/pom.xml | 2 +- api-gateway/api-gateway-service/pom.xml | 2 +- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- caching/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- data-mapper/pom.xml | 2 +- decorator/pom.xml | 2 +- delegation/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- event-driven-architecture/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-kit/pom.xml | 2 +- factory-method/pom.xml | 2 +- feature-toggle/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- hexagonal/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- monad/pom.xml | 2 +- monostate/pom.xml | 2 +- multiton/pom.xml | 2 +- mute-idiom/pom.xml | 2 +- mutex/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- page-object/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- producer-consumer/pom.xml | 2 +- promise/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- publish-subscribe/pom.xml | 2 +- reactor/pom.xml | 2 +- reader-writer-lock/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- semaphore/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- twin/pom.xml | 2 +- value-object/pom.xml | 2 +- visitor/pom.xml | 2 +- 92 files changed, 95 insertions(+), 95 deletions(-) diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index b7a348d26..be9aa68b9 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 abstract-document diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index ec0f700a5..1d54f0c86 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index 2c99796f4..d8414f85d 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 adapter diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index 169d0da94..46d7d72c7 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index 986540344..5d2d97fe6 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index f8844dd39..8869596f8 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index 6e9496ba3..85d5ebd92 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 aggregator-microservices diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index 87a1ebb5e..e24613b66 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 api-gateway-service diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index c29932fae..18771f343 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 48bfff9c3..9222751d9 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index 24e9663aa..a5d5aad37 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index d912df965..156804c5f 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index a7a3883c8..d1a3f0e88 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 bridge diff --git a/builder/pom.xml b/builder/pom.xml index 6c171bef4..e63f68b96 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index c6f7e0c37..72ad13d03 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 business-delegate diff --git a/caching/pom.xml b/caching/pom.xml index c20842a89..3aa5c689e 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 caching diff --git a/callback/pom.xml b/callback/pom.xml index 4fe17d143..fa8b61478 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 callback diff --git a/chain/pom.xml b/chain/pom.xml index ee3b92401..7550e61c0 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 chain diff --git a/command/pom.xml b/command/pom.xml index a27a56e54..cfe40ce56 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 command diff --git a/composite/pom.xml b/composite/pom.xml index ace29d8d1..aace20b3a 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 composite diff --git a/dao/pom.xml b/dao/pom.xml index f64eff8bc..85338a263 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 dao diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 7d1b83469..80679127f 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 data-mapper diff --git a/decorator/pom.xml b/decorator/pom.xml index 7ba2a4ee8..9991fdfe3 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 decorator diff --git a/delegation/pom.xml b/delegation/pom.xml index 2fca225b8..ccfe13348 100644 --- a/delegation/pom.xml +++ b/delegation/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index 88ccdd2d4..e04add78b 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 239bcf870..882ac218e 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -27,7 +27,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 4f31b2e7e..9be737c4c 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index b7de6e01b..91294b125 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 event-aggregator diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml index 99ed39891..972b4982c 100644 --- a/event-driven-architecture/pom.xml +++ b/event-driven-architecture/pom.xml @@ -31,7 +31,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 event-driven-architecture diff --git a/execute-around/pom.xml b/execute-around/pom.xml index 60bb6d0ab..e46835d68 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 execute-around diff --git a/facade/pom.xml b/facade/pom.xml index e0e0f4a4d..db7de2cd6 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 facade diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml index 6c936de5c..00f9285d5 100644 --- a/factory-kit/pom.xml +++ b/factory-kit/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 factory-kit diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 5081ecf99..8d720eb28 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 factory-method diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 78c182af9..6db201fb3 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index ca5e115d0..0dfe5d7a9 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index c07cca157..27ee4f408 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index 959fa5548..a153f97cc 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index 2a448cffe..ba6dcbe83 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index 076dfb44d..afe4e8c74 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 half-sync-half-async diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index ce1a7049a..004554e6b 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 hexagonal diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index 6bb6f95cc..d30d28e1e 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 9f5dd31f3..051ec1c15 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index 484030a33..98c9f8834 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 iterator diff --git a/layers/pom.xml b/layers/pom.xml index bf155b655..5af14908b 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index fe6d44c78..056d3b579 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index a6cdd028e..a01cf152a 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 mediator diff --git a/memento/pom.xml b/memento/pom.xml index a320f186c..7c58fb361 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index cbeba7a75..2b3c2e01e 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 3c01f6e8d..32b9d5b75 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 0aad02b27..6a4335280 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 model-view-presenter model-view-presenter diff --git a/monad/pom.xml b/monad/pom.xml index 7f128272d..4e9b36842 100644 --- a/monad/pom.xml +++ b/monad/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 monad diff --git a/monostate/pom.xml b/monostate/pom.xml index 64ee52abe..80f8f919b 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 monostate diff --git a/multiton/pom.xml b/multiton/pom.xml index 0b835ed4d..1d097a064 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 multiton diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml index c138c5ed4..00a4dff5d 100644 --- a/mute-idiom/pom.xml +++ b/mute-idiom/pom.xml @@ -21,7 +21,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 mute-idiom diff --git a/mutex/pom.xml b/mutex/pom.xml index 852006cd4..529652ea6 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 mutex diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index 119416694..763440cc4 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index 46b281468..ca2f4af86 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index dccaf64a3..f7bf1a1b5 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index d416f2a72..bc99d852c 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 naked-objects @@ -367,17 +367,17 @@ ${project.groupId} naked-objects-dom - 1.13.0-SNAPSHOT + 1.13.0 ${project.groupId} naked-objects-fixture - 1.13.0-SNAPSHOT + 1.13.0 ${project.groupId} naked-objects-webapp - 1.13.0-SNAPSHOT + 1.13.0 diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index 762438aca..64aa036e3 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0-SNAPSHOT + 1.13.0 naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index b0adad1af..2cd0a14f4 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index 666242d1d..dcb293bb3 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 object-pool diff --git a/observer/pom.xml b/observer/pom.xml index 847a7ea89..8fb018214 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 observer diff --git a/page-object/pom.xml b/page-object/pom.xml index e6f888b05..93a81d648 100644 --- a/page-object/pom.xml +++ b/page-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 page-object diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 9717f5e13..5cf56e3e9 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 poison-pill diff --git a/pom.xml b/pom.xml index fcf1671c8..ebb75aa9d 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 4.0.0 com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 pom 2014 diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index 20484245b..b26d31420 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 private-class-data diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index a72aa2c96..da849a753 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 producer-consumer diff --git a/promise/pom.xml b/promise/pom.xml index ca12515ee..e8b129b00 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 promise diff --git a/property/pom.xml b/property/pom.xml index 1e9d0f0c2..fc000c41b 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 property diff --git a/prototype/pom.xml b/prototype/pom.xml index e8bb4303a..308bf91db 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index f16736a2c..8b4191e98 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 proxy diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml index 6db6b3538..73476a966 100644 --- a/publish-subscribe/pom.xml +++ b/publish-subscribe/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 publish-subscribe diff --git a/reactor/pom.xml b/reactor/pom.xml index c06584c6f..62ffe374d 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 reactor diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index 2df3d5b14..417877b9c 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 reader-writer-lock diff --git a/repository/pom.xml b/repository/pom.xml index 56a448ec5..304a620ee 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index 7b411f46c..549fe3cbd 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 resource-acquisition-is-initialization diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 1b3bf8b5d..5f0f1c9cd 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 semaphore diff --git a/servant/pom.xml b/servant/pom.xml index c235b005c..145b726c3 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index 23d788595..da8fe17fe 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index 0f17b3dcc..e0eff0e38 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index ab9405c5e..d549025fd 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 singleton diff --git a/specification/pom.xml b/specification/pom.xml index 03c66540d..e3fcdd86e 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 specification diff --git a/state/pom.xml b/state/pom.xml index 134fbabe1..3c481c4e1 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 96098eabc..7d39dad57 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0-SNAPSHOT + 1.13.0 step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index e6fa58081..3902a1871 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index ee533df8f..1eeb18369 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index e7fa43103..19031bc23 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index e5dd3ba88..101673c9f 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 tolerant-reader diff --git a/twin/pom.xml b/twin/pom.xml index 1eb854b87..df839bf58 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 twin diff --git a/value-object/pom.xml b/value-object/pom.xml index ec8de1681..c2012093a 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 value-object diff --git a/visitor/pom.xml b/visitor/pom.xml index 54a90e184..39c7b45e5 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.13.0 visitor From 4ca205c03cc03ea5bc80fe27c0a4594f0d087582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Fri, 30 Sep 2016 19:40:28 +0300 Subject: [PATCH 075/145] Set version number for next development iteration --- abstract-document/pom.xml | 2 +- abstract-factory/pom.xml | 2 +- adapter/pom.xml | 2 +- aggregator-microservices/aggregator-service/pom.xml | 2 +- aggregator-microservices/information-microservice/pom.xml | 2 +- aggregator-microservices/inventory-microservice/pom.xml | 2 +- aggregator-microservices/pom.xml | 2 +- api-gateway/api-gateway-service/pom.xml | 2 +- api-gateway/image-microservice/pom.xml | 2 +- api-gateway/pom.xml | 2 +- api-gateway/price-microservice/pom.xml | 2 +- async-method-invocation/pom.xml | 2 +- bridge/pom.xml | 2 +- builder/pom.xml | 2 +- business-delegate/pom.xml | 2 +- caching/pom.xml | 2 +- callback/pom.xml | 2 +- chain/pom.xml | 2 +- command/pom.xml | 2 +- composite/pom.xml | 2 +- dao/pom.xml | 2 +- data-mapper/pom.xml | 2 +- decorator/pom.xml | 2 +- delegation/pom.xml | 2 +- dependency-injection/pom.xml | 2 +- double-checked-locking/pom.xml | 2 +- double-dispatch/pom.xml | 2 +- event-aggregator/pom.xml | 2 +- event-driven-architecture/pom.xml | 2 +- execute-around/pom.xml | 2 +- facade/pom.xml | 2 +- factory-kit/pom.xml | 2 +- factory-method/pom.xml | 2 +- feature-toggle/pom.xml | 2 +- fluentinterface/pom.xml | 2 +- flux/pom.xml | 2 +- flyweight/pom.xml | 2 +- front-controller/pom.xml | 2 +- half-sync-half-async/pom.xml | 2 +- hexagonal/pom.xml | 2 +- intercepting-filter/pom.xml | 2 +- interpreter/pom.xml | 2 +- iterator/pom.xml | 2 +- layers/pom.xml | 2 +- lazy-loading/pom.xml | 2 +- mediator/pom.xml | 2 +- memento/pom.xml | 2 +- message-channel/pom.xml | 2 +- model-view-controller/pom.xml | 2 +- model-view-presenter/pom.xml | 2 +- monad/pom.xml | 2 +- monostate/pom.xml | 2 +- multiton/pom.xml | 2 +- mute-idiom/pom.xml | 2 +- mutex/pom.xml | 2 +- naked-objects/dom/pom.xml | 2 +- naked-objects/fixture/pom.xml | 2 +- naked-objects/integtests/pom.xml | 2 +- naked-objects/pom.xml | 8 ++++---- naked-objects/webapp/pom.xml | 2 +- null-object/pom.xml | 2 +- object-pool/pom.xml | 2 +- observer/pom.xml | 2 +- page-object/pom.xml | 2 +- poison-pill/pom.xml | 2 +- pom.xml | 2 +- private-class-data/pom.xml | 2 +- producer-consumer/pom.xml | 2 +- promise/pom.xml | 2 +- property/pom.xml | 2 +- prototype/pom.xml | 2 +- proxy/pom.xml | 2 +- publish-subscribe/pom.xml | 2 +- reactor/pom.xml | 2 +- reader-writer-lock/pom.xml | 2 +- repository/pom.xml | 2 +- resource-acquisition-is-initialization/pom.xml | 2 +- semaphore/pom.xml | 2 +- servant/pom.xml | 2 +- service-layer/pom.xml | 2 +- service-locator/pom.xml | 2 +- singleton/pom.xml | 2 +- specification/pom.xml | 2 +- state/pom.xml | 2 +- step-builder/pom.xml | 2 +- strategy/pom.xml | 2 +- template-method/pom.xml | 2 +- thread-pool/pom.xml | 2 +- tolerant-reader/pom.xml | 2 +- twin/pom.xml | 2 +- value-object/pom.xml | 2 +- visitor/pom.xml | 2 +- 92 files changed, 95 insertions(+), 95 deletions(-) diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml index be9aa68b9..1ab6d5a2b 100644 --- a/abstract-document/pom.xml +++ b/abstract-document/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT abstract-document diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml index 1d54f0c86..5c3d4d16a 100644 --- a/abstract-factory/pom.xml +++ b/abstract-factory/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT abstract-factory diff --git a/adapter/pom.xml b/adapter/pom.xml index d8414f85d..83a930653 100644 --- a/adapter/pom.xml +++ b/adapter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT adapter diff --git a/aggregator-microservices/aggregator-service/pom.xml b/aggregator-microservices/aggregator-service/pom.xml index 46d7d72c7..520aefaa8 100644 --- a/aggregator-microservices/aggregator-service/pom.xml +++ b/aggregator-microservices/aggregator-service/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml index 5d2d97fe6..4cd1916c6 100644 --- a/aggregator-microservices/information-microservice/pom.xml +++ b/aggregator-microservices/information-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml index 8869596f8..1bf655b45 100644 --- a/aggregator-microservices/inventory-microservice/pom.xml +++ b/aggregator-microservices/inventory-microservice/pom.xml @@ -29,7 +29,7 @@ aggregator-microservices com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml index 85d5ebd92..cedc35eec 100644 --- a/aggregator-microservices/pom.xml +++ b/aggregator-microservices/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 aggregator-microservices diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml index e24613b66..e2ab9e6d9 100644 --- a/api-gateway/api-gateway-service/pom.xml +++ b/api-gateway/api-gateway-service/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 api-gateway-service diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml index 18771f343..db25b947b 100644 --- a/api-gateway/image-microservice/pom.xml +++ b/api-gateway/image-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml index 9222751d9..94f91cabb 100644 --- a/api-gateway/pom.xml +++ b/api-gateway/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 api-gateway diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml index a5d5aad37..ce47e9db0 100644 --- a/api-gateway/price-microservice/pom.xml +++ b/api-gateway/price-microservice/pom.xml @@ -29,7 +29,7 @@ api-gateway com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml index 156804c5f..e374b4753 100644 --- a/async-method-invocation/pom.xml +++ b/async-method-invocation/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT async-method-invocation diff --git a/bridge/pom.xml b/bridge/pom.xml index d1a3f0e88..4960dbd08 100644 --- a/bridge/pom.xml +++ b/bridge/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT bridge diff --git a/builder/pom.xml b/builder/pom.xml index e63f68b96..030990fa2 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT builder diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml index 72ad13d03..2b0ce9119 100644 --- a/business-delegate/pom.xml +++ b/business-delegate/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT business-delegate diff --git a/caching/pom.xml b/caching/pom.xml index 3aa5c689e..07347e5c7 100644 --- a/caching/pom.xml +++ b/caching/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT caching diff --git a/callback/pom.xml b/callback/pom.xml index fa8b61478..70dd8e5e2 100644 --- a/callback/pom.xml +++ b/callback/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT callback diff --git a/chain/pom.xml b/chain/pom.xml index 7550e61c0..5593d3e73 100644 --- a/chain/pom.xml +++ b/chain/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT chain diff --git a/command/pom.xml b/command/pom.xml index cfe40ce56..406c6d100 100644 --- a/command/pom.xml +++ b/command/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT command diff --git a/composite/pom.xml b/composite/pom.xml index aace20b3a..48d693d2f 100644 --- a/composite/pom.xml +++ b/composite/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT composite diff --git a/dao/pom.xml b/dao/pom.xml index 85338a263..e414f2b27 100644 --- a/dao/pom.xml +++ b/dao/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT dao diff --git a/data-mapper/pom.xml b/data-mapper/pom.xml index 80679127f..eb342e9eb 100644 --- a/data-mapper/pom.xml +++ b/data-mapper/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT data-mapper diff --git a/decorator/pom.xml b/decorator/pom.xml index 9991fdfe3..36c3eef01 100644 --- a/decorator/pom.xml +++ b/decorator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT decorator diff --git a/delegation/pom.xml b/delegation/pom.xml index ccfe13348..ed5f6608e 100644 --- a/delegation/pom.xml +++ b/delegation/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/dependency-injection/pom.xml b/dependency-injection/pom.xml index e04add78b..ab4619b63 100644 --- a/dependency-injection/pom.xml +++ b/dependency-injection/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT dependency-injection diff --git a/double-checked-locking/pom.xml b/double-checked-locking/pom.xml index 882ac218e..27c97479a 100644 --- a/double-checked-locking/pom.xml +++ b/double-checked-locking/pom.xml @@ -27,7 +27,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT double-checked-locking diff --git a/double-dispatch/pom.xml b/double-dispatch/pom.xml index 9be737c4c..5093cced8 100644 --- a/double-dispatch/pom.xml +++ b/double-dispatch/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT double-dispatch diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml index 91294b125..29d99ca2b 100644 --- a/event-aggregator/pom.xml +++ b/event-aggregator/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT event-aggregator diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml index 972b4982c..149ca7409 100644 --- a/event-driven-architecture/pom.xml +++ b/event-driven-architecture/pom.xml @@ -31,7 +31,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT event-driven-architecture diff --git a/execute-around/pom.xml b/execute-around/pom.xml index e46835d68..115c201cb 100644 --- a/execute-around/pom.xml +++ b/execute-around/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT execute-around diff --git a/facade/pom.xml b/facade/pom.xml index db7de2cd6..7816d51e6 100644 --- a/facade/pom.xml +++ b/facade/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT facade diff --git a/factory-kit/pom.xml b/factory-kit/pom.xml index 00f9285d5..32705cc46 100644 --- a/factory-kit/pom.xml +++ b/factory-kit/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT factory-kit diff --git a/factory-method/pom.xml b/factory-method/pom.xml index 8d720eb28..74dc91a02 100644 --- a/factory-method/pom.xml +++ b/factory-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT factory-method diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml index 6db201fb3..eebc433e8 100644 --- a/feature-toggle/pom.xml +++ b/feature-toggle/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/fluentinterface/pom.xml b/fluentinterface/pom.xml index 0dfe5d7a9..b438103e4 100644 --- a/fluentinterface/pom.xml +++ b/fluentinterface/pom.xml @@ -29,7 +29,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT 4.0.0 diff --git a/flux/pom.xml b/flux/pom.xml index 27ee4f408..50f5d395a 100644 --- a/flux/pom.xml +++ b/flux/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT flux diff --git a/flyweight/pom.xml b/flyweight/pom.xml index a153f97cc..eb168c486 100644 --- a/flyweight/pom.xml +++ b/flyweight/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT flyweight diff --git a/front-controller/pom.xml b/front-controller/pom.xml index ba6dcbe83..c08ed8d0f 100644 --- a/front-controller/pom.xml +++ b/front-controller/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT front-controller diff --git a/half-sync-half-async/pom.xml b/half-sync-half-async/pom.xml index afe4e8c74..d22e65a5e 100644 --- a/half-sync-half-async/pom.xml +++ b/half-sync-half-async/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT half-sync-half-async diff --git a/hexagonal/pom.xml b/hexagonal/pom.xml index 004554e6b..c489c45c4 100644 --- a/hexagonal/pom.xml +++ b/hexagonal/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT hexagonal diff --git a/intercepting-filter/pom.xml b/intercepting-filter/pom.xml index d30d28e1e..487ac1c14 100644 --- a/intercepting-filter/pom.xml +++ b/intercepting-filter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT intercepting-filter diff --git a/interpreter/pom.xml b/interpreter/pom.xml index 051ec1c15..e2c8f4d4d 100644 --- a/interpreter/pom.xml +++ b/interpreter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT interpreter diff --git a/iterator/pom.xml b/iterator/pom.xml index 98c9f8834..6d589b4a7 100644 --- a/iterator/pom.xml +++ b/iterator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT iterator diff --git a/layers/pom.xml b/layers/pom.xml index 5af14908b..811f5e050 100644 --- a/layers/pom.xml +++ b/layers/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT com.iluwatar.layers layers diff --git a/lazy-loading/pom.xml b/lazy-loading/pom.xml index 056d3b579..d70587395 100644 --- a/lazy-loading/pom.xml +++ b/lazy-loading/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT lazy-loading diff --git a/mediator/pom.xml b/mediator/pom.xml index a01cf152a..e3b227237 100644 --- a/mediator/pom.xml +++ b/mediator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT mediator diff --git a/memento/pom.xml b/memento/pom.xml index 7c58fb361..3dc7e5f52 100644 --- a/memento/pom.xml +++ b/memento/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT memento diff --git a/message-channel/pom.xml b/message-channel/pom.xml index 2b3c2e01e..7c1825b5e 100644 --- a/message-channel/pom.xml +++ b/message-channel/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT message-channel diff --git a/model-view-controller/pom.xml b/model-view-controller/pom.xml index 32b9d5b75..38e756313 100644 --- a/model-view-controller/pom.xml +++ b/model-view-controller/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT model-view-controller diff --git a/model-view-presenter/pom.xml b/model-view-presenter/pom.xml index 6a4335280..6fb7ab31e 100644 --- a/model-view-presenter/pom.xml +++ b/model-view-presenter/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT model-view-presenter model-view-presenter diff --git a/monad/pom.xml b/monad/pom.xml index 4e9b36842..482cf4e4d 100644 --- a/monad/pom.xml +++ b/monad/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT monad diff --git a/monostate/pom.xml b/monostate/pom.xml index 80f8f919b..d42870a17 100644 --- a/monostate/pom.xml +++ b/monostate/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT monostate diff --git a/multiton/pom.xml b/multiton/pom.xml index 1d097a064..28e208d0c 100644 --- a/multiton/pom.xml +++ b/multiton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT multiton diff --git a/mute-idiom/pom.xml b/mute-idiom/pom.xml index 00a4dff5d..cae190e86 100644 --- a/mute-idiom/pom.xml +++ b/mute-idiom/pom.xml @@ -21,7 +21,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT mute-idiom diff --git a/mutex/pom.xml b/mutex/pom.xml index 529652ea6..b53587aa3 100644 --- a/mutex/pom.xml +++ b/mutex/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT mutex diff --git a/naked-objects/dom/pom.xml b/naked-objects/dom/pom.xml index 763440cc4..7e16ca000 100644 --- a/naked-objects/dom/pom.xml +++ b/naked-objects/dom/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-dom diff --git a/naked-objects/fixture/pom.xml b/naked-objects/fixture/pom.xml index ca2f4af86..f31b10fe2 100644 --- a/naked-objects/fixture/pom.xml +++ b/naked-objects/fixture/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-fixture diff --git a/naked-objects/integtests/pom.xml b/naked-objects/integtests/pom.xml index f7bf1a1b5..547e88268 100644 --- a/naked-objects/integtests/pom.xml +++ b/naked-objects/integtests/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-integtests diff --git a/naked-objects/pom.xml b/naked-objects/pom.xml index bc99d852c..344a234ac 100644 --- a/naked-objects/pom.xml +++ b/naked-objects/pom.xml @@ -15,7 +15,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT naked-objects @@ -367,17 +367,17 @@ ${project.groupId} naked-objects-dom - 1.13.0 + 1.14.0-SNAPSHOT ${project.groupId} naked-objects-fixture - 1.13.0 + 1.14.0-SNAPSHOT ${project.groupId} naked-objects-webapp - 1.13.0 + 1.14.0-SNAPSHOT diff --git a/naked-objects/webapp/pom.xml b/naked-objects/webapp/pom.xml index 64aa036e3..1a729ade9 100644 --- a/naked-objects/webapp/pom.xml +++ b/naked-objects/webapp/pom.xml @@ -16,7 +16,7 @@ com.iluwatar naked-objects - 1.13.0 + 1.14.0-SNAPSHOT naked-objects-webapp diff --git a/null-object/pom.xml b/null-object/pom.xml index 2cd0a14f4..832337772 100644 --- a/null-object/pom.xml +++ b/null-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT null-object diff --git a/object-pool/pom.xml b/object-pool/pom.xml index dcb293bb3..927570b66 100644 --- a/object-pool/pom.xml +++ b/object-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT object-pool diff --git a/observer/pom.xml b/observer/pom.xml index 8fb018214..48909e1aa 100644 --- a/observer/pom.xml +++ b/observer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT observer diff --git a/page-object/pom.xml b/page-object/pom.xml index 93a81d648..207d04092 100644 --- a/page-object/pom.xml +++ b/page-object/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT page-object diff --git a/poison-pill/pom.xml b/poison-pill/pom.xml index 5cf56e3e9..432f19547 100644 --- a/poison-pill/pom.xml +++ b/poison-pill/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT poison-pill diff --git a/pom.xml b/pom.xml index ebb75aa9d..144b8efff 100644 --- a/pom.xml +++ b/pom.xml @@ -23,7 +23,7 @@ 4.0.0 com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT pom 2014 diff --git a/private-class-data/pom.xml b/private-class-data/pom.xml index b26d31420..1dc545cb1 100644 --- a/private-class-data/pom.xml +++ b/private-class-data/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT private-class-data diff --git a/producer-consumer/pom.xml b/producer-consumer/pom.xml index da849a753..5ac018f51 100644 --- a/producer-consumer/pom.xml +++ b/producer-consumer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT producer-consumer diff --git a/promise/pom.xml b/promise/pom.xml index e8b129b00..30a719f13 100644 --- a/promise/pom.xml +++ b/promise/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT promise diff --git a/property/pom.xml b/property/pom.xml index fc000c41b..af0c9f288 100644 --- a/property/pom.xml +++ b/property/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT property diff --git a/prototype/pom.xml b/prototype/pom.xml index 308bf91db..d0c03c0b6 100644 --- a/prototype/pom.xml +++ b/prototype/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT prototype diff --git a/proxy/pom.xml b/proxy/pom.xml index 8b4191e98..7484fc47e 100644 --- a/proxy/pom.xml +++ b/proxy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT proxy diff --git a/publish-subscribe/pom.xml b/publish-subscribe/pom.xml index 73476a966..de5b6068b 100644 --- a/publish-subscribe/pom.xml +++ b/publish-subscribe/pom.xml @@ -28,7 +28,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT publish-subscribe diff --git a/reactor/pom.xml b/reactor/pom.xml index 62ffe374d..5bb577c42 100644 --- a/reactor/pom.xml +++ b/reactor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT reactor diff --git a/reader-writer-lock/pom.xml b/reader-writer-lock/pom.xml index 417877b9c..f10acb8d7 100644 --- a/reader-writer-lock/pom.xml +++ b/reader-writer-lock/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT reader-writer-lock diff --git a/repository/pom.xml b/repository/pom.xml index 304a620ee..45f735b7a 100644 --- a/repository/pom.xml +++ b/repository/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT repository diff --git a/resource-acquisition-is-initialization/pom.xml b/resource-acquisition-is-initialization/pom.xml index 549fe3cbd..ef5de7532 100644 --- a/resource-acquisition-is-initialization/pom.xml +++ b/resource-acquisition-is-initialization/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT resource-acquisition-is-initialization diff --git a/semaphore/pom.xml b/semaphore/pom.xml index 5f0f1c9cd..4ee0edb9f 100644 --- a/semaphore/pom.xml +++ b/semaphore/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT semaphore diff --git a/servant/pom.xml b/servant/pom.xml index 145b726c3..1e99bc09b 100644 --- a/servant/pom.xml +++ b/servant/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT servant diff --git a/service-layer/pom.xml b/service-layer/pom.xml index da8fe17fe..830ced4f6 100644 --- a/service-layer/pom.xml +++ b/service-layer/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT service-layer diff --git a/service-locator/pom.xml b/service-locator/pom.xml index e0eff0e38..894908f92 100644 --- a/service-locator/pom.xml +++ b/service-locator/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT service-locator diff --git a/singleton/pom.xml b/singleton/pom.xml index d549025fd..2ba9fe6d4 100644 --- a/singleton/pom.xml +++ b/singleton/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT singleton diff --git a/specification/pom.xml b/specification/pom.xml index e3fcdd86e..49e56f3c1 100644 --- a/specification/pom.xml +++ b/specification/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT specification diff --git a/state/pom.xml b/state/pom.xml index 3c481c4e1..ec932532d 100644 --- a/state/pom.xml +++ b/state/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT state diff --git a/step-builder/pom.xml b/step-builder/pom.xml index 7d39dad57..cd53119ff 100644 --- a/step-builder/pom.xml +++ b/step-builder/pom.xml @@ -30,7 +30,7 @@ java-design-patterns com.iluwatar - 1.13.0 + 1.14.0-SNAPSHOT step-builder diff --git a/strategy/pom.xml b/strategy/pom.xml index 3902a1871..e57f42e08 100644 --- a/strategy/pom.xml +++ b/strategy/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT strategy diff --git a/template-method/pom.xml b/template-method/pom.xml index 1eeb18369..2098a801f 100644 --- a/template-method/pom.xml +++ b/template-method/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT template-method diff --git a/thread-pool/pom.xml b/thread-pool/pom.xml index 19031bc23..e1f9a4e49 100644 --- a/thread-pool/pom.xml +++ b/thread-pool/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT thread-pool diff --git a/tolerant-reader/pom.xml b/tolerant-reader/pom.xml index 101673c9f..9668b5fb3 100644 --- a/tolerant-reader/pom.xml +++ b/tolerant-reader/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT tolerant-reader diff --git a/twin/pom.xml b/twin/pom.xml index df839bf58..56fe7bd8a 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT twin diff --git a/value-object/pom.xml b/value-object/pom.xml index c2012093a..156d5b969 100644 --- a/value-object/pom.xml +++ b/value-object/pom.xml @@ -30,7 +30,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT value-object diff --git a/visitor/pom.xml b/visitor/pom.xml index 39c7b45e5..e1d3ca6a1 100644 --- a/visitor/pom.xml +++ b/visitor/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0 + 1.14.0-SNAPSHOT visitor From 9512f3ec701a8236ee2bf07bedd9ff9cb15aa154 Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Mon, 3 Oct 2016 21:59:36 +0200 Subject: [PATCH 076/145] Closes #436. Adds criticism to service locator pattern. --- service-locator/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service-locator/README.md b/service-locator/README.md index 31d82b13f..75a00ca57 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -33,6 +33,13 @@ improves the performance of application to great extent. * lookups of services are done quite frequently * large number of services are being used +## Consequences + +* Violates Interface Segregation Principle (ISP) by providing pattern consumers with an access +to a number of services that they don't potentially need. +* Creates hidden dependencies that can break the clients at runtime. +* Limits object composability by stopping the clients to specify needed dependencies for different objects instantiation. + ## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) From 12544caa759fa1ac0fec7bd5bde5dae9d7a4a082 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:05:11 +0100 Subject: [PATCH 077/145] Changes based on review feedback. --- event-asynchronous/README.md | 2 +- .../com/iluwatar/event/asynchronous/App.java | 12 ++--- .../iluwatar/event/asynchronous/Event.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 52 +++++++++++++++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index dde434aba..ef35d0b38 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -3,7 +3,7 @@ layout: pattern title: Event-based Asynchronous folder: event-asynchronous permalink: /patterns/event-asynchronous/ -categories: Other +categories: Concurrency tags: - difficulty-intermediate - performance diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 65ae02e56..5a2565940 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -106,23 +106,23 @@ public class App { try { // Create an Asynchronous event. int aEventId = eventManager.createAsync(60); - System.out.println("Event [" + aEventId + "] has been created."); + System.out.println("Async Event [" + aEventId + "] has been created."); eventManager.start(aEventId); - System.out.println("Event [" + aEventId + "] has been started."); + System.out.println("Async Event [" + aEventId + "] has been started."); // Create a Synchronous event. int sEventId = eventManager.create(60); - System.out.println("Event [" + sEventId + "] has been created."); + System.out.println("Sync Event [" + sEventId + "] has been created."); eventManager.start(sEventId); - System.out.println("Event [" + sEventId + "] has been started."); + System.out.println("Sync Event [" + sEventId + "] has been started."); eventManager.status(aEventId); eventManager.status(sEventId); eventManager.cancel(aEventId); - System.out.println("Event [" + aEventId + "] has been stopped."); + System.out.println("Async Event [" + aEventId + "] has been stopped."); eventManager.cancel(sEventId); - System.out.println("Event [" + sEventId + "] has been stopped."); + System.out.println("Sync Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index c2e14ad68..5e557fff2 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -75,7 +75,7 @@ public class Event implements IEvent, Runnable { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - Thread.sleep(5000); // Sleep for 5 seconds. + Thread.sleep(1000); // Sleep for 1 second. } catch (InterruptedException e) { return; } diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 6565d5bad..92bacda99 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -79,4 +79,56 @@ public class EventAsynchronousTest { System.out.println(e.getMessage()); } } + + @Test + public void testFullSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int sEventId = eventManager.create(eventTime); + assertTrue(eventManager.getEventPool().size() == 1); + eventManager.start(sEventId); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + // properly. + while (System.currentTimeMillis() < endTime) { + } + + assertTrue(eventManager.getEventPool().size() == 0); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testFullAsynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int aEventId1 = eventManager.createAsync(eventTime); + int aEventId2 = eventManager.createAsync(eventTime); + int aEventId3 = eventManager.createAsync(eventTime); + assertTrue(eventManager.getEventPool().size() == 3); + + eventManager.start(aEventId1); + eventManager.start(aEventId2); + eventManager.start(aEventId3); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + // properly. + while (System.currentTimeMillis() < endTime) { + } + + assertTrue(eventManager.getEventPool().size() == 0); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } } From 622376e0fa3b1723b86853c4c8a882b6bd378cb2 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:16:23 +0100 Subject: [PATCH 078/145] Updated version snapshot to 1.14.0 --- event-asynchronous/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml index 60ab8f0aa..1bc4549d4 100644 --- a/event-asynchronous/pom.xml +++ b/event-asynchronous/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.14.0-SNAPSHOT event-asynchronous From 8f1758c28f663f73ea79be8d2a1575fcc4d9fffe Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:46:16 +0100 Subject: [PATCH 079/145] Alter JUnit tests to run in lesser time. --- .../event/asynchronous/EventAsynchronousTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 92bacda99..213439203 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -84,15 +84,16 @@ public class EventAsynchronousTest { public void testFullSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int sEventId = eventManager.create(eventTime); assertTrue(eventManager.getEventPool().size() == 1); eventManager.start(sEventId); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete - // properly. + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to + // complete + // properly. while (System.currentTimeMillis() < endTime) { } @@ -108,7 +109,7 @@ public class EventAsynchronousTest { public void testFullAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int aEventId1 = eventManager.createAsync(eventTime); int aEventId2 = eventManager.createAsync(eventTime); @@ -120,7 +121,7 @@ public class EventAsynchronousTest { eventManager.start(aEventId3); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete // properly. while (System.currentTimeMillis() < endTime) { } From eea8785a22527899da48c56f64b63aa0db1df1b5 Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Tue, 4 Oct 2016 14:34:01 +0200 Subject: [PATCH 080/145] Fixes #437. Adds criticism to Singleton pattern. --- singleton/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/singleton/README.md b/singleton/README.md index e1bb42a45..38a05349b 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -36,6 +36,11 @@ Use the Singleton pattern when * [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) +## Consequences + +* Violates Single Responsibility Principle (SRP) by controlling their own creation and lifecycle. +* Encourages using a global shared instance which prevents an object and resources used by this object from being deallocated. + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) From 3a9d1684fd19b0987d8230031a6b05efd6abb0cc Mon Sep 17 00:00:00 2001 From: igeligel Date: Tue, 4 Oct 2016 22:33:06 +0200 Subject: [PATCH 081/145] Initial commit of the object mother #498 Add a simple king and queen classes which implement the behaviour of the royalty interface. Also wrote the object mother of royalty objects which is final so you can just call the static methods in it to create objects with a specific state to use them fast in tests. The tests are already created for testing the behaviour and the type of the objects which are created by the object mother. I also created the UML diagrams via object aid and updated the readme. --- object-mother/README.md | 31 ++++++ object-mother/etc/object-mother.png | Bin 0 -> 19705 bytes object-mother/etc/object-mother.ucls | 56 +++++++++++ object-mother/pom.xml | 48 ++++++++++ .../java/com/iluwatar/objectmother/King.java | 66 +++++++++++++ .../java/com/iluwatar/objectmother/Queen.java | 69 ++++++++++++++ .../com/iluwatar/objectmother/Royalty.java | 33 +++++++ .../objectmother/RoyaltyObjectMother.java | 83 ++++++++++++++++ .../test/RoyaltyObjectMotherTest.java | 89 ++++++++++++++++++ 9 files changed, 475 insertions(+) create mode 100644 object-mother/README.md create mode 100644 object-mother/etc/object-mother.png create mode 100644 object-mother/etc/object-mother.ucls create mode 100644 object-mother/pom.xml create mode 100644 object-mother/src/main/java/com/iluwatar/objectmother/King.java create mode 100644 object-mother/src/main/java/com/iluwatar/objectmother/Queen.java create mode 100644 object-mother/src/main/java/com/iluwatar/objectmother/Royalty.java create mode 100644 object-mother/src/main/java/com/iluwatar/objectmother/RoyaltyObjectMother.java create mode 100644 object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java diff --git a/object-mother/README.md b/object-mother/README.md new file mode 100644 index 000000000..9188c8b41 --- /dev/null +++ b/object-mother/README.md @@ -0,0 +1,31 @@ +--- +layout: pattern +title: Object Mother +folder: object-mother +permalink: /patterns/object-mother/ +pumlid: +categories: Creational +tags: + - Java + - Difficulty-Beginner +--- + +## Object Mother +Define a factory of immutable content with separated builder and factory interfaces. + +![alt text](./etc/object-mother.png "Object Mother") + +## Applicability +Use the Object Mother pattern when + +* You want consistent objects over several tests +* you want to reduce code for creation of objects in tests +* every test should run with fresh data + +## Credits + +* [Answer by David Brown](http://stackoverflow.com/questions/923319/what-is-an-objectmother) to the stackoverflow question: [What is an ObjectMother?](http://stackoverflow.com/questions/923319/what-is-an-objectmother) + +* [c2wiki - Object Mother](http://c2.com/cgi/wiki?ObjectMother) + +* [Nat Pryce - Test Data Builders: an alternative to the Object Mother pattern](http://www.natpryce.com/articles/000714.html) \ No newline at end of file diff --git a/object-mother/etc/object-mother.png b/object-mother/etc/object-mother.png new file mode 100644 index 0000000000000000000000000000000000000000..807343d9f9e42c2b3f306beb5c8e2199bbedafb1 GIT binary patch literal 19705 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 literal 0 HcmV?d00001 diff --git a/object-mother/etc/object-mother.ucls b/object-mother/etc/object-mother.ucls new file mode 100644 index 000000000..ef6cee5ef --- /dev/null +++ b/object-mother/etc/object-mother.ucls @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/object-mother/pom.xml b/object-mother/pom.xml new file mode 100644 index 000000000..91bdff8e2 --- /dev/null +++ b/object-mother/pom.xml @@ -0,0 +1,48 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.14.0-SNAPSHOT + + object-mother + + + junit + junit + test + + + org.mockito + mockito-core + test + + + \ No newline at end of file diff --git a/object-mother/src/main/java/com/iluwatar/objectmother/King.java b/object-mother/src/main/java/com/iluwatar/objectmother/King.java new file mode 100644 index 000000000..544b0bacb --- /dev/null +++ b/object-mother/src/main/java/com/iluwatar/objectmother/King.java @@ -0,0 +1,66 @@ +/** + * The MIT License + * Copyright (c) 2016 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.objectmother; + +public class King implements Royalty { + boolean isDrunk = false; + boolean isHappy = false; + + @Override + public void makeDrunk() { + isDrunk = true; + } + + @Override + public void makeSober() { + isDrunk = false; + } + + @Override + public void makeHappy() { + isHappy = true; + } + + @Override + public void makeUnhappy() { + isHappy = false; + } + + public boolean isHappy() { + return isHappy; + } + + /** + * Method to flirt to a queen. + * @param queen Queen which should be flirted. + */ + public void flirt(Queen queen) { + boolean flirtStatus = queen.getFlirted(this); + if (flirtStatus == false) { + this.makeUnhappy(); + } else { + this.makeHappy(); + } + + } +} diff --git a/object-mother/src/main/java/com/iluwatar/objectmother/Queen.java b/object-mother/src/main/java/com/iluwatar/objectmother/Queen.java new file mode 100644 index 000000000..3e18fbf3a --- /dev/null +++ b/object-mother/src/main/java/com/iluwatar/objectmother/Queen.java @@ -0,0 +1,69 @@ +/** + * The MIT License + * Copyright (c) 2016 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.objectmother; + +public class Queen implements Royalty { + private boolean isDrunk = false; + private boolean isHappy = false; + private boolean isFlirty = false; + + @Override + public void makeDrunk() { + isDrunk = true; + } + + @Override + public void makeSober() { + isDrunk = false; + } + + @Override + public void makeHappy() { + isHappy = true; + } + + @Override + public void makeUnhappy() { + isHappy = false; + } + + public boolean isFlirty() { + return isFlirty; + } + + public void setFlirtiness(boolean flirtiness) { + this.isFlirty = flirtiness; + } + + /** + * Method which is called when the king is flirting to a queen. + * @param king King who initialized the flirt. + * @return A value which describes if the flirt was successful or not. + */ + public boolean getFlirted(King king) { + if (this.isFlirty && king.isHappy && !king.isDrunk) { + return true; + } + return false; + } +} diff --git a/object-mother/src/main/java/com/iluwatar/objectmother/Royalty.java b/object-mother/src/main/java/com/iluwatar/objectmother/Royalty.java new file mode 100644 index 000000000..2bebc0939 --- /dev/null +++ b/object-mother/src/main/java/com/iluwatar/objectmother/Royalty.java @@ -0,0 +1,33 @@ +/** + * The MIT License + * Copyright (c) 2016 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.objectmother; + +public interface Royalty { + void makeDrunk(); + + void makeSober(); + + void makeHappy(); + + void makeUnhappy(); +} diff --git a/object-mother/src/main/java/com/iluwatar/objectmother/RoyaltyObjectMother.java b/object-mother/src/main/java/com/iluwatar/objectmother/RoyaltyObjectMother.java new file mode 100644 index 000000000..624a29132 --- /dev/null +++ b/object-mother/src/main/java/com/iluwatar/objectmother/RoyaltyObjectMother.java @@ -0,0 +1,83 @@ +/** + * The MIT License + * Copyright (c) 2016 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.objectmother; + +public final class RoyaltyObjectMother { + + /** + * Method to create a sober and unhappy king. The standard paramters are set. + * @return An instance of {@link com.iluwatar.objectmother.King} with the standard properties. + */ + public static King createSoberUnhappyKing() { + return new King(); + } + + /** + * Method of the object mother to create a drunk king. + * @return A drunk {@link com.iluwatar.objectmother.King}. + */ + public static King createDrunkKing() { + King king = new King(); + king.makeDrunk(); + return king; + } + + /** + * Method to create a happy king. + * @return A happy {@link com.iluwatar.objectmother.King}. + */ + public static King createHappyKing() { + King king = new King(); + king.makeHappy(); + return king; + } + + /** + * Method to create a happy and drunk king. + * @return A drunk and happy {@link com.iluwatar.objectmother.King}. + */ + public static King createHappyDrunkKing() { + King king = new King(); + king.makeHappy(); + king.makeDrunk(); + return king; + } + + /** + * Method to create a flirty queen. + * @return A flirty {@link com.iluwatar.objectmother.Queen}. + */ + public static Queen createFlirtyQueen() { + Queen queen = new Queen(); + queen.setFlirtiness(true); + return queen; + } + + /** + * Method to create a not flirty queen. + * @return A not flirty {@link com.iluwatar.objectmother.Queen}. + */ + public static Queen createNotFlirtyQueen() { + return new Queen(); + } +} diff --git a/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java b/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java new file mode 100644 index 000000000..feba71a1b --- /dev/null +++ b/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java @@ -0,0 +1,89 @@ +/** + * The MIT License + * Copyright (c) 2016 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.objectmother.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.iluwatar.objectmother.King; +import com.iluwatar.objectmother.Queen; +import com.iluwatar.objectmother.Royalty; +import com.iluwatar.objectmother.RoyaltyObjectMother; + +public class RoyaltyObjectMotherTest { + + @Test + public void unsuccessfulKingFlirt() { + King soberUnhappyKing = RoyaltyObjectMother.createSoberUnhappyKing(); + Queen flirtyQueen = RoyaltyObjectMother.createFlirtyQueen(); + soberUnhappyKing.flirt(flirtyQueen); + assertFalse(soberUnhappyKing.isHappy()); + } + + @Test + public void queenIsBlockingFlirtCauseDrunkKing() { + King soberUnhappyKing = RoyaltyObjectMother.createDrunkKing(); + Queen notFlirtyQueen = RoyaltyObjectMother.createNotFlirtyQueen(); + soberUnhappyKing.flirt(notFlirtyQueen); + assertFalse(soberUnhappyKing.isHappy()); + } + + @Test + public void queenIsBlockingFlirt() { + King soberUnhappyKing = RoyaltyObjectMother.createHappyKing(); + Queen notFlirtyQueen = RoyaltyObjectMother.createNotFlirtyQueen(); + soberUnhappyKing.flirt(notFlirtyQueen); + assertFalse(soberUnhappyKing.isHappy()); + } + + @Test + public void successfullKingFlirt() { + King soberUnhappyKing = RoyaltyObjectMother.createHappyKing(); + Queen flirtyQueen = RoyaltyObjectMother.createFlirtyQueen(); + soberUnhappyKing.flirt(flirtyQueen); + assertTrue(soberUnhappyKing.isHappy()); + } + + @Test + public void testQueenType() { + Royalty flirtyQueen = RoyaltyObjectMother.createFlirtyQueen(); + Royalty notFlirtyQueen = RoyaltyObjectMother.createNotFlirtyQueen(); + assertEquals(flirtyQueen.getClass(), Queen.class); + assertEquals(notFlirtyQueen.getClass(), Queen.class); + } + + @Test + public void testKingType() { + Royalty drunkKing = RoyaltyObjectMother.createDrunkKing(); + Royalty happyDrunkKing = RoyaltyObjectMother.createHappyDrunkKing(); + Royalty happyKing = RoyaltyObjectMother.createHappyKing(); + Royalty soberUnhappyKing = RoyaltyObjectMother.createSoberUnhappyKing(); + assertEquals(drunkKing.getClass(), King.class); + assertEquals(happyDrunkKing.getClass(), King.class); + assertEquals(happyKing.getClass(), King.class); + assertEquals(soberUnhappyKing.getClass(), King.class); + } +} From 863ea7538156398eba1d179e36e9379e5eeef968 Mon Sep 17 00:00:00 2001 From: igeligel Date: Thu, 6 Oct 2016 20:12:43 +0200 Subject: [PATCH 082/145] Add PlantUML file for the UML diagram #498 --- object-mother/etc/object-mother.urm.puml | 45 ++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 object-mother/etc/object-mother.urm.puml diff --git a/object-mother/etc/object-mother.urm.puml b/object-mother/etc/object-mother.urm.puml new file mode 100644 index 000000000..9b26450f0 --- /dev/null +++ b/object-mother/etc/object-mother.urm.puml @@ -0,0 +1,45 @@ +@startuml +package com.iluwatar.objectmother { + class RoyaltyObjectMother { + + RoyaltyObjectMother() + + createDrunkKing() : King {static} + + createFlirtyQueen() : Queen {static} + + createHappyDrunkKing() : King {static} + + createHappyKing() : King {static} + + createNotFlirtyQueen() : Queen {static} + + createSoberUnhappyKing() : King {static} + } + class Queen { + - isDrunk : boolean + - isFlirty : boolean + - isHappy : boolean + + Queen() + + getFlirted(king : King) : boolean + + isFlirty() : boolean + + makeDrunk() + + makeHappy() + + makeSober() + + makeUnhappy() + + setFlirtiness(flirtiness : boolean) + } + interface Royalty { + + makeDrunk() {abstract} + + makeHappy() {abstract} + + makeSober() {abstract} + + makeUnhappy() {abstract} + } + class King { + ~ isDrunk : boolean + ~ isHappy : boolean + + King() + + flirt(queen : Queen) + + isHappy() : boolean + + makeDrunk() + + makeHappy() + + makeSober() + + makeUnhappy() + } +} +Queen ..|> Royalty +King ..|> Royalty +@enduml \ No newline at end of file From f114b5b957c8ef432a504b24a56efa36599a9017 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Sun, 9 Oct 2016 12:43:43 +0200 Subject: [PATCH 083/145] Caching pattern: Documentation and diagram --- caching/README.md | 1 + caching/etc/caching.png | Bin 56234 -> 114847 bytes caching/etc/caching.ucls | 190 ++++++++---------- .../main/java/com/iluwatar/caching/App.java | 23 ++- .../com/iluwatar/caching/CachingPolicy.java | 2 +- .../java/com/iluwatar/caching/LruCache.java | 3 - .../com/iluwatar/caching/UserAccount.java | 2 - 7 files changed, 101 insertions(+), 120 deletions(-) diff --git a/caching/README.md b/caching/README.md index 6432ffcea..b48d060f0 100644 --- a/caching/README.md +++ b/caching/README.md @@ -27,3 +27,4 @@ Use the Caching pattern(s) when * [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) * [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) +* [Cache-Aside](https://msdn.microsoft.com/en-us/library/dn589799.aspx) diff --git a/caching/etc/caching.png b/caching/etc/caching.png index 6b3b2d0556c51b7c593028b2c9120a00c47716c1..b6ed703ab8b7dfce5e5d82f097dac534828fc93a 100644 GIT binary patch literal 114847 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

    $0ITcj(KRIbdLxc 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 diff --git a/caching/etc/caching.ucls b/caching/etc/caching.ucls index 815a62ec9..a058277ea 100644 --- a/caching/etc/caching.ucls +++ b/caching/etc/caching.ucls @@ -1,106 +1,90 @@ - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 4a3a9ab42..1b1b5e1ca 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -26,22 +26,23 @@ package com.iluwatar.caching; * * The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing * the resources immediately after their use. The resources retain their identity, are kept in some - * fast-access storage, and are re-used to avoid having to acquire them again. There are three main - * caching strategies/techniques in this pattern; each with their own pros and cons. They are: + * fast-access storage, and are re-used to avoid having to acquire them again. There are four main + * caching strategies/techniques in this pattern; each with their own pros and cons. They are; * write-through which writes data to the cache and DB in a single transaction, - * write-around which writes data immediately into the DB instead of the cache, and + * write-around which writes data immediately into the DB instead of the cache, * write-behind which writes data into the cache initially whilst the data is only - * written into the DB when the cache is full. The read-through strategy is also - * included in the mentioned three strategies -- returns data from the cache to the caller if - * it exists else queries from DB and stores it into the cache for future use. These - * strategies determine when the data in the cache should be written back to the backing store (i.e. - * Database) and help keep both data sources synchronized/up-to-date. This pattern can improve - * performance and also helps to maintain consistency between data held in the cache and the data in - * the underlying data store. + * written into the DB when the cache is full, and cache-aside which pushes the + * responsibility of keeping the data synchronized in both data sources to the application itself. + * The read-through strategy is also included in the mentioned four strategies -- + * returns data from the cache to the caller if it exists else queries from DB and + * stores it into the cache for future use. These strategies determine when the data in the cache + * should be written back to the backing store (i.e. Database) and help keep both data sources + * synchronized/up-to-date. This pattern can improve performance and also helps to maintain + * consistency between data held in the cache and the data in the underlying data store. *

    * In this example, the user account ({@link UserAccount}) entity is used as the underlying * application data. The cache itself is implemented as an internal (Java) data structure. It adopts - * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The three + * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four * strategies are individually tested. The testing of the cache is restricted towards saving and * querying of user accounts from the underlying data store ( {@link DbManager}). The main class ( * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java index 0c907fe88..5ad32f699 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -24,7 +24,7 @@ package com.iluwatar.caching; /** * - * Enum class containing the three caching strategies implemented in the pattern. + * Enum class containing the four caching strategies implemented in the pattern. * */ public enum CachingPolicy { diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 4527b5548..00e27ccf1 100644 --- a/caching/src/main/java/com/iluwatar/caching/LruCache.java +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -73,7 +73,6 @@ public class LruCache { } /** - * * Remove node from linked list. */ public void remove(Node node) { @@ -90,7 +89,6 @@ public class LruCache { } /** - * * Move node to the front of the list. */ public void setHead(Node node) { @@ -161,7 +159,6 @@ public class LruCache { } /** - * * Returns cache data in list form. */ public List getCacheDataInListForm() { diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java index e2444ad72..657f12fa3 100644 --- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java +++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java @@ -23,9 +23,7 @@ package com.iluwatar.caching; /** - * * Entity class (stored in cache and DB) used in the application. - * */ public class UserAccount { private String userId; From 85060784a7758af064aa883d66f029dd292ea9e8 Mon Sep 17 00:00:00 2001 From: dzmitryh Date: Sat, 15 Oct 2016 14:27:15 +0300 Subject: [PATCH 084/145] End process logic clause has been corrected. --- .../async/method/invocation/ThreadAsyncExecutor.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java index 7f96d9ab7..5a10232f0 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java @@ -57,12 +57,10 @@ public class ThreadAsyncExecutor implements AsyncExecutor { @Override public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { - if (asyncResult.isCompleted()) { - return asyncResult.getValue(); - } else { + if (!asyncResult.isCompleted()) { asyncResult.await(); - return asyncResult.getValue(); } + return asyncResult.getValue(); } /** From 37b930c3b75ebc17ac46f6ce9f93ef1ed8435aeb Mon Sep 17 00:00:00 2001 From: dzmitryh Date: Sat, 15 Oct 2016 14:29:32 +0300 Subject: [PATCH 085/145] Unused import removed. --- .../async/method/invocation/ThreadAsyncExecutorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java index b4a23222a..09126e99f 100644 --- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java @@ -31,7 +31,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import static org.junit.Assert.*; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; import static org.mockito.internal.verification.VerificationModeFactory.times; From 986c529eb67e5eccd999e4bc13d268a8c5997fc7 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 21:29:03 +0100 Subject: [PATCH 086/145] Moved config into a separate dir --- event-asynchronous/src/main/java/config.properties | 1 - event-asynchronous/src/main/resources/config.properties | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/main/resources/config.properties diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties deleted file mode 100644 index edbe90e05..000000000 --- a/event-asynchronous/src/main/java/config.properties +++ /dev/null @@ -1 +0,0 @@ -INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties new file mode 100644 index 000000000..7216f665d --- /dev/null +++ b/event-asynchronous/src/main/resources/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=YES \ No newline at end of file From 70318123fe762d919eda221e08b0f7bf2db5b8dd Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 22:22:06 +0100 Subject: [PATCH 087/145] Changed config to non-interactive --- event-asynchronous/src/main/resources/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index 7216f665d..edbe90e05 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 99677867c66a318f4f41e714a7ed867131cfcdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 18 Oct 2016 07:51:37 +0300 Subject: [PATCH 088/145] Event Based Asynchronous pattern: Add missing license header and puml diagram --- .../etc/event-asynchronous.urm.puml | 65 +++++++++++++++++++ .../src/main/resources/config.properties | 23 +++++++ 2 files changed, 88 insertions(+) create mode 100644 event-asynchronous/etc/event-asynchronous.urm.puml diff --git a/event-asynchronous/etc/event-asynchronous.urm.puml b/event-asynchronous/etc/event-asynchronous.urm.puml new file mode 100644 index 000000000..c5b183187 --- /dev/null +++ b/event-asynchronous/etc/event-asynchronous.urm.puml @@ -0,0 +1,65 @@ +@startuml +package com.iluwatar.event.asynchronous { + class Event { + - eventId : int + - eventListener : ThreadCompleteListener + - eventTime : int + - isComplete : boolean + - isSynchronous : boolean + - thread : Thread + + Event(eventId : int, eventTime : int, isSynchronous : boolean) + + addListener(listener : ThreadCompleteListener) + - completed() + + isSynchronous() : boolean + + removeListener(listener : ThreadCompleteListener) + + run() + + start() + + status() + + stop() + } + interface ThreadCompleteListener { + + completedEventHandler(int) {abstract} + } + class EventManager { + + MAX_EVENT_TIME : int {static} + + MAX_ID : int {static} + + MAX_RUNNING_EVENTS : int {static} + + MIN_ID : int {static} + - currentlyRunningSyncEvent : int + - eventPool : Map + - rand : Random + + EventManager() + + cancel(eventId : int) + + completedEventHandler(eventId : int) + + create(eventTime : int) : int + + createAsync(eventTime : int) : int + - createEvent(eventTime : int, isSynchronous : boolean) : int + - generateId() : int + + getEventPool() : Map + + numOfCurrentlyRunningSyncEvent() : int + + shutdown() + + start(eventId : int) + + status(eventId : int) + + statusOfAllEvents() + } + class App { + + PROP_FILE_NAME : String {static} + ~ interactiveMode : boolean + + App() + + main(args : String[]) {static} + + quickRun() + + run() + + runInteractiveMode() + + setUp() + } + interface IEvent { + + start() {abstract} + + status() {abstract} + + stop() {abstract} + } +} +Event --> "-eventListener" ThreadCompleteListener +EventManager --+ Map +Event ..|> IEvent +EventManager ..|> ThreadCompleteListener +@enduml \ No newline at end of file diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index edbe90e05..d8bfa1f7e 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +1,24 @@ +# +# The MIT License +# Copyright (c) 2014 Ilkka Seppälä +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. +# + INTERACTIVE_MODE=NO \ No newline at end of file From b66e8ecef970858e7823e6319ac378ba7fadfaea Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Tue, 18 Oct 2016 14:18:47 +0200 Subject: [PATCH 089/145] Adds more criticism to Singleton pattern. --- service-locator/README.md | 1 - singleton/README.md | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/service-locator/README.md b/service-locator/README.md index 75a00ca57..479c9ed0f 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -38,7 +38,6 @@ improves the performance of application to great extent. * Violates Interface Segregation Principle (ISP) by providing pattern consumers with an access to a number of services that they don't potentially need. * Creates hidden dependencies that can break the clients at runtime. -* Limits object composability by stopping the clients to specify needed dependencies for different objects instantiation. ## Credits diff --git a/singleton/README.md b/singleton/README.md index 38a05349b..4032ffaed 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -40,6 +40,8 @@ Use the Singleton pattern when * Violates Single Responsibility Principle (SRP) by controlling their own creation and lifecycle. * Encourages using a global shared instance which prevents an object and resources used by this object from being deallocated. +* Creates tightly coupled code that is difficult to test. +* Makes it almost impossible to subclass a Singleton. ## Credits From ffdaf2ec4717164f466294bd7c37427fe3cddcdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 19 Oct 2016 22:25:37 +0300 Subject: [PATCH 090/145] Add Travis instructions for SonarQube.com analysis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 613f737d7..4cfffe701 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,6 +6,7 @@ env: global: - GH_REF: github.com/iluwatar/java-design-patterns.git - secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg= + - secure: "eoWlW9GyTJY04P8K3pxayXwU9/hmptQg/LfirispQkV9YvmziCfSzXnatnBhNfud98sCzY8BScXnb+OWLTnjLKpId4rtEqb0aJ40Jc32cUKzgzFAUn7cNcDAbUIfyPAGVqyQqfj/11wYSADwWMMOPlW97ExUtoyiH2WenXuRHso=" before_install: - export DISPLAY=:99.0 @@ -17,6 +18,7 @@ install: after_success: - mvn clean test jacoco:report coveralls:report +- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=SONAR_TOKEN - bash update-ghpages.sh # use latest java version available instead of travis default From 19cb715d20f269eb00aa6569244bf090459eb6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 19 Oct 2016 23:08:51 +0300 Subject: [PATCH 091/145] Fix environment variable --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4cfffe701..ba226ff5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +18,7 @@ install: after_success: - mvn clean test jacoco:report coveralls:report -- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=SONAR_TOKEN +- mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=$SONAR_TOKEN - bash update-ghpages.sh # use latest java version available instead of travis default From 1c0278592703a20720bb00b7490d435819afffb2 Mon Sep 17 00:00:00 2001 From: Fabrice Bellingard Date: Sun, 23 Oct 2016 14:35:45 +0200 Subject: [PATCH 092/145] Add SonarQube.com badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1789398ec..214fa91ff 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Quality Gate](https://sonarqube.com/api/badges/gate?key=com.iluwatar%3Ajava-design-patterns)](https://sonarqube.com/dashboard/index/com.iluwatar%3Ajava-design-patterns) # Introduction From 0438811489c16948ad8418e9a3ce59bc10bd92c6 Mon Sep 17 00:00:00 2001 From: daniel-bryla Date: Sun, 23 Oct 2016 19:59:03 +0200 Subject: [PATCH 093/145] #502 Replaced usages of System.out with logger. --- .../com/iluwatar/abstractdocument/App.java | 17 +- .../com/iluwatar/abstractfactory/App.java | 21 +- .../iluwatar/adapter/BattleFishingBoat.java | 7 +- .../com/iluwatar/adapter/FishingBoat.java | 9 +- .../ProductInformationClientImpl.java | 6 +- .../ProductInventoryClientImpl.java | 6 +- .../iluwatar/async/method/invocation/App.java | 7 +- .../java/com/iluwatar/bridge/Excalibur.java | 13 +- .../java/com/iluwatar/bridge/Mjollnir.java | 13 +- .../com/iluwatar/bridge/Stormbringer.java | 13 +- .../main/java/com/iluwatar/builder/App.java | 10 +- .../business/delegate/EjbService.java | 7 +- .../business/delegate/JmsService.java | 7 +- .../main/java/com/iluwatar/caching/App.java | 30 +-- .../java/com/iluwatar/caching/CacheStore.java | 19 +- .../java/com/iluwatar/caching/LruCache.java | 9 +- .../main/java/com/iluwatar/callback/App.java | 7 +- .../com/iluwatar/callback/LambdasApp.java | 7 +- .../com/iluwatar/callback/SimpleTask.java | 7 +- .../com/iluwatar/chain/RequestHandler.java | 7 +- .../java/com/iluwatar/command/Target.java | 9 +- .../java/com/iluwatar/command/Wizard.java | 11 +- .../main/java/com/iluwatar/composite/App.java | 11 +- .../java/com/iluwatar/composite/Letter.java | 7 +- .../java/com/iluwatar/composite/Sentence.java | 7 +- .../java/com/iluwatar/composite/Word.java | 7 +- .../main/java/com/iluwatar/decorator/App.java | 13 +- .../com/iluwatar/decorator/SmartHostile.java | 9 +- .../java/com/iluwatar/decorator/Troll.java | 9 +- .../simple/printers/CanonPrinter.java | 6 +- .../simple/printers/EpsonPrinter.java | 6 +- .../delegation/simple/printers/HpPrinter.java | 6 +- .../dependency/injection/Tobacco.java | 9 +- .../iluwatar/doublechecked/locking/App.java | 7 +- .../doublechecked/locking/Inventory.java | 8 +- .../java/com/iluwatar/doubledispatch/App.java | 15 +- .../iluwatar/doubledispatch/Meteoroid.java | 17 +- .../doubledispatch/SpaceStationMir.java | 22 ++- .../event/aggregator/KingJoffrey.java | 7 +- .../eda/handler/UserCreatedEventHandler.java | 8 +- .../eda/handler/UserUpdatedEventHandler.java | 8 +- .../iluwatar/facade/DwarvenCartOperator.java | 7 +- .../iluwatar/facade/DwarvenGoldDigger.java | 7 +- .../iluwatar/facade/DwarvenMineWorker.java | 15 +- .../iluwatar/facade/DwarvenTunnelDigger.java | 7 +- .../java/com/iluwatar/factorykit/App.java | 8 +- .../java/com/iluwatar/factory/method/App.java | 9 +- .../java/com/iluwatar/featuretoggle/App.java | 12 +- .../com/iluwatar/fluentinterface/app/App.java | 14 +- .../com/iluwatar/flux/view/ContentView.java | 6 +- .../java/com/iluwatar/flux/view/MenuView.java | 8 +- .../com/iluwatar/flyweight/AlchemistShop.java | 9 +- .../com/iluwatar/flyweight/HealingPotion.java | 7 +- .../iluwatar/flyweight/HolyWaterPotion.java | 7 +- .../flyweight/InvisibilityPotion.java | 7 +- .../com/iluwatar/flyweight/PoisonPotion.java | 7 +- .../iluwatar/flyweight/StrengthPotion.java | 7 +- .../iluwatar/front/controller/ArcherView.java | 7 +- .../front/controller/CatapultView.java | 7 +- .../iluwatar/front/controller/ErrorView.java | 7 +- .../com/iluwatar/halfsynchalfasync/App.java | 9 +- .../administration/ConsoleAdministration.java | 26 +-- .../hexagonal/eventlog/StdOutEventLog.java | 26 +-- .../hexagonal/service/ConsoleLottery.java | 56 +++--- .../java/com/iluwatar/interpreter/App.java | 17 +- .../main/java/com/iluwatar/iterator/App.java | 19 +- .../com/iluwatar/layers/CakeViewImpl.java | 7 +- .../java/com/iluwatar/layers/StdOutTest.java | 2 + .../java/com/iluwatar/lazy/loading/App.java | 12 +- .../java/com/iluwatar/lazy/loading/Heavy.java | 11 +- .../iluwatar/lazy/loading/HolderNaive.java | 7 +- .../lazy/loading/HolderThreadSafe.java | 7 +- .../iluwatar/lazy/loading/Java8Holder.java | 7 +- .../iluwatar/mediator/PartyMemberBase.java | 11 +- .../main/java/com/iluwatar/memento/App.java | 17 +- .../com/iluwatar/message/channel/App.java | 6 +- .../model/view/controller/GiantView.java | 7 +- .../src/main/java/com/iluwatar/monad/App.java | 7 +- .../java/com/iluwatar/monostate/Server.java | 10 +- .../main/java/com/iluwatar/multiton/App.java | 23 ++- .../src/main/java/com/iluwatar/mute/App.java | 7 +- .../test/java/com/iluwatar/mute/MuteTest.java | 6 +- .../main/java/com/iluwatar/mutex/Thief.java | 9 +- naked-objects/dom/log4j.properties | 41 ---- naked-objects/integtests/logging.properties | 111 ----------- .../main/webapp/WEB-INF/logging.properties | 187 ------------------ .../com/iluwatar/nullobject/NodeImpl.java | 7 +- .../java/com/iluwatar/object/pool/App.java | 29 +-- .../main/java/com/iluwatar/observer/App.java | 6 +- .../java/com/iluwatar/observer/Hobbits.java | 13 +- .../main/java/com/iluwatar/observer/Orcs.java | 13 +- .../java/com/iluwatar/observer/Weather.java | 7 +- .../iluwatar/observer/generic/GHobbits.java | 13 +- .../com/iluwatar/observer/generic/GOrcs.java | 12 +- .../iluwatar/observer/generic/GWeather.java | 6 +- .../com/iluwatar/poison/pill/Consumer.java | 11 +- .../com/iluwatar/poison/pill/Producer.java | 8 +- pom.xml | 25 ++- .../privateclassdata/ImmutableStew.java | 10 +- .../com/iluwatar/privateclassdata/Stew.java | 12 +- .../com/iluwatar/producer/consumer/App.java | 7 +- .../iluwatar/producer/consumer/Consumer.java | 8 +- .../main/java/com/iluwatar/promise/App.java | 9 +- .../java/com/iluwatar/promise/Utility.java | 9 +- .../main/java/com/iluwatar/property/App.java | 12 +- .../main/java/com/iluwatar/prototype/App.java | 17 +- .../java/com/iluwatar/proxy/WizardTower.java | 7 +- .../com/iluwatar/proxy/WizardTowerProxy.java | 7 +- .../com/iluwatar/publish/subscribe/App.java | 6 +- .../com/iluwatar/reactor/app/AppClient.java | 14 +- .../iluwatar/reactor/app/LoggingHandler.java | 6 +- .../reactor/framework/NioDatagramChannel.java | 7 +- .../reactor/framework/NioReactor.java | 7 +- .../framework/NioServerSocketChannel.java | 7 +- .../com/iluwatar/reader/writer/lock/App.java | 7 +- .../iluwatar/reader/writer/lock/Reader.java | 9 +- .../iluwatar/reader/writer/lock/Writer.java | 9 +- .../writer/lock/ReaderAndWriterTest.java | 8 +- .../reader/writer/lock/ReaderTest.java | 6 +- .../reader/writer/lock/WriterTest.java | 6 +- .../java/com/iluwatar/repository/App.java | 18 +- .../com/iluwatar/repository/AppConfig.java | 18 +- .../acquisition/is/initialization/App.java | 9 +- .../is/initialization/SlidingDoor.java | 9 +- .../is/initialization/TreasureChest.java | 9 +- .../java/com/iluwatar/semaphore/Customer.java | 11 +- .../main/java/com/iluwatar/servant/App.java | 9 +- .../com/iluwatar/servicelayer/app/App.java | 24 ++- .../servicelayer/hibernate/HibernateUtil.java | 6 +- .../iluwatar/servicelocator/InitContext.java | 9 +- .../iluwatar/servicelocator/ServiceCache.java | 9 +- .../iluwatar/servicelocator/ServiceImpl.java | 7 +- .../main/java/com/iluwatar/singleton/App.java | 25 ++- .../com/iluwatar/specification/app/App.java | 16 +- .../java/com/iluwatar/state/AngryState.java | 9 +- .../com/iluwatar/state/PeacefulState.java | 9 +- .../java/com/iluwatar/stepbuilder/App.java | 11 +- .../main/java/com/iluwatar/strategy/App.java | 23 ++- .../com/iluwatar/strategy/MeleeStrategy.java | 7 +- .../iluwatar/strategy/ProjectileStrategy.java | 8 +- .../com/iluwatar/strategy/SpellStrategy.java | 8 +- .../templatemethod/HitAndRunMethod.java | 9 +- .../templatemethod/StealingMethod.java | 7 +- .../iluwatar/templatemethod/SubtleMethod.java | 9 +- .../java/com/iluwatar/threadpool/App.java | 9 +- .../java/com/iluwatar/threadpool/Worker.java | 8 +- .../java/com/iluwatar/tolerantreader/App.java | 23 ++- .../main/java/com/iluwatar/twin/BallItem.java | 9 +- .../java/com/iluwatar/twin/BallThread.java | 9 +- .../main/java/com/iluwatar/twin/GameItem.java | 7 +- .../java/com/iluwatar/value/object/App.java | 12 +- .../iluwatar/visitor/CommanderVisitor.java | 7 +- .../com/iluwatar/visitor/SergeantVisitor.java | 7 +- .../com/iluwatar/visitor/SoldierVisitor.java | 7 +- 154 files changed, 1155 insertions(+), 792 deletions(-) delete mode 100644 naked-objects/dom/log4j.properties delete mode 100644 naked-objects/integtests/logging.properties delete mode 100644 naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties diff --git a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java index d7758b6f7..35cdf7625 100644 --- a/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java +++ b/abstract-document/src/main/java/com/iluwatar/abstractdocument/App.java @@ -27,6 +27,8 @@ import com.iluwatar.abstractdocument.domain.HasModel; import com.iluwatar.abstractdocument.domain.HasParts; import com.iluwatar.abstractdocument.domain.HasPrice; import com.iluwatar.abstractdocument.domain.HasType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Arrays; import java.util.HashMap; @@ -44,11 +46,13 @@ import java.util.Map; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Executes the App */ public App() { - System.out.println("Constructing parts and car"); + LOGGER.info("Constructing parts and car"); Map carProperties = new HashMap<>(); carProperties.put(HasModel.PROPERTY, "300SL"); @@ -68,12 +72,11 @@ public class App { Car car = new Car(carProperties); - System.out.println("Here is our car:"); - System.out.println("-> model: " + car.getModel().get()); - System.out.println("-> price: " + car.getPrice().get()); - System.out.println("-> parts: "); - car.getParts().forEach(p -> System.out - .println("\t" + p.getType().get() + "/" + p.getModel().get() + "/" + p.getPrice().get())); + LOGGER.info("Here is our car:"); + LOGGER.info("-> model: {}", car.getModel().get()); + LOGGER.info("-> price: {}", car.getPrice().get()); + LOGGER.info("-> parts: "); + car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}", p.getType().get(), p.getModel().get(), p.getPrice().get())); } /** diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java index aae396f1d..abc75a951 100644 --- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java +++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.abstractfactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Abstract Factory pattern provides a way to encapsulate a group of individual factories that have a common theme @@ -39,6 +42,8 @@ package com.iluwatar.abstractfactory; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + private King king; private Castle castle; private Army army; @@ -98,17 +103,17 @@ public class App { App app = new App(); - System.out.println("Elf Kingdom"); + LOGGER.info("Elf Kingdom"); app.createKingdom(new ElfKingdomFactory()); - System.out.println(app.getArmy().getDescription()); - System.out.println(app.getCastle().getDescription()); - System.out.println(app.getKing().getDescription()); + LOGGER.info(app.getArmy().getDescription()); + LOGGER.info(app.getCastle().getDescription()); + LOGGER.info(app.getKing().getDescription()); - System.out.println("\nOrc Kingdom"); + LOGGER.info("Orc Kingdom"); app.createKingdom(new OrcKingdomFactory()); - System.out.println(app.getArmy().getDescription()); - System.out.println(app.getCastle().getDescription()); - System.out.println(app.getKing().getDescription()); + LOGGER.info(app.getArmy().getDescription()); + LOGGER.info(app.getCastle().getDescription()); + LOGGER.info(app.getKing().getDescription()); } diff --git a/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java index a591818fe..265f127c4 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java +++ b/adapter/src/main/java/com/iluwatar/adapter/BattleFishingBoat.java @@ -22,6 +22,9 @@ */ package com.iluwatar.adapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Adapter class. Adapts the interface of the device ({@link FishingBoat}) into {@link BattleShip} @@ -33,6 +36,8 @@ package com.iluwatar.adapter; */ public class BattleFishingBoat implements BattleShip { + private static final Logger LOGGER = LoggerFactory.getLogger(BattleFishingBoat.class); + private FishingBoat boat; public BattleFishingBoat() { @@ -41,7 +46,7 @@ public class BattleFishingBoat implements BattleShip { @Override public void fire() { - System.out.println("fire!"); + LOGGER.info("fire!"); } @Override diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java index 307437038..a10e90967 100644 --- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java +++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java @@ -22,6 +22,9 @@ */ package com.iluwatar.adapter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Device class (adaptee in the pattern). We want to reuse this class @@ -29,12 +32,14 @@ package com.iluwatar.adapter; */ public class FishingBoat { + private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class); + public void sail() { - System.out.println("The Boat is moving to that place"); + LOGGER.info("The Boat is moving to that place"); } public void fish() { - System.out.println("fishing ..."); + LOGGER.info("fishing ..."); } } diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java index 1c5c1527c..e86a84563 100644 --- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInformationClientImpl.java @@ -27,6 +27,8 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.IOException; @@ -37,6 +39,8 @@ import java.io.IOException; @Component public class ProductInformationClientImpl implements ProductInformationClient { + private static final Logger LOGGER = LoggerFactory.getLogger(ProductInformationClientImpl.class); + @Override public String getProductTitle() { String response = null; @@ -46,7 +50,7 @@ public class ProductInformationClientImpl implements ProductInformationClient { response = EntityUtils.toString(httpResponse.getEntity()); } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error("Exception caught.", e); } return response; } diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java index 14d0a32c4..c736462e6 100644 --- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java +++ b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/ProductInventoryClientImpl.java @@ -27,6 +27,8 @@ import org.apache.http.client.methods.HttpGet; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import java.io.IOException; @@ -37,6 +39,8 @@ import java.io.IOException; @Component public class ProductInventoryClientImpl implements ProductInventoryClient { + private static final Logger LOGGER = LoggerFactory.getLogger(ProductInventoryClientImpl.class); + @Override public int getProductInventories() { String response = "0"; @@ -46,7 +50,7 @@ public class ProductInventoryClientImpl implements ProductInventoryClient { response = EntityUtils.toString(httpResponse.getEntity()); } } catch (IOException e) { - e.printStackTrace(); + LOGGER.error("Exception caught.", e); } return Integer.parseInt(response); } diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java index 0a56dc166..ee33ea1db 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.async.method.invocation; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.Callable; /** @@ -54,6 +57,8 @@ import java.util.concurrent.Callable; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ @@ -120,6 +125,6 @@ public class App { } private static void log(String msg) { - System.out.println(String.format("[%1$-10s] - %2$s", Thread.currentThread().getName(), msg)); + LOGGER.info(msg); } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java index 2523b3557..28bcf13fa 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Excalibur.java @@ -22,6 +22,9 @@ */ package com.iluwatar.bridge; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Excalibur @@ -29,23 +32,25 @@ package com.iluwatar.bridge; */ public class Excalibur extends BlindingMagicWeaponImpl { + private static final Logger LOGGER = LoggerFactory.getLogger(Excalibur.class); + @Override public void wieldImp() { - System.out.println("wielding Excalibur"); + LOGGER.info("wielding Excalibur"); } @Override public void swingImp() { - System.out.println("swinging Excalibur"); + LOGGER.info("swinging Excalibur"); } @Override public void unwieldImp() { - System.out.println("unwielding Excalibur"); + LOGGER.info("unwielding Excalibur"); } @Override public void blindImp() { - System.out.println("bright light streams from Excalibur blinding the enemy"); + LOGGER.info("bright light streams from Excalibur blinding the enemy"); } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java index 0cc31b471..a57d75abc 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Mjollnir.java @@ -22,6 +22,9 @@ */ package com.iluwatar.bridge; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Mjollnir @@ -29,23 +32,25 @@ package com.iluwatar.bridge; */ public class Mjollnir extends FlyingMagicWeaponImpl { + private static final Logger LOGGER = LoggerFactory.getLogger(Mjollnir.class); + @Override public void wieldImp() { - System.out.println("wielding Mjollnir"); + LOGGER.info("wielding Mjollnir"); } @Override public void swingImp() { - System.out.println("swinging Mjollnir"); + LOGGER.info("swinging Mjollnir"); } @Override public void unwieldImp() { - System.out.println("unwielding Mjollnir"); + LOGGER.info("unwielding Mjollnir"); } @Override public void flyImp() { - System.out.println("Mjollnir hits the enemy in the air and returns back to the owner's hand"); + LOGGER.info("Mjollnir hits the enemy in the air and returns back to the owner's hand"); } } diff --git a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java index 91cad9cc2..e9d65bc8c 100644 --- a/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java +++ b/bridge/src/main/java/com/iluwatar/bridge/Stormbringer.java @@ -22,6 +22,9 @@ */ package com.iluwatar.bridge; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Stormbringer @@ -29,23 +32,25 @@ package com.iluwatar.bridge; */ public class Stormbringer extends SoulEatingMagicWeaponImpl { + private static final Logger LOGGER = LoggerFactory.getLogger(Stormbringer.class); + @Override public void wieldImp() { - System.out.println("wielding Stormbringer"); + LOGGER.info("wielding Stormbringer"); } @Override public void swingImp() { - System.out.println("swinging Stormbringer"); + LOGGER.info("swinging Stormbringer"); } @Override public void unwieldImp() { - System.out.println("unwielding Stormbringer"); + LOGGER.info("unwielding Stormbringer"); } @Override public void eatSoulImp() { - System.out.println("Stormbringer devours the enemy's soul"); + LOGGER.info("Stormbringer devours the enemy's soul"); } } diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java index d421de7b6..0790e3058 100644 --- a/builder/src/main/java/com/iluwatar/builder/App.java +++ b/builder/src/main/java/com/iluwatar/builder/App.java @@ -23,6 +23,8 @@ package com.iluwatar.builder; import com.iluwatar.builder.Hero.Builder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -50,6 +52,8 @@ import com.iluwatar.builder.Hero.Builder; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -60,18 +64,18 @@ public class App { Hero mage = new Hero.Builder(Profession.MAGE, "Riobard").withHairColor(HairColor.BLACK) .withWeapon(Weapon.DAGGER).build(); - System.out.println(mage); + LOGGER.info(mage.toString()); Hero warrior = new Hero.Builder(Profession.WARRIOR, "Amberjill").withHairColor(HairColor.BLOND) .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD) .build(); - System.out.println(warrior); + LOGGER.info(warrior.toString()); Hero thief = new Hero.Builder(Profession.THIEF, "Desmond").withHairType(HairType.BALD) .withWeapon(Weapon.BOW).build(); - System.out.println(thief); + LOGGER.info(thief.toString()); } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java index 7296f63b4..9c890b108 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/EjbService.java @@ -22,6 +22,9 @@ */ package com.iluwatar.business.delegate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Service EJB implementation @@ -29,8 +32,10 @@ package com.iluwatar.business.delegate; */ public class EjbService implements BusinessService { + private static final Logger LOGGER = LoggerFactory.getLogger(EjbService.class); + @Override public void doProcessing() { - System.out.println("EjbService is now processing"); + LOGGER.info("EjbService is now processing"); } } diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java index 5b71ce57d..5b5fa6247 100644 --- a/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java +++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/JmsService.java @@ -22,6 +22,9 @@ */ package com.iluwatar.business.delegate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Service JMS implementation @@ -29,8 +32,10 @@ package com.iluwatar.business.delegate; */ public class JmsService implements BusinessService { + private static final Logger LOGGER = LoggerFactory.getLogger(JmsService.class); + @Override public void doProcessing() { - System.out.println("JmsService is now processing"); + LOGGER.info("JmsService is now processing"); } } diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 8e5a84085..377a4bfa6 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.caching; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing @@ -59,6 +62,9 @@ package com.iluwatar.caching; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + + /** * Program entry point * @@ -80,13 +86,13 @@ public class App { * Read-through and write-through */ public void useReadAndWriteThroughStrategy() { - System.out.println("# CachingPolicy.THROUGH"); + LOGGER.info("# CachingPolicy.THROUGH"); AppManager.initCachingPolicy(CachingPolicy.THROUGH); UserAccount userAccount1 = new UserAccount("001", "John", "He is a boy."); AppManager.save(userAccount1); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("001"); AppManager.find("001"); } @@ -95,21 +101,21 @@ public class App { * Read-through and write-around */ public void useReadThroughAndWriteAroundStrategy() { - System.out.println("# CachingPolicy.AROUND"); + LOGGER.info("# CachingPolicy.AROUND"); AppManager.initCachingPolicy(CachingPolicy.AROUND); UserAccount userAccount2 = new UserAccount("002", "Jane", "She is a girl."); AppManager.save(userAccount2); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("002"); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); userAccount2 = AppManager.find("002"); userAccount2.setUserName("Jane G."); AppManager.save(userAccount2); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("002"); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("002"); } @@ -117,7 +123,7 @@ public class App { * Read-through and write-behind */ public void useReadThroughAndWriteBehindStrategy() { - System.out.println("# CachingPolicy.BEHIND"); + LOGGER.info("# CachingPolicy.BEHIND"); AppManager.initCachingPolicy(CachingPolicy.BEHIND); UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food."); @@ -127,13 +133,13 @@ public class App { AppManager.save(userAccount3); AppManager.save(userAccount4); AppManager.save(userAccount5); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("003"); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child."); AppManager.save(userAccount6); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("004"); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); } } diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index 5903f8219..3b3226553 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -22,6 +22,9 @@ */ package com.iluwatar.caching; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.List; /** @@ -31,6 +34,8 @@ import java.util.List; */ public class CacheStore { + private static final Logger LOGGER = LoggerFactory.getLogger(CacheStore.class); + static LruCache cache; private CacheStore() { @@ -52,10 +57,10 @@ public class CacheStore { */ public static UserAccount readThrough(String userId) { if (cache.contains(userId)) { - System.out.println("# Cache Hit!"); + LOGGER.info("# Cache Hit!"); return cache.get(userId); } - System.out.println("# Cache Miss!"); + LOGGER.info("# Cache Miss!"); UserAccount userAccount = DbManager.readFromDb(userId); cache.set(userId, userAccount); return userAccount; @@ -91,13 +96,13 @@ public class CacheStore { */ public static UserAccount readThroughWithWriteBackPolicy(String userId) { if (cache.contains(userId)) { - System.out.println("# Cache Hit!"); + LOGGER.info("# Cache Hit!"); return cache.get(userId); } - System.out.println("# Cache Miss!"); + LOGGER.info("# Cache Miss!"); UserAccount userAccount = DbManager.readFromDb(userId); if (cache.isFull()) { - System.out.println("# Cache is FULL! Writing LRU data to DB..."); + LOGGER.info("# Cache is FULL! Writing LRU data to DB..."); UserAccount toBeWrittenToDb = cache.getLruData(); DbManager.upsertDb(toBeWrittenToDb); } @@ -110,7 +115,7 @@ public class CacheStore { */ public static void writeBehind(UserAccount userAccount) { if (cache.isFull() && !cache.contains(userAccount.getUserId())) { - System.out.println("# Cache is FULL! Writing LRU data to DB..."); + LOGGER.info("# Cache is FULL! Writing LRU data to DB..."); UserAccount toBeWrittenToDb = cache.getLruData(); DbManager.upsertDb(toBeWrittenToDb); } @@ -130,7 +135,7 @@ public class CacheStore { * Writes remaining content in the cache into the DB. */ public static void flushCache() { - System.out.println("# flushCache..."); + LOGGER.info("# flushCache..."); if (null == cache) { return; } diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 5c5549afd..614b42ac4 100644 --- a/caching/src/main/java/com/iluwatar/caching/LruCache.java +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -22,6 +22,9 @@ */ package com.iluwatar.caching; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,6 +41,8 @@ import java.util.Map; */ public class LruCache { + private static final Logger LOGGER = LoggerFactory.getLogger(LruCache.class); + class Node { String userId; UserAccount userAccount; @@ -117,7 +122,7 @@ public class LruCache { } else { Node newNode = new Node(userId, userAccount); if (cache.size() >= capacity) { - System.out.println("# Cache is FULL! Removing " + end.userId + " from cache..."); + LOGGER.info("# Cache is FULL! Removing {} from cache...", end.userId); cache.remove(end.userId); // remove LRU data from cache. remove(end); setHead(newNode); @@ -136,7 +141,7 @@ public class LruCache { * Invalidate cache for user */ public void invalidate(String userId) { - System.out.println("# " + userId + " has been updated! Removing older version from cache..."); + LOGGER.info("# {} has been updated! Removing older version from cache...", userId); Node toBeRemoved = cache.get(userId); remove(toBeRemoved); cache.remove(userId); diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java index bc1b2890e..ece6f8cce 100644 --- a/callback/src/main/java/com/iluwatar/callback/App.java +++ b/callback/src/main/java/com/iluwatar/callback/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Callback pattern is more native for functional languages where functions are treated as @@ -31,6 +34,8 @@ package com.iluwatar.callback; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ @@ -39,7 +44,7 @@ public class App { Callback callback = new Callback() { @Override public void call() { - System.out.println("I'm done now."); + LOGGER.info("I'm done now."); } }; task.executeWith(callback); diff --git a/callback/src/main/java/com/iluwatar/callback/LambdasApp.java b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java index 78ca63e0b..33a49a461 100644 --- a/callback/src/main/java/com/iluwatar/callback/LambdasApp.java +++ b/callback/src/main/java/com/iluwatar/callback/LambdasApp.java @@ -22,6 +22,9 @@ */ package com.iluwatar.callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * This example generates the exact same output as {@link App} however the callback has been @@ -30,12 +33,14 @@ package com.iluwatar.callback; */ public class LambdasApp { + private static final Logger LOGGER = LoggerFactory.getLogger(LambdasApp.class); + /** * Program entry point */ public static void main(String[] args) { Task task = new SimpleTask(); - Callback c = () -> System.out.println("I'm done now."); + Callback c = () -> LOGGER.info("I'm done now."); task.executeWith(c); } } diff --git a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java index 2a7385607..2c56bea07 100644 --- a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java +++ b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java @@ -22,6 +22,9 @@ */ package com.iluwatar.callback; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Implementation of task that need to be executed @@ -29,8 +32,10 @@ package com.iluwatar.callback; */ public class SimpleTask extends Task { + private static final Logger LOGGER = LoggerFactory.getLogger(SimpleTask.class); + @Override public void execute() { - System.out.println("Perform some important activity and after call the callback method."); + LOGGER.info("Perform some important activity and after call the callback method."); } } diff --git a/chain/src/main/java/com/iluwatar/chain/RequestHandler.java b/chain/src/main/java/com/iluwatar/chain/RequestHandler.java index 78d68e5dc..2fd774f99 100644 --- a/chain/src/main/java/com/iluwatar/chain/RequestHandler.java +++ b/chain/src/main/java/com/iluwatar/chain/RequestHandler.java @@ -22,6 +22,9 @@ */ package com.iluwatar.chain; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * RequestHandler @@ -29,6 +32,8 @@ package com.iluwatar.chain; */ public abstract class RequestHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(RequestHandler.class); + private RequestHandler next; public RequestHandler(RequestHandler next) { @@ -45,7 +50,7 @@ public abstract class RequestHandler { } protected void printHandling(Request req) { - System.out.println(this + " handling request \"" + req + "\""); + LOGGER.info("{} handling request \"{}\"", this, req); } @Override diff --git a/command/src/main/java/com/iluwatar/command/Target.java b/command/src/main/java/com/iluwatar/command/Target.java index 2e49ab663..3bfbde926 100644 --- a/command/src/main/java/com/iluwatar/command/Target.java +++ b/command/src/main/java/com/iluwatar/command/Target.java @@ -22,6 +22,9 @@ */ package com.iluwatar.command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Base class for spell targets. @@ -29,6 +32,8 @@ package com.iluwatar.command; */ public abstract class Target { + private static final Logger LOGGER = LoggerFactory.getLogger(Target.class); + private Size size; private Visibility visibility; @@ -56,8 +61,6 @@ public abstract class Target { * Print status */ public void printStatus() { - System.out.println(String.format("%s, [size=%s] [visibility=%s]", this, getSize(), - getVisibility())); - System.out.println(); + LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility()); } } diff --git a/command/src/main/java/com/iluwatar/command/Wizard.java b/command/src/main/java/com/iluwatar/command/Wizard.java index bd0ef85d4..abfc49967 100644 --- a/command/src/main/java/com/iluwatar/command/Wizard.java +++ b/command/src/main/java/com/iluwatar/command/Wizard.java @@ -22,6 +22,9 @@ */ package com.iluwatar.command; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Deque; import java.util.LinkedList; @@ -32,6 +35,8 @@ import java.util.LinkedList; */ public class Wizard { + private static final Logger LOGGER = LoggerFactory.getLogger(Wizard.class); + private Deque undoStack = new LinkedList<>(); private Deque redoStack = new LinkedList<>(); @@ -41,7 +46,7 @@ public class Wizard { * Cast spell */ public void castSpell(Command command, Target target) { - System.out.println(this + " casts " + command + " at " + target); + LOGGER.info("{} casts {} at {}", this, command, target); command.execute(target); undoStack.offerLast(command); } @@ -53,7 +58,7 @@ public class Wizard { if (!undoStack.isEmpty()) { Command previousSpell = undoStack.pollLast(); redoStack.offerLast(previousSpell); - System.out.println(this + " undoes " + previousSpell); + LOGGER.info("{} undoes {}", this, previousSpell); previousSpell.undo(); } } @@ -65,7 +70,7 @@ public class Wizard { if (!redoStack.isEmpty()) { Command previousSpell = redoStack.pollLast(); undoStack.offerLast(previousSpell); - System.out.println(this + " redoes " + previousSpell); + LOGGER.info("{} redoes {}", this, previousSpell); previousSpell.redo(); } } diff --git a/composite/src/main/java/com/iluwatar/composite/App.java b/composite/src/main/java/com/iluwatar/composite/App.java index cfe37876f..07d4f0b68 100644 --- a/composite/src/main/java/com/iluwatar/composite/App.java +++ b/composite/src/main/java/com/iluwatar/composite/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.composite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * The Composite pattern is a partitioning design pattern. The Composite pattern describes that a * group of objects is to be treated in the same way as a single instance of an object. The intent @@ -35,20 +38,22 @@ package com.iluwatar.composite; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * * @param args command line args */ public static void main(String[] args) { - System.out.println("Message from the orcs: "); + LOGGER.info("Message from the orcs: "); LetterComposite orcMessage = new Messenger().messageFromOrcs(); orcMessage.print(); - System.out.println("\n"); + LOGGER.info("\n"); - System.out.println("Message from the elves: "); + LOGGER.info("Message from the elves: "); LetterComposite elfMessage = new Messenger().messageFromElves(); elfMessage.print(); diff --git a/composite/src/main/java/com/iluwatar/composite/Letter.java b/composite/src/main/java/com/iluwatar/composite/Letter.java index d6a4005d2..c8a6ccfce 100644 --- a/composite/src/main/java/com/iluwatar/composite/Letter.java +++ b/composite/src/main/java/com/iluwatar/composite/Letter.java @@ -22,6 +22,9 @@ */ package com.iluwatar.composite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Letter @@ -29,6 +32,8 @@ package com.iluwatar.composite; */ public class Letter extends LetterComposite { + private static final Logger LOGGER = LoggerFactory.getLogger(Letter.class); + private char c; public Letter(char c) { @@ -37,7 +42,7 @@ public class Letter extends LetterComposite { @Override protected void printThisBefore() { - System.out.print(c); + LOGGER.info(String.valueOf(c)); } @Override diff --git a/composite/src/main/java/com/iluwatar/composite/Sentence.java b/composite/src/main/java/com/iluwatar/composite/Sentence.java index eea1d4b1d..ca8698f4f 100644 --- a/composite/src/main/java/com/iluwatar/composite/Sentence.java +++ b/composite/src/main/java/com/iluwatar/composite/Sentence.java @@ -22,6 +22,9 @@ */ package com.iluwatar.composite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.List; /** @@ -31,6 +34,8 @@ import java.util.List; */ public class Sentence extends LetterComposite { + private static final Logger LOGGER = LoggerFactory.getLogger(Sentence.class); + /** * Constructor */ @@ -47,6 +52,6 @@ public class Sentence extends LetterComposite { @Override protected void printThisAfter() { - System.out.print("."); + LOGGER.info("."); } } diff --git a/composite/src/main/java/com/iluwatar/composite/Word.java b/composite/src/main/java/com/iluwatar/composite/Word.java index 819f166dc..4e5cfb31d 100644 --- a/composite/src/main/java/com/iluwatar/composite/Word.java +++ b/composite/src/main/java/com/iluwatar/composite/Word.java @@ -22,6 +22,9 @@ */ package com.iluwatar.composite; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.List; /** @@ -31,6 +34,8 @@ import java.util.List; */ public class Word extends LetterComposite { + private static final Logger LOGGER = LoggerFactory.getLogger(Word.class); + /** * Constructor */ @@ -42,7 +47,7 @@ public class Word extends LetterComposite { @Override protected void printThisBefore() { - System.out.print(" "); + LOGGER.info(" "); } @Override diff --git a/decorator/src/main/java/com/iluwatar/decorator/App.java b/decorator/src/main/java/com/iluwatar/decorator/App.java index bdc574fbc..2f0b1aa4e 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/App.java +++ b/decorator/src/main/java/com/iluwatar/decorator/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.decorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Decorator pattern is a more flexible alternative to subclassing. The Decorator class @@ -36,6 +39,8 @@ package com.iluwatar.decorator; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -44,17 +49,17 @@ public class App { public static void main(String[] args) { // simple troll - System.out.println("A simple looking troll approaches."); + LOGGER.info("A simple looking troll approaches."); Hostile troll = new Troll(); troll.attack(); troll.fleeBattle(); - System.out.printf("Simple troll power %d.\n", troll.getAttackPower()); + LOGGER.info("Simple troll power {}.\n", troll.getAttackPower()); // change the behavior of the simple troll by adding a decorator - System.out.println("\nA smart looking troll surprises you."); + LOGGER.info("A smart looking troll surprises you."); Hostile smart = new SmartHostile(troll); smart.attack(); smart.fleeBattle(); - System.out.printf("Smart troll power %d.\n", smart.getAttackPower()); + LOGGER.info("Smart troll power {}.\n", smart.getAttackPower()); } } diff --git a/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java index 3b4b86276..9a4a136ad 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java +++ b/decorator/src/main/java/com/iluwatar/decorator/SmartHostile.java @@ -22,6 +22,9 @@ */ package com.iluwatar.decorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * SmartHostile is a decorator for {@link Hostile} objects. The calls to the {@link Hostile} interface * are intercepted and decorated. Finally the calls are delegated to the decorated {@link Hostile} @@ -30,6 +33,8 @@ package com.iluwatar.decorator; */ public class SmartHostile implements Hostile { + private static final Logger LOGGER = LoggerFactory.getLogger(SmartHostile.class); + private Hostile decorated; public SmartHostile(Hostile decorated) { @@ -38,7 +43,7 @@ public class SmartHostile implements Hostile { @Override public void attack() { - System.out.println("It throws a rock at you!"); + LOGGER.info("It throws a rock at you!"); decorated.attack(); } @@ -50,7 +55,7 @@ public class SmartHostile implements Hostile { @Override public void fleeBattle() { - System.out.println("It calls for help!"); + LOGGER.info("It calls for help!"); decorated.fleeBattle(); } } diff --git a/decorator/src/main/java/com/iluwatar/decorator/Troll.java b/decorator/src/main/java/com/iluwatar/decorator/Troll.java index 628adda4b..0fff3b812 100644 --- a/decorator/src/main/java/com/iluwatar/decorator/Troll.java +++ b/decorator/src/main/java/com/iluwatar/decorator/Troll.java @@ -22,6 +22,9 @@ */ package com.iluwatar.decorator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Troll implements {@link Hostile} interface directly. @@ -29,9 +32,11 @@ package com.iluwatar.decorator; */ public class Troll implements Hostile { + private static final Logger LOGGER = LoggerFactory.getLogger(Troll.class); + @Override public void attack() { - System.out.println("The troll swings at you with a club!"); + LOGGER.info("The troll swings at you with a club!"); } @Override @@ -41,6 +46,6 @@ public class Troll implements Hostile { @Override public void fleeBattle() { - System.out.println("The troll shrieks in horror and runs away!"); + LOGGER.info("The troll shrieks in horror and runs away!"); } } diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java index 5e3966d85..1664e86e4 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/CanonPrinter.java @@ -23,6 +23,8 @@ package com.iluwatar.delegation.simple.printers; import com.iluwatar.delegation.simple.Printer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Specialised Implementation of {@link Printer} for a Canon Printer, in @@ -32,12 +34,14 @@ import com.iluwatar.delegation.simple.Printer; */ public class CanonPrinter implements Printer { + private static final Logger LOGGER = LoggerFactory.getLogger(CanonPrinter.class); + /** * {@inheritDoc} */ @Override public void print(String message) { - System.out.print("Canon Printer : " + message); + LOGGER.info("Canon Printer : {}", message); } } diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java index d37fbf613..f67f9defa 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/EpsonPrinter.java @@ -23,6 +23,8 @@ package com.iluwatar.delegation.simple.printers; import com.iluwatar.delegation.simple.Printer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Specialised Implementation of {@link Printer} for a Epson Printer, in @@ -32,12 +34,14 @@ import com.iluwatar.delegation.simple.Printer; */ public class EpsonPrinter implements Printer { + private static final Logger LOGGER = LoggerFactory.getLogger(EpsonPrinter.class); + /** * {@inheritDoc} */ @Override public void print(String message) { - System.out.print("Epson Printer : " + message); + LOGGER.info("Epson Printer : {}", message); } } diff --git a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java index f81debf62..fdd89081b 100644 --- a/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java +++ b/delegation/src/main/java/com/iluwatar/delegation/simple/printers/HpPrinter.java @@ -23,6 +23,8 @@ package com.iluwatar.delegation.simple.printers; import com.iluwatar.delegation.simple.Printer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Specialised Implementation of {@link Printer} for a HP Printer, in @@ -32,12 +34,14 @@ import com.iluwatar.delegation.simple.Printer; */ public class HpPrinter implements Printer { + private static final Logger LOGGER = LoggerFactory.getLogger(HpPrinter.class); + /** * {@inheritDoc} */ @Override public void print(String message) { - System.out.print("HP Printer : " + message); + LOGGER.info("HP Printer : {}", message); } } diff --git a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java index 74a564ab5..c003007e0 100644 --- a/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java +++ b/dependency-injection/src/main/java/com/iluwatar/dependency/injection/Tobacco.java @@ -22,6 +22,9 @@ */ package com.iluwatar.dependency.injection; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Tobacco abstraction @@ -29,8 +32,10 @@ package com.iluwatar.dependency.injection; */ public abstract class Tobacco { + private static final Logger LOGGER = LoggerFactory.getLogger(Tobacco.class); + public void smoke(Wizard wizard) { - System.out.println(String.format("%s smoking %s", wizard.getClass().getSimpleName(), this - .getClass().getSimpleName())); + LOGGER.info("{} smoking {}", wizard.getClass().getSimpleName(), + this.getClass().getSimpleName()); } } diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java index 98309e181..9ce969037 100644 --- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.doublechecked.locking; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -40,6 +43,8 @@ import java.util.concurrent.TimeUnit; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -58,7 +63,7 @@ public class App { try { executorService.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown"); } } } diff --git a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java index 176203a44..09a14d320 100644 --- a/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java +++ b/double-checked-locking/src/main/java/com/iluwatar/doublechecked/locking/Inventory.java @@ -22,6 +22,9 @@ */ package com.iluwatar.doublechecked.locking; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -35,6 +38,8 @@ import java.util.concurrent.locks.ReentrantLock; */ public class Inventory { + private static final Logger LOGGER = LoggerFactory.getLogger(Inventory.class); + private final int inventorySize; private final List items; private final Lock lock; @@ -57,8 +62,7 @@ public class Inventory { try { if (items.size() < inventorySize) { items.add(item); - System.out.println(Thread.currentThread() + ": items.size()=" + items.size() - + ", inventorySize=" + inventorySize); + LOGGER.info("{}: items.size()={}, inventorySize={}", Thread.currentThread(), items.size(), inventorySize); return true; } } finally { diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java index 40a0485a5..601b0fcc0 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.doubledispatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.List; @@ -45,6 +48,8 @@ import java.util.List; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -58,8 +63,8 @@ public class App { objects.add(new SpaceStationMir(1, 1, 2, 2)); objects.add(new Meteoroid(10, 10, 15, 15)); objects.add(new SpaceStationIss(12, 12, 14, 14)); - objects.stream().forEach(o -> System.out.println(o)); - System.out.println(""); + objects.stream().forEach(o -> LOGGER.info(o.toString())); + LOGGER.info(""); // collision check objects.stream().forEach(o1 -> objects.stream().forEach(o2 -> { @@ -67,10 +72,10 @@ public class App { o1.collision(o2); } })); - System.out.println(""); + LOGGER.info(""); // output eventual object statuses - objects.stream().forEach(o -> System.out.println(o)); - System.out.println(""); + objects.stream().forEach(o -> LOGGER.info(o.toString())); + LOGGER.info(""); } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java index cc68a85ec..fcda7f312 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/Meteoroid.java @@ -22,6 +22,9 @@ */ package com.iluwatar.doubledispatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Meteoroid game object @@ -29,6 +32,8 @@ package com.iluwatar.doubledispatch; */ public class Meteoroid extends GameObject { + private static final Logger LOGGER = LoggerFactory.getLogger(Meteoroid.class); + public Meteoroid(int left, int top, int right, int bottom) { super(left, top, right, bottom); } @@ -40,25 +45,21 @@ public class Meteoroid extends GameObject { @Override public void collisionResolve(FlamingAsteroid asteroid) { - System.out.println(String.format("%s hits %s.", asteroid.getClass().getSimpleName(), this - .getClass().getSimpleName())); + LOGGER.info("{} hits {}.", asteroid.getClass().getSimpleName(), this.getClass().getSimpleName()); } @Override public void collisionResolve(Meteoroid meteoroid) { - System.out.println(String.format("%s hits %s.", meteoroid.getClass().getSimpleName(), this - .getClass().getSimpleName())); + LOGGER.info("{} hits {}.", meteoroid.getClass().getSimpleName(), this.getClass().getSimpleName()); } @Override public void collisionResolve(SpaceStationMir mir) { - System.out.println(String.format("%s hits %s.", mir.getClass().getSimpleName(), this.getClass() - .getSimpleName())); + LOGGER.info("{} hits {}.", mir.getClass().getSimpleName(), this.getClass().getSimpleName()); } @Override public void collisionResolve(SpaceStationIss iss) { - System.out.println(String.format("%s hits %s.", iss.getClass().getSimpleName(), this.getClass() - .getSimpleName())); + LOGGER.info("{} hits {}.", iss.getClass().getSimpleName(), this.getClass().getSimpleName()); } } diff --git a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java index e7a55d0ee..c1f750059 100644 --- a/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java +++ b/double-dispatch/src/main/java/com/iluwatar/doubledispatch/SpaceStationMir.java @@ -22,6 +22,9 @@ */ package com.iluwatar.doubledispatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Space station Mir game object @@ -29,6 +32,8 @@ package com.iluwatar.doubledispatch; */ public class SpaceStationMir extends GameObject { + private static final Logger LOGGER = LoggerFactory.getLogger(SpaceStationMir.class); + public SpaceStationMir(int left, int top, int right, int bottom) { super(left, top, right, bottom); } @@ -40,31 +45,30 @@ public class SpaceStationMir extends GameObject { @Override public void collisionResolve(FlamingAsteroid asteroid) { - System.out.println(String.format("%s hits %s. %s is damaged! %s is set on fire!", asteroid - .getClass().getSimpleName(), this.getClass().getSimpleName(), this.getClass() - .getSimpleName(), this.getClass().getSimpleName())); + LOGGER.info("{} hits {}. {} is damaged! {} is set on fire!", asteroid.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName(), this.getClass().getSimpleName()); setDamaged(true); setOnFire(true); } @Override public void collisionResolve(Meteoroid meteoroid) { - System.out.println(String.format("%s hits %s. %s is damaged!", meteoroid.getClass() - .getSimpleName(), this.getClass().getSimpleName(), this.getClass().getSimpleName())); + LOGGER.info("{} hits {}. {} is damaged!", meteoroid.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName()); setDamaged(true); } @Override public void collisionResolve(SpaceStationMir mir) { - System.out.println(String.format("%s hits %s. %s is damaged!", mir.getClass().getSimpleName(), - this.getClass().getSimpleName(), this.getClass().getSimpleName())); + LOGGER.info("{} hits {}. {} is damaged!", mir.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName()); setDamaged(true); } @Override public void collisionResolve(SpaceStationIss iss) { - System.out.println(String.format("%s hits %s. %s is damaged!", iss.getClass().getSimpleName(), - this.getClass().getSimpleName(), this.getClass().getSimpleName())); + LOGGER.info("{} hits {}. {} is damaged!", iss.getClass().getSimpleName(), + this.getClass().getSimpleName(), this.getClass().getSimpleName()); setDamaged(true); } } diff --git a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java index fdda59693..1a0ad3345 100644 --- a/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java +++ b/event-aggregator/src/main/java/com/iluwatar/event/aggregator/KingJoffrey.java @@ -22,6 +22,9 @@ */ package com.iluwatar.event.aggregator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * KingJoffrey observes events from {@link KingsHand}. @@ -29,8 +32,10 @@ package com.iluwatar.event.aggregator; */ public class KingJoffrey implements EventObserver { + private static final Logger LOGGER = LoggerFactory.getLogger(KingJoffrey.class); + @Override public void onEvent(Event e) { - System.out.println("Received event from the King's Hand: " + e.toString()); + LOGGER.info("Received event from the King's Hand: {}", e.toString()); } } diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java index 3ef4e8255..ded6b76ce 100644 --- a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserCreatedEventHandler.java @@ -24,17 +24,19 @@ package com.iluwatar.eda.handler; import com.iluwatar.eda.event.UserCreatedEvent; import com.iluwatar.eda.framework.Handler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Handles the {@link UserCreatedEvent} message. */ public class UserCreatedEventHandler implements Handler { + private static final Logger LOGGER = LoggerFactory.getLogger(UserCreatedEventHandler.class); + @Override public void onEvent(UserCreatedEvent event) { - - System.out.println(String.format( - "User '%s' has been Created!", event.getUser().getUsername())); + LOGGER.info("User '{}' has been Created!", event.getUser().getUsername()); } } diff --git a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java index 0311d5781..a0bf28c57 100644 --- a/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java +++ b/event-driven-architecture/src/main/java/com/iluwatar/eda/handler/UserUpdatedEventHandler.java @@ -24,16 +24,18 @@ package com.iluwatar.eda.handler; import com.iluwatar.eda.event.UserUpdatedEvent; import com.iluwatar.eda.framework.Handler; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Handles the {@link UserUpdatedEvent} message. */ public class UserUpdatedEventHandler implements Handler { + private static final Logger LOGGER = LoggerFactory.getLogger(UserUpdatedEventHandler.class); + @Override public void onEvent(UserUpdatedEvent event) { - - System.out.println(String.format( - "User '%s' has been Updated!", event.getUser().getUsername())); + LOGGER.info("User '{}' has been Updated!", event.getUser().getUsername()); } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java b/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java index bdc839f57..61e64a153 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenCartOperator.java @@ -22,6 +22,9 @@ */ package com.iluwatar.facade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * DwarvenCartOperator is one of the goldmine subsystems. @@ -29,9 +32,11 @@ package com.iluwatar.facade; */ public class DwarvenCartOperator extends DwarvenMineWorker { + private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenCartOperator.class); + @Override public void work() { - System.out.println(name() + " moves gold chunks out of the mine."); + LOGGER.info("{} moves gold chunks out of the mine.", name()); } @Override diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java index 54fa821f4..2eb464edf 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenGoldDigger.java @@ -22,6 +22,9 @@ */ package com.iluwatar.facade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * DwarvenGoldDigger is one of the goldmine subsystems. @@ -29,9 +32,11 @@ package com.iluwatar.facade; */ public class DwarvenGoldDigger extends DwarvenMineWorker { + private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenGoldDigger.class); + @Override public void work() { - System.out.println(name() + " digs for gold."); + LOGGER.info("{} digs for gold.", name()); } @Override diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java b/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java index f27054c53..48ec1db48 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenMineWorker.java @@ -22,6 +22,9 @@ */ package com.iluwatar.facade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * DwarvenMineWorker is one of the goldmine subsystems. @@ -29,20 +32,22 @@ package com.iluwatar.facade; */ public abstract class DwarvenMineWorker { + private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenMineWorker.class); + public void goToSleep() { - System.out.println(name() + " goes to sleep."); + LOGGER.info("{} goes to sleep.", name()); } public void wakeUp() { - System.out.println(name() + " wakes up."); + LOGGER.info("{} wakes up.", name()); } public void goHome() { - System.out.println(name() + " goes home."); + LOGGER.info("{} goes home.", name()); } public void goToMine() { - System.out.println(name() + " goes to the mine."); + LOGGER.info("{} goes to the mine.", name()); } private void action(Action action) { @@ -63,7 +68,7 @@ public abstract class DwarvenMineWorker { work(); break; default: - System.out.println("Undefined action"); + LOGGER.info("Undefined action"); break; } } diff --git a/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java b/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java index 74d8b89cc..54fcca73a 100644 --- a/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java +++ b/facade/src/main/java/com/iluwatar/facade/DwarvenTunnelDigger.java @@ -22,6 +22,9 @@ */ package com.iluwatar.facade; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * DwarvenTunnelDigger is one of the goldmine subsystems. @@ -29,9 +32,11 @@ package com.iluwatar.facade; */ public class DwarvenTunnelDigger extends DwarvenMineWorker { + private static final Logger LOGGER = LoggerFactory.getLogger(DwarvenTunnelDigger.class); + @Override public void work() { - System.out.println(name() + " creates another promising tunnel."); + LOGGER.info("{} creates another promising tunnel.", name()); } @Override diff --git a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java index f27bee170..c091cb1eb 100644 --- a/factory-kit/src/main/java/com/iluwatar/factorykit/App.java +++ b/factory-kit/src/main/java/com/iluwatar/factorykit/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.factorykit; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Factory-kit is a creational pattern which defines a factory of immutable content * with separated builder and factory interfaces to deal with the problem of @@ -36,6 +39,9 @@ package com.iluwatar.factorykit; * be mapped explicitly with desired class type in the factory instance. */ public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point. * @@ -49,6 +55,6 @@ public class App { builder.add(WeaponType.BOW, Bow::new); }); Weapon axe = factory.create(WeaponType.AXE); - System.out.println(axe); + LOGGER.info(axe.toString()); } } diff --git a/factory-method/src/main/java/com/iluwatar/factory/method/App.java b/factory-method/src/main/java/com/iluwatar/factory/method/App.java index cd7a6e6e7..a49d8bd32 100644 --- a/factory-method/src/main/java/com/iluwatar/factory/method/App.java +++ b/factory-method/src/main/java/com/iluwatar/factory/method/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.factory.method; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Factory Method is a creational design pattern which uses factory methods to deal with the @@ -38,6 +41,8 @@ package com.iluwatar.factory.method; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + private final Blacksmith blacksmith; /** @@ -70,8 +75,8 @@ public class App { private void manufactureWeapons() { Weapon weapon; weapon = blacksmith.manufactureWeapon(WeaponType.SPEAR); - System.out.println(weapon); + LOGGER.info(weapon.toString()); weapon = blacksmith.manufactureWeapon(WeaponType.AXE); - System.out.println(weapon); + LOGGER.info(weapon.toString()); } } diff --git a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java index debe99580..c2bccb851 100644 --- a/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java +++ b/feature-toggle/src/main/java/com/iluwatar/featuretoggle/App.java @@ -27,6 +27,8 @@ import com.iluwatar.featuretoggle.pattern.Service; import com.iluwatar.featuretoggle.pattern.propertiesversion.PropertiesFeatureToggleVersion; import com.iluwatar.featuretoggle.user.User; import com.iluwatar.featuretoggle.user.UserGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Properties; @@ -45,6 +47,8 @@ import java.util.Properties; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Block 1 shows the {@link PropertiesFeatureToggleVersion} being run with {@link Properties} setting the feature * toggle to enabled. @@ -70,7 +74,7 @@ public class App { properties.put("enhancedWelcome", true); Service service = new PropertiesFeatureToggleVersion(properties); final String welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code")); - System.out.println(welcomeMessage); + LOGGER.info(welcomeMessage); // --------------------------------------------- @@ -78,7 +82,7 @@ public class App { turnedOff.put("enhancedWelcome", false); Service turnedOffService = new PropertiesFeatureToggleVersion(turnedOff); final String welcomeMessageturnedOff = turnedOffService.getWelcomeMessage(new User("Jamie No Code")); - System.out.println(welcomeMessageturnedOff); + LOGGER.info(welcomeMessageturnedOff); // -------------------------------------------- @@ -90,7 +94,7 @@ public class App { final String welcomeMessagePaidUser = service.getWelcomeMessage(paidUser); final String welcomeMessageFreeUser = service.getWelcomeMessage(freeUser); - System.out.println(welcomeMessageFreeUser); - System.out.println(welcomeMessagePaidUser); + LOGGER.info(welcomeMessageFreeUser); + LOGGER.info(welcomeMessagePaidUser); } } diff --git a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java index 1be2b1e70..30678359d 100644 --- a/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java +++ b/fluentinterface/src/main/java/com/iluwatar/fluentinterface/app/App.java @@ -35,6 +35,8 @@ import java.util.function.Predicate; import com.iluwatar.fluentinterface.fluentiterable.FluentIterable; import com.iluwatar.fluentinterface.fluentiterable.lazy.LazyFluentIterable; import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * The Fluent Interface pattern is useful when you want to provide an easy readable, flowing API. @@ -50,6 +52,8 @@ import com.iluwatar.fluentinterface.fluentiterable.simple.SimpleFluentIterable; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ @@ -74,9 +78,7 @@ public class App { .fromCopyOf(integerList) .filter(number -> number % 2 == 0) .first() - .ifPresent( - evenNumber -> System.out.println(String.format("The first even number is: %d", - evenNumber))); + .ifPresent(evenNumber -> LOGGER.info("The first even number is: {}", evenNumber)); List transformedList = @@ -97,9 +99,7 @@ public class App { .filter(negatives()) .first(2) .last() - .ifPresent( - lastOfFirstTwo -> System.out.println(String.format( - "The last of the first two negatives is: %d", lastOfFirstTwo))); + .ifPresent(lastOfFirstTwo -> LOGGER.info("The last of the first two negatives is: {}", lastOfFirstTwo)); } private static Function transformToString() { @@ -126,6 +126,6 @@ public class App { joiner.add(iterator.next().toString()); } - System.out.println(joiner); + LOGGER.info(joiner.toString()); } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/ContentView.java b/flux/src/main/java/com/iluwatar/flux/view/ContentView.java index cb351bfae..dd5405a02 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/ContentView.java +++ b/flux/src/main/java/com/iluwatar/flux/view/ContentView.java @@ -25,6 +25,8 @@ package com.iluwatar.flux.view; import com.iluwatar.flux.action.Content; import com.iluwatar.flux.store.ContentStore; import com.iluwatar.flux.store.Store; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -33,6 +35,8 @@ import com.iluwatar.flux.store.Store; */ public class ContentView implements View { + private static final Logger LOGGER = LoggerFactory.getLogger(ContentView.class); + private Content content = Content.PRODUCTS; @Override @@ -44,6 +48,6 @@ public class ContentView implements View { @Override public void render() { - System.out.println(content.toString()); + LOGGER.info(content.toString()); } } diff --git a/flux/src/main/java/com/iluwatar/flux/view/MenuView.java b/flux/src/main/java/com/iluwatar/flux/view/MenuView.java index 6cd9005dc..81d1e49d5 100644 --- a/flux/src/main/java/com/iluwatar/flux/view/MenuView.java +++ b/flux/src/main/java/com/iluwatar/flux/view/MenuView.java @@ -26,6 +26,8 @@ import com.iluwatar.flux.action.MenuItem; import com.iluwatar.flux.dispatcher.Dispatcher; import com.iluwatar.flux.store.MenuStore; import com.iluwatar.flux.store.Store; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -34,6 +36,8 @@ import com.iluwatar.flux.store.Store; */ public class MenuView implements View { + private static final Logger LOGGER = LoggerFactory.getLogger(MenuView.class); + private MenuItem selected = MenuItem.HOME; @Override @@ -47,9 +51,9 @@ public class MenuView implements View { public void render() { for (MenuItem item : MenuItem.values()) { if (selected.equals(item)) { - System.out.println(String.format("* %s", item.toString())); + LOGGER.info("* {}", item); } else { - System.out.println(item.toString()); + LOGGER.info(item.toString()); } } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java index 507de7a6a..5cde4c248 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/AlchemistShop.java @@ -22,6 +22,9 @@ */ package com.iluwatar.flyweight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -33,6 +36,8 @@ import java.util.List; */ public class AlchemistShop { + private static final Logger LOGGER = LoggerFactory.getLogger(AlchemistShop.class); + private List topShelf; private List bottomShelf; @@ -88,13 +93,13 @@ public class AlchemistShop { */ public void enumerate() { - System.out.println("Enumerating top shelf potions\n"); + LOGGER.info("Enumerating top shelf potions\n"); for (Potion p : topShelf) { p.drink(); } - System.out.println("\nEnumerating bottom shelf potions\n"); + LOGGER.info("Enumerating bottom shelf potions\n"); for (Potion p : bottomShelf) { p.drink(); diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java index 464675a61..e25107bdb 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/HealingPotion.java @@ -22,6 +22,9 @@ */ package com.iluwatar.flyweight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * HealingPotion @@ -29,8 +32,10 @@ package com.iluwatar.flyweight; */ public class HealingPotion implements Potion { + private static final Logger LOGGER = LoggerFactory.getLogger(HealingPotion.class); + @Override public void drink() { - System.out.println("You feel healed. (Potion=" + System.identityHashCode(this) + ")"); + LOGGER.info("You feel healed. (Potion={})", System.identityHashCode(this)); } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java index b05b4af11..83a5ca6d1 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/HolyWaterPotion.java @@ -22,6 +22,9 @@ */ package com.iluwatar.flyweight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * HolyWaterPotion @@ -29,8 +32,10 @@ package com.iluwatar.flyweight; */ public class HolyWaterPotion implements Potion { + private static final Logger LOGGER = LoggerFactory.getLogger(HolyWaterPotion.class); + @Override public void drink() { - System.out.println("You feel blessed. (Potion=" + System.identityHashCode(this) + ")"); + LOGGER.info("You feel blessed. (Potion={})", System.identityHashCode(this)); } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java index 5aeb5d3a4..1e3087c4b 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/InvisibilityPotion.java @@ -22,6 +22,9 @@ */ package com.iluwatar.flyweight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * InvisibilityPotion @@ -29,8 +32,10 @@ package com.iluwatar.flyweight; */ public class InvisibilityPotion implements Potion { + private static final Logger LOGGER = LoggerFactory.getLogger(InvisibilityPotion.class); + @Override public void drink() { - System.out.println("You become invisible. (Potion=" + System.identityHashCode(this) + ")"); + LOGGER.info("You become invisible. (Potion={})", System.identityHashCode(this)); } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java index a9d13088e..83c32b192 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/PoisonPotion.java @@ -22,6 +22,9 @@ */ package com.iluwatar.flyweight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * PoisonPotion @@ -29,8 +32,10 @@ package com.iluwatar.flyweight; */ public class PoisonPotion implements Potion { + private static final Logger LOGGER = LoggerFactory.getLogger(PoisonPotion.class); + @Override public void drink() { - System.out.println("Urgh! This is poisonous. (Potion=" + System.identityHashCode(this) + ")"); + LOGGER.info("Urgh! This is poisonous. (Potion={})", System.identityHashCode(this)); } } diff --git a/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java b/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java index 2c21e7df1..81f5a9d46 100644 --- a/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java +++ b/flyweight/src/main/java/com/iluwatar/flyweight/StrengthPotion.java @@ -22,6 +22,9 @@ */ package com.iluwatar.flyweight; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * StrengthPotion @@ -29,8 +32,10 @@ package com.iluwatar.flyweight; */ public class StrengthPotion implements Potion { + private static final Logger LOGGER = LoggerFactory.getLogger(StrengthPotion.class); + @Override public void drink() { - System.out.println("You feel strong. (Potion=" + System.identityHashCode(this) + ")"); + LOGGER.info("You feel strong. (Potion={})", System.identityHashCode(this)); } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java index a7cf8ca27..ddd27d0e8 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ArcherView.java @@ -22,15 +22,20 @@ */ package com.iluwatar.front.controller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * View for archers. * */ public class ArcherView implements View { + + private static final Logger LOGGER = LoggerFactory.getLogger(ArcherView.class); @Override public void display() { - System.out.println("Displaying archers"); + LOGGER.info("Displaying archers"); } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java index 90e4c7896..f3b7e308e 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/CatapultView.java @@ -22,6 +22,9 @@ */ package com.iluwatar.front.controller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * View for catapults. @@ -29,8 +32,10 @@ package com.iluwatar.front.controller; */ public class CatapultView implements View { + private static final Logger LOGGER = LoggerFactory.getLogger(CatapultView.class); + @Override public void display() { - System.out.println("Displaying catapults"); + LOGGER.info("Displaying catapults"); } } diff --git a/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java b/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java index cbfd4bd2e..00dbc582b 100644 --- a/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java +++ b/front-controller/src/main/java/com/iluwatar/front/controller/ErrorView.java @@ -22,6 +22,9 @@ */ package com.iluwatar.front.controller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * View for errors. @@ -29,8 +32,10 @@ package com.iluwatar.front.controller; */ public class ErrorView implements View { + private static final Logger LOGGER = LoggerFactory.getLogger(ErrorView.class); + @Override public void display() { - System.out.println("Error 500"); + LOGGER.error("Error 500"); } } diff --git a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java index 17839bb32..3186b292a 100644 --- a/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java +++ b/half-sync-half-async/src/main/java/com/iluwatar/halfsynchalfasync/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.halfsynchalfasync; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.LinkedBlockingQueue; /** @@ -66,6 +69,8 @@ import java.util.concurrent.LinkedBlockingQueue; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -128,7 +133,7 @@ public class App { @Override public void onPostCall(Long result) { // Handle the result of computation - System.out.println(result); + LOGGER.info(result.toString()); } @Override @@ -141,7 +146,7 @@ public class App { try { Thread.sleep(i); } catch (InterruptedException e) { - System.out.println(e); + LOGGER.error("Exception caught.", e); } return i * (i + 1) / 2; } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java index ea2f33699..e83521c22 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/administration/ConsoleAdministration.java @@ -30,6 +30,8 @@ import com.iluwatar.hexagonal.domain.LotteryService; import com.iluwatar.hexagonal.module.LotteryModule; import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; import com.iluwatar.hexagonal.sampledata.SampleData; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Scanner; @@ -38,6 +40,8 @@ import java.util.Scanner; */ public class ConsoleAdministration { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleAdministration.class); + /** * Program entry point */ @@ -53,29 +57,29 @@ public class ConsoleAdministration { printMainMenu(); String cmd = readString(scanner); if (cmd.equals("1")) { - administartion.getAllSubmittedTickets().forEach((k,v)->System.out.println("Key: " + k + " Value: " + v)); + administartion.getAllSubmittedTickets().forEach((k,v)->LOGGER.info("Key: {}, Value: {}", k, v)); } else if (cmd.equals("2")) { LotteryNumbers numbers = administartion.performLottery(); - System.out.println("The winning numbers: " + numbers.getNumbersAsString()); - System.out.println("Time to reset the database for next round, eh?"); + LOGGER.info("The winning numbers: {}", numbers.getNumbersAsString()); + LOGGER.info("Time to reset the database for next round, eh?"); } else if (cmd.equals("3")) { administartion.resetLottery(); - System.out.println("The lottery ticket database was cleared."); + LOGGER.info("The lottery ticket database was cleared."); } else if (cmd.equals("4")) { exit = true; } else { - System.out.println("Unknown command: " + cmd); + LOGGER.info("Unknown command: {}", cmd); } } } private static void printMainMenu() { - System.out.println(""); - System.out.println("### Lottery Administration Console ###"); - System.out.println("(1) Show all submitted tickets"); - System.out.println("(2) Perform lottery draw"); - System.out.println("(3) Reset lottery ticket database"); - System.out.println("(4) Exit"); + LOGGER.info(""); + LOGGER.info("### Lottery Administration Console ###"); + LOGGER.info("(1) Show all submitted tickets"); + LOGGER.info("(2) Perform lottery draw"); + LOGGER.info("(3) Reset lottery ticket database"); + LOGGER.info("(4) Exit"); } private static String readString(Scanner scanner) { diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java index 4150dd401..c63759e8e 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/eventlog/StdOutEventLog.java @@ -23,42 +23,42 @@ package com.iluwatar.hexagonal.eventlog; import com.iluwatar.hexagonal.domain.PlayerDetails; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Standard output event log */ public class StdOutEventLog implements LotteryEventLog { + private static final Logger LOGGER = LoggerFactory.getLogger(StdOutEventLog.class); + @Override public void ticketSubmitted(PlayerDetails details) { - System.out.println(String.format("Lottery ticket for %s was submitted. Bank account %s was charged for 3 credits.", - details.getEmail(), details.getBankAccount())); + LOGGER.info("Lottery ticket for {} was submitted. Bank account {} was charged for 3 credits.", + details.getEmail(), details.getBankAccount()); } @Override public void ticketDidNotWin(PlayerDetails details) { - System.out.println(String.format("Lottery ticket for %s was checked and unfortunately did not win this time.", - details.getEmail())); + LOGGER.info("Lottery ticket for {} was checked and unfortunately did not win this time.", details.getEmail()); } @Override public void ticketWon(PlayerDetails details, int prizeAmount) { - System.out - .println(String.format("Lottery ticket for %s has won! The bank account %s was deposited with %d credits.", - details.getEmail(), details.getBankAccount(), prizeAmount)); + LOGGER.info("Lottery ticket for {} has won! The bank account {} was deposited with {} credits.", + details.getEmail(), details.getBankAccount(), prizeAmount); } @Override public void prizeError(PlayerDetails details, int prizeAmount) { - System.out - .println(String.format("Lottery ticket for %s has won! Unfortunately the bank credit transfer of %d failed.", - details.getEmail(), prizeAmount)); + LOGGER.error("Lottery ticket for {} has won! Unfortunately the bank credit transfer of {} failed.", + details.getEmail(), prizeAmount); } @Override public void ticketSubmitError(PlayerDetails details) { - System.out.println( - String.format("Lottery ticket for %s could not be submitted because the credit transfer of 3 credits failed.", - details.getEmail())); + LOGGER.error("Lottery ticket for {} could not be submitted because the credit transfer of 3 credits failed.", + details.getEmail()); } } diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index cc13d389d..00d61b668 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -24,6 +24,7 @@ package com.iluwatar.hexagonal.service; import com.google.inject.Guice; import com.google.inject.Injector; +import com.iluwatar.hexagonal.administration.ConsoleAdministration; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryService; @@ -33,6 +34,8 @@ import com.iluwatar.hexagonal.domain.LotteryTicketId; import com.iluwatar.hexagonal.domain.PlayerDetails; import com.iluwatar.hexagonal.module.LotteryModule; import com.iluwatar.hexagonal.mongo.MongoConnectionPropertiesLoader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.HashSet; import java.util.Optional; @@ -44,6 +47,7 @@ import java.util.Set; */ public class ConsoleLottery { + private static final Logger LOGGER = LoggerFactory.getLogger(ConsoleLottery.class); /** * Program entry point @@ -59,25 +63,25 @@ public class ConsoleLottery { printMainMenu(); String cmd = readString(scanner); if (cmd.equals("1")) { - System.out.println("What is the account number?"); + LOGGER.info("What is the account number?"); String account = readString(scanner); - System.out.println(String.format("The account %s has %d credits.", account, bank.getFunds(account))); + LOGGER.info("The account {} has {} credits.", account, bank.getFunds(account)); } else if (cmd.equals("2")) { - System.out.println("What is the account number?"); + LOGGER.info("What is the account number?"); String account = readString(scanner); - System.out.println("How many credits do you want to deposit?"); + LOGGER.info("How many credits do you want to deposit?"); String amount = readString(scanner); bank.setFunds(account, Integer.parseInt(amount)); - System.out.println(String.format("The account %s now has %d credits.", account, bank.getFunds(account))); + LOGGER.info("The account {} now has {} credits.", account, bank.getFunds(account)); } else if (cmd.equals("3")) { - System.out.println("What is your email address?"); + LOGGER.info("What is your email address?"); String email = readString(scanner); - System.out.println("What is your bank account number?"); + LOGGER.info("What is your bank account number?"); String account = readString(scanner); - System.out.println("What is your phone number?"); + LOGGER.info("What is your phone number?"); String phone = readString(scanner); PlayerDetails details = new PlayerDetails(email, account, phone); - System.out.println("Give 4 comma separated lottery numbers?"); + LOGGER.info("Give 4 comma separated lottery numbers?"); String numbers = readString(scanner); try { String[] parts = numbers.split(","); @@ -89,17 +93,17 @@ public class ConsoleLottery { LotteryTicket lotteryTicket = new LotteryTicket(new LotteryTicketId(), details, lotteryNumbers); Optional id = service.submitTicket(lotteryTicket); if (id.isPresent()) { - System.out.println("Submitted lottery ticket with id: " + id.get()); + LOGGER.info("Submitted lottery ticket with id: {}", id.get()); } else { - System.out.println("Failed submitting lottery ticket - please try again."); + LOGGER.info("Failed submitting lottery ticket - please try again."); } } catch (Exception e) { - System.out.println("Failed submitting lottery ticket - please try again."); + LOGGER.info("Failed submitting lottery ticket - please try again."); } } else if (cmd.equals("4")) { - System.out.println("What is the ID of the lottery ticket?"); + LOGGER.info("What is the ID of the lottery ticket?"); String id = readString(scanner); - System.out.println("Give the 4 comma separated winning numbers?"); + LOGGER.info("Give the 4 comma separated winning numbers?"); String numbers = readString(scanner); try { String[] parts = numbers.split(","); @@ -110,31 +114,31 @@ public class ConsoleLottery { LotteryTicketCheckResult result = service.checkTicketForPrize( new LotteryTicketId(Integer.parseInt(id)), LotteryNumbers.create(winningNumbers)); if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.WIN_PRIZE)) { - System.out.println("Congratulations! The lottery ticket has won!"); + LOGGER.info("Congratulations! The lottery ticket has won!"); } else if (result.getResult().equals(LotteryTicketCheckResult.CheckResult.NO_PRIZE)) { - System.out.println("Unfortunately the lottery ticket did not win."); + LOGGER.info("Unfortunately the lottery ticket did not win."); } else { - System.out.println("Such lottery ticket has not been submitted."); + LOGGER.info("Such lottery ticket has not been submitted."); } } catch (Exception e) { - System.out.println("Failed checking the lottery ticket - please try again."); + LOGGER.info("Failed checking the lottery ticket - please try again."); } } else if (cmd.equals("5")) { exit = true; } else { - System.out.println("Unknown command"); + LOGGER.info("Unknown command"); } } } private static void printMainMenu() { - System.out.println(""); - System.out.println("### Lottery Service Console ###"); - System.out.println("(1) Query lottery account funds"); - System.out.println("(2) Add funds to lottery account"); - System.out.println("(3) Submit ticket"); - System.out.println("(4) Check ticket"); - System.out.println("(5) Exit"); + LOGGER.info(""); + LOGGER.info("### Lottery Service Console ###"); + LOGGER.info("(1) Query lottery account funds"); + LOGGER.info("(2) Add funds to lottery account"); + LOGGER.info("(3) Submit ticket"); + LOGGER.info("(4) Check ticket"); + LOGGER.info("(5) Exit"); } private static String readString(Scanner scanner) { diff --git a/interpreter/src/main/java/com/iluwatar/interpreter/App.java b/interpreter/src/main/java/com/iluwatar/interpreter/App.java index 708f06e6f..1cf732b0d 100644 --- a/interpreter/src/main/java/com/iluwatar/interpreter/App.java +++ b/interpreter/src/main/java/com/iluwatar/interpreter/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.interpreter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Stack; /** @@ -37,6 +40,8 @@ import java.util.Stack; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * * Program entry point. @@ -56,21 +61,21 @@ public class App { if (isOperator(s)) { Expression rightExpression = stack.pop(); Expression leftExpression = stack.pop(); - System.out.println(String.format("popped from stack left: %d right: %d", - leftExpression.interpret(), rightExpression.interpret())); + LOGGER.info("popped from stack left: {} right: {}", + leftExpression.interpret(), rightExpression.interpret()); Expression operator = getOperatorInstance(s, leftExpression, rightExpression); - System.out.println(String.format("operator: %s", operator)); + LOGGER.info("operator: {}", operator); int result = operator.interpret(); NumberExpression resultExpression = new NumberExpression(result); stack.push(resultExpression); - System.out.println(String.format("push result to stack: %d", resultExpression.interpret())); + LOGGER.info("push result to stack: {}", resultExpression.interpret()); } else { Expression i = new NumberExpression(s); stack.push(i); - System.out.println(String.format("push to stack: %d", i.interpret())); + LOGGER.info("push to stack: {}", i.interpret()); } } - System.out.println(String.format("result: %d", stack.pop().interpret())); + LOGGER.info("result: {}", stack.pop().interpret()); } public static boolean isOperator(String s) { diff --git a/iterator/src/main/java/com/iluwatar/iterator/App.java b/iterator/src/main/java/com/iluwatar/iterator/App.java index 8da0a7433..44bc1dacd 100644 --- a/iterator/src/main/java/com/iluwatar/iterator/App.java +++ b/iterator/src/main/java/com/iluwatar/iterator/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.iterator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Iterator pattern is a design pattern in which an iterator is used to traverse a container and @@ -34,6 +37,8 @@ package com.iluwatar.iterator; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -44,28 +49,28 @@ public class App { ItemIterator ringIterator = chest.iterator(ItemType.RING); while (ringIterator.hasNext()) { - System.out.println(ringIterator.next()); + LOGGER.info(ringIterator.next().toString()); } - System.out.println("----------"); + LOGGER.info("----------"); ItemIterator potionIterator = chest.iterator(ItemType.POTION); while (potionIterator.hasNext()) { - System.out.println(potionIterator.next()); + LOGGER.info(potionIterator.next().toString()); } - System.out.println("----------"); + LOGGER.info("----------"); ItemIterator weaponIterator = chest.iterator(ItemType.WEAPON); while (weaponIterator.hasNext()) { - System.out.println(weaponIterator.next()); + LOGGER.info(weaponIterator.next().toString()); } - System.out.println("----------"); + LOGGER.info("----------"); ItemIterator it = chest.iterator(ItemType.ANY); while (it.hasNext()) { - System.out.println(it.next()); + LOGGER.info(it.next().toString()); } } } diff --git a/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java index bc489e16e..bb636af7a 100644 --- a/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java +++ b/layers/src/main/java/com/iluwatar/layers/CakeViewImpl.java @@ -22,6 +22,9 @@ */ package com.iluwatar.layers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * View implementation for displaying cakes @@ -29,6 +32,8 @@ package com.iluwatar.layers; */ public class CakeViewImpl implements View { + private static final Logger LOGGER = LoggerFactory.getLogger(CakeViewImpl.class); + private CakeBakingService cakeBakingService; public CakeViewImpl(CakeBakingService cakeBakingService) { @@ -36,6 +41,6 @@ public class CakeViewImpl implements View { } public void render() { - cakeBakingService.getAllCakes().stream().forEach(cake -> System.out.println(cake)); + cakeBakingService.getAllCakes().stream().forEach(cake -> LOGGER.info(cake.toString())); } } diff --git a/layers/src/test/java/com/iluwatar/layers/StdOutTest.java b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java index 3c94b178c..67a554873 100644 --- a/layers/src/test/java/com/iluwatar/layers/StdOutTest.java +++ b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java @@ -24,6 +24,8 @@ package com.iluwatar.layers; import org.junit.After; import org.junit.Before; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.io.PrintStream; diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java index 7a658a8c6..b33520243 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.lazy.loading; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Lazy loading idiom defers object creation until needed. @@ -33,6 +36,9 @@ package com.iluwatar.lazy.loading; * */ public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -43,16 +49,16 @@ public class App { // Simple lazy loader - not thread safe HolderNaive holderNaive = new HolderNaive(); Heavy heavy = holderNaive.getHeavy(); - System.out.println("heavy=" + heavy); + LOGGER.info("heavy={}", heavy); // Thread safe lazy loader, but with heavy synchronization on each access HolderThreadSafe holderThreadSafe = new HolderThreadSafe(); Heavy another = holderThreadSafe.getHeavy(); - System.out.println("another=" + another); + LOGGER.info("another={}", another); // The most efficient lazy loader utilizing Java 8 features Java8Holder java8Holder = new Java8Holder(); Heavy next = java8Holder.getHeavy(); - System.out.println("next=" + next); + LOGGER.info("next={}", next); } } diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java index 57e8e263e..ad4eb3de2 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Heavy.java @@ -22,6 +22,9 @@ */ package com.iluwatar.lazy.loading; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Heavy objects are expensive to create. @@ -29,16 +32,18 @@ package com.iluwatar.lazy.loading; */ public class Heavy { + private static final Logger LOGGER = LoggerFactory.getLogger(Heavy.class); + /** * Constructor */ public Heavy() { - System.out.println("Creating Heavy ..."); + LOGGER.info("Creating Heavy ..."); try { Thread.sleep(1000); } catch (InterruptedException e) { - e.printStackTrace(); + LOGGER.error("Exception caught.", e); } - System.out.println("... Heavy created"); + LOGGER.info("... Heavy created"); } } diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java index 75c65d1b9..0e3d2c718 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderNaive.java @@ -22,6 +22,9 @@ */ package com.iluwatar.lazy.loading; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Simple implementation of the lazy loading idiom. However, this is not thread safe. @@ -29,13 +32,15 @@ package com.iluwatar.lazy.loading; */ public class HolderNaive { + private static final Logger LOGGER = LoggerFactory.getLogger(HolderNaive.class); + private Heavy heavy; /** * Constructor */ public HolderNaive() { - System.out.println("HolderNaive created"); + LOGGER.info("HolderNaive created"); } /** diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java index c6e0fcfd9..35dd2e4f0 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/HolderThreadSafe.java @@ -22,6 +22,9 @@ */ package com.iluwatar.lazy.loading; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Same as HolderNaive but with added synchronization. This implementation is thread safe, but each @@ -30,13 +33,15 @@ package com.iluwatar.lazy.loading; */ public class HolderThreadSafe { + private static final Logger LOGGER = LoggerFactory.getLogger(HolderThreadSafe.class); + private Heavy heavy; /** * Constructor */ public HolderThreadSafe() { - System.out.println("HolderThreadSafe created"); + LOGGER.info("HolderThreadSafe created"); } /** diff --git a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java index e4ce394cc..95e8856c9 100644 --- a/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java +++ b/lazy-loading/src/main/java/com/iluwatar/lazy/loading/Java8Holder.java @@ -22,6 +22,9 @@ */ package com.iluwatar.lazy.loading; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.function.Supplier; /** @@ -32,10 +35,12 @@ import java.util.function.Supplier; */ public class Java8Holder { + private static final Logger LOGGER = LoggerFactory.getLogger(Java8Holder.class); + private Supplier heavy = () -> createAndCacheHeavy(); public Java8Holder() { - System.out.println("Java8Holder created"); + LOGGER.info("Java8Holder created"); } public Heavy getHeavy() { diff --git a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java index 7d3ca3dbf..9d9b9cd2d 100644 --- a/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java +++ b/mediator/src/main/java/com/iluwatar/mediator/PartyMemberBase.java @@ -22,6 +22,9 @@ */ package com.iluwatar.mediator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Abstract base class for party members. @@ -29,23 +32,25 @@ package com.iluwatar.mediator; */ public abstract class PartyMemberBase implements PartyMember { + private static final Logger LOGGER = LoggerFactory.getLogger(PartyMemberBase.class); + protected Party party; @Override public void joinedParty(Party party) { - System.out.println(this + " joins the party"); + LOGGER.info("{} joins the party", this); this.party = party; } @Override public void partyAction(Action action) { - System.out.println(this + " " + action.getDescription()); + LOGGER.info("{} {}", this, action.getDescription()); } @Override public void act(Action action) { if (party != null) { - System.out.println(this + " " + action.toString()); + LOGGER.info("{} {}", this, action); party.act(this, action); } } diff --git a/memento/src/main/java/com/iluwatar/memento/App.java b/memento/src/main/java/com/iluwatar/memento/App.java index e1ee349b1..c346b7941 100644 --- a/memento/src/main/java/com/iluwatar/memento/App.java +++ b/memento/src/main/java/com/iluwatar/memento/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.memento; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Stack; /** @@ -45,6 +48,8 @@ import java.util.Stack; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ @@ -52,22 +57,22 @@ public class App { Stack states = new Stack<>(); Star star = new Star(StarType.SUN, 10000000, 500000); - System.out.println(star); + LOGGER.info(star.toString()); states.add(star.getMemento()); star.timePasses(); - System.out.println(star); + LOGGER.info(star.toString()); states.add(star.getMemento()); star.timePasses(); - System.out.println(star); + LOGGER.info(star.toString()); states.add(star.getMemento()); star.timePasses(); - System.out.println(star); + LOGGER.info(star.toString()); states.add(star.getMemento()); star.timePasses(); - System.out.println(star); + LOGGER.info(star.toString()); while (states.size() > 0) { star.setMemento(states.pop()); - System.out.println(star); + LOGGER.info(star.toString()); } } } diff --git a/message-channel/src/main/java/com/iluwatar/message/channel/App.java b/message-channel/src/main/java/com/iluwatar/message/channel/App.java index dab04bd37..2e132213d 100644 --- a/message-channel/src/main/java/com/iluwatar/message/channel/App.java +++ b/message-channel/src/main/java/com/iluwatar/message/channel/App.java @@ -25,6 +25,8 @@ package com.iluwatar.message.channel; import org.apache.camel.CamelContext; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultCamelContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -50,6 +52,8 @@ import org.apache.camel.impl.DefaultCamelContext; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ @@ -66,7 +70,7 @@ public class App { }); context.start(); - context.getRoutes().stream().forEach(r -> System.out.println(r)); + context.getRoutes().stream().forEach(r -> LOGGER.info(r.toString())); context.stop(); } } diff --git a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java index dd4361487..61815aab8 100644 --- a/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java +++ b/model-view-controller/src/main/java/com/iluwatar/model/view/controller/GiantView.java @@ -22,6 +22,9 @@ */ package com.iluwatar.model.view.controller; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * GiantView displays the giant @@ -29,7 +32,9 @@ package com.iluwatar.model.view.controller; */ public class GiantView { + private static final Logger LOGGER = LoggerFactory.getLogger(GiantView.class); + public void displayGiant(GiantModel giant) { - System.out.println(giant); + LOGGER.info(giant.toString()); } } diff --git a/monad/src/main/java/com/iluwatar/monad/App.java b/monad/src/main/java/com/iluwatar/monad/App.java index 7b28fdcf8..e707156d6 100644 --- a/monad/src/main/java/com/iluwatar/monad/App.java +++ b/monad/src/main/java/com/iluwatar/monad/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.monad; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; @@ -42,6 +45,8 @@ import java.util.function.Predicate; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point. * @@ -49,7 +54,7 @@ public class App { */ public static void main(String[] args) { User user = new User("user", 24, Sex.FEMALE, "foobar.com"); - System.out.println(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") + LOGGER.info(Validator.of(user).validate(User::getName, Objects::nonNull, "name is null") .validate(User::getName, name -> !name.isEmpty(), "name is empty") .validate(User::getEmail, email -> !email.contains("@"), "email doesn't containt '@'") .validate(User::getAge, age -> age > 20 && age < 30, "age isn't between...").get().toString()); diff --git a/monostate/src/main/java/com/iluwatar/monostate/Server.java b/monostate/src/main/java/com/iluwatar/monostate/Server.java index bf700a57a..86a4985cc 100644 --- a/monostate/src/main/java/com/iluwatar/monostate/Server.java +++ b/monostate/src/main/java/com/iluwatar/monostate/Server.java @@ -22,6 +22,9 @@ */ package com.iluwatar.monostate; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Server class. Each Server sits behind a LoadBalancer which delegates the call to the servers @@ -29,6 +32,9 @@ package com.iluwatar.monostate; * */ public class Server { + + private static final Logger LOGGER = LoggerFactory.getLogger(Server.class); + public final String host; public final int port; public final int id; @@ -51,7 +57,7 @@ public class Server { } public void serve(Request request) { - System.out.println("Server ID " + id + " associated to host : " + getHost() + " and Port " - + getPort() + " Processed request with value " + request.value); + LOGGER.info("Server ID {} associated to host : {} and port {}. Processed request with value {}", + id, host, port, request.value); } } diff --git a/multiton/src/main/java/com/iluwatar/multiton/App.java b/multiton/src/main/java/com/iluwatar/multiton/App.java index 1ffd57a34..55cc8a2aa 100644 --- a/multiton/src/main/java/com/iluwatar/multiton/App.java +++ b/multiton/src/main/java/com/iluwatar/multiton/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.multiton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Whereas Singleton design pattern introduces single globally accessible object the Multiton @@ -35,20 +38,22 @@ package com.iluwatar.multiton; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * * @param args command line args */ public static void main(String[] args) { - System.out.println("KHAMUL=" + Nazgul.getInstance(NazgulName.KHAMUL)); - System.out.println("MURAZOR=" + Nazgul.getInstance(NazgulName.MURAZOR)); - System.out.println("DWAR=" + Nazgul.getInstance(NazgulName.DWAR)); - System.out.println("JI_INDUR=" + Nazgul.getInstance(NazgulName.JI_INDUR)); - System.out.println("AKHORAHIL=" + Nazgul.getInstance(NazgulName.AKHORAHIL)); - System.out.println("HOARMURATH=" + Nazgul.getInstance(NazgulName.HOARMURATH)); - System.out.println("ADUNAPHEL=" + Nazgul.getInstance(NazgulName.ADUNAPHEL)); - System.out.println("REN=" + Nazgul.getInstance(NazgulName.REN)); - System.out.println("UVATHA=" + Nazgul.getInstance(NazgulName.UVATHA)); + LOGGER.info("KHAMUL={}", Nazgul.getInstance(NazgulName.KHAMUL)); + LOGGER.info("MURAZOR={}", Nazgul.getInstance(NazgulName.MURAZOR)); + LOGGER.info("DWAR={}", Nazgul.getInstance(NazgulName.DWAR)); + LOGGER.info("JI_INDUR={}", Nazgul.getInstance(NazgulName.JI_INDUR)); + LOGGER.info("AKHORAHIL={}", Nazgul.getInstance(NazgulName.AKHORAHIL)); + LOGGER.info("HOARMURATH={}", Nazgul.getInstance(NazgulName.HOARMURATH)); + LOGGER.info("ADUNAPHEL={}", Nazgul.getInstance(NazgulName.ADUNAPHEL)); + LOGGER.info("REN={}", Nazgul.getInstance(NazgulName.REN)); + LOGGER.info("UVATHA={}", Nazgul.getInstance(NazgulName.UVATHA)); } } diff --git a/mute-idiom/src/main/java/com/iluwatar/mute/App.java b/mute-idiom/src/main/java/com/iluwatar/mute/App.java index 8a2aca41e..35c9d2a55 100644 --- a/mute-idiom/src/main/java/com/iluwatar/mute/App.java +++ b/mute-idiom/src/main/java/com/iluwatar/mute/App.java @@ -23,6 +23,9 @@ package com.iluwatar.mute; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.ByteArrayOutputStream; import java.io.IOException; import java.sql.SQLException; @@ -46,6 +49,8 @@ import java.sql.SQLException; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point. * @@ -88,7 +93,7 @@ public class App { } private static void utilizeResource(Resource resource) throws SQLException { - System.out.println("Utilizing acquired resource: " + resource); + LOGGER.info("Utilizing acquired resource: {}", resource); } private static Resource acquireResource() throws SQLException { diff --git a/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java b/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java index 58cbfe893..a7a010c3c 100644 --- a/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java +++ b/mute-idiom/src/test/java/com/iluwatar/mute/MuteTest.java @@ -32,9 +32,13 @@ import java.io.PrintStream; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class MuteTest { + private static final Logger LOGGER = LoggerFactory.getLogger(MuteTest.class); + private static final String MESSAGE = "should not occur"; @Rule public ExpectedException exception = ExpectedException.none(); @@ -69,7 +73,7 @@ public class MuteTest { private void methodNotThrowingAnyException() { - System.out.println("Executed successfully"); + LOGGER.info("Executed successfully"); } private void methodThrowingException() throws Exception { diff --git a/mutex/src/main/java/com/iluwatar/mutex/Thief.java b/mutex/src/main/java/com/iluwatar/mutex/Thief.java index d2225876c..d31ec3fa8 100644 --- a/mutex/src/main/java/com/iluwatar/mutex/Thief.java +++ b/mutex/src/main/java/com/iluwatar/mutex/Thief.java @@ -22,12 +22,17 @@ */ package com.iluwatar.mutex; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Thief is a class which continually tries to acquire a jar and take a bean * from it. When the jar is empty the thief stops. */ public class Thief extends Thread { + private static final Logger LOGGER = LoggerFactory.getLogger(Thief.class); + /** * The name of the thief. */ @@ -53,10 +58,10 @@ public class Thief extends Thread { while (jar.takeBean()) { beans = beans + 1; - System.out.println(name + " took a bean."); + LOGGER.info("{} took a bean.", name); } - System.out.println(name + " took " + beans + " beans."); + LOGGER.info("{} took {} beans.", name, beans); } } diff --git a/naked-objects/dom/log4j.properties b/naked-objects/dom/log4j.properties deleted file mode 100644 index ca165acc7..000000000 --- a/naked-objects/dom/log4j.properties +++ /dev/null @@ -1,41 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - -# LOG4J Configuration -# =================== - -# Basic logging goes to "datanucleus.log" -log4j.appender.A1=org.apache.log4j.FileAppender -log4j.appender.A1.File=datanucleus.log -log4j.appender.A1.layout=org.apache.log4j.PatternLayout -log4j.appender.A1.layout.ConversionPattern=%d{HH:mm:ss,SSS} (%t) %-5p [%c] - %m%n -#log4j.appender.A1.Threshold=INFO - -# Categories -# Each category can be set to a "level", and to direct to an appender - -# Default to DEBUG level for all DataNucleus categories -log4j.logger.DataNucleus = DEBUG, A1 - -log4j.category.com.mchange.v2.c3p0=INFO, A1 -log4j.category.com.mchange.v2.resourcepool=INFO, A1 -log4j.category.org.logicalcobwebs.proxool=INFO,A1 - - -# Hbase libs logging -log4j.category.org.apache.hadoop=INFO,A1 -log4j.category.org.apache.zookeeper=INFO,A1 \ No newline at end of file diff --git a/naked-objects/integtests/logging.properties b/naked-objects/integtests/logging.properties deleted file mode 100644 index b55249569..000000000 --- a/naked-objects/integtests/logging.properties +++ /dev/null @@ -1,111 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -# -# Isis uses log4j is used to provide system logging -# -log4j.rootCategory=INFO, Console - -# The console appender -log4j.appender.Console=org.apache.log4j.ConsoleAppender -log4j.appender.Console.target=System.out -log4j.appender.Console.layout=org.apache.log4j.PatternLayout -log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} [%-20c{1} %-10t %-5p] %m%n - -log4j.appender.File=org.apache.log4j.RollingFileAppender -log4j.appender.File.file=isis.log -log4j.appender.File.append=false -log4j.appender.File.layout=org.apache.log4j.PatternLayout -log4j.appender.File.layout.ConversionPattern=%d [%-20c{1} %-10t %-5p] %m%n - -log4j.appender.translations-po=org.apache.log4j.FileAppender -log4j.appender.translations-po.File=./translations.pot -log4j.appender.translations-po.Append=false -log4j.appender.translations-po.layout=org.apache.log4j.PatternLayout -log4j.appender.translations-po.layout.ConversionPattern=%m%n - -! turn on the internal log4j debugging flag so we can see what it is doing -#log4j.debug=true - - -# DataNucleus -# the first two log the DML and DDL (if set to DEBUG) -log4j.logger.DataNucleus.Datastore.Native=WARN, Console -log4j.logger.DataNucleus.Datastore.Schema=DEBUG, Console -# the remainder can probably be left to WARN -log4j.logger.DataNucleus.Persistence=WARN, Console -log4j.logger.DataNucleus.Transaction=WARN, Console -log4j.logger.DataNucleus.Connection=WARN, Console -log4j.logger.DataNucleus.Query=WARN, Console -log4j.logger.DataNucleus.Cache=WARN, Console -log4j.logger.DataNucleus.MetaData=WARN, Console -log4j.logger.DataNucleus.Datastore=WARN, Console -log4j.logger.DataNucleus.Datastore.Persist=WARN, Console -log4j.logger.DataNucleus.Datastore.Retrieve=WARN, Console -log4j.logger.DataNucleus.General=WARN, Console -log4j.logger.DataNucleus.Lifecycle=WARN, Console -log4j.logger.DataNucleus.ValueGeneration=WARN, Console -log4j.logger.DataNucleus.Enhancer=WARN, Console -log4j.logger.DataNucleus.SchemaTool=ERROR, Console -log4j.logger.DataNucleus.JDO=WARN, Console -log4j.logger.DataNucleus.JPA=ERROR, Console -log4j.logger.DataNucleus.JCA=WARN, Console -log4j.logger.DataNucleus.IDE=ERROR, Console - -log4j.additivity.DataNucleus.Datastore.Native=false -log4j.additivity.DataNucleus.Datastore.Schema=false -log4j.additivity.DataNucleus.Datastore.Persistence=false -log4j.additivity.DataNucleus.Datastore.Transaction=false -log4j.additivity.DataNucleus.Datastore.Connection=false -log4j.additivity.DataNucleus.Datastore.Query=false -log4j.additivity.DataNucleus.Datastore.Cache=false -log4j.additivity.DataNucleus.Datastore.MetaData=false -log4j.additivity.DataNucleus.Datastore.Datastore=false -log4j.additivity.DataNucleus.Datastore.Datastore.Persist=false -log4j.additivity.DataNucleus.Datastore.Datastore.Retrieve=false -log4j.additivity.DataNucleus.Datastore.General=false -log4j.additivity.DataNucleus.Datastore.Lifecycle=false -log4j.additivity.DataNucleus.Datastore.ValueGeneration=false -log4j.additivity.DataNucleus.Datastore.Enhancer=false -log4j.additivity.DataNucleus.Datastore.SchemaTool=false -log4j.additivity.DataNucleus.Datastore.JDO=false -log4j.additivity.DataNucleus.Datastore.JPA=false -log4j.additivity.DataNucleus.Datastore.JCA=false -log4j.additivity.DataNucleus.Datastore.IDE=false - - - - -# if using log4jdbc-remix as JDBC driver -#log4j.logger.jdbc.sqlonly=DEBUG, sql, Console -#log4j.additivity.jdbc.sqlonly=false -#log4j.logger.jdbc.resultsettable=DEBUG, jdbc, Console -#log4j.additivity.jdbc.resultsettable=false - -#log4j.logger.jdbc.audit=WARN,jdbc, Console -#log4j.additivity.jdbc.audit=false -#log4j.logger.jdbc.resultset=WARN,jdbc -#log4j.additivity.jdbc.resultset=false -#log4j.logger.jdbc.sqltiming=WARN,sqltiming -#log4j.additivity.jdbc.sqltiming=false -#log4j.logger.jdbc.connection=FATAL,connection -#log4j.additivity.jdbc.connection=false - - -log4j.logger.org.apache.isis.core.runtime.services.i18n.po.PoWriter=INFO,translations-po -log4j.additivity.org.apache.isis.core.runtime.services.i18n.po.PotWriter=false diff --git a/naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties b/naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties deleted file mode 100644 index 62fd8ea5e..000000000 --- a/naked-objects/webapp/src/main/webapp/WEB-INF/logging.properties +++ /dev/null @@ -1,187 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. - - -# -# Isis uses log4j is used to provide system logging -# -log4j.rootCategory=INFO, Console -#log4j.rootCategory=DEBUG, Console - - -# The console appender -log4j.appender.Console=org.apache.log4j.ConsoleAppender -log4j.appender.Console.target=System.out -log4j.appender.Console.layout=org.apache.log4j.PatternLayout -log4j.appender.Console.layout.ConversionPattern=%d{ABSOLUTE} [%-20c{1} %-10t %-5p] %m%n - - -# The stderr appender -log4j.appender.Stderr=org.apache.log4j.ConsoleAppender -log4j.appender.Stderr.target=System.err -log4j.appender.Stderr.layout=org.apache.log4j.PatternLayout -log4j.appender.Stderr.layout.ConversionPattern=%d{ABSOLUTE} [%-20c{1} %-10t %-5p] %m%n - - -# other appenders -log4j.appender.File=org.apache.log4j.RollingFileAppender -log4j.appender.File.file=isis.log -log4j.appender.File.append=false -log4j.appender.File.layout=org.apache.log4j.PatternLayout -log4j.appender.File.layout.ConversionPattern=%d [%-20c{1} %-10t %-5p] %m%n - -log4j.appender.sql=org.apache.log4j.FileAppender -log4j.appender.sql.File=./logs/sql.log -log4j.appender.sql.Append=false -log4j.appender.sql.layout=org.apache.log4j.PatternLayout -log4j.appender.sql.layout.ConversionPattern=-----> %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n%n - -log4j.appender.sqltiming=org.apache.log4j.FileAppender -log4j.appender.sqltiming.File=./logs/sqltiming.log -log4j.appender.sqltiming.Append=false -log4j.appender.sqltiming.layout=org.apache.log4j.PatternLayout -log4j.appender.sqltiming.layout.ConversionPattern=-----> %d{yyyy-MM-dd HH:mm:ss.SSS} %m%n%n - -log4j.appender.jdbc=org.apache.log4j.FileAppender -log4j.appender.jdbc.File=./logs/jdbc.log -log4j.appender.jdbc.Append=false -log4j.appender.jdbc.layout=org.apache.log4j.PatternLayout -log4j.appender.jdbc.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %m%n - -log4j.appender.connection=org.apache.log4j.FileAppender -log4j.appender.connection.File=./logs/connection.log -log4j.appender.connection.Append=false -log4j.appender.connection.layout=org.apache.log4j.PatternLayout -log4j.appender.connection.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss.SSS} %m%n - - - - -! turn on the internal log4j debugging flag so we can see what it is doing -#log4j.debug=true - - -# DataNucleus -# the first two log the DML and DDL (if set to DEBUG) -log4j.logger.DataNucleus.Datastore.Native=WARN, Console -log4j.logger.DataNucleus.Datastore.Schema=DEBUG, Console -# the remainder can probably be left to WARN -log4j.logger.DataNucleus.Persistence=WARN, Console -log4j.logger.DataNucleus.Transaction=WARN, Console -log4j.logger.DataNucleus.Connection=WARN, Console -log4j.logger.DataNucleus.Query=WARN, Console -log4j.logger.DataNucleus.Cache=WARN, Console -log4j.logger.DataNucleus.MetaData=WARN, Console -log4j.logger.DataNucleus.Datastore=WARN, Console -log4j.logger.DataNucleus.Datastore.Persist=WARN, Console -log4j.logger.DataNucleus.Datastore.Retrieve=WARN, Console -log4j.logger.DataNucleus.General=WARN, Console -log4j.logger.DataNucleus.Lifecycle=WARN, Console -log4j.logger.DataNucleus.ValueGeneration=WARN, Console -log4j.logger.DataNucleus.Enhancer=WARN, Console -log4j.logger.DataNucleus.SchemaTool=ERROR, Console -log4j.logger.DataNucleus.JDO=WARN, Console -log4j.logger.DataNucleus.JPA=ERROR, Console -log4j.logger.DataNucleus.JCA=WARN, Console -log4j.logger.DataNucleus.IDE=ERROR, Console - -log4j.additivity.DataNucleus.Datastore.Native=false -log4j.additivity.DataNucleus.Datastore.Schema=false -log4j.additivity.DataNucleus.Datastore.Persistence=false -log4j.additivity.DataNucleus.Datastore.Transaction=false -log4j.additivity.DataNucleus.Datastore.Connection=false -log4j.additivity.DataNucleus.Datastore.Query=false -log4j.additivity.DataNucleus.Datastore.Cache=false -log4j.additivity.DataNucleus.Datastore.MetaData=false -log4j.additivity.DataNucleus.Datastore.Datastore=false -log4j.additivity.DataNucleus.Datastore.Datastore.Persist=false -log4j.additivity.DataNucleus.Datastore.Datastore.Retrieve=false -log4j.additivity.DataNucleus.Datastore.General=false -log4j.additivity.DataNucleus.Datastore.Lifecycle=false -log4j.additivity.DataNucleus.Datastore.ValueGeneration=false -log4j.additivity.DataNucleus.Datastore.Enhancer=false -log4j.additivity.DataNucleus.Datastore.SchemaTool=false -log4j.additivity.DataNucleus.Datastore.JDO=false -log4j.additivity.DataNucleus.Datastore.JPA=false -log4j.additivity.DataNucleus.Datastore.JCA=false -log4j.additivity.DataNucleus.Datastore.IDE=false - - -# if using log4jdbc-remix as JDBC driver -#log4j.logger.jdbc.sqlonly=DEBUG, sql, Console -#log4j.additivity.jdbc.sqlonly=false -#log4j.logger.jdbc.resultsettable=DEBUG, jdbc, Console -#log4j.additivity.jdbc.resultsettable=false - -#log4j.logger.jdbc.audit=WARN,jdbc, Console -#log4j.additivity.jdbc.audit=false -#log4j.logger.jdbc.resultset=WARN,jdbc -#log4j.additivity.jdbc.resultset=false -#log4j.logger.jdbc.sqltiming=WARN,sqltiming -#log4j.additivity.jdbc.sqltiming=false -#log4j.logger.jdbc.connection=FATAL,connection -#log4j.additivity.jdbc.connection=false - - - -# track Isis/JDO lifecycle integration - -#log4j.logger.org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.FrameworkSynchronizer=DEBUG, Console -#log4j.additivity.org.apache.isis.runtimes.dflt.objectstores.jdo.datanucleus.persistence.FrameworkSynchronizer=false - -#log4j.logger.org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener=DEBUG,Console -#log4j.additivity.org.apache.isis.objectstore.jdo.datanucleus.persistence.IsisLifecycleListener=false - - - - -# track Isis/Wicket lifecycle integration - -#log4j.logger.org.apache.isis.viewer.wicket.viewer.integration.wicket.WebRequestCycleForIsis=DEBUG, Console -#log4j.additivity.org.apache.isis.viewer.wicket.viewer.integration.wicket.WebRequestCycleForIsis=false - -#log4j.logger.org.apache.isis.viewer.wicket.viewer.integration.isis.IsisContextForWicket=INFO,Console -#log4j.additivity.org.apache.isis.viewer.wicket.viewer.integration.isis.IsisContextForWicket=false - - - - -# quieten some of the noisier classes in Isis' bootstrapping -log4j.logger.org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder=WARN,Console -log4j.additivity.org.apache.isis.core.metamodel.specloader.specimpl.FacetedMethodsBuilder=false - -log4j.logger.org.apache.isis.core.metamodel.specloader.ServiceInitializer=WARN,Console -log4j.additivity.org.apache.isis.core.metamodel.specloader.ServiceInitializer=false - -log4j.logger.org.apache.isis.core.runtime.services.ServicesInstallerFromConfiguration=WARN,Console -log4j.additivity.org.apache.isis.core.runtime.services.ServicesInstallerFromConfiguration=false - -log4j.logger.org.apache.isis.core.commons.config.IsisConfigurationDefault=WARN,Console -log4j.additivity.org.apache.isis.core.commons.config.IsisConfigurationDefault=false - -log4j.logger.org.apache.isis.core.runtime.installers.InstallerLookupDefault=WARN,Console -log4j.additivity.org.apache.isis.core.runtime.installers.InstallerLookupDefault=false - - -# quieten Shiro -log4j.logger.org.apache.shiro.realm.AuthorizingRealm=WARN,Console -log4j.additivity.log4j.logger.org.apache.shiro.realm.AuthorizingRealm=false - - -# Application-specific logging -log4j.logger.dom.simple.SimpleObject=DEBUG, Stderr -log4j.additivity.dom.simple.SimpleObject=false \ No newline at end of file diff --git a/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java b/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java index 46787ac96..c2fd66849 100644 --- a/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java +++ b/null-object/src/main/java/com/iluwatar/nullobject/NodeImpl.java @@ -22,6 +22,9 @@ */ package com.iluwatar.nullobject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Implementation for binary tree's normal nodes. @@ -29,6 +32,8 @@ package com.iluwatar.nullobject; */ public class NodeImpl implements Node { + private static final Logger LOGGER = LoggerFactory.getLogger(NodeImpl.class); + private final String name; private final Node left; private final Node right; @@ -64,7 +69,7 @@ public class NodeImpl implements Node { @Override public void walk() { - System.out.println(name); + LOGGER.info(name); if (left.getTreeSize() > 0) { left.walk(); } diff --git a/object-pool/src/main/java/com/iluwatar/object/pool/App.java b/object-pool/src/main/java/com/iluwatar/object/pool/App.java index 66b3e3f90..b49dd70d7 100644 --- a/object-pool/src/main/java/com/iluwatar/object/pool/App.java +++ b/object-pool/src/main/java/com/iluwatar/object/pool/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.object.pool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * When it is necessary to work with a large number of objects that are particularly expensive to @@ -44,6 +47,8 @@ package com.iluwatar.object.pool; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -51,24 +56,24 @@ public class App { */ public static void main(String[] args) { OliphauntPool pool = new OliphauntPool(); - System.out.println(pool); + LOGGER.info(pool.toString()); Oliphaunt oliphaunt1 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt1); - System.out.println(pool); + LOGGER.info("Checked out {}", oliphaunt1); + LOGGER.info(pool.toString()); Oliphaunt oliphaunt2 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt2); + LOGGER.info("Checked out {}", oliphaunt2); Oliphaunt oliphaunt3 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt3); - System.out.println(pool); - System.out.println("Checking in " + oliphaunt1); + LOGGER.info("Checked out {}", oliphaunt3); + LOGGER.info(pool.toString()); + LOGGER.info("Checking in {}", oliphaunt1); pool.checkIn(oliphaunt1); - System.out.println("Checking in " + oliphaunt2); + LOGGER.info("Checking in {}", oliphaunt2); pool.checkIn(oliphaunt2); - System.out.println(pool); + LOGGER.info(pool.toString()); Oliphaunt oliphaunt4 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt4); + LOGGER.info("Checked out {}", oliphaunt4); Oliphaunt oliphaunt5 = pool.checkOut(); - System.out.println("Checked out " + oliphaunt5); - System.out.println(pool); + LOGGER.info("Checked out {}", oliphaunt5); + LOGGER.info(pool.toString()); } } diff --git a/observer/src/main/java/com/iluwatar/observer/App.java b/observer/src/main/java/com/iluwatar/observer/App.java index dcced00d4..d6a8e0df6 100644 --- a/observer/src/main/java/com/iluwatar/observer/App.java +++ b/observer/src/main/java/com/iluwatar/observer/App.java @@ -25,6 +25,8 @@ package com.iluwatar.observer; import com.iluwatar.observer.generic.GHobbits; import com.iluwatar.observer.generic.GOrcs; import com.iluwatar.observer.generic.GWeather; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -41,6 +43,8 @@ import com.iluwatar.observer.generic.GWeather; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -58,7 +62,7 @@ public class App { weather.timePasses(); // Generic observer inspired by Java Generics and Collection by Naftalin & Wadler - System.out.println("\n--Running generic version--"); + LOGGER.info("--Running generic version--"); GWeather gWeather = new GWeather(); gWeather.addObserver(new GOrcs()); gWeather.addObserver(new GHobbits()); diff --git a/observer/src/main/java/com/iluwatar/observer/Hobbits.java b/observer/src/main/java/com/iluwatar/observer/Hobbits.java index ed9636bd6..74ffee713 100644 --- a/observer/src/main/java/com/iluwatar/observer/Hobbits.java +++ b/observer/src/main/java/com/iluwatar/observer/Hobbits.java @@ -22,6 +22,9 @@ */ package com.iluwatar.observer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Hobbits @@ -29,20 +32,22 @@ package com.iluwatar.observer; */ public class Hobbits implements WeatherObserver { + private static final Logger LOGGER = LoggerFactory.getLogger(Hobbits.class); + @Override public void update(WeatherType currentWeather) { switch (currentWeather) { case COLD: - System.out.println("The hobbits are shivering in the cold weather."); + LOGGER.info("The hobbits are shivering in the cold weather."); break; case RAINY: - System.out.println("The hobbits look for cover from the rain."); + LOGGER.info("The hobbits look for cover from the rain."); break; case SUNNY: - System.out.println("The happy hobbits bade in the warm sun."); + LOGGER.info("The happy hobbits bade in the warm sun."); break; case WINDY: - System.out.println("The hobbits hold their hats tightly in the windy weather."); + LOGGER.info("The hobbits hold their hats tightly in the windy weather."); break; default: break; diff --git a/observer/src/main/java/com/iluwatar/observer/Orcs.java b/observer/src/main/java/com/iluwatar/observer/Orcs.java index ce9c09944..65ddc840f 100644 --- a/observer/src/main/java/com/iluwatar/observer/Orcs.java +++ b/observer/src/main/java/com/iluwatar/observer/Orcs.java @@ -22,6 +22,9 @@ */ package com.iluwatar.observer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Orcs @@ -29,20 +32,22 @@ package com.iluwatar.observer; */ public class Orcs implements WeatherObserver { + private static final Logger LOGGER = LoggerFactory.getLogger(Orcs.class); + @Override public void update(WeatherType currentWeather) { switch (currentWeather) { case COLD: - System.out.println("The orcs are freezing cold."); + LOGGER.info("The orcs are freezing cold."); break; case RAINY: - System.out.println("The orcs are dripping wet."); + LOGGER.info("The orcs are dripping wet."); break; case SUNNY: - System.out.println("The sun hurts the orcs' eyes."); + LOGGER.info("The sun hurts the orcs' eyes."); break; case WINDY: - System.out.println("The orc smell almost vanishes in the wind."); + LOGGER.info("The orc smell almost vanishes in the wind."); break; default: break; diff --git a/observer/src/main/java/com/iluwatar/observer/Weather.java b/observer/src/main/java/com/iluwatar/observer/Weather.java index f6aad3881..c89ffd366 100644 --- a/observer/src/main/java/com/iluwatar/observer/Weather.java +++ b/observer/src/main/java/com/iluwatar/observer/Weather.java @@ -22,6 +22,9 @@ */ package com.iluwatar.observer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.List; @@ -33,6 +36,8 @@ import java.util.List; */ public class Weather { + private static final Logger LOGGER = LoggerFactory.getLogger(Weather.class); + private WeatherType currentWeather; private List observers; @@ -55,7 +60,7 @@ public class Weather { public void timePasses() { WeatherType[] enumValues = WeatherType.values(); currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; - System.out.println("The weather changed to " + currentWeather + "."); + LOGGER.info("The weather changed to {}.", currentWeather); notifyObservers(); } diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java b/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java index da84b9aab..f70a1a62e 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GHobbits.java @@ -23,6 +23,8 @@ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -30,20 +32,23 @@ import com.iluwatar.observer.WeatherType; * */ public class GHobbits implements Race { + + private static final Logger LOGGER = LoggerFactory.getLogger(GHobbits.class); + @Override public void update(GWeather weather, WeatherType weatherType) { switch (weatherType) { case COLD: - System.out.println("The hobbits are shivering in the cold weather."); + LOGGER.info("The hobbits are shivering in the cold weather."); break; case RAINY: - System.out.println("The hobbits look for cover from the rain."); + LOGGER.info("The hobbits look for cover from the rain."); break; case SUNNY: - System.out.println("The happy hobbits bade in the warm sun."); + LOGGER.info("The happy hobbits bade in the warm sun."); break; case WINDY: - System.out.println("The hobbits hold their hats tightly in the windy weather."); + LOGGER.info("The hobbits hold their hats tightly in the windy weather."); break; default: break; diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java b/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java index 9f41aa6cc..3171f4e7c 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GOrcs.java @@ -23,6 +23,8 @@ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -31,20 +33,22 @@ import com.iluwatar.observer.WeatherType; */ public class GOrcs implements Race { + private static final Logger LOGGER = LoggerFactory.getLogger(GOrcs.class); + @Override public void update(GWeather weather, WeatherType weatherType) { switch (weatherType) { case COLD: - System.out.println("The orcs are freezing cold."); + LOGGER.info("The orcs are freezing cold."); break; case RAINY: - System.out.println("The orcs are dripping wet."); + LOGGER.info("The orcs are dripping wet."); break; case SUNNY: - System.out.println("The sun hurts the orcs' eyes."); + LOGGER.info("The sun hurts the orcs' eyes."); break; case WINDY: - System.out.println("The orc smell almost vanishes in the wind."); + LOGGER.info("The orc smell almost vanishes in the wind."); break; default: break; diff --git a/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java b/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java index 137000760..631da479e 100644 --- a/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java +++ b/observer/src/main/java/com/iluwatar/observer/generic/GWeather.java @@ -23,6 +23,8 @@ package com.iluwatar.observer.generic; import com.iluwatar.observer.WeatherType; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -31,6 +33,8 @@ import com.iluwatar.observer.WeatherType; */ public class GWeather extends Observable { + private static final Logger LOGGER = LoggerFactory.getLogger(GWeather.class); + private WeatherType currentWeather; public GWeather() { @@ -43,7 +47,7 @@ public class GWeather extends Observable { public void timePasses() { WeatherType[] enumValues = WeatherType.values(); currentWeather = enumValues[(currentWeather.ordinal() + 1) % enumValues.length]; - System.out.println("The weather changed to " + currentWeather + "."); + LOGGER.info("The weather changed to {}.", currentWeather); notifyObservers(currentWeather); } } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java index c811225e1..6a586c52d 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Consumer.java @@ -23,11 +23,15 @@ package com.iluwatar.poison.pill; import com.iluwatar.poison.pill.Message.Headers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Class responsible for receiving and handling submitted to the queue messages */ public class Consumer { + + private static final Logger LOGGER = LoggerFactory.getLogger(Consumer.class); private final MqSubscribePoint queue; private final String name; @@ -46,19 +50,18 @@ public class Consumer { try { msg = queue.take(); if (Message.POISON_PILL.equals(msg)) { - System.out.println(String.format("Consumer %s receive request to terminate.", name)); + LOGGER.info("Consumer {} receive request to terminate.", name); break; } } catch (InterruptedException e) { // allow thread to exit - System.err.println(e); + LOGGER.error("Exception caught.", e); return; } String sender = msg.getHeader(Headers.SENDER); String body = msg.getBody(); - System.out.println(String.format("Message [%s] from [%s] received by [%s]", body, sender, - name)); + LOGGER.info("Message [{}] from [{}] received by [{}]", body, sender, name); } } } diff --git a/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java b/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java index 15dcad40e..af07ed679 100644 --- a/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java +++ b/poison-pill/src/main/java/com/iluwatar/poison/pill/Producer.java @@ -25,6 +25,8 @@ package com.iluwatar.poison.pill; import java.util.Date; import com.iluwatar.poison.pill.Message.Headers; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Class responsible for producing unit of work that can be expressed as message and submitted to @@ -32,6 +34,8 @@ import com.iluwatar.poison.pill.Message.Headers; */ public class Producer { + private static final Logger LOGGER = LoggerFactory.getLogger(Producer.class); + private final MqPublishPoint queue; private final String name; private boolean isStopped; @@ -62,7 +66,7 @@ public class Producer { queue.put(msg); } catch (InterruptedException e) { // allow thread to exit - System.err.println(e); + LOGGER.error("Exception caught.", e); } } @@ -75,7 +79,7 @@ public class Producer { queue.put(Message.POISON_PILL); } catch (InterruptedException e) { // allow thread to exit - System.err.println(e); + LOGGER.error("Exception caught.", e); } } } diff --git a/pom.xml b/pom.xml index 144b8efff..d4d7ff581 100644 --- a/pom.xml +++ b/pom.xml @@ -39,7 +39,6 @@ 0.7.2.201409121644 1.4 2.16.1 - 1.2.17 19.0 1.15.1 1.10.19 @@ -49,6 +48,8 @@ 1.4.1 4.0 3.3.0 + 1.7.21 + 1.1.7 abstract-factory @@ -210,11 +211,6 @@ ${mockito.version} test - - log4j - log4j - ${log4j.version} - com.google.guava guava @@ -251,6 +247,23 @@ + + + org.slf4j + slf4j-api + ${slf4j.version} + + + ch.qos.logback + logback-classic + ${logback.version} + + + ch.qos.logback + logback-core + ${logback.version} + + diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java index b9d2ad09c..73bfb948f 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/ImmutableStew.java @@ -22,6 +22,9 @@ */ package com.iluwatar.privateclassdata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Immutable stew class, protected with Private Class Data pattern @@ -29,6 +32,8 @@ package com.iluwatar.privateclassdata; */ public class ImmutableStew { + private static final Logger LOGGER = LoggerFactory.getLogger(ImmutableStew.class); + private StewData data; public ImmutableStew(int numPotatoes, int numCarrots, int numMeat, int numPeppers) { @@ -39,8 +44,7 @@ public class ImmutableStew { * Mix the stew */ public void mix() { - System.out.println(String.format( - "Mixing the immutable stew we find: %d potatoes, %d carrots, %d meat and %d peppers", - data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers())); + LOGGER.info("Mixing the immutable stew we find: {} potatoes, {} carrots, {} meat and {} peppers", + data.getNumPotatoes(), data.getNumCarrots(), data.getNumMeat(), data.getNumPeppers()); } } diff --git a/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java b/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java index 9c9033018..665edf418 100644 --- a/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java +++ b/private-class-data/src/main/java/com/iluwatar/privateclassdata/Stew.java @@ -22,6 +22,9 @@ */ package com.iluwatar.privateclassdata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Mutable stew class @@ -29,6 +32,8 @@ package com.iluwatar.privateclassdata; */ public class Stew { + private static final Logger LOGGER = LoggerFactory.getLogger(Stew.class); + private int numPotatoes; private int numCarrots; private int numMeat; @@ -48,16 +53,15 @@ public class Stew { * Mix the stew */ public void mix() { - System.out.println(String.format( - "Mixing the stew we find: %d potatoes, %d carrots, %d meat and %d peppers", numPotatoes, - numCarrots, numMeat, numPeppers)); + LOGGER.info("Mixing the stew we find: {} potatoes, {} carrots, {} meat and {} peppers", + numPotatoes, numCarrots, numMeat, numPeppers); } /** * Taste the stew */ public void taste() { - System.out.println("Tasting the stew"); + LOGGER.info("Tasting the stew"); if (numPotatoes > 0) { numPotatoes--; } diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java index 42f6dfa79..dca2457c4 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.producer.consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -37,6 +40,8 @@ import java.util.concurrent.TimeUnit; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -72,7 +77,7 @@ public class App { executorService.awaitTermination(10, TimeUnit.SECONDS); executorService.shutdownNow(); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown"); } } } diff --git a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java index f1fa920f3..880b76ba5 100644 --- a/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java +++ b/producer-consumer/src/main/java/com/iluwatar/producer/consumer/Consumer.java @@ -22,11 +22,16 @@ */ package com.iluwatar.producer.consumer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Class responsible for consume the {@link Item} produced by {@link Producer} */ public class Consumer { + private static final Logger LOGGER = LoggerFactory.getLogger(Consumer.class); + private final ItemQueue queue; private final String name; @@ -42,8 +47,7 @@ public class Consumer { public void consume() throws InterruptedException { Item item = queue.take(); - System.out.println(String.format("Consumer [%s] consume item [%s] produced by [%s]", name, - item.getId(), item.getProducer())); + LOGGER.info("Consumer [{}] consume item [{}] produced by [{}]", name, item.getId(), item.getProducer()); } } diff --git a/promise/src/main/java/com/iluwatar/promise/App.java b/promise/src/main/java/com/iluwatar/promise/App.java index 672c20bfa..7e1f174ef 100644 --- a/promise/src/main/java/com/iluwatar/promise/App.java +++ b/promise/src/main/java/com/iluwatar/promise/App.java @@ -21,6 +21,9 @@ * THE SOFTWARE. */ package com.iluwatar.promise; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CountDownLatch; @@ -60,6 +63,8 @@ import java.util.concurrent.Executors; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + private static final String DEFAULT_URL = "https://raw.githubusercontent.com/iluwatar/java-design-patterns/Promise/promise/README.md"; private final ExecutorService executor; private final CountDownLatch stopLatch; @@ -98,7 +103,7 @@ public class App { lowestFrequencyChar() .thenAccept( charFrequency -> { - System.out.println("Char with lowest frequency is: " + charFrequency); + LOGGER.info("Char with lowest frequency is: {}", charFrequency); taskCompleted(); } ); @@ -112,7 +117,7 @@ public class App { countLines() .thenAccept( count -> { - System.out.println("Line count is: " + count); + LOGGER.info("Line count is: {}", count); taskCompleted(); } ); diff --git a/promise/src/main/java/com/iluwatar/promise/Utility.java b/promise/src/main/java/com/iluwatar/promise/Utility.java index d451600a3..769a6fcd1 100644 --- a/promise/src/main/java/com/iluwatar/promise/Utility.java +++ b/promise/src/main/java/com/iluwatar/promise/Utility.java @@ -22,6 +22,9 @@ */ package com.iluwatar.promise; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.BufferedReader; import java.io.File; import java.io.FileReader; @@ -38,6 +41,8 @@ import java.util.Map.Entry; public class Utility { + private static final Logger LOGGER = LoggerFactory.getLogger(Utility.class); + /** * Calculates character frequency of the file provided. * @param fileLocation location of the file. @@ -104,7 +109,7 @@ public class Utility { * @return the absolute path of the file downloaded. */ public static String downloadFile(String urlString) throws MalformedURLException, IOException { - System.out.println("Downloading contents from url: " + urlString); + LOGGER.info("Downloading contents from url: {}", urlString); URL url = new URL(urlString); File file = File.createTempFile("promise_pattern", null); try (Reader reader = new InputStreamReader(url.openStream()); @@ -114,7 +119,7 @@ public class Utility { writer.write(line); writer.write("\n"); } - System.out.println("File downloaded at: " + file.getAbsolutePath()); + LOGGER.info("File downloaded at: {}", file.getAbsolutePath()); return file.getAbsolutePath(); } catch (IOException ex) { throw ex; diff --git a/property/src/main/java/com/iluwatar/property/App.java b/property/src/main/java/com/iluwatar/property/App.java index e3f7cc92b..576bada18 100644 --- a/property/src/main/java/com/iluwatar/property/App.java +++ b/property/src/main/java/com/iluwatar/property/App.java @@ -23,6 +23,8 @@ package com.iluwatar.property; import com.iluwatar.property.Character.Type; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -39,6 +41,8 @@ import com.iluwatar.property.Character.Type; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -67,16 +71,16 @@ public class App { /* usage */ Character mag = new Character("Player_1", mageProto); mag.set(Stats.ARMOR, 8); - System.out.println(mag); + LOGGER.info(mag.toString()); Character warrior = new Character("Player_2", warProto); - System.out.println(warrior); + LOGGER.info(warrior.toString()); Character rogue = new Character("Player_3", rogueProto); - System.out.println(rogue); + LOGGER.info(rogue.toString()); Character rogueDouble = new Character("Player_4", rogue); rogueDouble.set(Stats.ATTACK_POWER, 12); - System.out.println(rogueDouble); + LOGGER.info(rogueDouble.toString()); } } diff --git a/prototype/src/main/java/com/iluwatar/prototype/App.java b/prototype/src/main/java/com/iluwatar/prototype/App.java index cb04ec5f2..bb98194ce 100644 --- a/prototype/src/main/java/com/iluwatar/prototype/App.java +++ b/prototype/src/main/java/com/iluwatar/prototype/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.prototype; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Prototype pattern is a creational design pattern in software development. It is used when the @@ -36,6 +39,8 @@ package com.iluwatar.prototype; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -51,16 +56,16 @@ public class App { mage = factory.createMage(); warlord = factory.createWarlord(); beast = factory.createBeast(); - System.out.println(mage); - System.out.println(warlord); - System.out.println(beast); + LOGGER.info(mage.toString()); + LOGGER.info(warlord.toString()); + LOGGER.info(beast.toString()); factory = new HeroFactoryImpl(new OrcMage(), new OrcWarlord(), new OrcBeast()); mage = factory.createMage(); warlord = factory.createWarlord(); beast = factory.createBeast(); - System.out.println(mage); - System.out.println(warlord); - System.out.println(beast); + LOGGER.info(mage.toString()); + LOGGER.info(warlord.toString()); + LOGGER.info(beast.toString()); } } diff --git a/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java b/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java index 573d38374..d5daab305 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java +++ b/proxy/src/main/java/com/iluwatar/proxy/WizardTower.java @@ -22,6 +22,9 @@ */ package com.iluwatar.proxy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The object to be proxyed. @@ -29,8 +32,10 @@ package com.iluwatar.proxy; */ public class WizardTower { + private static final Logger LOGGER = LoggerFactory.getLogger(WizardTower.class); + public void enter(Wizard wizard) { - System.out.println(wizard + " enters the tower."); + LOGGER.info("{} enters the tower.", wizard); } } diff --git a/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java b/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java index 985184afe..1048724d4 100644 --- a/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java +++ b/proxy/src/main/java/com/iluwatar/proxy/WizardTowerProxy.java @@ -22,6 +22,9 @@ */ package com.iluwatar.proxy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The proxy controlling access to the {@link WizardTower}. @@ -29,6 +32,8 @@ package com.iluwatar.proxy; */ public class WizardTowerProxy extends WizardTower { + private static final Logger LOGGER = LoggerFactory.getLogger(WizardTowerProxy.class); + private static final int NUM_WIZARDS_ALLOWED = 3; private int numWizards; @@ -39,7 +44,7 @@ public class WizardTowerProxy extends WizardTower { super.enter(wizard); numWizards++; } else { - System.out.println(wizard + " is not allowed to enter!"); + LOGGER.info("{} is not allowed to enter!", wizard); } } } diff --git a/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java b/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java index c4e423b04..ff66e4e58 100644 --- a/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java +++ b/publish-subscribe/src/main/java/com/iluwatar/publish/subscribe/App.java @@ -26,6 +26,8 @@ import org.apache.camel.CamelContext; import org.apache.camel.ProducerTemplate; import org.apache.camel.builder.RouteBuilder; import org.apache.camel.impl.DefaultCamelContext; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -48,6 +50,8 @@ import org.apache.camel.impl.DefaultCamelContext; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ @@ -61,7 +65,7 @@ public class App { }); ProducerTemplate template = context.createProducerTemplate(); context.start(); - context.getRoutes().stream().forEach(r -> System.out.println(r)); + context.getRoutes().stream().forEach(r -> LOGGER.info(r.toString())); template.sendBody("direct:origin", "Hello from origin"); context.stop(); } diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java index 29c2f83fa..77e4e1f4f 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/AppClient.java @@ -22,6 +22,9 @@ */ package com.iluwatar.reactor.app; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; @@ -41,6 +44,9 @@ import java.util.concurrent.TimeUnit; * requests to Reactor. */ public class AppClient { + + private static final Logger LOGGER = LoggerFactory.getLogger(AppClient.class); + private final ExecutorService service = Executors.newFixedThreadPool(4); /** @@ -126,9 +132,9 @@ public class AppClient { byte[] data = new byte[1024]; int read = inputStream.read(data, 0, data.length); if (read == 0) { - System.out.println("Read zero bytes"); + LOGGER.info("Read zero bytes"); } else { - System.out.println(new String(data, 0, read)); + LOGGER.info(new String(data, 0, read)); } artificialDelayOf(100); @@ -171,9 +177,9 @@ public class AppClient { DatagramPacket reply = new DatagramPacket(data, data.length); socket.receive(reply); if (reply.getLength() == 0) { - System.out.println("Read zero bytes"); + LOGGER.info("Read zero bytes"); } else { - System.out.println(new String(reply.getData(), 0, reply.getLength())); + LOGGER.info(new String(reply.getData(), 0, reply.getLength())); } artificialDelayOf(100); diff --git a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java index ab2bcfb1a..e1095d34e 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java +++ b/reactor/src/main/java/com/iluwatar/reactor/app/LoggingHandler.java @@ -28,6 +28,8 @@ import java.nio.channels.SelectionKey; import com.iluwatar.reactor.framework.AbstractNioChannel; import com.iluwatar.reactor.framework.ChannelHandler; import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Logging server application logic. It logs the incoming requests on standard console and returns a @@ -35,6 +37,8 @@ import com.iluwatar.reactor.framework.NioDatagramChannel.DatagramPacket; */ public class LoggingHandler implements ChannelHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(LoggingHandler.class); + private static final byte[] ACK = "Data logged successfully".getBytes(); /** @@ -76,6 +80,6 @@ public class LoggingHandler implements ChannelHandler { private static void doLogging(ByteBuffer data) { // assuming UTF-8 :( - System.out.println(new String(data.array(), 0, data.limit())); + LOGGER.info(new String(data.array(), 0, data.limit())); } } diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java index 4e493163f..52d2d68b0 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioDatagramChannel.java @@ -22,6 +22,9 @@ */ package com.iluwatar.reactor.framework; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -35,6 +38,8 @@ import java.nio.channels.SelectionKey; */ public class NioDatagramChannel extends AbstractNioChannel { + private static final Logger LOGGER = LoggerFactory.getLogger(NioDatagramChannel.class); + private final int port; /** @@ -99,7 +104,7 @@ public class NioDatagramChannel extends AbstractNioChannel { public void bind() throws IOException { getJavaChannel().socket().bind(new InetSocketAddress(InetAddress.getLocalHost(), port)); getJavaChannel().configureBlocking(false); - System.out.println("Bound UDP socket at port: " + port); + LOGGER.info("Bound UDP socket at port: {}", port); } /** diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java index 3d5ebf5a0..0facf1935 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioReactor.java @@ -22,6 +22,9 @@ */ package com.iluwatar.reactor.framework; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; @@ -53,6 +56,8 @@ import java.util.concurrent.TimeUnit; */ public class NioReactor { + private static final Logger LOGGER = LoggerFactory.getLogger(NioReactor.class); + private final Selector selector; private final Dispatcher dispatcher; /** @@ -86,7 +91,7 @@ public class NioReactor { public void start() throws IOException { reactorMain.execute(() -> { try { - System.out.println("Reactor started, waiting for events..."); + LOGGER.info("Reactor started, waiting for events..."); eventLoop(); } catch (IOException e) { e.printStackTrace(); diff --git a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java index c7d67fd13..d1c6bb62a 100644 --- a/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java +++ b/reactor/src/main/java/com/iluwatar/reactor/framework/NioServerSocketChannel.java @@ -22,6 +22,9 @@ */ package com.iluwatar.reactor.framework; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; @@ -36,6 +39,8 @@ import java.nio.channels.SocketChannel; */ public class NioServerSocketChannel extends AbstractNioChannel { + private static final Logger LOGGER = LoggerFactory.getLogger(NioServerSocketChannel.class); + private final int port; /** @@ -96,7 +101,7 @@ public class NioServerSocketChannel extends AbstractNioChannel { ((ServerSocketChannel) getJavaChannel()).socket().bind( new InetSocketAddress(InetAddress.getLocalHost(), port)); ((ServerSocketChannel) getJavaChannel()).configureBlocking(false); - System.out.println("Bound TCP socket at port: " + port); + LOGGER.info("Bound TCP socket at port: {}", port); } /** diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java index fd5d28ed5..90f7d5e59 100644 --- a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/App.java @@ -23,6 +23,9 @@ package com.iluwatar.reader.writer.lock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; @@ -48,6 +51,8 @@ import java.util.stream.IntStream; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -71,7 +76,7 @@ public class App { try { executeService.awaitTermination(5, TimeUnit.SECONDS); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown"); } } diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java index 2b837b341..644772bf1 100644 --- a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Reader.java @@ -22,6 +22,9 @@ */ package com.iluwatar.reader.writer.lock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.locks.Lock; /** @@ -29,6 +32,8 @@ import java.util.concurrent.locks.Lock; */ public class Reader implements Runnable { + private static final Logger LOGGER = LoggerFactory.getLogger(Reader.class); + private Lock readLock; private String name; @@ -55,8 +60,8 @@ public class Reader implements Runnable { * */ public void read() throws InterruptedException { - System.out.println(name + " begin"); + LOGGER.info("{} begin", name); Thread.sleep(250); - System.out.println(name + " finish"); + LOGGER.info("{} finish", name); } } diff --git a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java index fc3c3bb88..ce946f74f 100644 --- a/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java +++ b/reader-writer-lock/src/main/java/com/iluwatar/reader/writer/lock/Writer.java @@ -22,6 +22,9 @@ */ package com.iluwatar.reader.writer.lock; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.concurrent.locks.Lock; /** @@ -29,6 +32,8 @@ import java.util.concurrent.locks.Lock; */ public class Writer implements Runnable { + private static final Logger LOGGER = LoggerFactory.getLogger(Writer.class); + private Lock writeLock; private String name; @@ -55,8 +60,8 @@ public class Writer implements Runnable { * Simulate the write operation */ public void write() throws InterruptedException { - System.out.println(name + " begin"); + LOGGER.info("{} begin", name); Thread.sleep(250); - System.out.println(name + " finish"); + LOGGER.info("{} finish", name); } } diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java index a2496a3c0..dc8feb04f 100644 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java @@ -31,13 +31,15 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.InOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author hongshuwei@gmail.com */ public class ReaderAndWriterTest extends StdOutTest { - + private static final Logger LOGGER = LoggerFactory.getLogger(ReaderAndWriterTest.class); /** * Verify reader and writer can only get the lock to read and write orderly @@ -60,7 +62,7 @@ public class ReaderAndWriterTest extends StdOutTest { try { executeService.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown", e); } final InOrder inOrder = inOrder(getStdOutMock()); @@ -91,7 +93,7 @@ public class ReaderAndWriterTest extends StdOutTest { try { executeService.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown", e); } final InOrder inOrder = inOrder(getStdOutMock()); diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java index 7d51e977c..a51120bf8 100644 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java @@ -31,12 +31,16 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.InOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author hongshuwei@gmail.com */ public class ReaderTest extends StdOutTest { + private static final Logger LOGGER = LoggerFactory.getLogger(ReaderTest.class); + /** * Verify that multiple readers can get the read lock concurrently */ @@ -57,7 +61,7 @@ public class ReaderTest extends StdOutTest { try { executeService.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown", e); } // Read operation will hold the read lock 250 milliseconds, so here we prove that multiple reads diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java index 765c491ff..729b5eff3 100644 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java @@ -31,12 +31,16 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; import org.mockito.InOrder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @author hongshuwei@gmail.com */ public class WriterTest extends StdOutTest { + private static final Logger LOGGER = LoggerFactory.getLogger(WriterTest.class); + /** * Verify that multiple writers will get the lock in order. */ @@ -58,7 +62,7 @@ public class WriterTest extends StdOutTest { try { executeService.awaitTermination(10, TimeUnit.SECONDS); } catch (InterruptedException e) { - System.out.println("Error waiting for ExecutorService shutdown"); + LOGGER.error("Error waiting for ExecutorService shutdown", e); } // Write operation will hold the write lock 250 milliseconds, so here we verify that when two // writer execute concurrently, the second writer can only writes only when the first one is diff --git a/repository/src/main/java/com/iluwatar/repository/App.java b/repository/src/main/java/com/iluwatar/repository/App.java index 2807ae7ca..ab61852d2 100644 --- a/repository/src/main/java/com/iluwatar/repository/App.java +++ b/repository/src/main/java/com/iluwatar/repository/App.java @@ -24,6 +24,8 @@ package com.iluwatar.repository; import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.support.ClassPathXmlApplicationContext; /** @@ -43,6 +45,8 @@ import org.springframework.context.support.ClassPathXmlApplicationContext; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -67,12 +71,12 @@ public class App { repository.save(terry); // Count Person records - System.out.println("Count Person records: " + repository.count()); + LOGGER.info("Count Person records: {}", repository.count()); // Print all records List persons = (List) repository.findAll(); for (Person person : persons) { - System.out.println(person); + LOGGER.info(person.toString()); } // Update Person @@ -80,24 +84,24 @@ public class App { nasta.setSurname("Spotakova"); repository.save(nasta); - System.out.println("Find by id 2: " + repository.findOne(2L)); + LOGGER.info("Find by id 2: {}", repository.findOne(2L)); // Remove record from Person repository.delete(2L); // count records - System.out.println("Count Person records: " + repository.count()); + LOGGER.info("Count Person records: {}", repository.count()); // find by name Person p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); - System.out.println("Find by John is " + p); + LOGGER.info("Find by John is {}", p); // find by age persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); - System.out.println("Find Person with age between 20,40: "); + LOGGER.info("Find Person with age between 20,40: "); for (Person person : persons) { - System.out.println(person); + LOGGER.info(person.toString()); } repository.deleteAll(); diff --git a/repository/src/main/java/com/iluwatar/repository/AppConfig.java b/repository/src/main/java/com/iluwatar/repository/AppConfig.java index 3e7093358..09f6753bb 100644 --- a/repository/src/main/java/com/iluwatar/repository/AppConfig.java +++ b/repository/src/main/java/com/iluwatar/repository/AppConfig.java @@ -30,6 +30,8 @@ import javax.sql.DataSource; import org.apache.commons.dbcp.BasicDataSource; import org.hibernate.jpa.HibernatePersistenceProvider; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @@ -44,6 +46,8 @@ import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean; @EnableJpaRepositories public class AppConfig { + private static final Logger LOGGER = LoggerFactory.getLogger(AppConfig.class); + /** * Creation of H2 db * @@ -117,12 +121,12 @@ public class AppConfig { repository.save(terry); // Count Person records - System.out.println("Count Person records: " + repository.count()); + LOGGER.info("Count Person records: {}", repository.count()); // Print all records List persons = (List) repository.findAll(); for (Person person : persons) { - System.out.println(person); + LOGGER.info(person.toString()); } // Update Person @@ -130,24 +134,24 @@ public class AppConfig { nasta.setSurname("Spotakova"); repository.save(nasta); - System.out.println("Find by id 2: " + repository.findOne(2L)); + LOGGER.info("Find by id 2: {}", repository.findOne(2L)); // Remove record from Person repository.delete(2L); // count records - System.out.println("Count Person records: " + repository.count()); + LOGGER.info("Count Person records: {}", repository.count()); // find by name Person p = repository.findOne(new PersonSpecifications.NameEqualSpec("John")); - System.out.println("Find by John is " + p); + LOGGER.info("Find by John is {}", p); // find by age persons = repository.findAll(new PersonSpecifications.AgeBetweenSpec(20, 40)); - System.out.println("Find Person with age between 20,40: "); + LOGGER.info("Find Person with age between 20,40: "); for (Person person : persons) { - System.out.println(person); + LOGGER.info(person.toString()); } context.close(); diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java index 413fb0eab..bb722a36d 100644 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.resource.acquisition.is.initialization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Resource Acquisition Is Initialization pattern was developed for exception safe resource @@ -44,17 +47,19 @@ package com.iluwatar.resource.acquisition.is.initialization; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ public static void main(String[] args) throws Exception { try (SlidingDoor slidingDoor = new SlidingDoor()) { - System.out.println("Walking in."); + LOGGER.info("Walking in."); } try (TreasureChest treasureChest = new TreasureChest()) { - System.out.println("Looting contents."); + LOGGER.info("Looting contents."); } } } diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java index 3a1a35f2d..ac8512d15 100644 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/SlidingDoor.java @@ -22,6 +22,9 @@ */ package com.iluwatar.resource.acquisition.is.initialization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * SlidingDoor resource @@ -29,12 +32,14 @@ package com.iluwatar.resource.acquisition.is.initialization; */ public class SlidingDoor implements AutoCloseable { + private static final Logger LOGGER = LoggerFactory.getLogger(SlidingDoor.class); + public SlidingDoor() { - System.out.println("Sliding door opens."); + LOGGER.info("Sliding door opens."); } @Override public void close() throws Exception { - System.out.println("Sliding door closes."); + LOGGER.info("Sliding door closes."); } } diff --git a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java index e7b7ebab6..f020b5fa3 100644 --- a/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java +++ b/resource-acquisition-is-initialization/src/main/java/com/iluwatar/resource/acquisition/is/initialization/TreasureChest.java @@ -22,6 +22,9 @@ */ package com.iluwatar.resource.acquisition.is.initialization; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.Closeable; import java.io.IOException; @@ -32,12 +35,14 @@ import java.io.IOException; */ public class TreasureChest implements Closeable { + private static final Logger LOGGER = LoggerFactory.getLogger(TreasureChest.class); + public TreasureChest() { - System.out.println("Treasure chest opens."); + LOGGER.info("Treasure chest opens."); } @Override public void close() throws IOException { - System.out.println("Treasure chest closes."); + LOGGER.info("Treasure chest closes."); } } diff --git a/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java index 0a4713438..e0546edbe 100644 --- a/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java +++ b/semaphore/src/main/java/com/iluwatar/semaphore/Customer.java @@ -22,12 +22,17 @@ */ package com.iluwatar.semaphore; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A Customer attempts to repeatedly take Fruit from the FruitShop by * taking Fruit from FruitBowl instances. */ public class Customer extends Thread { + private static final Logger LOGGER = LoggerFactory.getLogger(Customer.class); + /** * Name of the Customer. */ @@ -63,13 +68,13 @@ public class Customer extends Thread { Fruit fruit; if (bowl != null && (fruit = bowl.take()) != null) { - System.out.println(name + " took an " + fruit); + LOGGER.info("{} took an {}", name, fruit); fruitBowl.put(fruit); fruitShop.returnBowl(bowl); } } - - System.out.println(name + " took " + fruitBowl); + + LOGGER.info("{} took {}", name, fruitBowl); } diff --git a/servant/src/main/java/com/iluwatar/servant/App.java b/servant/src/main/java/com/iluwatar/servant/App.java index 92829441d..aeb379846 100644 --- a/servant/src/main/java/com/iluwatar/servant/App.java +++ b/servant/src/main/java/com/iluwatar/servant/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.servant; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; @@ -35,6 +38,8 @@ import java.util.ArrayList; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + static Servant jenkins = new Servant("Jenkins"); static Servant travis = new Servant("Travis"); @@ -73,9 +78,9 @@ public class App { // check your luck if (servant.checkIfYouWillBeHanged(guests)) { - System.out.println(servant.name + " will live another day"); + LOGGER.info("{} will live another day", servant.name); } else { - System.out.println("Poor " + servant.name + ". His days are numbered"); + LOGGER.info("Poor {}. His days are numbered", servant.name); } } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java index 8282d800c..bf15c5885 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/app/App.java @@ -35,6 +35,8 @@ import com.iluwatar.servicelayer.spellbook.SpellbookDaoImpl; import com.iluwatar.servicelayer.wizard.Wizard; import com.iluwatar.servicelayer.wizard.WizardDao; import com.iluwatar.servicelayer.wizard.WizardDaoImpl; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -57,6 +59,8 @@ import com.iluwatar.servicelayer.wizard.WizardDaoImpl; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -180,27 +184,27 @@ public class App { public static void queryData() { MagicService service = new MagicServiceImpl(new WizardDaoImpl(), new SpellbookDaoImpl(), new SpellDaoImpl()); - System.out.println("Enumerating all wizards"); + LOGGER.info("Enumerating all wizards"); for (Wizard w : service.findAllWizards()) { - System.out.println(w.getName()); + LOGGER.info(w.getName()); } - System.out.println("Enumerating all spellbooks"); + LOGGER.info("Enumerating all spellbooks"); for (Spellbook s : service.findAllSpellbooks()) { - System.out.println(s.getName()); + LOGGER.info(s.getName()); } - System.out.println("Enumerating all spells"); + LOGGER.info("Enumerating all spells"); for (Spell s : service.findAllSpells()) { - System.out.println(s.getName()); + LOGGER.info(s.getName()); } - System.out.println("Find wizards with spellbook 'Book of Idores'"); + LOGGER.info("Find wizards with spellbook 'Book of Idores'"); List wizardsWithSpellbook = service.findWizardsWithSpellbook("Book of Idores"); for (Wizard w : wizardsWithSpellbook) { - System.out.println(String.format("%s has 'Book of Idores'", w.getName())); + LOGGER.info("{} has 'Book of Idores'", w.getName()); } - System.out.println("Find wizards with spell 'Fireball'"); + LOGGER.info("Find wizards with spell 'Fireball'"); List wizardsWithSpell = service.findWizardsWithSpell("Fireball"); for (Wizard w : wizardsWithSpell) { - System.out.println(String.format("%s has 'Fireball'", w.getName())); + LOGGER.info("{} has 'Fireball'", w.getName()); } } } diff --git a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java index b30b97b65..eead0e197 100644 --- a/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java +++ b/service-layer/src/main/java/com/iluwatar/servicelayer/hibernate/HibernateUtil.java @@ -28,12 +28,16 @@ import com.iluwatar.servicelayer.wizard.Wizard; import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * Produces the Hibernate {@link SessionFactory}. */ public final class HibernateUtil { + private static final Logger LOGGER = LoggerFactory.getLogger(HibernateUtil.class); + /** * The cached session factory */ @@ -59,7 +63,7 @@ public final class HibernateUtil { .setProperty("hibernate.show_sql", "true") .setProperty("hibernate.hbm2ddl.auto", "create-drop").buildSessionFactory(); } catch (Throwable ex) { - System.err.println("Initial SessionFactory creation failed." + ex); + LOGGER.error("Initial SessionFactory creation failed.", ex); throw new ExceptionInInitializerError(ex); } } diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java b/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java index 8063fc818..134eb6d89 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/InitContext.java @@ -22,6 +22,9 @@ */ package com.iluwatar.servicelocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * For JNDI lookup of services from the web.xml. Will match name of the service name that is being * requested and return a newly created service object with the name @@ -30,6 +33,8 @@ package com.iluwatar.servicelocator; */ public class InitContext { + private static final Logger LOGGER = LoggerFactory.getLogger(InitContext.class); + /** * Perform the lookup based on the service name. The returned object will need to be casted into a * {@link Service} @@ -39,10 +44,10 @@ public class InitContext { */ public Object lookup(String serviceName) { if (serviceName.equals("jndi/serviceA")) { - System.out.println("Looking up service A and creating new service for A"); + LOGGER.info("Looking up service A and creating new service for A"); return new ServiceImpl("jndi/serviceA"); } else if (serviceName.equals("jndi/serviceB")) { - System.out.println("Looking up service B and creating new service for B"); + LOGGER.info("Looking up service B and creating new service for B"); return new ServiceImpl("jndi/serviceB"); } else { return null; diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java index 7e2169fcb..89b2a6638 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceCache.java @@ -22,6 +22,9 @@ */ package com.iluwatar.servicelocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.HashMap; import java.util.Map; @@ -35,6 +38,8 @@ import java.util.Map; */ public class ServiceCache { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceCache.class); + private final Map serviceCache; public ServiceCache() { @@ -52,8 +57,8 @@ public class ServiceCache { for (String serviceJndiName : serviceCache.keySet()) { if (serviceJndiName.equals(serviceName)) { cachedService = serviceCache.get(serviceJndiName); - System.out.println("(cache call) Fetched service " + cachedService.getName() + "(" - + cachedService.getId() + ") from cache... !"); + LOGGER.info("(cache call) Fetched service {}({}) from cache... !", + cachedService.getName(), cachedService.getId()); } } return cachedService; diff --git a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java index 543fb8480..328926ce2 100644 --- a/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java +++ b/service-locator/src/main/java/com/iluwatar/servicelocator/ServiceImpl.java @@ -22,6 +22,9 @@ */ package com.iluwatar.servicelocator; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * This is a single service implementation of a sample service. This is the actual service that will * process the request. The reference for this service is to be looked upon in the JNDI server that @@ -31,6 +34,8 @@ package com.iluwatar.servicelocator; */ public class ServiceImpl implements Service { + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceImpl.class); + private final String serviceName; private final int id; @@ -57,6 +62,6 @@ public class ServiceImpl implements Service { @Override public void execute() { - System.out.println("Service " + getName() + " is now executing with id " + getId()); + LOGGER.info("Service {} is now executing with id {}", getName(), getId()); } } diff --git a/singleton/src/main/java/com/iluwatar/singleton/App.java b/singleton/src/main/java/com/iluwatar/singleton/App.java index 4b505085a..31dcd1803 100644 --- a/singleton/src/main/java/com/iluwatar/singleton/App.java +++ b/singleton/src/main/java/com/iluwatar/singleton/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.singleton; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Singleton pattern ensures that the class can have only one existing instance per Java classloader * instance and provides global access to it. @@ -60,6 +63,8 @@ package com.iluwatar.singleton; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point. * @@ -70,35 +75,35 @@ public class App { // eagerly initialized singleton IvoryTower ivoryTower1 = IvoryTower.getInstance(); IvoryTower ivoryTower2 = IvoryTower.getInstance(); - System.out.println("ivoryTower1=" + ivoryTower1); - System.out.println("ivoryTower2=" + ivoryTower2); + LOGGER.info("ivoryTower1={}", ivoryTower1); + LOGGER.info("ivoryTower2={}", ivoryTower2); // lazily initialized singleton ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower1 = ThreadSafeLazyLoadedIvoryTower.getInstance(); ThreadSafeLazyLoadedIvoryTower threadSafeIvoryTower2 = ThreadSafeLazyLoadedIvoryTower.getInstance(); - System.out.println("threadSafeIvoryTower1=" + threadSafeIvoryTower1); - System.out.println("threadSafeIvoryTower2=" + threadSafeIvoryTower2); + LOGGER.info("threadSafeIvoryTower1={}", threadSafeIvoryTower1); + LOGGER.info("threadSafeIvoryTower2={}", threadSafeIvoryTower2); // enum singleton EnumIvoryTower enumIvoryTower1 = EnumIvoryTower.INSTANCE; EnumIvoryTower enumIvoryTower2 = EnumIvoryTower.INSTANCE; - System.out.println("enumIvoryTower1=" + enumIvoryTower1); - System.out.println("enumIvoryTower2=" + enumIvoryTower2); + LOGGER.info("enumIvoryTower1={}", enumIvoryTower1); + LOGGER.info("enumIvoryTower2={}", enumIvoryTower2); // double checked locking ThreadSafeDoubleCheckLocking dcl1 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl1); + LOGGER.info(dcl1.toString()); ThreadSafeDoubleCheckLocking dcl2 = ThreadSafeDoubleCheckLocking.getInstance(); - System.out.println(dcl2); + LOGGER.info(dcl2.toString()); // initialize on demand holder idiom InitializingOnDemandHolderIdiom demandHolderIdiom = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom); + LOGGER.info(demandHolderIdiom.toString()); InitializingOnDemandHolderIdiom demandHolderIdiom2 = InitializingOnDemandHolderIdiom.getInstance(); - System.out.println(demandHolderIdiom2); + LOGGER.info(demandHolderIdiom2.toString()); } } diff --git a/specification/src/main/java/com/iluwatar/specification/app/App.java b/specification/src/main/java/com/iluwatar/specification/app/App.java index 7cbd38470..7e85d2713 100644 --- a/specification/src/main/java/com/iluwatar/specification/app/App.java +++ b/specification/src/main/java/com/iluwatar/specification/app/App.java @@ -37,6 +37,8 @@ import com.iluwatar.specification.property.Color; import com.iluwatar.specification.property.Movement; import com.iluwatar.specification.selector.ColorSelector; import com.iluwatar.specification.selector.MovementSelector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** * @@ -52,6 +54,8 @@ import com.iluwatar.specification.selector.MovementSelector; * */ public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); /** * Program entry point @@ -62,22 +66,22 @@ public class App { Arrays.asList(new Goblin(), new Octopus(), new Dragon(), new Shark(), new Troll(), new KillerBee()); // find all walking creatures - System.out.println("Find all walking creatures"); + LOGGER.info("Find all walking creatures"); List walkingCreatures = creatures.stream().filter(new MovementSelector(Movement.WALKING)) .collect(Collectors.toList()); - walkingCreatures.stream().forEach(System.out::println); + walkingCreatures.stream().forEach(c -> LOGGER.info(c.toString())); // find all dark creatures - System.out.println("Find all dark creatures"); + LOGGER.info("Find all dark creatures"); List darkCreatures = creatures.stream().filter(new ColorSelector(Color.DARK)).collect(Collectors.toList()); - darkCreatures.stream().forEach(System.out::println); + darkCreatures.stream().forEach(c -> LOGGER.info(c.toString())); // find all red and flying creatures - System.out.println("Find all red and flying creatures"); + LOGGER.info("Find all red and flying creatures"); List redAndFlyingCreatures = creatures.stream() .filter(new ColorSelector(Color.RED).and(new MovementSelector(Movement.FLYING))) .collect(Collectors.toList()); - redAndFlyingCreatures.stream().forEach(System.out::println); + redAndFlyingCreatures.stream().forEach(c -> LOGGER.info(c.toString())); } } diff --git a/state/src/main/java/com/iluwatar/state/AngryState.java b/state/src/main/java/com/iluwatar/state/AngryState.java index c58f85ae1..3ebbed142 100644 --- a/state/src/main/java/com/iluwatar/state/AngryState.java +++ b/state/src/main/java/com/iluwatar/state/AngryState.java @@ -22,6 +22,9 @@ */ package com.iluwatar.state; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Angry state. @@ -29,6 +32,8 @@ package com.iluwatar.state; */ public class AngryState implements State { + private static final Logger LOGGER = LoggerFactory.getLogger(AngryState.class); + private Mammoth mammoth; public AngryState(Mammoth mammoth) { @@ -37,12 +42,12 @@ public class AngryState implements State { @Override public void observe() { - System.out.println(String.format("%s is furious!", mammoth)); + LOGGER.info("{} is furious!", mammoth); } @Override public void onEnterState() { - System.out.println(String.format("%s gets angry!", mammoth)); + LOGGER.info("{} gets angry!", mammoth); } } diff --git a/state/src/main/java/com/iluwatar/state/PeacefulState.java b/state/src/main/java/com/iluwatar/state/PeacefulState.java index 23f4e893c..870298632 100644 --- a/state/src/main/java/com/iluwatar/state/PeacefulState.java +++ b/state/src/main/java/com/iluwatar/state/PeacefulState.java @@ -22,6 +22,9 @@ */ package com.iluwatar.state; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Peaceful state. @@ -29,6 +32,8 @@ package com.iluwatar.state; */ public class PeacefulState implements State { + private static final Logger LOGGER = LoggerFactory.getLogger(PeacefulState.class); + private Mammoth mammoth; public PeacefulState(Mammoth mammoth) { @@ -37,12 +42,12 @@ public class PeacefulState implements State { @Override public void observe() { - System.out.println(String.format("%s is calm and peaceful.", mammoth)); + LOGGER.info("{} is calm and peaceful.", mammoth); } @Override public void onEnterState() { - System.out.println(String.format("%s calms down.", mammoth)); + LOGGER.info("{} calms down.", mammoth); } } diff --git a/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java index aeb759ba8..fe5c5a8cd 100644 --- a/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java +++ b/step-builder/src/main/java/com/iluwatar/stepbuilder/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.stepbuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * Step Builder Pattern * @@ -56,6 +59,8 @@ package com.iluwatar.stepbuilder; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -67,18 +72,18 @@ public class App { CharacterStepBuilder.newBuilder().name("Amberjill").fighterClass("Paladin") .withWeapon("Sword").noAbilities().build(); - System.out.println(warrior); + LOGGER.info(warrior.toString()); Character mage = CharacterStepBuilder.newBuilder().name("Riobard").wizardClass("Sorcerer") .withSpell("Fireball").withAbility("Fire Aura").withAbility("Teleport") .noMoreAbilities().build(); - System.out.println(mage); + LOGGER.info(mage.toString()); Character thief = CharacterStepBuilder.newBuilder().name("Desmond").fighterClass("Rogue").noWeapon().build(); - System.out.println(thief); + LOGGER.info(thief.toString()); } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/App.java b/strategy/src/main/java/com/iluwatar/strategy/App.java index be8826fe3..28485701c 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/App.java +++ b/strategy/src/main/java/com/iluwatar/strategy/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.strategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * The Strategy pattern (also known as the policy pattern) is a software design pattern that enables @@ -37,6 +40,8 @@ package com.iluwatar.strategy; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point * @@ -44,27 +49,27 @@ public class App { */ public static void main(String[] args) { // GoF Strategy pattern - System.out.println("Green dragon spotted ahead!"); + LOGGER.info("Green dragon spotted ahead!"); DragonSlayer dragonSlayer = new DragonSlayer(new MeleeStrategy()); dragonSlayer.goToBattle(); - System.out.println("Red dragon emerges."); + LOGGER.info("Red dragon emerges."); dragonSlayer.changeStrategy(new ProjectileStrategy()); dragonSlayer.goToBattle(); - System.out.println("Black dragon lands before you."); + LOGGER.info("Black dragon lands before you."); dragonSlayer.changeStrategy(new SpellStrategy()); dragonSlayer.goToBattle(); // Java 8 Strategy pattern - System.out.println("Green dragon spotted ahead!"); + LOGGER.info("Green dragon spotted ahead!"); dragonSlayer = new DragonSlayer( - () -> System.out.println("With your Excalibur you severe the dragon's head!")); + () -> LOGGER.info("With your Excalibur you severe the dragon's head!")); dragonSlayer.goToBattle(); - System.out.println("Red dragon emerges."); - dragonSlayer.changeStrategy(() -> System.out.println( + LOGGER.info("Red dragon emerges."); + dragonSlayer.changeStrategy(() -> LOGGER.info( "You shoot the dragon with the magical crossbow and it falls dead on the ground!")); dragonSlayer.goToBattle(); - System.out.println("Black dragon lands before you."); - dragonSlayer.changeStrategy(() -> System.out.println( + LOGGER.info("Black dragon lands before you."); + dragonSlayer.changeStrategy(() -> LOGGER.info( "You cast the spell of disintegration and the dragon vaporizes in a pile of dust!")); dragonSlayer.goToBattle(); } diff --git a/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java index d17ff9041..d67f8b559 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/MeleeStrategy.java @@ -22,6 +22,9 @@ */ package com.iluwatar.strategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Melee strategy. @@ -29,8 +32,10 @@ package com.iluwatar.strategy; */ public class MeleeStrategy implements DragonSlayingStrategy { + private static final Logger LOGGER = LoggerFactory.getLogger(MeleeStrategy.class); + @Override public void execute() { - System.out.println("With your Excalibur you sever the dragon's head!"); + LOGGER.info("With your Excalibur you sever the dragon's head!"); } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java index 7cd731167..ea88fdc8b 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/ProjectileStrategy.java @@ -22,6 +22,9 @@ */ package com.iluwatar.strategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Projectile strategy. @@ -29,9 +32,10 @@ package com.iluwatar.strategy; */ public class ProjectileStrategy implements DragonSlayingStrategy { + private static final Logger LOGGER = LoggerFactory.getLogger(ProjectileStrategy.class); + @Override public void execute() { - System.out - .println("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); + LOGGER.info("You shoot the dragon with the magical crossbow and it falls dead on the ground!"); } } diff --git a/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java b/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java index 6309ed31b..3264799bf 100644 --- a/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java +++ b/strategy/src/main/java/com/iluwatar/strategy/SpellStrategy.java @@ -22,6 +22,9 @@ */ package com.iluwatar.strategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Spell strategy. @@ -29,10 +32,11 @@ package com.iluwatar.strategy; */ public class SpellStrategy implements DragonSlayingStrategy { + private static final Logger LOGGER = LoggerFactory.getLogger(SpellStrategy.class); + @Override public void execute() { - System.out - .println("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); + LOGGER.info("You cast the spell of disintegration and the dragon vaporizes in a pile of dust!"); } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java index 7a78576a1..49cbbd7dc 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/HitAndRunMethod.java @@ -22,6 +22,9 @@ */ package com.iluwatar.templatemethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * HitAndRunMethod implementation of {@link StealingMethod}. @@ -29,6 +32,8 @@ package com.iluwatar.templatemethod; */ public class HitAndRunMethod extends StealingMethod { + private static final Logger LOGGER = LoggerFactory.getLogger(HitAndRunMethod.class); + @Override protected String pickTarget() { return "old goblin woman"; @@ -36,11 +41,11 @@ public class HitAndRunMethod extends StealingMethod { @Override protected void confuseTarget(String target) { - System.out.println("Approach the " + target + " from behind."); + LOGGER.info("Approach the {} from behind.", target); } @Override protected void stealTheItem(String target) { - System.out.println("Grab the handbag and run away fast!"); + LOGGER.info("Grab the handbag and run away fast!"); } } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java index c8c584cdd..85896d922 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/StealingMethod.java @@ -22,6 +22,9 @@ */ package com.iluwatar.templatemethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * StealingMethod defines skeleton for the algorithm. @@ -29,6 +32,8 @@ package com.iluwatar.templatemethod; */ public abstract class StealingMethod { + private static final Logger LOGGER = LoggerFactory.getLogger(StealingMethod.class); + protected abstract String pickTarget(); protected abstract void confuseTarget(String target); @@ -40,7 +45,7 @@ public abstract class StealingMethod { */ public void steal() { String target = pickTarget(); - System.out.println("The target has been chosen as " + target + "."); + LOGGER.info("The target has been chosen as {}.", target); confuseTarget(target); stealTheItem(target); } diff --git a/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java b/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java index 4fdb5d758..c7855fe4e 100644 --- a/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java +++ b/template-method/src/main/java/com/iluwatar/templatemethod/SubtleMethod.java @@ -22,6 +22,9 @@ */ package com.iluwatar.templatemethod; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * SubtleMethod implementation of {@link StealingMethod}. @@ -29,6 +32,8 @@ package com.iluwatar.templatemethod; */ public class SubtleMethod extends StealingMethod { + private static final Logger LOGGER = LoggerFactory.getLogger(SubtleMethod.class); + @Override protected String pickTarget() { return "shop keeper"; @@ -36,11 +41,11 @@ public class SubtleMethod extends StealingMethod { @Override protected void confuseTarget(String target) { - System.out.println("Approach the " + target + " with tears running and hug him!"); + LOGGER.info("Approach the {} with tears running and hug him!", target); } @Override protected void stealTheItem(String target) { - System.out.println("While in close contact grab the " + target + "'s wallet."); + LOGGER.info("While in close contact grab the {}'s wallet.", target); } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/App.java b/thread-pool/src/main/java/com/iluwatar/threadpool/App.java index 16fbca35a..20d773c82 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/App.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.threadpool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutorService; @@ -43,6 +46,8 @@ import java.util.concurrent.Executors; * */ public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); /** * Program entry point @@ -51,7 +56,7 @@ public class App { */ public static void main(String[] args) { - System.out.println("Program started"); + LOGGER.info("Program started"); // Create a list of tasks to be executed List tasks = new ArrayList<>(); @@ -89,6 +94,6 @@ public class App { while (!executor.isTerminated()) { Thread.yield(); } - System.out.println("Program finished"); + LOGGER.info("Program finished"); } } diff --git a/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java b/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java index 1354cab41..1740b289e 100644 --- a/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java +++ b/thread-pool/src/main/java/com/iluwatar/threadpool/Worker.java @@ -22,6 +22,9 @@ */ package com.iluwatar.threadpool; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * Worker implements {@link Runnable} and thus can be executed by {@link ExecutorService} @@ -29,6 +32,8 @@ package com.iluwatar.threadpool; */ public class Worker implements Runnable { + private static final Logger LOGGER = LoggerFactory.getLogger(Worker.class); + private final Task task; public Worker(final Task task) { @@ -37,8 +42,7 @@ public class Worker implements Runnable { @Override public void run() { - System.out.println(String.format("%s processing %s", Thread.currentThread().getName(), - task.toString())); + LOGGER.info("{} processing {}", Thread.currentThread().getName(), task.toString()); try { Thread.sleep(task.getTimeMs()); } catch (InterruptedException e) { diff --git a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java index 066b6e737..14ebcab40 100644 --- a/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java +++ b/tolerant-reader/src/main/java/com/iluwatar/tolerantreader/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.tolerantreader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; /** @@ -41,31 +44,33 @@ import java.io.IOException; */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * Program entry point */ public static void main(String[] args) throws IOException, ClassNotFoundException { // Write V1 RainbowFish fishV1 = new RainbowFish("Zed", 10, 11, 12); - System.out.println(String.format("fishV1 name=%s age=%d length=%d weight=%d", fishV1.getName(), - fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons())); + LOGGER.info("fishV1 name={} age={} length={} weight={}", fishV1.getName(), + fishV1.getAge(), fishV1.getLengthMeters(), fishV1.getWeightTons()); RainbowFishSerializer.writeV1(fishV1, "fish1.out"); // Read V1 RainbowFish deserializedFishV1 = RainbowFishSerializer.readV1("fish1.out"); - System.out.println(String.format("deserializedFishV1 name=%s age=%d length=%d weight=%d", + LOGGER.info("deserializedFishV1 name={} age={} length={} weight={}", deserializedFishV1.getName(), deserializedFishV1.getAge(), - deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons())); + deserializedFishV1.getLengthMeters(), deserializedFishV1.getWeightTons()); // Write V2 RainbowFishV2 fishV2 = new RainbowFishV2("Scar", 5, 12, 15, true, true, true); - System.out.println(String.format( - "fishV2 name=%s age=%d length=%d weight=%d sleeping=%b hungry=%b angry=%b", + LOGGER.info( + "fishV2 name={} age={} length={} weight={} sleeping={} hungry={} angry={}", fishV2.getName(), fishV2.getAge(), fishV2.getLengthMeters(), fishV2.getWeightTons(), - fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping())); + fishV2.getHungry(), fishV2.getAngry(), fishV2.getSleeping()); RainbowFishSerializer.writeV2(fishV2, "fish2.out"); // Read V2 with V1 method RainbowFish deserializedFishV2 = RainbowFishSerializer.readV1("fish2.out"); - System.out.println(String.format("deserializedFishV2 name=%s age=%d length=%d weight=%d", + LOGGER.info("deserializedFishV2 name={} age={} length={} weight={}", deserializedFishV2.getName(), deserializedFishV2.getAge(), - deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons())); + deserializedFishV2.getLengthMeters(), deserializedFishV2.getWeightTons()); } } diff --git a/twin/src/main/java/com/iluwatar/twin/BallItem.java b/twin/src/main/java/com/iluwatar/twin/BallItem.java index 0188b5731..0c47bc91c 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallItem.java +++ b/twin/src/main/java/com/iluwatar/twin/BallItem.java @@ -23,6 +23,9 @@ package com.iluwatar.twin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * This class represents a Ball which extends {@link GameItem} and implements the logic for ball * item, like move and draw. It hold a reference of {@link BallThread} to delegate the suspend and @@ -30,6 +33,8 @@ package com.iluwatar.twin; */ public class BallItem extends GameItem { + private static final Logger LOGGER = LoggerFactory.getLogger(BallItem.class); + private boolean isSuspended; private BallThread twin; @@ -41,11 +46,11 @@ public class BallItem extends GameItem { @Override public void doDraw() { - System.out.println("doDraw"); + LOGGER.info("doDraw"); } public void move() { - System.out.println("move"); + LOGGER.info("move"); } @Override diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index 194d85b06..6dea979be 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -23,6 +23,9 @@ package com.iluwatar.twin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * This class is a UI thread for drawing the {@link BallItem}, and provide the method for suspend * and resume. It hold the reference of {@link BallItem} to delegate the draw task. @@ -31,6 +34,8 @@ package com.iluwatar.twin; public class BallThread extends Thread { + private static final Logger LOGGER = LoggerFactory.getLogger(BallThread.class); + private BallItem twin; private volatile boolean isSuspended; @@ -61,12 +66,12 @@ public class BallThread extends Thread { public void suspendMe() { isSuspended = true; - System.out.println("Begin to suspend BallThread"); + LOGGER.info("Begin to suspend BallThread"); } public void resumeMe() { isSuspended = false; - System.out.println("Begin to resume BallThread"); + LOGGER.info("Begin to resume BallThread"); } public void stopMe() { diff --git a/twin/src/main/java/com/iluwatar/twin/GameItem.java b/twin/src/main/java/com/iluwatar/twin/GameItem.java index 08d7dcce7..da2cef7a2 100644 --- a/twin/src/main/java/com/iluwatar/twin/GameItem.java +++ b/twin/src/main/java/com/iluwatar/twin/GameItem.java @@ -24,16 +24,21 @@ package com.iluwatar.twin; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * GameItem is a common class which provides some common methods for game object. */ public abstract class GameItem { + private static final Logger LOGGER = LoggerFactory.getLogger(GameItem.class); + /** * Template method, do some common logic before draw */ public void draw() { - System.out.println("draw"); + LOGGER.info("draw"); doDraw(); } diff --git a/value-object/src/main/java/com/iluwatar/value/object/App.java b/value-object/src/main/java/com/iluwatar/value/object/App.java index 1e943d054..238258441 100644 --- a/value-object/src/main/java/com/iluwatar/value/object/App.java +++ b/value-object/src/main/java/com/iluwatar/value/object/App.java @@ -22,6 +22,9 @@ */ package com.iluwatar.value.object; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * A Value Object are objects which follow value semantics rather than reference semantics. This * means value objects' equality are not based on identity. Two value objects are equal when they @@ -38,6 +41,9 @@ package com.iluwatar.value.object; * Colebourne's term VALJO : http://blog.joda.org/2014/03/valjos-value-java-objects.html */ public class App { + + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + /** * This practice creates three HeroStats(Value object) and checks equality between those. */ @@ -46,9 +52,9 @@ public class App { HeroStat statB = HeroStat.valueOf(10, 5, 0); HeroStat statC = HeroStat.valueOf(5, 1, 8); - System.out.println(statA.toString()); + LOGGER.info(statA.toString()); - System.out.println("Is statA and statB equal : " + statA.equals(statB)); - System.out.println("Is statA and statC equal : " + statA.equals(statC)); + LOGGER.info("Is statA and statB equal : {}", statA.equals(statB)); + LOGGER.info("Is statA and statC equal : {}", statA.equals(statC)); } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java index 6e54c7861..466c12f51 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/CommanderVisitor.java @@ -22,6 +22,9 @@ */ package com.iluwatar.visitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * CommanderVisitor @@ -29,6 +32,8 @@ package com.iluwatar.visitor; */ public class CommanderVisitor implements UnitVisitor { + private static final Logger LOGGER = LoggerFactory.getLogger(CommanderVisitor.class); + @Override public void visitSoldier(Soldier soldier) { // Do nothing @@ -41,6 +46,6 @@ public class CommanderVisitor implements UnitVisitor { @Override public void visitCommander(Commander commander) { - System.out.println("Good to see you " + commander); + LOGGER.info("Good to see you {}", commander); } } diff --git a/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java index 4fca0a5bd..aa06cb2ad 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/SergeantVisitor.java @@ -22,6 +22,9 @@ */ package com.iluwatar.visitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * SergeantVisitor @@ -29,6 +32,8 @@ package com.iluwatar.visitor; */ public class SergeantVisitor implements UnitVisitor { + private static final Logger LOGGER = LoggerFactory.getLogger(SergeantVisitor.class); + @Override public void visitSoldier(Soldier soldier) { // Do nothing @@ -36,7 +41,7 @@ public class SergeantVisitor implements UnitVisitor { @Override public void visitSergeant(Sergeant sergeant) { - System.out.println("Hello " + sergeant); + LOGGER.info("Hello {}", sergeant); } @Override diff --git a/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java b/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java index fff24f699..0907a2531 100644 --- a/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java +++ b/visitor/src/main/java/com/iluwatar/visitor/SoldierVisitor.java @@ -22,6 +22,9 @@ */ package com.iluwatar.visitor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** * * SoldierVisitor @@ -29,9 +32,11 @@ package com.iluwatar.visitor; */ public class SoldierVisitor implements UnitVisitor { + private static final Logger LOGGER = LoggerFactory.getLogger(SoldierVisitor.class); + @Override public void visitSoldier(Soldier soldier) { - System.out.println("Greetings " + soldier); + LOGGER.info("Greetings {}", soldier); } @Override From 56b088425814c975e9983a3aa3d99be2ada6d1e8 Mon Sep 17 00:00:00 2001 From: igeligel Date: Mon, 24 Oct 2016 15:28:27 +0200 Subject: [PATCH 094/145] Change name of variables in test #498 --- .../test/RoyaltyObjectMotherTest.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java b/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java index feba71a1b..ebe536d24 100644 --- a/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java +++ b/object-mother/src/test/java/com/iluwatar/objectmother/test/RoyaltyObjectMotherTest.java @@ -45,26 +45,26 @@ public class RoyaltyObjectMotherTest { @Test public void queenIsBlockingFlirtCauseDrunkKing() { - King soberUnhappyKing = RoyaltyObjectMother.createDrunkKing(); + King drunkUnhappyKing = RoyaltyObjectMother.createDrunkKing(); Queen notFlirtyQueen = RoyaltyObjectMother.createNotFlirtyQueen(); - soberUnhappyKing.flirt(notFlirtyQueen); - assertFalse(soberUnhappyKing.isHappy()); + drunkUnhappyKing.flirt(notFlirtyQueen); + assertFalse(drunkUnhappyKing.isHappy()); } @Test public void queenIsBlockingFlirt() { - King soberUnhappyKing = RoyaltyObjectMother.createHappyKing(); + King soberHappyKing = RoyaltyObjectMother.createHappyKing(); Queen notFlirtyQueen = RoyaltyObjectMother.createNotFlirtyQueen(); - soberUnhappyKing.flirt(notFlirtyQueen); - assertFalse(soberUnhappyKing.isHappy()); + soberHappyKing.flirt(notFlirtyQueen); + assertFalse(soberHappyKing.isHappy()); } @Test public void successfullKingFlirt() { - King soberUnhappyKing = RoyaltyObjectMother.createHappyKing(); + King soberHappyKing = RoyaltyObjectMother.createHappyKing(); Queen flirtyQueen = RoyaltyObjectMother.createFlirtyQueen(); - soberUnhappyKing.flirt(flirtyQueen); - assertTrue(soberUnhappyKing.isHappy()); + soberHappyKing.flirt(flirtyQueen); + assertTrue(soberHappyKing.isHappy()); } @Test From 20295316c2f43e36ac3748551bf45ce60194c635 Mon Sep 17 00:00:00 2001 From: igeligel Date: Mon, 24 Oct 2016 15:30:17 +0200 Subject: [PATCH 095/145] add entry to parent pom.xml #498 --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 144b8efff..7aec509d4 100644 --- a/pom.xml +++ b/pom.xml @@ -132,6 +132,7 @@ aggregator-microservices promise page-object + object-mother From 74ac79b01ecb5fe47810e3cfba71e274349144a6 Mon Sep 17 00:00:00 2001 From: igeligel Date: Mon, 24 Oct 2016 15:33:25 +0200 Subject: [PATCH 096/145] Fix pom.xml --- pom.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 598386753..fc68a3830 100644 --- a/pom.xml +++ b/pom.xml @@ -132,13 +132,10 @@ aggregator-microservices promise page-object -<<<<<<< HEAD + event-asynchronous object-mother -======= - event-asynchronous ->>>>>>> refs/remotes/iluwatar/master From 6aed26e61e2af5d3574eaf6b842094ea041947da Mon Sep 17 00:00:00 2001 From: igeligel Date: Mon, 24 Oct 2016 15:43:52 +0200 Subject: [PATCH 097/145] Fix pom.xml Deleted tag because i added it one time more than neccessary --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index fc68a3830..76e108a57 100644 --- a/pom.xml +++ b/pom.xml @@ -135,7 +135,6 @@ event-asynchronous object-mother - From 2a77ac29e994cecc50f5ee5e461abdcb316dbaa3 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Wed, 26 Oct 2016 16:59:36 +0530 Subject: [PATCH 098/145] FirstCut++ FirstCut++ --- module/README.md | 29 +++ module/pom.xml | 45 +++++ .../main/java/com/iluwatar/module/App.java | 73 +++++++ .../iluwatar/module/FilePrinterModule.java | 100 ++++++++++ module/src/main/resources/log4j.xml | 41 ++++ .../java/com/iluwatar/module/AppTest.java | 37 ++++ .../java/com/iluwatar/module/ModuleTest.java | 180 ++++++++++++++++++ pom.xml | 1 + 8 files changed, 506 insertions(+) create mode 100644 module/README.md create mode 100644 module/pom.xml create mode 100644 module/src/main/java/com/iluwatar/module/App.java create mode 100644 module/src/main/java/com/iluwatar/module/FilePrinterModule.java create mode 100644 module/src/main/resources/log4j.xml create mode 100644 module/src/test/java/com/iluwatar/module/AppTest.java create mode 100644 module/src/test/java/com/iluwatar/module/ModuleTest.java diff --git a/module/README.md b/module/README.md new file mode 100644 index 000000000..d3d6b8746 --- /dev/null +++ b/module/README.md @@ -0,0 +1,29 @@ +--- +layout: pattern +title: Module +folder: module +permalink: /patterns/module/ +pumlid: JShB3OGm303HLg20nFVjnYGM1CN6ycTfVtFSsnjfzY5jPgUqkLqHwXy0mxUU8wuyqidQ8q4IjJqCO-QBWGOtVh5qyd5AKOmW4mT6Nu2-ZiAekapH_hkcSTNa-GC0 +categories: Persistence Tier +tags: + - Java + - Difficulty-Beginner +--- + +## Intent +A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself + +![alt text](./etc/module.png "Module") + +## Applicability +The module pattern is a design pattern used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept. + +The Module pattern can be considered a creational pattern and a structural pattern. It manages the creation and organization of other elements, and groups them as the structural pattern does. + +An object that applies this pattern can provide the equivalent of a namespace, providing the initialization and finalization process of a static class or a class with static members with cleaner, more concise syntax and semantics. + +It supports specific cases where a class or object can be considered structured, procedural data. And, vice versa, migrate structured, procedural data, and considered as object-oriented. + +## Credits + +* [Module](https://en.wikipedia.org/wiki/Module_pattern) diff --git a/module/pom.xml b/module/pom.xml new file mode 100644 index 000000000..bc43e3e09 --- /dev/null +++ b/module/pom.xml @@ -0,0 +1,45 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.14.0-SNAPSHOT + + module + + + junit + junit + test + + + log4j + log4j + + + diff --git a/module/src/main/java/com/iluwatar/module/App.java b/module/src/main/java/com/iluwatar/module/App.java new file mode 100644 index 000000000..9dab6a00c --- /dev/null +++ b/module/src/main/java/com/iluwatar/module/App.java @@ -0,0 +1,73 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.module; + +import java.io.FileNotFoundException; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory + * objects from the database. Its responsibility is to transfer data between the + * two and also to isolate them from each other. With Data Mapper the in-memory + * objects needn't know even that there's a database present; they need no SQL + * interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a + * form of Mapper , Data Mapper itself is even unknown to the domain layer. + *

    + * The below example demonstrates basic CRUD operations: Create, Read, Update, + * and Delete. + * + */ +public final class App { + + private static final String OUTPUT_FILE = "output.txt"; + private static final String ERROR_FILE = "error.txt"; + + public static FilePrinterModule filePrinterModule = null; + + public static void prepare() throws FileNotFoundException { + filePrinterModule = FilePrinterModule.getSingleton(); + + filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + } + + public static void unprepare() { + filePrinterModule.unprepare(); + } + + public static final void execute(final String... args) { + filePrinterModule.printString("Hello World"); + } + + /** + * Program entry point. + * + * @param args + * command line args. + * @throws FileNotFoundException + */ + public static final void main(final String... args) + throws FileNotFoundException { + prepare(); + execute(args); + unprepare(); + } + + private App() { + } +} diff --git a/module/src/main/java/com/iluwatar/module/FilePrinterModule.java b/module/src/main/java/com/iluwatar/module/FilePrinterModule.java new file mode 100644 index 000000000..879492248 --- /dev/null +++ b/module/src/main/java/com/iluwatar/module/FilePrinterModule.java @@ -0,0 +1,100 @@ +package com.iluwatar.module; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +import org.apache.log4j.Logger; + +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +public final class FilePrinterModule { + + private static final Logger logger = Logger + .getLogger(FilePrinterModule.class); + + private static FilePrinterModule singleton = null; + + public PrintStream output = null; + public PrintStream error = null; + + private FilePrinterModule() { + } + + public static final FilePrinterModule getSingleton() { + + if (FilePrinterModule.singleton == null) { + FilePrinterModule.singleton = new FilePrinterModule(); + } + + return FilePrinterModule.singleton; + } + + /** + * + * @throws FileNotFoundException + */ + public final void prepare(final String outputFile, final String errorFile) + throws FileNotFoundException { + + logger.debug("MainModule::prepare();"); + + this.output = new PrintStream(new FileOutputStream(outputFile)); + this.error = new PrintStream(new FileOutputStream(errorFile)); + } + + /** + * + */ + public final void unprepare() { + + if (this.output != null) { + + this.output.flush(); + this.output.close(); + } + + if (this.error != null) { + + this.error.flush(); + this.error.close(); + } + + logger.debug("MainModule::unprepare();"); + } + + /** + * + * @param value + */ + public final void printString(final String value) { + this.output.print(value); + } + + /** + * + * @param value + */ + public final void printErrorString(final String value) { + this.error.print(value); + } +} diff --git a/module/src/main/resources/log4j.xml b/module/src/main/resources/log4j.xml new file mode 100644 index 000000000..b591c17e1 --- /dev/null +++ b/module/src/main/resources/log4j.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/module/src/test/java/com/iluwatar/module/AppTest.java b/module/src/test/java/com/iluwatar/module/AppTest.java new file mode 100644 index 000000000..46c12ef38 --- /dev/null +++ b/module/src/test/java/com/iluwatar/module/AppTest.java @@ -0,0 +1,37 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.module; + +import java.io.FileNotFoundException; + +import com.iluwatar.module.App; + +import org.junit.Test; + +/** + * Tests that Data-Mapper example runs without errors. + */ +public final class AppTest { + + @Test + public void test() throws FileNotFoundException { + final String[] args = {}; + App.main(args); + } +} diff --git a/module/src/test/java/com/iluwatar/module/ModuleTest.java b/module/src/test/java/com/iluwatar/module/ModuleTest.java new file mode 100644 index 000000000..797e7f26a --- /dev/null +++ b/module/src/test/java/com/iluwatar/module/ModuleTest.java @@ -0,0 +1,180 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.module; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import org.apache.log4j.Logger; +import org.junit.Test; + +/** + * The Data Mapper (DM) is a layer of software that separates the in-memory + * objects from the database. Its responsibility is to transfer data between the + * two and also to isolate them from each other. With Data Mapper the in-memory + * objects needn't know even that there's a database present; they need no SQL + * interface code, and certainly no knowledge of the database schema. (The + * database schema is always ignorant of the objects that use it.) Since it's a + * form of Mapper , Data Mapper itself is even unknown to the domain layer. + *

    + */ +public class ModuleTest { + + private static final Logger logger = Logger.getLogger(ModuleTest.class); + + private static final String OUTPUT_FILE = "output.txt"; + private static final String ERROR_FILE = "error.txt"; + + private static final String MESSAGE = "MESSAGE"; + private static final String ERROR = "ERROR"; + + /** + * This test verify that 'MESSAGE' is perfectly printed in output file + * + * @throws IOException + */ + @Test + public void testPositiveMessage() throws IOException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FilePrinterModule filePrinterModule = FilePrinterModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + + /* Print 'Message' in file */ + filePrinterModule.printString(MESSAGE); + + /* Test if 'Message' is printed in file */ + assertEquals(readFirstLine(OUTPUT_FILE), MESSAGE); + + /* Unprepare to cleanup the modules */ + filePrinterModule.unprepare(); + } + + /** + * This test verify that nothing is printed in output file + * + * @throws IOException + */ + @Test + public void testNegativeMessage() throws IOException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FilePrinterModule filePrinterModule = FilePrinterModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + + /* Test if nothing is printed in file */ + assertEquals(readFirstLine(OUTPUT_FILE), null); + + /* Unprepare to cleanup the modules */ + filePrinterModule.unprepare(); + } + + /** + * This test verify that 'ERROR' is perfectly printed in error file + * + * @throws FileNotFoundException + */ + @Test + public void testPositiveErrorMessage() throws FileNotFoundException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FilePrinterModule filePrinterModule = FilePrinterModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + + /* Print 'Error' in file */ + filePrinterModule.printErrorString(ERROR); + + /* Test if 'Message' is printed in file */ + assertEquals(readFirstLine(ERROR_FILE), ERROR); + + /* Unprepare to cleanup the modules */ + filePrinterModule.unprepare(); + } + + /** + * This test verify that nothing is printed in error file + * + * @throws FileNotFoundException + */ + @Test + public void testNegativeErrorMessage() throws FileNotFoundException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FilePrinterModule filePrinterModule = FilePrinterModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + + /* Test if nothing is printed in file */ + assertEquals(readFirstLine(ERROR_FILE), null); + + /* Unprepare to cleanup the modules */ + filePrinterModule.unprepare(); + } + + /** + * Utility method to read first line of a file + * + * @param file + * @return + */ + private static final String readFirstLine(final String file) { + + String firstLine = null; + BufferedReader bufferedReader = null; + try { + + /* Create a buffered reader */ + bufferedReader = new BufferedReader(new FileReader(file)); + + /* Read the line */ + firstLine = bufferedReader.readLine(); + + logger.info("ModuleTest::readFile() : firstLine : " + firstLine); + + } catch (final IOException e) { + logger.error("ModuleTest::readFile()", e); + } finally { + + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (final IOException e) { + logger.error("ModuleTest::readFile()", e); + } + } + } + + return firstLine; + } +} diff --git a/pom.xml b/pom.xml index 538058a81..a73992607 100644 --- a/pom.xml +++ b/pom.xml @@ -123,6 +123,7 @@ factory-kit feature-toggle value-object + module monad mute-idiom mutex From 7015e95ac3b8560423b0a417683ab90ea5814599 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Thu, 27 Oct 2016 15:55:08 +0530 Subject: [PATCH 099/145] SecondCut++ SecondCut++ --- module/README.md | 8 +- module/etc/module.png | Bin 0 -> 18027 bytes module/etc/module.ucls | 69 ++++++ .../main/java/com/iluwatar/module/App.java | 28 ++- .../iluwatar/module/ConsoleLoggerModule.java | 106 +++++++++ ...interModule.java => FileLoggerModule.java} | 61 +++--- .../java/com/iluwatar/module/ModuleTest.java | 203 +++++++++++++++--- 7 files changed, 403 insertions(+), 72 deletions(-) create mode 100644 module/etc/module.png create mode 100644 module/etc/module.ucls create mode 100644 module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java rename module/src/main/java/com/iluwatar/module/{FilePrinterModule.java => FileLoggerModule.java} (57%) diff --git a/module/README.md b/module/README.md index d3d6b8746..24bd3f543 100644 --- a/module/README.md +++ b/module/README.md @@ -4,26 +4,22 @@ title: Module folder: module permalink: /patterns/module/ pumlid: JShB3OGm303HLg20nFVjnYGM1CN6ycTfVtFSsnjfzY5jPgUqkLqHwXy0mxUU8wuyqidQ8q4IjJqCO-QBWGOtVh5qyd5AKOmW4mT6Nu2-ZiAekapH_hkcSTNa-GC0 -categories: Persistence Tier +categories: Creational Pattern tags: - Java - Difficulty-Beginner --- ## Intent -A layer of mappers that moves data between objects and a database while keeping them independent of each other and the mapper itself +Module pattern is used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept. ![alt text](./etc/module.png "Module") ## Applicability -The module pattern is a design pattern used to implement the concept of software modules, defined by modular programming, in a programming language with incomplete direct support for the concept. - The Module pattern can be considered a creational pattern and a structural pattern. It manages the creation and organization of other elements, and groups them as the structural pattern does. An object that applies this pattern can provide the equivalent of a namespace, providing the initialization and finalization process of a static class or a class with static members with cleaner, more concise syntax and semantics. -It supports specific cases where a class or object can be considered structured, procedural data. And, vice versa, migrate structured, procedural data, and considered as object-oriented. - ## Credits * [Module](https://en.wikipedia.org/wiki/Module_pattern) diff --git a/module/etc/module.png b/module/etc/module.png new file mode 100644 index 0000000000000000000000000000000000000000..a26807d290c0d2aced42c5e5d48cf0780e9c4d2b GIT binary patch literal 18027 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�QD01sQ8D4HlOTEw0vasSc)Ge?0K=E8P#m9PxJtPyh{Zk>ZR=T{hlcnGl zMVB3%d5M{ouiD6BU7qh8s43sHH1=gZc+E|(k!rPa@S2yqKQ$m=ISLmJBHik>n zdz<@JX*ehsur?s7)SV@d%A*OQgA9)ej*LuFol%4-CTJ0r?&b<5hXwz0(_I3jtItus508|sB>em>pF@mk}>SaZCMQ7da-=-;JZQ~n%!2L zHJ(j&%*aW0q8Vz6`I4s(f424!I1vbR>r8f&sLAMukpu12w3lMY0Ur0KO&d@g{Q#Ld<5d3vF#IC*_Jf6PbIaPr2r1KhPwqSUb7@72 zV;tspeKUjqLd+O4)&bj`(8EdmRwbA=rd3G=e4IjO#Ab_H3^KvoaVnjb*Ocf9#Tc+f4*@Rso8Dp%H-fj`$ITQe?*I{`6`5X~H)R@F zm8G&474s6iWX_&L34^Q*rd(GjrHW97HbKfsT1Ftj+{kfV%{xmTGWialIly+zMyN9{g<0F<)raH z*N8o(S|Qa3bEsmvGV1Ni%k~0`7nkKQJM$thnh=%?Fe>EI(nvtHV;+WZqs477sXz%9x~sv*^siPYXaRs% z{V-rWW7)5G{rxa@co;78u2FO|Q{ltG4efgZve20ZRxLzO&yd#f@cQ}2(NNhyAE z?~;Ss;bCl|9A)T+z9Nlj3F-K7{K5ELJ)r;j zWCZp`58W8!FZU^Z68veffr*{>$fmJaea!cGWRq|lW!v(NF_}?mv?jZpx{Cc7OyZ+* z22V`<LGdrtIk*#g zt-0$}aoFmm^((}cXK}Yf$f{V!NJnL8G^K4W_kEa!gh3ii8q(O*?&%Q?(vhI`FMys6 zr%vCHK)?a55@TndnS^!+Y=myIp_wQCeKK0z@ir^Yk;jDVcvE#MFmdWYP$;>D-vmncd(1P`fDkmYI{w*O+ z(Cw(s6DK=6+qf&1$9lRkUce<*!05r2t!{v#WQ62e{n)D;ev==H``I&;ldc0Z3Ujv{ zWl#fMcb5kuA|et_X6f5rcEt;13orDhN+-5t2n6`%>3J(rMkq&dYDybXDl;lvzJb>F zB&6F)&tIbEwUY>X8Q!vUe5#Z#%XMqBOBwXS%pg%P`AiW8WuTZxG9rmJ0DoebyQ^Q` z-9aJ8CJ}F1POnd?CTWx|FlKjmccvwnn{AfEjob&^XBg3&XY3bemKC!&^%aiq9<_?kIWlY)- z_CcvXH09z_jw((kVj77emJ&g=As8&2+k_o9#^d~}i;TP1M@l!^A|9c{t4VirTxIB_ z691m4%N)|&S}dWQZ5ARGGwwq`zPCE0B$v^B8S`mG4?;%PD$lG6v%!Ni#F9s^CPGtn z;r@5?gLFavG$&~6alVAFO+91SkXArEj7j!+bZ;z>R1?uTwgVmE#~gS3)MXW&jo@egaUhx&uZgSgDk2z7|}=d_n<=Vm?5uquG1mLS!-0>OI*5XvHr!&@GnKMkgqSW196~qV z!wk>#AW}nXn+H3q2e>lp)VP9(Dskd!uqY}hm@+9~sYfd|U}kuadLzWFRhI9vzpit> zt5f@WQ@?MpFvZN=oSOgjx0WbdwEl~d;x?$MdIQ%rcwUK|dcZ5-t|H@zjX^oWrDG&$LoG0u}BWWAj82b-9J(>DSgU z`s35H{hIx1cy9ST^~P(z&uzMrpt8h(fs9)WR#3V>V6KQiQow9*}D_M4;68UO|S1 zTd%C2V%O|-R1^{HJX zEh#PCUmH9j}BwTHPZh2oi zIS_|&5NQ)GD%-2!qTi-S;rcGeUjeAK-N~Ks#9G*1&Q#NFt+09E)yL?z=ygUa?MBL@ zXGfnph$5!SGFXEW_#ms})iEviH39!wo|dE1gGgj4>w0&SJn~YQc&m~TE9mB|^JX}M zlL}tB;LadFhFGqOuDSehc5ANv(W6JzO|RDO)2lpC@K@Gt?AtM|A{p_gD$AgKv{*%W z?^XuZX)vxlhjNq%-DUC7siJFYdD<%4ijQyNbtcpQxUlSp`6!ZzLgwZ9wR>j>!Y69n zRK|EO#XkYa-rOwKr*@&803%x%-cy-HK+c>|AbqB7h_bEspUJx2qAN9N92v!!l1o+k zO%JL7Q;abqjyh&=&Qf8Xy(@<68R9l6JDNP!nJY~uQg+5u-)R9m-5(t^lbH-2R+}N? zm3fpUqdGvZn9~a?eOo!vuhBLpqtI|tdRor%8@W2p+@oNy)MVvY{7cIq%|%v74ttcl zOQFDHQ62IKsCLzXB0YvJI6`})y9j2-9mc523RU8%-XkL5-g6VdF7G91LJpy zFHEjlLc32roc-LNE$z<_60mNx#_|VzO9ear9Hja@dGBb27!O{gD{=%4mib(n32)wi39|`rMaeWwoWNYe zQv|l{l`>Qrx}m*gshg-syn7JWd8wK7A!JzRa7PH$aeVb`E5#si~@BCa8l}$h?AVxbIgI zQ}bRAR#xV=>jVH-<61sUob|(O^2#OGSM)o7y%gI$FnyEc%aAUYFkWh@6T2i}zubRx zK+3#pHy50*v`}B53)By{@x5tauDrCOTwBSTqEiCn=KUdPI z+OwMX`YhW|Yd4ttGGbyCU;Yx%>B0JDkdEb}SlN2$a{87xjWu+!!C*QNap-gJPl{uA{4zx4!diK<_*fZ;o~S;x(oql#J9nnd5Ky zGAarMS)q4TZ(OrmK3cdqsD}1Xv+3Ndqhrp~_wYD~X@CyD30BFz;F`~b4rRI8TZRHD zaMtU=;2=wmp5g!({)Fp>4dZ3XK(&W?y(AKoUe_zkAd()GFHXEq4UGCjqi(S86>hRw z6W?5;B=kGoaRSGHIQH?qmcAQqF>G3U7Rk61c3B)E>t~0HNj~tkY6d2#i?CW93f-&@ zT6`!q)c)q8poAU|l5UJA4M{gAv_yU69~zaM!6LUOV%h#TI!$%gi@o-rOHeU`l*<+8 z7i=PgmQMYb1`V*_COG$hJi8iKj4;urd%%&JtSNo;?G-pR0~KReSr?wg#bQ6D>M%5`lLq^9yQHw zYTa62M&bWo*wZe9_XDxwQaRzQ^P1}THa^_s7RkZw09Yx)u!3P(Cp-4Pv1LiFn3PaU zF*E9*G<=f;nQEN{8N-{5Vb%ZRs!_BlmrfDF;-gKD8=KElBV)(_9{cF^t zAlioKB#IgN7B`;~F$JJr-#how?p1T8=_0RBm5$sw`U`6fA09oVPLNHYTx8W`~2sy{tE>CX zFTyv7c|`pC5qfY?E{+$oIhaQr4Rxs?9-sRGAx1z5u<|0SIza~xPGl3jg7_*_)^zI z4IbYIdvJkJ>()@I<;32e12ESlH4>Ncjb{MC{U3TxbNK9tyyggFkbi0FP?}d89a_~hKn-)Qa#C5H& z^8pG#z&OJwd}gpL5GHl^+WD<`Y-Yc1&a}jU$T6ye*0HqnSYga_1&!^l(dF6xD;HsY zZ3e&cNk6e#+CMh#jyDIUrNV(<*kBr|n*&XV<%ton+nkrs*!KC#IOHS;ASU|jw+OWW zr3fq_%Lt;QECWP=!T=%xq3)MXEK6xMseq{Y}s+~u^epOf;c6L3D=#{&zz0OHdM z)*U~w_AEI==vkiv*5pOt;TgH+{DOW|%0K9_u408;&eeqk5x_4nT+N)kjP5PVC;N!~ ze|pGi2##K#jZewmNFU}I4z@ro6PNbK_k8B2;IgDpC$J;FdqXS49FmOEwloiqwVehx zJKoU0Q?_afMwwl4jye-4qAx+Tp1rBN(|I z?@Le6-nC2tCEH%XyS2aqb3PKdYFQ{m+|+m={_OrDn#ARKaY&$%4a=NyMYt1%ui%tk zN$G~!2Zs(9qeFV&;C5gwQug4ei`rSZ&teqGKrsHzw#T8l{I)V@_nqKu@lIO}cK7YN z%IMcL_dLnb*wXl5$^E>hlJk|J3evGI3BzKqW!P^T@jh6%tMo$g2=4W&U*A=2k^88U z-Gu^$b^Te&+ef?e(CPo_S`llIrHdLTYum=1L2O+wp!;&obEdY~^aq*Y0*en4OJ-zT zdf3WIjVuJasvmVNw@4nJ#2a~|-dqQkN==Lj2KT|XZY$OUQev$Kxs3~KS`VcHN-F+8 zJTys1#h&l&@d@sGa=V$H5(_#1NNcfB8ZnidQS(97g+2#*Ly8gj31{Ip;SC8M=K{tb zycI_Vg}!AGcavI@lV(AX6v7yrqJ+IU5-n2vv>t5ur-!3WS70lzHRJSBBD+p;R!4Zg+aLE%hu*QM+EnCJQT_I zyb{i=PHjCXm)*__H{;XS6`e;&OS288XS{}3xwJ1eGxx-PDhaiIk`~3pXEZu=P)iQK zpn>t4Y~~cjK9JZ6vNC#yN5d$++(Rxol&v_kX@@BhPMn$+%&fk!8581%wOn zFGFtSrmqj3f2|+!b*#49C0`znKVIiPJKE`yb9ZBZcX&8JKF(IPGT*tof3{NaV^X2; zfx8UG5r9D$gSfk<~z>P5;8v$y+MdttMB8#MJ zQ&#_Iq`k59ALsW$vtKc49^X26nN7>LlTPbVd9;z!K#2xD5-fgW5L5ED$=sf;!#Z1zSN0B2S|q`W9C$ zbbv-lNVa08zF1x=e^$-G=Z0H9$^O{0Zb4|k;PGWyt|6Yl!Q@+JvO*7kya}n%sfOj3 zCO~o04%mCiX$OSIM-pNmyMg;6eFls#$4Gu*ZJRzi$=S5}e87Fx$Ksk@eb_*C+Nq7{ z080Ss{`nZ6yyXxNFNMYW>SFTygM*yWvno$YmV36S9$ist)mMl9VIQRE z?w2snw^J=Wx0%i;c4{}zmed=Yp#79+v9OuoEL!mfl)t8kSD<#*3d?xHQTQm#FxTPw z`KJQrwH7Pf4u*&vYyV&P)3ludbAU%(p?X^&>M{K=YhuH1%=vX_M@G^sk4)5)4^TxR zsy;wmmnXG$U9$YVyyr27?4S%MM(2a69Iy!5F_Wped#NQTj zgW2KtG^x0b?;w@`TdMiLB&9ZRuF>V&!)3W|cGt06tIk zrdpXW;4Zu;eKgzI&p&B`P?vO>4 z(Gu-lah&IAc=hdOpwu3?GwcMWZ?b^U@*jWCqjs6eKGSAHhPKhEMLKJ?;7rhhVlb_ zfStE7Fe3V&N1OOLMFX>3r~98ENQ<#f2rU*;{zo{CwOZ@qM0z$ zj{ono85fxF0L-fQ9^}#1?5B0t9M2BYI{gq@R|DtlJ1+eDoW{L@o|uk%3C)Am_XVGD z)kyN+b0O(HzzV=Pc9|cP;CoSgV*!7OWuM_uB>815o)Hf`H}_0j+`0 zLevk-y#D-_fD+-HU~zoUxUa{ku$NDE6~lnRc>zwyZ|LBG7a(JW#@$-xbwk20He8Y1xWY6DudHvpe2>SsZ->@DoH<<1ZvrjE47Q%ZQ<;PXnq3E`{1_DF&~T zL*#g3ihaVu=)z9_9vE7Ruw!T~04IPv4Af0czUrddTv55JTOq^p_r8Z|#(ET|(ONRx zHCfC=?JqYy3=`ez6JRVp!VIDbGsc4$H%OtE8-155soO=2ULCBU4w`h9XzdiX|-rP)4S| zRr(6m0{#IP$;y3v)Z-xIJe`pbP0e|iGB4RjtAt;s-d;tjKSi0v@aKi5LSJ>Kh*p^o z0$JERvIBR(>8YDpD!$vgq9(36k-@f*C`z6dw)gI5tSuHLWO=EKfBQ8|OEf}~xHmvT z)n(X@xuv1AMeNVsYD!DPqT{2_l?B`~^jr;lE&pD~-JDd-|4uvs>g|OpQAlss%r+V{ zwlF3vXp#|Wzw-;7F+@445sKA;35#(kxcP;`q5%9DfV>DrPU;~|Ime$+ZSkg1c>vh9 z>@UY-yP+CYEMWgVCo}}OJ_T@nhECM$T=pT<-w#?f16(3nTR-YkIGf6C(Qmo*4K5I1h)5E~KOQYkTj z5qcPmaDr<%|5srYA&T^23_JXL{v|e36X+RA zc=uZiBbEdXON-4m9o%JoPYdL zqy21dfzu%9sHg4fh|W_NR~yFmZd0RyeJBSjbQ0UasKuq{Q|`Qd=6jXCF0%J zn>#}}-=V!O`JiJmi^-Lb;ZBDe=c|c+%z1e_+O;nw%@bS>Y1ZEK-&fbNdEMbn0X7v= zp#S?6{bqJ1Nsx()mCVeFcJCKA>;(0W1g1WAMY^sU zL0aozQGQJ71b<=54|V0*|Nq6WhK7?hF_q})rRWdXSoHa1SXdbpNL(x|Mm+chtT*ou b&*xx@wCm+^Q{eyCf+Z;`E0Qa$`}Y3>Bi8;O literal 0 HcmV?d00001 diff --git a/module/etc/module.ucls b/module/etc/module.ucls new file mode 100644 index 000000000..d2519b856 --- /dev/null +++ b/module/etc/module.ucls @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/module/src/main/java/com/iluwatar/module/App.java b/module/src/main/java/com/iluwatar/module/App.java index 9dab6a00c..0ca43d5c2 100644 --- a/module/src/main/java/com/iluwatar/module/App.java +++ b/module/src/main/java/com/iluwatar/module/App.java @@ -35,23 +35,35 @@ import java.io.FileNotFoundException; */ public final class App { - private static final String OUTPUT_FILE = "output.txt"; - private static final String ERROR_FILE = "error.txt"; - - public static FilePrinterModule filePrinterModule = null; + public static FileLoggerModule fileLoggerModule = null; + public static ConsoleLoggerModule consoleLoggerModule = null; public static void prepare() throws FileNotFoundException { - filePrinterModule = FilePrinterModule.getSingleton(); + + fileLoggerModule = FileLoggerModule.getSingleton(); + consoleLoggerModule = ConsoleLoggerModule.getSingleton(); - filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + /* Prepare modules */ + fileLoggerModule.prepare(); + consoleLoggerModule.prepare(); } public static void unprepare() { - filePrinterModule.unprepare(); + + /* Close all resources */ + fileLoggerModule.unprepare(); + consoleLoggerModule.unprepare(); } public static final void execute(final String... args) { - filePrinterModule.printString("Hello World"); + + /* Send logs on file system */ + fileLoggerModule.printString("Message"); + fileLoggerModule.printErrorString("Error"); + + /* Send logs on console */ + consoleLoggerModule.printString("Message"); + consoleLoggerModule.printErrorString("Error"); } /** diff --git a/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java b/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java new file mode 100644 index 000000000..097f85b0e --- /dev/null +++ b/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java @@ -0,0 +1,106 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.module; + +import java.io.FileNotFoundException; +import java.io.PrintStream; + +import org.apache.log4j.Logger; + +/** + * The Module pattern can be considered a Creational pattern and a Structural + * pattern. It manages the creation and organization of other elements, and + * groups them as the structural pattern does. An object that applies this + * pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with + * static members with cleaner, more concise syntax and semantics. + *

    + * The below example demonstrates a Console logger module, which can print + * simple and error messages in two designated formats + */ +public class ConsoleLoggerModule { + + private static final Logger logger = Logger + .getLogger(ConsoleLoggerModule.class); + + private static ConsoleLoggerModule singleton = null; + + public PrintStream output = null; + public PrintStream error = null; + + private ConsoleLoggerModule() { + } + + public static final ConsoleLoggerModule getSingleton() { + + if (ConsoleLoggerModule.singleton == null) { + ConsoleLoggerModule.singleton = new ConsoleLoggerModule(); + } + + return ConsoleLoggerModule.singleton; + } + + /** + * + * @throws FileNotFoundException + */ + public final void prepare() { + + logger.debug("ConsoleLoggerModule::prepare();"); + + this.output = new PrintStream(System.out); + this.error = new PrintStream(System.err); + } + + /** + * + */ + public final void unprepare() { + + if (this.output != null) { + + this.output.flush(); + this.output.close(); + } + + if (this.error != null) { + + this.error.flush(); + this.error.close(); + } + + logger.debug("ConsoleLoggerModule::unprepare();"); + } + + /** + * + * @param value + */ + public final void printString(final String value) { + this.output.println(value); + } + + /** + * + * @param value + */ + public final void printErrorString(final String value) { + this.error.println(value); + } +} diff --git a/module/src/main/java/com/iluwatar/module/FilePrinterModule.java b/module/src/main/java/com/iluwatar/module/FileLoggerModule.java similarity index 57% rename from module/src/main/java/com/iluwatar/module/FilePrinterModule.java rename to module/src/main/java/com/iluwatar/module/FileLoggerModule.java index 879492248..d1df062de 100644 --- a/module/src/main/java/com/iluwatar/module/FilePrinterModule.java +++ b/module/src/main/java/com/iluwatar/module/FileLoggerModule.java @@ -1,11 +1,3 @@ -package com.iluwatar.module; - -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.PrintStream; - -import org.apache.log4j.Logger; - /** * The MIT License Copyright (c) 2016 Amit Dixit * @@ -27,39 +19,60 @@ import org.apache.log4j.Logger; * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ -public final class FilePrinterModule { +package com.iluwatar.module; + +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.PrintStream; + +import org.apache.log4j.Logger; + +/** + * The Module pattern can be considered a Creational pattern and a Structural + * pattern. It manages the creation and organization of other elements, and + * groups them as the structural pattern does. An object that applies this + * pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with + * static members with cleaner, more concise syntax and semantics. + *

    + * The below example demonstrates a File logger module, which can print simple + * and error messages in two designated files + */ +public final class FileLoggerModule { private static final Logger logger = Logger - .getLogger(FilePrinterModule.class); + .getLogger(FileLoggerModule.class); - private static FilePrinterModule singleton = null; + private static FileLoggerModule singleton = null; + + private static final String OUTPUT_FILE = "output.txt"; + private static final String ERROR_FILE = "error.txt"; public PrintStream output = null; public PrintStream error = null; - private FilePrinterModule() { + private FileLoggerModule() { } - public static final FilePrinterModule getSingleton() { + public static final FileLoggerModule getSingleton() { - if (FilePrinterModule.singleton == null) { - FilePrinterModule.singleton = new FilePrinterModule(); + if (FileLoggerModule.singleton == null) { + FileLoggerModule.singleton = new FileLoggerModule(); } - return FilePrinterModule.singleton; + return FileLoggerModule.singleton; } /** * * @throws FileNotFoundException */ - public final void prepare(final String outputFile, final String errorFile) - throws FileNotFoundException { + public final void prepare() throws FileNotFoundException { - logger.debug("MainModule::prepare();"); + logger.debug("FileLoggerModule::prepare();"); - this.output = new PrintStream(new FileOutputStream(outputFile)); - this.error = new PrintStream(new FileOutputStream(errorFile)); + this.output = new PrintStream(new FileOutputStream(OUTPUT_FILE)); + this.error = new PrintStream(new FileOutputStream(ERROR_FILE)); } /** @@ -79,7 +92,7 @@ public final class FilePrinterModule { this.error.close(); } - logger.debug("MainModule::unprepare();"); + logger.debug("FileLoggerModule::unprepare();"); } /** @@ -87,7 +100,7 @@ public final class FilePrinterModule { * @param value */ public final void printString(final String value) { - this.output.print(value); + this.output.println(value); } /** @@ -95,6 +108,6 @@ public final class FilePrinterModule { * @param value */ public final void printErrorString(final String value) { - this.error.print(value); + this.error.println(value); } } diff --git a/module/src/test/java/com/iluwatar/module/ModuleTest.java b/module/src/test/java/com/iluwatar/module/ModuleTest.java index 797e7f26a..cf3435850 100644 --- a/module/src/test/java/com/iluwatar/module/ModuleTest.java +++ b/module/src/test/java/com/iluwatar/module/ModuleTest.java @@ -24,19 +24,21 @@ import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; import org.apache.log4j.Logger; import org.junit.Test; /** - * The Data Mapper (DM) is a layer of software that separates the in-memory - * objects from the database. Its responsibility is to transfer data between the - * two and also to isolate them from each other. With Data Mapper the in-memory - * objects needn't know even that there's a database present; they need no SQL - * interface code, and certainly no knowledge of the database schema. (The - * database schema is always ignorant of the objects that use it.) Since it's a - * form of Mapper , Data Mapper itself is even unknown to the domain layer. + * The Module pattern can be considered a Creational pattern and a Structural + * pattern. It manages the creation and organization of other elements, and + * groups them as the structural pattern does. An object that applies this + * pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with + * static members with cleaner, more concise syntax and semantics. *

    + * The below example demonstrates a JUnit test for testing two different + * modules: File Logger and Console Logger */ public class ModuleTest { @@ -54,23 +56,23 @@ public class ModuleTest { * @throws IOException */ @Test - public void testPositiveMessage() throws IOException { + public void positiveTestConsoleMessage() throws IOException { /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FilePrinterModule filePrinterModule = FilePrinterModule + final FileLoggerModule fileLoggerModule = FileLoggerModule .getSingleton(); /* Prepare the essential sub modules, to perform the sequence of jobs */ - filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + fileLoggerModule.prepare(); /* Print 'Message' in file */ - filePrinterModule.printString(MESSAGE); + fileLoggerModule.printString(MESSAGE); - /* Test if 'Message' is printed in file */ - assertEquals(readFirstLine(OUTPUT_FILE), MESSAGE); + /* Test if 'Message' is printed on console */ + assertEquals(readFirstLine(), MESSAGE); /* Unprepare to cleanup the modules */ - filePrinterModule.unprepare(); + fileLoggerModule.unprepare(); } /** @@ -79,20 +81,20 @@ public class ModuleTest { * @throws IOException */ @Test - public void testNegativeMessage() throws IOException { + public void negativeTestConsoleMessage() throws IOException { /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FilePrinterModule filePrinterModule = FilePrinterModule + final ConsoleLoggerModule consoleLoggerModule = ConsoleLoggerModule .getSingleton(); /* Prepare the essential sub modules, to perform the sequence of jobs */ - filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + consoleLoggerModule.prepare(); - /* Test if nothing is printed in file */ - assertEquals(readFirstLine(OUTPUT_FILE), null); + /* Test if nothing is printed on console */ + assertEquals(readFirstLine(), null); /* Unprepare to cleanup the modules */ - filePrinterModule.unprepare(); + consoleLoggerModule.unprepare(); } /** @@ -101,23 +103,23 @@ public class ModuleTest { * @throws FileNotFoundException */ @Test - public void testPositiveErrorMessage() throws FileNotFoundException { + public void positiveTestConsoleErrorMessage() { /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FilePrinterModule filePrinterModule = FilePrinterModule + final ConsoleLoggerModule consoleLoggerModule = ConsoleLoggerModule .getSingleton(); /* Prepare the essential sub modules, to perform the sequence of jobs */ - filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + consoleLoggerModule.prepare(); /* Print 'Error' in file */ - filePrinterModule.printErrorString(ERROR); + consoleLoggerModule.printErrorString(ERROR); - /* Test if 'Message' is printed in file */ - assertEquals(readFirstLine(ERROR_FILE), ERROR); + /* Test if 'Message' is printed on console */ + assertEquals(readFirstLine(), ERROR); /* Unprepare to cleanup the modules */ - filePrinterModule.unprepare(); + consoleLoggerModule.unprepare(); } /** @@ -126,20 +128,152 @@ public class ModuleTest { * @throws FileNotFoundException */ @Test - public void testNegativeErrorMessage() throws FileNotFoundException { + public void negativeTestConsoleErrorMessage() { /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FilePrinterModule filePrinterModule = FilePrinterModule + final ConsoleLoggerModule consoleLoggerModule = ConsoleLoggerModule .getSingleton(); /* Prepare the essential sub modules, to perform the sequence of jobs */ - filePrinterModule.prepare(OUTPUT_FILE, ERROR_FILE); + consoleLoggerModule.prepare(); + + /* Test if nothing is printed on console */ + assertEquals(readFirstLine(), null); + + /* Unprepare to cleanup the modules */ + consoleLoggerModule.unprepare(); + } + + /** + * This test verify that 'MESSAGE' is perfectly printed in output file + * + * @throws IOException + */ + @Test + public void positiveTestFileMessage() throws IOException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FileLoggerModule fileLoggerModule = FileLoggerModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Print 'Message' in file */ + fileLoggerModule.printString(MESSAGE); + + /* Test if 'Message' is printed in file */ + assertEquals(readFirstLine(OUTPUT_FILE), MESSAGE); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * This test verify that nothing is printed in output file + * + * @throws IOException + */ + @Test + public void negativeTestFileMessage() throws IOException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FileLoggerModule fileLoggerModule = FileLoggerModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Test if nothing is printed in file */ + assertEquals(readFirstLine(OUTPUT_FILE), null); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * This test verify that 'ERROR' is perfectly printed in error file + * + * @throws FileNotFoundException + */ + @Test + public void positiveTestFileErrorMessage() throws FileNotFoundException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FileLoggerModule fileLoggerModule = FileLoggerModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Print 'Error' in file */ + fileLoggerModule.printErrorString(ERROR); + + /* Test if 'Message' is printed in file */ + assertEquals(readFirstLine(ERROR_FILE), ERROR); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * This test verify that nothing is printed in error file + * + * @throws FileNotFoundException + */ + @Test + public void negativeTestFileErrorMessage() throws FileNotFoundException { + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + final FileLoggerModule fileLoggerModule = FileLoggerModule + .getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); /* Test if nothing is printed in file */ assertEquals(readFirstLine(ERROR_FILE), null); /* Unprepare to cleanup the modules */ - filePrinterModule.unprepare(); + fileLoggerModule.unprepare(); + } + + /** + * Utility method to read first line of a file + * + * @param file + * @return + */ + private static final String readFirstLine() { + + String firstLine = null; + BufferedReader bufferedReader = null; + try { + + /* Create a buffered reader */ + bufferedReader = new BufferedReader( + new InputStreamReader(System.in)); + + /* Read the line */ + firstLine = bufferedReader.readLine(); + + logger.info("ModuleTest::readFirstLineFromConsole() : firstLine : " + + firstLine); + + } catch (final IOException e) { + logger.error("ModuleTest::readFirstLineFromConsole()", e); + } finally { + + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (final IOException e) { + logger.error("ModuleTest::readFirstLineFromConsole()", e); + } + } + } + + return firstLine; } /** @@ -160,17 +294,18 @@ public class ModuleTest { /* Read the line */ firstLine = bufferedReader.readLine(); - logger.info("ModuleTest::readFile() : firstLine : " + firstLine); + logger.info("ModuleTest::readFirstLine() : firstLine : " + + firstLine); } catch (final IOException e) { - logger.error("ModuleTest::readFile()", e); + logger.error("ModuleTest::readFirstLine()", e); } finally { if (bufferedReader != null) { try { bufferedReader.close(); } catch (final IOException e) { - logger.error("ModuleTest::readFile()", e); + logger.error("ModuleTest::readFirstLine()", e); } } } From 1ace4c05d6c8141964285032edb17e136e0ba69f Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Thu, 27 Oct 2016 15:59:51 +0530 Subject: [PATCH 100/145] App++ App++ --- .../main/java/com/iluwatar/module/App.java | 19 +++++++++---------- .../java/com/iluwatar/module/AppTest.java | 1 - 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/module/src/main/java/com/iluwatar/module/App.java b/module/src/main/java/com/iluwatar/module/App.java index 0ca43d5c2..a3ecdb31c 100644 --- a/module/src/main/java/com/iluwatar/module/App.java +++ b/module/src/main/java/com/iluwatar/module/App.java @@ -21,16 +21,15 @@ package com.iluwatar.module; import java.io.FileNotFoundException; /** - * The Data Mapper (DM) is a layer of software that separates the in-memory - * objects from the database. Its responsibility is to transfer data between the - * two and also to isolate them from each other. With Data Mapper the in-memory - * objects needn't know even that there's a database present; they need no SQL - * interface code, and certainly no knowledge of the database schema. (The - * database schema is always ignorant of the objects that use it.) Since it's a - * form of Mapper , Data Mapper itself is even unknown to the domain layer. + * The Module pattern can be considered a Creational pattern and a Structural + * pattern. It manages the creation and organization of other elements, and + * groups them as the structural pattern does. An object that applies this + * pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with + * static members with cleaner, more concise syntax and semantics. *

    - * The below example demonstrates basic CRUD operations: Create, Read, Update, - * and Delete. + * The below example demonstrates a use case for testing two different modules: + * File Logger and Console Logger * */ public final class App { @@ -39,7 +38,7 @@ public final class App { public static ConsoleLoggerModule consoleLoggerModule = null; public static void prepare() throws FileNotFoundException { - + fileLoggerModule = FileLoggerModule.getSingleton(); consoleLoggerModule = ConsoleLoggerModule.getSingleton(); diff --git a/module/src/test/java/com/iluwatar/module/AppTest.java b/module/src/test/java/com/iluwatar/module/AppTest.java index 46c12ef38..eb348d758 100644 --- a/module/src/test/java/com/iluwatar/module/AppTest.java +++ b/module/src/test/java/com/iluwatar/module/AppTest.java @@ -25,7 +25,6 @@ import com.iluwatar.module.App; import org.junit.Test; /** - * Tests that Data-Mapper example runs without errors. */ public final class AppTest { From 7ba6cb43fdc38b534db583f7ec01e816b437f05d Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Thu, 27 Oct 2016 16:00:01 +0530 Subject: [PATCH 101/145] App App --- module/src/test/java/com/iluwatar/module/AppTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/module/src/test/java/com/iluwatar/module/AppTest.java b/module/src/test/java/com/iluwatar/module/AppTest.java index eb348d758..0f20b4959 100644 --- a/module/src/test/java/com/iluwatar/module/AppTest.java +++ b/module/src/test/java/com/iluwatar/module/AppTest.java @@ -25,6 +25,7 @@ import com.iluwatar.module.App; import org.junit.Test; /** + * Tests that Module example runs without errors. */ public final class AppTest { From ea7752c5e183d2981c29ce45bb4d989ef2eefab6 Mon Sep 17 00:00:00 2001 From: Amit Dixit Date: Thu, 27 Oct 2016 18:30:07 +0530 Subject: [PATCH 102/145] checkstyle errors removed checkstyle errors removed --- module/etc/module.urm.puml | 43 +++ .../main/java/com/iluwatar/module/App.java | 101 +++--- .../iluwatar/module/ConsoleLoggerModule.java | 122 +++---- .../com/iluwatar/module/FileLoggerModule.java | 151 +++++---- .../java/com/iluwatar/module/AppTest.java | 10 +- .../iluwatar/module/FileLoggerModuleTest.java | 182 ++++++++++ .../java/com/iluwatar/module/ModuleTest.java | 315 ------------------ 7 files changed, 425 insertions(+), 499 deletions(-) create mode 100644 module/etc/module.urm.puml create mode 100644 module/src/test/java/com/iluwatar/module/FileLoggerModuleTest.java delete mode 100644 module/src/test/java/com/iluwatar/module/ModuleTest.java diff --git a/module/etc/module.urm.puml b/module/etc/module.urm.puml new file mode 100644 index 000000000..d78f12da8 --- /dev/null +++ b/module/etc/module.urm.puml @@ -0,0 +1,43 @@ +@startuml +package com.iluwatar.module { + class App { + + consoleLoggerModule : ConsoleLoggerModule {static} + + fileLoggerModule : FileLoggerModule {static} + - App() + + execute(args : String[]) {static} + + main(args : String[]) {static} + + prepare() {static} + + unprepare() {static} + } + class ConsoleLoggerModule { + - LOGGER : Logger {static} + + error : PrintStream + + output : PrintStream + - singleton : ConsoleLoggerModule {static} + - ConsoleLoggerModule() + + getSingleton() : ConsoleLoggerModule {static} + + prepare() + + printErrorString(value : String) + + printString(value : String) + + unprepare() + } + class FileLoggerModule { + - ERROR_FILE : String {static} + - LOGGER : Logger {static} + - OUTPUT_FILE : String {static} + + error : PrintStream + + output : PrintStream + - singleton : FileLoggerModule {static} + - FileLoggerModule() + + getSingleton() : FileLoggerModule {static} + + prepare() + + printErrorString(value : String) + + printString(value : String) + + unprepare() + } +} +FileLoggerModule --> "-singleton" FileLoggerModule +App --> "-consoleLoggerModule" ConsoleLoggerModule +ConsoleLoggerModule --> "-singleton" ConsoleLoggerModule +App --> "-fileLoggerModule" FileLoggerModule +@enduml \ No newline at end of file diff --git a/module/src/main/java/com/iluwatar/module/App.java b/module/src/main/java/com/iluwatar/module/App.java index a3ecdb31c..cfa50975f 100644 --- a/module/src/main/java/com/iluwatar/module/App.java +++ b/module/src/main/java/com/iluwatar/module/App.java @@ -21,64 +21,75 @@ package com.iluwatar.module; import java.io.FileNotFoundException; /** - * The Module pattern can be considered a Creational pattern and a Structural - * pattern. It manages the creation and organization of other elements, and - * groups them as the structural pattern does. An object that applies this - * pattern can provide the equivalent of a namespace, providing the - * initialization and finalization process of a static class or a class with - * static members with cleaner, more concise syntax and semantics. + * The Module pattern can be considered a Creational pattern and a Structural pattern. It manages + * the creation and organization of other elements, and groups them as the structural pattern does. + * An object that applies this pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with static members with + * cleaner, more concise syntax and semantics. *

    - * The below example demonstrates a use case for testing two different modules: - * File Logger and Console Logger + * The below example demonstrates a use case for testing two different modules: File Logger and + * Console Logger * */ public final class App { - public static FileLoggerModule fileLoggerModule = null; - public static ConsoleLoggerModule consoleLoggerModule = null; + public static FileLoggerModule fileLoggerModule = null; + public static ConsoleLoggerModule consoleLoggerModule = null; - public static void prepare() throws FileNotFoundException { + /** + * Following method performs the initialization + * + * @throws FileNotFoundException if program is not able to find log files (output.txt and + * error.txt) + */ + public static void prepare() throws FileNotFoundException { - fileLoggerModule = FileLoggerModule.getSingleton(); - consoleLoggerModule = ConsoleLoggerModule.getSingleton(); + fileLoggerModule = FileLoggerModule.getSingleton(); + consoleLoggerModule = ConsoleLoggerModule.getSingleton(); - /* Prepare modules */ - fileLoggerModule.prepare(); - consoleLoggerModule.prepare(); - } + /* Prepare modules */ + fileLoggerModule.prepare(); + consoleLoggerModule.prepare(); + } - public static void unprepare() { + /** + * Following method performs the finalization + */ + public static void unprepare() { - /* Close all resources */ - fileLoggerModule.unprepare(); - consoleLoggerModule.unprepare(); - } + /* Close all resources */ + fileLoggerModule.unprepare(); + consoleLoggerModule.unprepare(); + } - public static final void execute(final String... args) { + /** + * Following method is main executor + * + * @param args for providing default program arguments + */ + public static void execute(final String... args) { - /* Send logs on file system */ - fileLoggerModule.printString("Message"); - fileLoggerModule.printErrorString("Error"); + /* Send logs on file system */ + fileLoggerModule.printString("Message"); + fileLoggerModule.printErrorString("Error"); - /* Send logs on console */ - consoleLoggerModule.printString("Message"); - consoleLoggerModule.printErrorString("Error"); - } + /* Send logs on console */ + consoleLoggerModule.printString("Message"); + consoleLoggerModule.printErrorString("Error"); + } - /** - * Program entry point. - * - * @param args - * command line args. - * @throws FileNotFoundException - */ - public static final void main(final String... args) - throws FileNotFoundException { - prepare(); - execute(args); - unprepare(); - } + /** + * Program entry point. + * + * @param args command line args. + * @throws FileNotFoundException if program is not able to find log files (output.txt and + * error.txt) + */ + public static void main(final String... args) throws FileNotFoundException { + prepare(); + execute(args); + unprepare(); + } - private App() { - } + private App() {} } diff --git a/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java b/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java index 097f85b0e..8f5941951 100644 --- a/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java +++ b/module/src/main/java/com/iluwatar/module/ConsoleLoggerModule.java @@ -18,89 +18,91 @@ */ package com.iluwatar.module; -import java.io.FileNotFoundException; import java.io.PrintStream; import org.apache.log4j.Logger; /** - * The Module pattern can be considered a Creational pattern and a Structural - * pattern. It manages the creation and organization of other elements, and - * groups them as the structural pattern does. An object that applies this - * pattern can provide the equivalent of a namespace, providing the - * initialization and finalization process of a static class or a class with - * static members with cleaner, more concise syntax and semantics. + * The Module pattern can be considered a Creational pattern and a Structural pattern. It manages + * the creation and organization of other elements, and groups them as the structural pattern does. + * An object that applies this pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with static members with + * cleaner, more concise syntax and semantics. *

    - * The below example demonstrates a Console logger module, which can print - * simple and error messages in two designated formats + * The below example demonstrates a Console logger module, which can print simple and error messages + * in two designated formats */ -public class ConsoleLoggerModule { +public final class ConsoleLoggerModule { - private static final Logger logger = Logger - .getLogger(ConsoleLoggerModule.class); + private static final Logger LOGGER = Logger.getLogger(ConsoleLoggerModule.class); - private static ConsoleLoggerModule singleton = null; + private static ConsoleLoggerModule singleton = null; - public PrintStream output = null; - public PrintStream error = null; + public PrintStream output = null; + public PrintStream error = null; - private ConsoleLoggerModule() { - } + private ConsoleLoggerModule() {} - public static final ConsoleLoggerModule getSingleton() { + /** + * Static method to get single instance of class + * + * @return singleton instance of ConsoleLoggerModule + */ + public static ConsoleLoggerModule getSingleton() { - if (ConsoleLoggerModule.singleton == null) { - ConsoleLoggerModule.singleton = new ConsoleLoggerModule(); - } + if (ConsoleLoggerModule.singleton == null) { + ConsoleLoggerModule.singleton = new ConsoleLoggerModule(); + } - return ConsoleLoggerModule.singleton; - } + return ConsoleLoggerModule.singleton; + } - /** - * - * @throws FileNotFoundException - */ - public final void prepare() { + /** + * Following method performs the initialization + */ + public void prepare() { - logger.debug("ConsoleLoggerModule::prepare();"); + LOGGER.debug("ConsoleLoggerModule::prepare();"); - this.output = new PrintStream(System.out); - this.error = new PrintStream(System.err); - } + this.output = new PrintStream(System.out); + this.error = new PrintStream(System.err); + } - /** - * - */ - public final void unprepare() { + /** + * Following method performs the finalization + */ + public void unprepare() { - if (this.output != null) { + if (this.output != null) { - this.output.flush(); - this.output.close(); - } + this.output.flush(); + this.output.close(); + } - if (this.error != null) { + if (this.error != null) { - this.error.flush(); - this.error.close(); - } + this.error.flush(); + this.error.close(); + } - logger.debug("ConsoleLoggerModule::unprepare();"); - } + LOGGER.debug("ConsoleLoggerModule::unprepare();"); + } - /** - * - * @param value - */ - public final void printString(final String value) { - this.output.println(value); - } + /** + * Used to print a message + * + * @param value will be printed on console + */ + public void printString(final String value) { + this.output.println(value); + } - /** - * - * @param value - */ - public final void printErrorString(final String value) { - this.error.println(value); - } + /** + * Used to print a error message + * + * @param value will be printed on error console + */ + public void printErrorString(final String value) { + this.error.println(value); + } } diff --git a/module/src/main/java/com/iluwatar/module/FileLoggerModule.java b/module/src/main/java/com/iluwatar/module/FileLoggerModule.java index d1df062de..fae66191b 100644 --- a/module/src/main/java/com/iluwatar/module/FileLoggerModule.java +++ b/module/src/main/java/com/iluwatar/module/FileLoggerModule.java @@ -1,23 +1,20 @@ /** * The MIT License Copyright (c) 2016 Amit Dixit * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package com.iluwatar.module; @@ -28,86 +25,92 @@ import java.io.PrintStream; import org.apache.log4j.Logger; /** - * The Module pattern can be considered a Creational pattern and a Structural - * pattern. It manages the creation and organization of other elements, and - * groups them as the structural pattern does. An object that applies this - * pattern can provide the equivalent of a namespace, providing the - * initialization and finalization process of a static class or a class with - * static members with cleaner, more concise syntax and semantics. + * The Module pattern can be considered a Creational pattern and a Structural pattern. It manages + * the creation and organization of other elements, and groups them as the structural pattern does. + * An object that applies this pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with static members with + * cleaner, more concise syntax and semantics. *

    - * The below example demonstrates a File logger module, which can print simple - * and error messages in two designated files + * The below example demonstrates a File logger module, which can print simple and error messages in + * two designated files */ public final class FileLoggerModule { - private static final Logger logger = Logger - .getLogger(FileLoggerModule.class); + private static final Logger LOGGER = Logger.getLogger(FileLoggerModule.class); - private static FileLoggerModule singleton = null; + private static FileLoggerModule singleton = null; - private static final String OUTPUT_FILE = "output.txt"; - private static final String ERROR_FILE = "error.txt"; + private static final String OUTPUT_FILE = "output.txt"; + private static final String ERROR_FILE = "error.txt"; - public PrintStream output = null; - public PrintStream error = null; + public PrintStream output = null; + public PrintStream error = null; - private FileLoggerModule() { - } + private FileLoggerModule() {} - public static final FileLoggerModule getSingleton() { + /** + * Static method to get single instance of class + * + * @return singleton instance of FileLoggerModule + */ + public static FileLoggerModule getSingleton() { - if (FileLoggerModule.singleton == null) { - FileLoggerModule.singleton = new FileLoggerModule(); - } + if (FileLoggerModule.singleton == null) { + FileLoggerModule.singleton = new FileLoggerModule(); + } - return FileLoggerModule.singleton; - } + return FileLoggerModule.singleton; + } - /** - * - * @throws FileNotFoundException - */ - public final void prepare() throws FileNotFoundException { + /** + * Following method performs the initialization + * + * @throws FileNotFoundException if program is not able to find log files (output.txt and + * error.txt) + */ + public void prepare() throws FileNotFoundException { - logger.debug("FileLoggerModule::prepare();"); + LOGGER.debug("FileLoggerModule::prepare();"); - this.output = new PrintStream(new FileOutputStream(OUTPUT_FILE)); - this.error = new PrintStream(new FileOutputStream(ERROR_FILE)); - } + this.output = new PrintStream(new FileOutputStream(OUTPUT_FILE)); + this.error = new PrintStream(new FileOutputStream(ERROR_FILE)); + } - /** - * - */ - public final void unprepare() { + /** + * Following method performs the finalization + */ + public void unprepare() { - if (this.output != null) { + if (this.output != null) { - this.output.flush(); - this.output.close(); - } + this.output.flush(); + this.output.close(); + } - if (this.error != null) { + if (this.error != null) { - this.error.flush(); - this.error.close(); - } + this.error.flush(); + this.error.close(); + } - logger.debug("FileLoggerModule::unprepare();"); - } + LOGGER.debug("FileLoggerModule::unprepare();"); + } - /** - * - * @param value - */ - public final void printString(final String value) { - this.output.println(value); - } + /** + * Used to print a message + * + * @param value will be printed in file + */ + public void printString(final String value) { + this.output.println(value); + } - /** - * - * @param value - */ - public final void printErrorString(final String value) { - this.error.println(value); - } + /** + * Used to print a error message + * + * @param value will be printed on error file + */ + public void printErrorString(final String value) { + this.error.println(value); + } } diff --git a/module/src/test/java/com/iluwatar/module/AppTest.java b/module/src/test/java/com/iluwatar/module/AppTest.java index 0f20b4959..21b32407d 100644 --- a/module/src/test/java/com/iluwatar/module/AppTest.java +++ b/module/src/test/java/com/iluwatar/module/AppTest.java @@ -29,9 +29,9 @@ import org.junit.Test; */ public final class AppTest { - @Test - public void test() throws FileNotFoundException { - final String[] args = {}; - App.main(args); - } + @Test + public void test() throws FileNotFoundException { + final String[] args = {}; + App.main(args); + } } diff --git a/module/src/test/java/com/iluwatar/module/FileLoggerModuleTest.java b/module/src/test/java/com/iluwatar/module/FileLoggerModuleTest.java new file mode 100644 index 000000000..ecefb307a --- /dev/null +++ b/module/src/test/java/com/iluwatar/module/FileLoggerModuleTest.java @@ -0,0 +1,182 @@ +/** + * The MIT License Copyright (c) 2016 Amit Dixit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and + * associated documentation files (the "Software"), to deal in the Software without restriction, + * including without limitation the rights to use, copy, modify, merge, publish, distribute, + * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all copies or + * substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT + * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.iluwatar.module; + +import static org.junit.Assert.assertEquals; + +import java.io.BufferedReader; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; + +import org.apache.log4j.Logger; +import org.junit.Test; + +/** + * The Module pattern can be considered a Creational pattern and a Structural pattern. It manages + * the creation and organization of other elements, and groups them as the structural pattern does. + * An object that applies this pattern can provide the equivalent of a namespace, providing the + * initialization and finalization process of a static class or a class with static members with + * cleaner, more concise syntax and semantics. + *

    + * The below example demonstrates a JUnit test for testing two different modules: File Logger and + * Console Logger + */ +public final class FileLoggerModuleTest { + + private static final Logger LOGGER = Logger.getLogger(FileLoggerModuleTest.class); + + private static final String OUTPUT_FILE = "output.txt"; + private static final String ERROR_FILE = "error.txt"; + + private static final String MESSAGE = "MESSAGE"; + private static final String ERROR = "ERROR"; + + + /** + * This test verify that 'MESSAGE' is perfectly printed in output file + * + * @throws IOException if program is not able to find log files (output.txt and error.txt) + */ + @Test + public void positiveTestFileMessage() throws IOException { + + /* Get singletong instance of File Logger Module */ + final FileLoggerModule fileLoggerModule = FileLoggerModule.getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Print 'Message' in file */ + fileLoggerModule.printString(MESSAGE); + + /* Test if 'Message' is printed in file */ + assertEquals(readFirstLine(OUTPUT_FILE), MESSAGE); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * This test verify that nothing is printed in output file + * + * @throws IOException if program is not able to find log files (output.txt and error.txt) + */ + @Test + public void negativeTestFileMessage() throws IOException { + + /* Get singletong instance of File Logger Module */ + final FileLoggerModule fileLoggerModule = FileLoggerModule.getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Test if nothing is printed in file */ + assertEquals(readFirstLine(OUTPUT_FILE), null); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * This test verify that 'ERROR' is perfectly printed in error file + * + * @throws FileNotFoundException if program is not able to find log files (output.txt and + * error.txt) + */ + @Test + public void positiveTestFileErrorMessage() throws FileNotFoundException { + + /* Get singletong instance of File Logger Module */ + final FileLoggerModule fileLoggerModule = FileLoggerModule.getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Print 'Error' in file */ + fileLoggerModule.printErrorString(ERROR); + + /* Test if 'Message' is printed in file */ + assertEquals(readFirstLine(ERROR_FILE), ERROR); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * This test verify that nothing is printed in error file + * + * @throws FileNotFoundException if program is not able to find log files (output.txt and + * error.txt) + */ + @Test + public void negativeTestFileErrorMessage() throws FileNotFoundException { + + /* Get singletong instance of File Logger Module */ + final FileLoggerModule fileLoggerModule = FileLoggerModule.getSingleton(); + + /* Prepare the essential sub modules, to perform the sequence of jobs */ + fileLoggerModule.prepare(); + + /* Test if nothing is printed in file */ + assertEquals(readFirstLine(ERROR_FILE), null); + + /* Unprepare to cleanup the modules */ + fileLoggerModule.unprepare(); + } + + /** + * Utility method to read first line of a file + * + * @param file as file name to be read + * @return a string value as first line in file + */ + private static final String readFirstLine(final String file) { + + String firstLine = null; + BufferedReader bufferedReader = null; + try { + + /* Create a buffered reader */ + bufferedReader = new BufferedReader(new FileReader(file)); + + while (bufferedReader.ready()) { + + /* Read the line */ + firstLine = bufferedReader.readLine(); + } + + LOGGER.info("ModuleTest::readFirstLine() : firstLine : " + firstLine); + + } catch (final IOException e) { + LOGGER.error("ModuleTest::readFirstLine()", e); + } finally { + + if (bufferedReader != null) { + try { + bufferedReader.close(); + } catch (final IOException e) { + LOGGER.error("ModuleTest::readFirstLine()", e); + } + } + } + + return firstLine; + } +} diff --git a/module/src/test/java/com/iluwatar/module/ModuleTest.java b/module/src/test/java/com/iluwatar/module/ModuleTest.java deleted file mode 100644 index cf3435850..000000000 --- a/module/src/test/java/com/iluwatar/module/ModuleTest.java +++ /dev/null @@ -1,315 +0,0 @@ -/** - * The MIT License Copyright (c) 2016 Amit Dixit - * - * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and - * associated documentation files (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, publish, distribute, - * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT - * NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, - * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ -package com.iluwatar.module; - -import static org.junit.Assert.assertEquals; - -import java.io.BufferedReader; -import java.io.FileNotFoundException; -import java.io.FileReader; -import java.io.IOException; -import java.io.InputStreamReader; - -import org.apache.log4j.Logger; -import org.junit.Test; - -/** - * The Module pattern can be considered a Creational pattern and a Structural - * pattern. It manages the creation and organization of other elements, and - * groups them as the structural pattern does. An object that applies this - * pattern can provide the equivalent of a namespace, providing the - * initialization and finalization process of a static class or a class with - * static members with cleaner, more concise syntax and semantics. - *

    - * The below example demonstrates a JUnit test for testing two different - * modules: File Logger and Console Logger - */ -public class ModuleTest { - - private static final Logger logger = Logger.getLogger(ModuleTest.class); - - private static final String OUTPUT_FILE = "output.txt"; - private static final String ERROR_FILE = "error.txt"; - - private static final String MESSAGE = "MESSAGE"; - private static final String ERROR = "ERROR"; - - /** - * This test verify that 'MESSAGE' is perfectly printed in output file - * - * @throws IOException - */ - @Test - public void positiveTestConsoleMessage() throws IOException { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FileLoggerModule fileLoggerModule = FileLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - fileLoggerModule.prepare(); - - /* Print 'Message' in file */ - fileLoggerModule.printString(MESSAGE); - - /* Test if 'Message' is printed on console */ - assertEquals(readFirstLine(), MESSAGE); - - /* Unprepare to cleanup the modules */ - fileLoggerModule.unprepare(); - } - - /** - * This test verify that nothing is printed in output file - * - * @throws IOException - */ - @Test - public void negativeTestConsoleMessage() throws IOException { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final ConsoleLoggerModule consoleLoggerModule = ConsoleLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - consoleLoggerModule.prepare(); - - /* Test if nothing is printed on console */ - assertEquals(readFirstLine(), null); - - /* Unprepare to cleanup the modules */ - consoleLoggerModule.unprepare(); - } - - /** - * This test verify that 'ERROR' is perfectly printed in error file - * - * @throws FileNotFoundException - */ - @Test - public void positiveTestConsoleErrorMessage() { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final ConsoleLoggerModule consoleLoggerModule = ConsoleLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - consoleLoggerModule.prepare(); - - /* Print 'Error' in file */ - consoleLoggerModule.printErrorString(ERROR); - - /* Test if 'Message' is printed on console */ - assertEquals(readFirstLine(), ERROR); - - /* Unprepare to cleanup the modules */ - consoleLoggerModule.unprepare(); - } - - /** - * This test verify that nothing is printed in error file - * - * @throws FileNotFoundException - */ - @Test - public void negativeTestConsoleErrorMessage() { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final ConsoleLoggerModule consoleLoggerModule = ConsoleLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - consoleLoggerModule.prepare(); - - /* Test if nothing is printed on console */ - assertEquals(readFirstLine(), null); - - /* Unprepare to cleanup the modules */ - consoleLoggerModule.unprepare(); - } - - /** - * This test verify that 'MESSAGE' is perfectly printed in output file - * - * @throws IOException - */ - @Test - public void positiveTestFileMessage() throws IOException { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FileLoggerModule fileLoggerModule = FileLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - fileLoggerModule.prepare(); - - /* Print 'Message' in file */ - fileLoggerModule.printString(MESSAGE); - - /* Test if 'Message' is printed in file */ - assertEquals(readFirstLine(OUTPUT_FILE), MESSAGE); - - /* Unprepare to cleanup the modules */ - fileLoggerModule.unprepare(); - } - - /** - * This test verify that nothing is printed in output file - * - * @throws IOException - */ - @Test - public void negativeTestFileMessage() throws IOException { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FileLoggerModule fileLoggerModule = FileLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - fileLoggerModule.prepare(); - - /* Test if nothing is printed in file */ - assertEquals(readFirstLine(OUTPUT_FILE), null); - - /* Unprepare to cleanup the modules */ - fileLoggerModule.unprepare(); - } - - /** - * This test verify that 'ERROR' is perfectly printed in error file - * - * @throws FileNotFoundException - */ - @Test - public void positiveTestFileErrorMessage() throws FileNotFoundException { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FileLoggerModule fileLoggerModule = FileLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - fileLoggerModule.prepare(); - - /* Print 'Error' in file */ - fileLoggerModule.printErrorString(ERROR); - - /* Test if 'Message' is printed in file */ - assertEquals(readFirstLine(ERROR_FILE), ERROR); - - /* Unprepare to cleanup the modules */ - fileLoggerModule.unprepare(); - } - - /** - * This test verify that nothing is printed in error file - * - * @throws FileNotFoundException - */ - @Test - public void negativeTestFileErrorMessage() throws FileNotFoundException { - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - final FileLoggerModule fileLoggerModule = FileLoggerModule - .getSingleton(); - - /* Prepare the essential sub modules, to perform the sequence of jobs */ - fileLoggerModule.prepare(); - - /* Test if nothing is printed in file */ - assertEquals(readFirstLine(ERROR_FILE), null); - - /* Unprepare to cleanup the modules */ - fileLoggerModule.unprepare(); - } - - /** - * Utility method to read first line of a file - * - * @param file - * @return - */ - private static final String readFirstLine() { - - String firstLine = null; - BufferedReader bufferedReader = null; - try { - - /* Create a buffered reader */ - bufferedReader = new BufferedReader( - new InputStreamReader(System.in)); - - /* Read the line */ - firstLine = bufferedReader.readLine(); - - logger.info("ModuleTest::readFirstLineFromConsole() : firstLine : " - + firstLine); - - } catch (final IOException e) { - logger.error("ModuleTest::readFirstLineFromConsole()", e); - } finally { - - if (bufferedReader != null) { - try { - bufferedReader.close(); - } catch (final IOException e) { - logger.error("ModuleTest::readFirstLineFromConsole()", e); - } - } - } - - return firstLine; - } - - /** - * Utility method to read first line of a file - * - * @param file - * @return - */ - private static final String readFirstLine(final String file) { - - String firstLine = null; - BufferedReader bufferedReader = null; - try { - - /* Create a buffered reader */ - bufferedReader = new BufferedReader(new FileReader(file)); - - /* Read the line */ - firstLine = bufferedReader.readLine(); - - logger.info("ModuleTest::readFirstLine() : firstLine : " - + firstLine); - - } catch (final IOException e) { - logger.error("ModuleTest::readFirstLine()", e); - } finally { - - if (bufferedReader != null) { - try { - bufferedReader.close(); - } catch (final IOException e) { - logger.error("ModuleTest::readFirstLine()", e); - } - } - } - - return firstLine; - } -} From 27d6d500bcc5cf31f59f2f6efe26271c30462b3a Mon Sep 17 00:00:00 2001 From: daniel-bryla Date: Fri, 28 Oct 2016 09:35:59 +0200 Subject: [PATCH 103/145] #502 Reverted changes in composite example, due to nature of this example using logger isn't good idea --- .../src/main/java/com/iluwatar/composite/Letter.java | 9 ++------- .../src/main/java/com/iluwatar/composite/Sentence.java | 9 ++------- composite/src/main/java/com/iluwatar/composite/Word.java | 9 ++------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/composite/src/main/java/com/iluwatar/composite/Letter.java b/composite/src/main/java/com/iluwatar/composite/Letter.java index c8a6ccfce..088cdd44c 100644 --- a/composite/src/main/java/com/iluwatar/composite/Letter.java +++ b/composite/src/main/java/com/iluwatar/composite/Letter.java @@ -22,18 +22,13 @@ */ package com.iluwatar.composite; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - /** - * + * * Letter * */ public class Letter extends LetterComposite { - private static final Logger LOGGER = LoggerFactory.getLogger(Letter.class); - private char c; public Letter(char c) { @@ -42,7 +37,7 @@ public class Letter extends LetterComposite { @Override protected void printThisBefore() { - LOGGER.info(String.valueOf(c)); + System.out.print(c); } @Override diff --git a/composite/src/main/java/com/iluwatar/composite/Sentence.java b/composite/src/main/java/com/iluwatar/composite/Sentence.java index ca8698f4f..bccf6c751 100644 --- a/composite/src/main/java/com/iluwatar/composite/Sentence.java +++ b/composite/src/main/java/com/iluwatar/composite/Sentence.java @@ -22,20 +22,15 @@ */ package com.iluwatar.composite; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.List; /** - * + * * Sentence * */ public class Sentence extends LetterComposite { - private static final Logger LOGGER = LoggerFactory.getLogger(Sentence.class); - /** * Constructor */ @@ -52,6 +47,6 @@ public class Sentence extends LetterComposite { @Override protected void printThisAfter() { - LOGGER.info("."); + System.out.print("."); } } diff --git a/composite/src/main/java/com/iluwatar/composite/Word.java b/composite/src/main/java/com/iluwatar/composite/Word.java index 4e5cfb31d..0174dc88a 100644 --- a/composite/src/main/java/com/iluwatar/composite/Word.java +++ b/composite/src/main/java/com/iluwatar/composite/Word.java @@ -22,20 +22,15 @@ */ package com.iluwatar.composite; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.util.List; /** - * + * * Word * */ public class Word extends LetterComposite { - private static final Logger LOGGER = LoggerFactory.getLogger(Word.class); - /** * Constructor */ @@ -47,7 +42,7 @@ public class Word extends LetterComposite { @Override protected void printThisBefore() { - LOGGER.info(" "); + System.out.print(" "); } @Override From 124fd33da07de5ea5e10a69a237cdce157cc5bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 1 Nov 2016 21:31:54 +0200 Subject: [PATCH 104/145] Remove use of coveralls-maven-plugin (sonarqube.com covers this) --- .travis.yml | 1 - README.md | 1 - pom.xml | 40 ---------------------------------------- 3 files changed, 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba226ff5c..817b6635f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,6 @@ install: - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e after_success: -- mvn clean test jacoco:report coveralls:report - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=$SONAR_TOKEN - bash update-ghpages.sh diff --git a/README.md b/README.md index 214fa91ff..6eed9561b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ # Design patterns implemented in Java [![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns) -[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Quality Gate](https://sonarqube.com/api/badges/gate?key=com.iluwatar%3Ajava-design-patterns)](https://sonarqube.com/dashboard/index/com.iluwatar%3Ajava-design-patterns) diff --git a/pom.xml b/pom.xml index 538058a81..b8440f664 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,6 @@ 1.4.190 4.12 3.0 - 4.0.0 0.7.2.201409121644 1.4 2.16.1 @@ -320,29 +319,10 @@ 1.8 - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - org.jacoco jacoco-maven-plugin ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - **com.steadystate* - - prepare-agent @@ -378,26 +358,6 @@ - - - org.jacoco - jacoco-maven-plugin - 0.7.5.201505241946 - - - - prepare-agent - - - - report - prepare-package - - report - - - - org.apache.maven.plugins maven-surefire-plugin From e138163c4f1688678dc8bc43e9683b142d534fe7 Mon Sep 17 00:00:00 2001 From: daniel-bryla Date: Fri, 4 Nov 2016 11:47:06 +0100 Subject: [PATCH 105/145] #502 Adjusted tests for logger introduction --- .../com/iluwatar/decorator/TrollTest.java | 61 ++++++----- .../delegation/simple/DelegateTest.java | 55 ++++++++-- .../injection/AdvancedWizardTest.java | 30 ++++-- .../dependency/injection/GuiceWizardTest.java | 38 ++++--- .../injection/SimpleWizardTest.java | 25 ++++- .../dependency/injection/StdOutTest.java | 75 ------------- .../injection/utils/InMemoryAppender.java | 54 ++++++++++ .../doublechecked/locking/InventoryTest.java | 71 ++++++------ .../doubledispatch/CollisionTest.java | 54 +--------- .../event/aggregator/KingJoffreyTest.java | 67 +++++++----- .../facade/DwarvenGoldmineFacadeTest.java | 98 +++++++++-------- .../front/controller/CommandTest.java | 27 +++-- .../front/controller/FrontControllerTest.java | 27 +++-- .../iluwatar/front/controller/StdOutTest.java | 76 ------------- .../iluwatar/front/controller/ViewTest.java | 29 +++-- .../controller/utils/InMemoryAppender.java | 54 ++++++++++ .../hexagonal/service/ConsoleLottery.java | 1 - .../com/iluwatar/layers/CakeViewImplTest.java | 49 ++++++++- .../java/com/iluwatar/layers/StdOutTest.java | 78 -------------- .../iluwatar/mediator/PartyMemberTest.java | 84 ++++++++------- .../model/view/controller/GiantViewTest.java | 60 ++++++----- .../com/iluwatar/nullobject/NullNodeTest.java | 11 +- .../com/iluwatar/nullobject/StdOutTest.java | 76 ------------- .../com/iluwatar/nullobject/TreeTest.java | 68 +++++++++--- .../com/iluwatar/observer/StdOutTest.java | 76 ------------- .../observer/WeatherObserverTest.java | 29 +++-- .../com/iluwatar/observer/WeatherTest.java | 29 +++-- .../observer/generic/GWeatherTest.java | 37 ++++--- .../observer/generic/ObserverTest.java | 31 ++++-- .../observer/utils/InMemoryAppender.java | 58 ++++++++++ .../iluwatar/poison/pill/ConsumerTest.java | 54 ++++++++-- .../com/iluwatar/poison/pill/StdOutTest.java | 75 ------------- .../privateclassdata/ImmutableStewTest.java | 39 ++++--- .../iluwatar/privateclassdata/StdOutTest.java | 75 ------------- .../iluwatar/privateclassdata/StewTest.java | 27 +++-- .../utils/InMemoryAppender.java | 53 +++++++++ .../producer/consumer/ConsumerTest.java | 12 +-- .../producer/consumer/StdOutTest.java | 75 ------------- .../java/com/iluwatar/proxy/StdOutTest.java | 75 ------------- .../iluwatar/proxy/WizardTowerProxyTest.java | 35 ++++-- .../com/iluwatar/proxy/WizardTowerTest.java | 35 ++++-- .../proxy/utils/InMemoryAppender.java | 58 ++++++++++ .../writer/lock/ReaderAndWriterTest.java | 44 +++++--- .../reader/writer/lock/ReaderTest.java | 38 ++++--- .../reader/writer/lock/StdOutTest.java | 76 ------------- .../reader/writer/lock/WriterTest.java | 37 ++++--- .../writer/lock/utils/InMemoryAppender.java | 54 ++++++++++ .../is/initialization/ClosableTest.java | 56 ++++++++-- .../is/initialization/StdOutTest.java | 75 ------------- .../java/com/iluwatar/state/MammothTest.java | 76 +++++++------ .../strategy/DragonSlayingStrategyTest.java | 76 +++++++------ .../templatemethod/StealingMethodTest.java | 102 ++++++++++-------- .../java/com/iluwatar/twin/BallItemTest.java | 66 ++++++++++-- .../java/com/iluwatar/twin/StdOutTest.java | 75 ------------- .../java/com/iluwatar/visitor/StdOutTest.java | 75 ------------- .../com/iluwatar/visitor/VisitorTest.java | 58 ++++++++-- 56 files changed, 1447 insertions(+), 1602 deletions(-) delete mode 100644 dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java create mode 100644 dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java delete mode 100644 front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java create mode 100644 front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java delete mode 100644 layers/src/test/java/com/iluwatar/layers/StdOutTest.java delete mode 100644 null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java delete mode 100644 observer/src/test/java/com/iluwatar/observer/StdOutTest.java create mode 100644 observer/src/test/java/com/iluwatar/observer/utils/InMemoryAppender.java delete mode 100644 poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java delete mode 100644 private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java create mode 100644 private-class-data/src/test/java/com/iluwatar/privateclassdata/utils/InMemoryAppender.java delete mode 100644 producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java delete mode 100644 proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java create mode 100644 proxy/src/test/java/com/iluwatar/proxy/utils/InMemoryAppender.java delete mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java create mode 100644 reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/utils/InMemoryAppender.java delete mode 100644 resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java delete mode 100644 twin/src/test/java/com/iluwatar/twin/StdOutTest.java delete mode 100644 visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java diff --git a/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java index 84b0f6d20..652c1fcdc 100644 --- a/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java +++ b/decorator/src/test/java/com/iluwatar/decorator/TrollTest.java @@ -22,16 +22,18 @@ */ package com.iluwatar.decorator; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; import static org.junit.Assert.assertEquals; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.*; -import static org.mockito.internal.verification.VerificationModeFactory.times; /** * Date: 12/7/15 - 7:26 PM @@ -40,31 +42,16 @@ import static org.mockito.internal.verification.VerificationModeFactory.times; */ public class TrollTest { - /** - * The mocked standard out stream, required since the actions don't have any influence on other - * objects, except for writing to the std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ @Before public void setUp() { - System.setOut(this.stdOutMock); + appender = new InMemoryAppender(Troll.class); } - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ @After public void tearDown() { - System.setOut(this.stdOutOrig); + appender.stop(); } @Test @@ -73,12 +60,34 @@ public class TrollTest { assertEquals(10, troll.getAttackPower()); troll.attack(); - verify(this.stdOutMock, times(1)).println(eq("The troll swings at you with a club!")); + assertEquals("The troll swings at you with a club!", appender.getLastMessage()); troll.fleeBattle(); - verify(this.stdOutMock, times(1)).println(eq("The troll shrieks in horror and runs away!")); + assertEquals("The troll shrieks in horror and runs away!", appender.getLastMessage()); - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(2, appender.getLogSize()); } -} \ No newline at end of file + private class InMemoryAppender extends AppenderBase { + + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getMessage(); + } + + public int getLogSize() { + return log.size(); + } + } +} diff --git a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java index 6a19c9a1a..c1f32a8e0 100644 --- a/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java +++ b/delegation/src/test/java/com/iluwatar/delegation/simple/DelegateTest.java @@ -22,28 +22,44 @@ */ package com.iluwatar.delegation.simple; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import com.iluwatar.delegation.simple.printers.CanonPrinter; import com.iluwatar.delegation.simple.printers.EpsonPrinter; import com.iluwatar.delegation.simple.printers.HpPrinter; -import org.junit.Rule; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.junit.contrib.java.lang.system.SystemOutRule; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; import static org.junit.Assert.assertEquals; public class DelegateTest { - private static final String MESSAGE = "Test Message Printed"; + private InMemoryAppender appender; - @Rule - public final SystemOutRule systemOutRule = new SystemOutRule().enableLog(); + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } + + private static final String MESSAGE = "Test Message Printed"; @Test public void testCanonPrinter() throws Exception { PrinterController printerController = new PrinterController(new CanonPrinter()); printerController.print(MESSAGE); - assertEquals("Canon Printer : Test Message Printed", systemOutRule.getLog()); + assertEquals("Canon Printer : Test Message Printed", appender.getLastMessage()); } @Test @@ -51,7 +67,7 @@ public class DelegateTest { PrinterController printerController = new PrinterController(new HpPrinter()); printerController.print(MESSAGE); - assertEquals("HP Printer : Test Message Printed", systemOutRule.getLog()); + assertEquals("HP Printer : Test Message Printed", appender.getLastMessage()); } @Test @@ -59,7 +75,30 @@ public class DelegateTest { PrinterController printerController = new PrinterController(new EpsonPrinter()); printerController.print(MESSAGE); - assertEquals("Epson Printer : Test Message Printed", systemOutRule.getLog()); + assertEquals("Epson Printer : Test Message Printed", appender.getLastMessage()); + } + + private class InMemoryAppender extends AppenderBase { + + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + + public int getLogSize() { + return log.size(); + } } } diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java index d1f5e574c..a0578626f 100644 --- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/AdvancedWizardTest.java @@ -22,18 +22,31 @@ */ package com.iluwatar.dependency.injection; +import com.iluwatar.dependency.injection.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/10/15 - 8:40 PM * * @author Jeroen Meulemeester */ -public class AdvancedWizardTest extends StdOutTest { +public class AdvancedWizardTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Tobacco.class); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Test if the {@link AdvancedWizard} smokes whatever instance of {@link Tobacco} is passed to him @@ -51,12 +64,13 @@ public class AdvancedWizardTest extends StdOutTest { advancedWizard.smoke(); // Verify if the wizard is smoking the correct tobacco ... - verify(getStdOutMock(), times(1)).println("AdvancedWizard smoking " + tobacco.getClass().getSimpleName()); + assertEquals("AdvancedWizard smoking " + tobacco.getClass().getSimpleName(), appender.getLastMessage()); - // ... and nothing else is happening. - verifyNoMoreInteractions(getStdOutMock()); } + // ... and nothing else is happening. + assertEquals(tobaccos.length, appender.getLogSize()); + } -} \ No newline at end of file +} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java index 1d3d679df..811773678 100644 --- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/GuiceWizardTest.java @@ -25,19 +25,31 @@ package com.iluwatar.dependency.injection; import com.google.inject.AbstractModule; import com.google.inject.Guice; import com.google.inject.Injector; - +import com.iluwatar.dependency.injection.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/10/15 - 8:57 PM * * @author Jeroen Meulemeester */ -public class GuiceWizardTest extends StdOutTest { +public class GuiceWizardTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Tobacco.class); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Test if the {@link GuiceWizard} smokes whatever instance of {@link Tobacco} is passed to him @@ -55,12 +67,11 @@ public class GuiceWizardTest extends StdOutTest { guiceWizard.smoke(); // Verify if the wizard is smoking the correct tobacco ... - verify(getStdOutMock(), times(1)).println("GuiceWizard smoking " + tobacco.getClass().getSimpleName()); - - // ... and nothing else is happening. - verifyNoMoreInteractions(getStdOutMock()); + assertEquals("GuiceWizard smoking " + tobacco.getClass().getSimpleName(), appender.getLastMessage()); } + // ... and nothing else is happening. + assertEquals(tobaccos.length, appender.getLogSize()); } /** @@ -89,12 +100,11 @@ public class GuiceWizardTest extends StdOutTest { guiceWizard.smoke(); // Verify if the wizard is smoking the correct tobacco ... - verify(getStdOutMock(), times(1)).println("GuiceWizard smoking " + tobaccoClass.getSimpleName()); - - // ... and nothing else is happening. - verifyNoMoreInteractions(getStdOutMock()); + assertEquals("GuiceWizard smoking " + tobaccoClass.getSimpleName(), appender.getLastMessage()); } + // ... and nothing else is happening. + assertEquals(tobaccos.length, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java index e5a856e8d..cda3eac47 100644 --- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/SimpleWizardTest.java @@ -22,16 +22,31 @@ */ package com.iluwatar.dependency.injection; +import com.iluwatar.dependency.injection.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import static org.mockito.Mockito.*; +import static org.junit.Assert.assertEquals; /** * Date: 12/10/15 - 8:26 PM * * @author Jeroen Meulemeester */ -public class SimpleWizardTest extends StdOutTest { +public class SimpleWizardTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Tobacco.class); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Test if the {@link SimpleWizard} does the only thing it can do: Smoke it's {@link @@ -41,8 +56,8 @@ public class SimpleWizardTest extends StdOutTest { public void testSmoke() { final SimpleWizard simpleWizard = new SimpleWizard(); simpleWizard.smoke(); - verify(getStdOutMock(), times(1)).println("SimpleWizard smoking OldTobyTobacco"); - verifyNoMoreInteractions(getStdOutMock()); + assertEquals("SimpleWizard smoking OldTobyTobacco", appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java deleted file mode 100644 index 57272c511..000000000 --- a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.dependency.injection; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since the actions of the wizard don't - * have any influence on any other accessible objects, except for writing to std-out using {@link - * System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } -} diff --git a/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java new file mode 100644 index 000000000..b4c60b8aa --- /dev/null +++ b/dependency-injection/src/test/java/com/iluwatar/dependency/injection/utils/InMemoryAppender.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.dependency.injection.utils; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +public class InMemoryAppender extends AppenderBase { + + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + + public int getLogSize() { + return log.size(); + } +} diff --git a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java index 485c9573e..9f5f608b0 100644 --- a/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java +++ b/double-checked-locking/src/test/java/com/iluwatar/doublechecked/locking/InventoryTest.java @@ -22,24 +22,23 @@ */ package com.iluwatar.doublechecked.locking; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.ArgumentCaptor; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import static junit.framework.Assert.assertTrue; import static junit.framework.TestCase.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.junit.Assert.assertTrue; /** * Date: 12/10/15 - 9:34 PM @@ -48,31 +47,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; */ public class InventoryTest { - /** - * The mocked standard out {@link PrintStream}, used to verify a steady increasing size of the - * {@link Inventory} while adding items from multiple threads concurrently - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ @Before public void setUp() { - System.setOut(this.stdOutMock); + appender = new InMemoryAppender(Inventory.class); } - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ @After public void tearDown() { - System.setOut(this.stdOutOrig); + appender.stop(); } /** @@ -112,21 +96,32 @@ public class InventoryTest { assertNotNull(items); assertEquals(INVENTORY_SIZE, items.size()); - // Capture all stdOut messages ... - final ArgumentCaptor stdOutCaptor = ArgumentCaptor.forClass(String.class); - verify(this.stdOutMock, times(INVENTORY_SIZE)).println(stdOutCaptor.capture()); - - // ... verify if we got all 1000 - final List values = stdOutCaptor.getAllValues(); - assertEquals(INVENTORY_SIZE, values.size()); + assertEquals(INVENTORY_SIZE, appender.getLogSize()); // ... and check if the inventory size is increasing continuously - for (int i = 0; i < values.size(); i++) { - assertNotNull(values.get(i)); - assertTrue(values.get(i).contains("items.size()=" + (i + 1))); + for (int i = 0; i < items.size(); i++) { + assertTrue(appender.log.get(i).getFormattedMessage().contains("items.size()=" + (i + 1))); } - - verifyNoMoreInteractions(this.stdOutMock); } -} \ No newline at end of file + + + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + } + +} diff --git a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java index dbc8fc55e..6576d6d2f 100644 --- a/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java +++ b/double-dispatch/src/test/java/com/iluwatar/doubledispatch/CollisionTest.java @@ -22,17 +22,9 @@ */ package com.iluwatar.doubledispatch; -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; import java.util.Objects; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; /** * Date: 12/10/15 - 8:37 PM @@ -41,43 +33,6 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; */ public abstract class CollisionTest { - /** - * The mocked standard out {@link PrintStream}, required if some of the actions on the tested - * object don't have a direct influence on any other accessible objects, except for writing to - * std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - /** * Get the tested object * @@ -106,9 +61,6 @@ public abstract class CollisionTest { tested.collision(other); - verify(getStdOutMock(), times(1)).println(description); - verifyNoMoreInteractions(getStdOutMock()); - testOnFire(other, tested, otherOnFire); testDamaged(other, tested, otherDamaged); @@ -129,8 +81,8 @@ public abstract class CollisionTest { final String targetName = target.getClass().getSimpleName(); final String otherName = other.getClass().getSimpleName(); - final String errorMessage = expectTargetOnFire - ? "Expected [" + targetName + "] to be on fire after colliding with [" + otherName + "] but it was not!" + final String errorMessage = expectTargetOnFire + ? "Expected [" + targetName + "] to be on fire after colliding with [" + otherName + "] but it was not!" : "Expected [" + targetName + "] not to be on fire after colliding with [" + otherName + "] but it was!"; assertEquals(errorMessage, expectTargetOnFire, target.isOnFire()); @@ -149,7 +101,7 @@ public abstract class CollisionTest { final String otherName = other.getClass().getSimpleName(); final String errorMessage = expectedDamage - ? "Expected [" + targetName + "] to be damaged after colliding with [" + otherName + "] but it was not!" + ? "Expected [" + targetName + "] to be damaged after colliding with [" + otherName + "] but it was not!" : "Expected [" + targetName + "] not to be damaged after colliding with [" + otherName + "] but it was!"; assertEquals(errorMessage, expectedDamage, target.isDamaged()); diff --git a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java index 3e0028ac4..e06d10b6a 100644 --- a/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java +++ b/event-aggregator/src/test/java/com/iluwatar/event/aggregator/KingJoffreyTest.java @@ -22,17 +22,18 @@ */ package com.iluwatar.event.aggregator; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyZeroInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/12/15 - 3:04 PM @@ -41,31 +42,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; */ public class KingJoffreyTest { - /** - * The mocked standard out {@link PrintStream}, required since {@link KingJoffrey} does nothing - * except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ @Before public void setUp() { - System.setOut(this.stdOutMock); + appender = new InMemoryAppender(KingJoffrey.class); } - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ @After public void tearDown() { - System.setOut(this.stdOutOrig); + appender.stop(); } /** @@ -75,15 +61,38 @@ public class KingJoffreyTest { public void testOnEvent() { final KingJoffrey kingJoffrey = new KingJoffrey(); - for (final Event event : Event.values()) { - verifyZeroInteractions(this.stdOutMock); + for (int i = 0; i < Event.values().length; ++i) { + assertEquals(i, appender.getLogSize()); + Event event = Event.values()[i]; kingJoffrey.onEvent(event); final String expectedMessage = "Received event from the King's Hand: " + event.toString(); - verify(this.stdOutMock, times(1)).println(expectedMessage); - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(expectedMessage, appender.getLastMessage()); + assertEquals(i + 1, appender.getLogSize()); } } -} \ No newline at end of file + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + + public int getLogSize() { + return log.size(); + } + } + +} diff --git a/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java index 4a3b218e2..7718f7d41 100644 --- a/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java +++ b/facade/src/test/java/com/iluwatar/facade/DwarvenGoldmineFacadeTest.java @@ -22,17 +22,19 @@ */ package com.iluwatar.facade; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; -import static org.mockito.Matchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.internal.verification.VerificationModeFactory.times; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Date: 12/9/15 - 9:40 PM @@ -41,35 +43,19 @@ import static org.mockito.internal.verification.VerificationModeFactory.times; */ public class DwarvenGoldmineFacadeTest { - /** - * The mocked standard out {@link PrintStream}, required since the actions on the gold mine facade - * don't have any influence on any other accessible objects, except for writing to std-out using - * {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ @Before public void setUp() { - System.setOut(this.stdOutMock); + appender = new InMemoryAppender(); } - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ @After public void tearDown() { - System.setOut(this.stdOutOrig); + appender.stop(); } - /** + /** * Test a complete day cycle in the gold mine by executing all three different steps: {@link * DwarvenGoldmineFacade#startNewDay()}, {@link DwarvenGoldmineFacade#digOutGold()} and {@link * DwarvenGoldmineFacade#endDay()}. @@ -82,44 +68,68 @@ public class DwarvenGoldmineFacadeTest { goldMine.startNewDay(); // On the start of a day, all workers should wake up ... - verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger wakes up.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator wakes up.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger wakes up.")); + assertTrue(appender.logContains("Dwarf gold digger wakes up.")); + assertTrue(appender.logContains("Dwarf cart operator wakes up.")); + assertTrue(appender.logContains("Dwarven tunnel digger wakes up.")); // ... and go to the mine - verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes to the mine.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes to the mine.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes to the mine.")); + assertTrue(appender.logContains("Dwarf gold digger goes to the mine.")); + assertTrue(appender.logContains("Dwarf cart operator goes to the mine.")); + assertTrue(appender.logContains("Dwarven tunnel digger goes to the mine.")); // No other actions were invoked, so the workers shouldn't have done (printed) anything else - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(6, appender.getLogSize()); // Now do some actual work, start digging gold! goldMine.digOutGold(); // Since we gave the dig command, every worker should be doing it's job ... - verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger digs for gold.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator moves gold chunks out of the mine.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger creates another promising tunnel.")); + assertTrue(appender.logContains("Dwarf gold digger digs for gold.")); + assertTrue(appender.logContains("Dwarf cart operator moves gold chunks out of the mine.")); + assertTrue(appender.logContains("Dwarven tunnel digger creates another promising tunnel.")); // Again, they shouldn't be doing anything else. - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(9, appender.getLogSize()); // Enough gold, lets end the day. goldMine.endDay(); // Check if the workers go home ... - verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes home.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes home.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes home.")); + assertTrue(appender.logContains("Dwarf gold digger goes home.")); + assertTrue(appender.logContains("Dwarf cart operator goes home.")); + assertTrue(appender.logContains("Dwarven tunnel digger goes home.")); // ... and go to sleep. We need well rested workers the next day :) - verify(this.stdOutMock, times(1)).println(eq("Dwarf gold digger goes to sleep.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarf cart operator goes to sleep.")); - verify(this.stdOutMock, times(1)).println(eq("Dwarven tunnel digger goes to sleep.")); + assertTrue(appender.logContains("Dwarf gold digger goes to sleep.")); + assertTrue(appender.logContains("Dwarf cart operator goes to sleep.")); + assertTrue(appender.logContains("Dwarven tunnel digger goes to sleep.")); // Every worker should be sleeping now, no other actions allowed - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(15, appender.getLogSize()); } + private class InMemoryAppender extends AppenderBase { + + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getFormattedMessage().equals(message)); + } + } + + } diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java index c4d9ae625..0b5c9f593 100644 --- a/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java +++ b/front-controller/src/test/java/com/iluwatar/front/controller/CommandTest.java @@ -22,6 +22,9 @@ */ package com.iluwatar.front.controller; +import com.iluwatar.front.controller.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -30,9 +33,7 @@ import org.junit.runners.Parameterized.Parameters; import java.util.ArrayList; import java.util.List; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/13/15 - 1:39 PM @@ -40,7 +41,19 @@ import static org.mockito.Mockito.verifyZeroInteractions; * @author Jeroen Meulemeester */ @RunWith(Parameterized.class) -public class CommandTest extends StdOutTest { +public class CommandTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } @Parameters public static List data() { @@ -75,10 +88,10 @@ public class CommandTest extends StdOutTest { @Test public void testDisplay() { final FrontController frontController = new FrontController(); - verifyZeroInteractions(getStdOutMock()); + assertEquals(0, appender.getLogSize()); frontController.handleRequest(request); - verify(getStdOutMock()).println(displayMessage); - verifyNoMoreInteractions(getStdOutMock()); + assertEquals(displayMessage, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } } diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java index 0822ffcd0..46a26e6fb 100644 --- a/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java +++ b/front-controller/src/test/java/com/iluwatar/front/controller/FrontControllerTest.java @@ -22,6 +22,9 @@ */ package com.iluwatar.front.controller; +import com.iluwatar.front.controller.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -30,9 +33,7 @@ import org.junit.runners.Parameterized.Parameters; import java.util.ArrayList; import java.util.List; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/13/15 - 1:39 PM @@ -40,7 +41,19 @@ import static org.mockito.Mockito.verifyZeroInteractions; * @author Jeroen Meulemeester */ @RunWith(Parameterized.class) -public class FrontControllerTest extends StdOutTest { +public class FrontControllerTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } @Parameters public static List data() { @@ -74,10 +87,10 @@ public class FrontControllerTest extends StdOutTest { @Test public void testDisplay() { - verifyZeroInteractions(getStdOutMock()); + assertEquals(0, appender.getLogSize()); this.command.process(); - verify(getStdOutMock()).println(displayMessage); - verifyNoMoreInteractions(getStdOutMock()); + assertEquals(displayMessage, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } } diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java deleted file mode 100644 index bc32a1b3c..000000000 --- a/front-controller/src/test/java/com/iluwatar/front/controller/StdOutTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.front.controller; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since the actions of the views don't have - * any influence on any other accessible objects, except for writing to std-out using {@link - * System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java b/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java index cdabd66ef..bca070305 100644 --- a/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java +++ b/front-controller/src/test/java/com/iluwatar/front/controller/ViewTest.java @@ -22,6 +22,9 @@ */ package com.iluwatar.front.controller; +import com.iluwatar.front.controller.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -30,9 +33,7 @@ import org.junit.runners.Parameterized.Parameters; import java.util.ArrayList; import java.util.List; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/13/15 - 1:39 PM @@ -40,7 +41,19 @@ import static org.mockito.Mockito.verifyZeroInteractions; * @author Jeroen Meulemeester */ @RunWith(Parameterized.class) -public class ViewTest extends StdOutTest { +public class ViewTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } @Parameters public static List data() { @@ -74,10 +87,10 @@ public class ViewTest extends StdOutTest { @Test public void testDisplay() { - verifyZeroInteractions(getStdOutMock()); + assertEquals(0, appender.getLogSize()); this.view.display(); - verify(getStdOutMock()).println(displayMessage); - verifyNoMoreInteractions(getStdOutMock()); + assertEquals(displayMessage, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java b/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java new file mode 100644 index 000000000..1747deac5 --- /dev/null +++ b/front-controller/src/test/java/com/iluwatar/front/controller/utils/InMemoryAppender.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.front.controller.utils; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +public class InMemoryAppender extends AppenderBase { + + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + + public int getLogSize() { + return log.size(); + } +} diff --git a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java index 00d61b668..077b0f235 100644 --- a/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java +++ b/hexagonal/src/main/java/com/iluwatar/hexagonal/service/ConsoleLottery.java @@ -24,7 +24,6 @@ package com.iluwatar.hexagonal.service; import com.google.inject.Guice; import com.google.inject.Injector; -import com.iluwatar.hexagonal.administration.ConsoleAdministration; import com.iluwatar.hexagonal.banking.WireTransfers; import com.iluwatar.hexagonal.domain.LotteryNumbers; import com.iluwatar.hexagonal.domain.LotteryService; diff --git a/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java b/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java index a69aa23bb..3a9214ae1 100644 --- a/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java +++ b/layers/src/test/java/com/iluwatar/layers/CakeViewImplTest.java @@ -22,11 +22,19 @@ */ package com.iluwatar.layers; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; import java.util.ArrayList; +import java.util.LinkedList; import java.util.List; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.*; /** @@ -34,7 +42,19 @@ import static org.mockito.Mockito.*; * * @author Jeroen Meulemeester */ -public class CakeViewImplTest extends StdOutTest { +public class CakeViewImplTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(CakeViewImpl.class); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Verify if the cake view renders the expected result @@ -56,11 +76,34 @@ public class CakeViewImplTest extends StdOutTest { final CakeViewImpl cakeView = new CakeViewImpl(bakingService); - verifyZeroInteractions(getStdOutMock()); + assertEquals(0, appender.getLogSize()); cakeView.render(); - verify(getStdOutMock(), times(1)).println(cake); + assertEquals(cake.toString(), appender.getLastMessage()); } + private class InMemoryAppender extends AppenderBase { + + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + + public int getLogSize() { + return log.size(); + } + } + } diff --git a/layers/src/test/java/com/iluwatar/layers/StdOutTest.java b/layers/src/test/java/com/iluwatar/layers/StdOutTest.java deleted file mode 100644 index 67a554873..000000000 --- a/layers/src/test/java/com/iluwatar/layers/StdOutTest.java +++ /dev/null @@ -1,78 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.layers; - -import org.junit.After; -import org.junit.Before; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since the actions of the views don't have - * any influence on any other accessible objects, except for writing to std-out using {@link - * System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java b/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java index 0bf43f9cd..e5f7e2719 100644 --- a/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java +++ b/mediator/src/test/java/com/iluwatar/mediator/PartyMemberTest.java @@ -22,22 +22,25 @@ */ package com.iluwatar.mediator; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedList; +import java.util.List; import java.util.function.Supplier; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; /** * Date: 12/19/15 - 10:13 PM @@ -57,34 +60,6 @@ public class PartyMemberTest { ); } - /** - * The mocked standard out {@link PrintStream}, required since some actions on a {@link - * PartyMember} have any influence on any other accessible objects, except for writing to std-out - * using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - /** * The factory, used to create a new instance of the tested party member */ @@ -99,6 +74,18 @@ public class PartyMemberTest { this.memberSupplier = memberSupplier; } + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(PartyMemberBase.class); + } + + @After + public void tearDown() { + appender.stop(); + } + /** * Verify if a party action triggers the correct output to the std-Out */ @@ -108,10 +95,10 @@ public class PartyMemberTest { for (final Action action : Action.values()) { member.partyAction(action); - verify(this.stdOutMock).println(member.toString() + " " + action.getDescription()); + assertEquals(member.toString() + " " + action.getDescription(), appender.getLastMessage()); } - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(Action.values().length, appender.getLogSize()); } /** @@ -122,19 +109,19 @@ public class PartyMemberTest { final PartyMember member = this.memberSupplier.get(); member.act(Action.GOLD); - verifyZeroInteractions(this.stdOutMock); + assertEquals(0, appender.getLogSize()); final Party party = mock(Party.class); member.joinedParty(party); - verify(this.stdOutMock).println(member.toString() + " joins the party"); + assertEquals(member.toString() + " joins the party", appender.getLastMessage()); for (final Action action : Action.values()) { member.act(action); - verify(this.stdOutMock).println(member.toString() + " " + action.toString()); + assertEquals(member.toString() + " " + action.toString(), appender.getLastMessage()); verify(party).act(member, action); } - verifyNoMoreInteractions(party, this.stdOutMock); + assertEquals(Action.values().length + 1, appender.getLogSize()); } /** @@ -147,4 +134,27 @@ public class PartyMemberTest { assertEquals(memberClass.getSimpleName(), member.toString()); } + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + } + + } diff --git a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java index 89d503d4e..9ea0ae96e 100644 --- a/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java +++ b/model-view-controller/src/test/java/com/iluwatar/model/view/controller/GiantViewTest.java @@ -22,15 +22,19 @@ */ package com.iluwatar.model.view.controller; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; /** * Date: 12/20/15 - 2:04 PM @@ -39,32 +43,16 @@ import static org.mockito.Mockito.verifyNoMoreInteractions; */ public class GiantViewTest { - /** - * The mocked standard out {@link PrintStream}, required since the actions of the views don't have - * any influence on any other accessible objects, except for writing to std-out using {@link - * System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ @Before public void setUp() { - System.setOut(this.stdOutMock); + appender = new InMemoryAppender(GiantView.class); } - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ @After public void tearDown() { - System.setOut(this.stdOutOrig); + appender.stop(); } /** @@ -78,9 +66,29 @@ public class GiantViewTest { final GiantModel model = mock(GiantModel.class); view.displayGiant(model); - verify(this.stdOutMock).println(model); - verifyNoMoreInteractions(model, this.stdOutMock); - + assertEquals(model.toString(), appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file + public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getMessage(); + } + + public int getLogSize() { + return log.size(); + } + } +} diff --git a/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java b/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java index 1375b8949..98168fc0a 100644 --- a/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java +++ b/null-object/src/test/java/com/iluwatar/nullobject/NullNodeTest.java @@ -23,19 +23,15 @@ package com.iluwatar.nullobject; import org.junit.Test; -import org.mockito.Mockito; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertSame; +import static org.junit.Assert.*; /** * Date: 12/26/15 - 11:47 PM * * @author Jeroen Meulemeester */ -public class NullNodeTest extends StdOutTest { +public class NullNodeTest { /** * Verify if {@link NullNode#getInstance()} actually returns the same object instance @@ -59,7 +55,6 @@ public class NullNodeTest extends StdOutTest { @Test public void testWalk() throws Exception { NullNode.getInstance().walk(); - Mockito.verifyZeroInteractions(getStdOutMock()); } -} \ No newline at end of file +} diff --git a/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java b/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java deleted file mode 100644 index 0c0122132..000000000 --- a/null-object/src/test/java/com/iluwatar/nullobject/StdOutTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.nullobject; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since walking through the tree has no - * influence on any other accessible object, except for writing to std-out using {@link - * System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java b/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java index 6c77cd236..2712b0356 100644 --- a/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java +++ b/null-object/src/test/java/com/iluwatar/nullobject/TreeTest.java @@ -22,20 +22,37 @@ */ package com.iluwatar.nullobject; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; +import org.slf4j.LoggerFactory; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertSame; +import java.util.LinkedList; +import java.util.List; + +import static org.junit.Assert.*; /** * Date: 12/26/15 - 11:44 PM * * @author Jeroen Meulemeester */ -public class TreeTest extends StdOutTest { +public class TreeTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } /** * During the tests, the same tree structure will be used, shown below. End points will be @@ -79,15 +96,14 @@ public class TreeTest extends StdOutTest { public void testWalk() { TREE_ROOT.walk(); - final InOrder inOrder = Mockito.inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("root"); - inOrder.verify(getStdOutMock()).println("level1_a"); - inOrder.verify(getStdOutMock()).println("level2_a"); - inOrder.verify(getStdOutMock()).println("level3_a"); - inOrder.verify(getStdOutMock()).println("level3_b"); - inOrder.verify(getStdOutMock()).println("level2_b"); - inOrder.verify(getStdOutMock()).println("level1_b"); - inOrder.verifyNoMoreInteractions(); + assertTrue(appender.logContains("root")); + assertTrue(appender.logContains("level1_a")); + assertTrue(appender.logContains("level2_a")); + assertTrue(appender.logContains("level3_a")); + assertTrue(appender.logContains("level3_b")); + assertTrue(appender.logContains("level2_b")); + assertTrue(appender.logContains("level1_b")); + assertEquals(7, appender.getLogSize()); } @Test @@ -120,4 +136,26 @@ public class TreeTest extends StdOutTest { assertSame(NullNode.getInstance(), level1.getLeft()); } + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getMessage().equals(message)); + } + + public int getLogSize() { + return log.size(); + } + } + } diff --git a/observer/src/test/java/com/iluwatar/observer/StdOutTest.java b/observer/src/test/java/com/iluwatar/observer/StdOutTest.java deleted file mode 100644 index afd870ae4..000000000 --- a/observer/src/test/java/com/iluwatar/observer/StdOutTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.observer; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/27/15 - 12:16 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since changes in the weather doesn't has - * any influence on any other accessible objects, except for writing to std-out using {@link - * System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - protected final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java b/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java index a06c19952..1ccf4bf12 100644 --- a/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java +++ b/observer/src/test/java/com/iluwatar/observer/WeatherObserverTest.java @@ -22,20 +22,33 @@ */ package com.iluwatar.observer; +import com.iluwatar.observer.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import java.util.function.Supplier; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/27/15 - 11:44 AM * * @author Jeroen Meulemeester */ -public abstract class WeatherObserverTest extends StdOutTest { +public abstract class WeatherObserverTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } /** * The observer instance factory @@ -71,11 +84,11 @@ public abstract class WeatherObserverTest extends Std @Test public void testObserver() { final O observer = this.factory.get(); - verifyZeroInteractions(getStdOutMock()); + assertEquals(0, appender.getLogSize()); observer.update(this.weather); - verify(getStdOutMock()).println(this.response); - verifyNoMoreInteractions(getStdOutMock()); + assertEquals(response, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/observer/src/test/java/com/iluwatar/observer/WeatherTest.java b/observer/src/test/java/com/iluwatar/observer/WeatherTest.java index 7a38e7137..9d53b2e23 100644 --- a/observer/src/test/java/com/iluwatar/observer/WeatherTest.java +++ b/observer/src/test/java/com/iluwatar/observer/WeatherTest.java @@ -22,9 +22,13 @@ */ package com.iluwatar.observer; +import com.iluwatar.observer.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; +import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.inOrder; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -36,7 +40,19 @@ import static org.mockito.Mockito.verifyZeroInteractions; * * @author Jeroen Meulemeester */ -public class WeatherTest extends StdOutTest { +public class WeatherTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Weather.class); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Add a {@link WeatherObserver}, verify if it gets notified of a weather change, remove the @@ -51,14 +67,15 @@ public class WeatherTest extends StdOutTest { verifyZeroInteractions(observer); weather.timePasses(); - verify(getStdOutMock()).println("The weather changed to rainy."); + assertEquals("The weather changed to rainy.", appender.getLastMessage()); verify(observer).update(WeatherType.RAINY); weather.removeObserver(observer); weather.timePasses(); - verify(getStdOutMock()).println("The weather changed to windy."); + assertEquals("The weather changed to windy.", appender.getLastMessage()); - verifyNoMoreInteractions(observer, getStdOutMock()); + verifyNoMoreInteractions(observer); + assertEquals(2, appender.getLogSize()); } /** @@ -70,7 +87,7 @@ public class WeatherTest extends StdOutTest { final Weather weather = new Weather(); weather.addObserver(observer); - final InOrder inOrder = inOrder(observer, getStdOutMock()); + final InOrder inOrder = inOrder(observer); final WeatherType[] weatherTypes = WeatherType.values(); for (int i = 1; i < 20; i++) { weather.timePasses(); @@ -80,4 +97,4 @@ public class WeatherTest extends StdOutTest { verifyNoMoreInteractions(observer); } -} \ No newline at end of file +} diff --git a/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java b/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java index 758dbca8c..61d619a60 100644 --- a/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java +++ b/observer/src/test/java/com/iluwatar/observer/generic/GWeatherTest.java @@ -22,25 +22,35 @@ */ package com.iluwatar.observer.generic; -import com.iluwatar.observer.StdOutTest; import com.iluwatar.observer.WeatherObserver; import com.iluwatar.observer.WeatherType; - +import com.iluwatar.observer.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; +import static org.mockito.Mockito.*; /** * Date: 12/27/15 - 11:08 AM * * @author Jeroen Meulemeester */ -public class GWeatherTest extends StdOutTest { +public class GWeatherTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(GWeather.class); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Add a {@link WeatherObserver}, verify if it gets notified of a weather change, remove the @@ -55,14 +65,15 @@ public class GWeatherTest extends StdOutTest { verifyZeroInteractions(observer); weather.timePasses(); - verify(getStdOutMock()).println("The weather changed to rainy."); + assertEquals("The weather changed to rainy.", appender.getLastMessage()); verify(observer).update(weather, WeatherType.RAINY); weather.removeObserver(observer); weather.timePasses(); - verify(getStdOutMock()).println("The weather changed to windy."); + assertEquals("The weather changed to windy.", appender.getLastMessage()); - verifyNoMoreInteractions(observer, getStdOutMock()); + verifyNoMoreInteractions(observer); + assertEquals(2, appender.getLogSize()); } /** @@ -74,7 +85,7 @@ public class GWeatherTest extends StdOutTest { final GWeather weather = new GWeather(); weather.addObserver(observer); - final InOrder inOrder = inOrder(observer, getStdOutMock()); + final InOrder inOrder = inOrder(observer); final WeatherType[] weatherTypes = WeatherType.values(); for (int i = 1; i < 20; i++) { weather.timePasses(); @@ -84,4 +95,4 @@ public class GWeatherTest extends StdOutTest { verifyNoMoreInteractions(observer); } -} \ No newline at end of file +} diff --git a/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java b/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java index 33d8daaf5..d01d30992 100644 --- a/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java +++ b/observer/src/test/java/com/iluwatar/observer/generic/ObserverTest.java @@ -22,23 +22,34 @@ */ package com.iluwatar.observer.generic; -import com.iluwatar.observer.StdOutTest; import com.iluwatar.observer.WeatherType; - +import com.iluwatar.observer.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import java.util.function.Supplier; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/27/15 - 11:44 AM * * @author Jeroen Meulemeester */ -public abstract class ObserverTest extends StdOutTest { +public abstract class ObserverTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } /** * The observer instance factory @@ -74,11 +85,11 @@ public abstract class ObserverTest extends StdOutTest { @Test public void testObserver() { final O observer = this.factory.get(); - verifyZeroInteractions(getStdOutMock()); + assertEquals(0, appender.getLogSize()); observer.update(null, this.weather); - verify(getStdOutMock()).println(this.response); - verifyNoMoreInteractions(getStdOutMock()); + assertEquals(this.response, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/observer/src/test/java/com/iluwatar/observer/utils/InMemoryAppender.java b/observer/src/test/java/com/iluwatar/observer/utils/InMemoryAppender.java new file mode 100644 index 000000000..4e6e49768 --- /dev/null +++ b/observer/src/test/java/com/iluwatar/observer/utils/InMemoryAppender.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.observer.utils; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java index 01a5dfaa6..983287c3c 100644 --- a/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java +++ b/poison-pill/src/test/java/com/iluwatar/poison/pill/ConsumerTest.java @@ -22,19 +22,38 @@ */ package com.iluwatar.poison.pill; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; +import org.slf4j.LoggerFactory; import java.time.LocalDateTime; +import java.util.LinkedList; +import java.util.List; -import static org.mockito.Mockito.inOrder; +import static org.junit.Assert.assertTrue; /** * Date: 12/27/15 - 9:45 PM * * @author Jeroen Meulemeester */ -public class ConsumerTest extends StdOutTest { +public class ConsumerTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Consumer.class); + } + + @After + public void tearDown() { + appender.stop(); + } @Test public void testConsume() throws Exception { @@ -52,12 +71,9 @@ public class ConsumerTest extends StdOutTest { new Consumer("NSA", queue).consume(); - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Message [Hello!] from [you] received by [NSA]"); - inOrder.verify(getStdOutMock()).println("Message [Hi!] from [me] received by [NSA]"); - inOrder.verify(getStdOutMock()).println("Consumer NSA receive request to terminate."); - inOrder.verifyNoMoreInteractions(); - + assertTrue(appender.logContains("Message [Hello!] from [you] received by [NSA]")); + assertTrue(appender.logContains("Message [Hi!] from [me] received by [NSA]")); + assertTrue(appender.logContains("Consumer NSA receive request to terminate.")); } /** @@ -75,4 +91,22 @@ public class ConsumerTest extends StdOutTest { return msg; } -} \ No newline at end of file + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getFormattedMessage().equals(message)); + } + } + +} diff --git a/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java b/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java deleted file mode 100644 index f1b3c4840..000000000 --- a/poison-pill/src/test/java/com/iluwatar/poison/pill/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.poison.pill; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java index 58867d303..431375ef7 100644 --- a/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/ImmutableStewTest.java @@ -22,18 +22,31 @@ */ package com.iluwatar.privateclassdata; +import com.iluwatar.privateclassdata.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.verify; +import static org.junit.Assert.assertEquals; /** * Date: 12/27/15 - 10:46 PM * * @author Jeroen Meulemeester */ -public class ImmutableStewTest extends StdOutTest { +public class ImmutableStewTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Verify if mixing the stew doesn't change the internal state @@ -41,15 +54,14 @@ public class ImmutableStewTest extends StdOutTest { @Test public void testMix() { final Stew stew = new Stew(1, 2, 3, 4); - final String message = "Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"; + final String expectedMessage = "Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"; - final InOrder inOrder = inOrder(getStdOutMock()); for (int i = 0; i < 20; i++) { stew.mix(); - inOrder.verify(getStdOutMock()).println(message); + assertEquals(expectedMessage, appender.getLastMessage()); } - inOrder.verifyNoMoreInteractions(); + assertEquals(20, appender.getLogSize()); } /** @@ -60,15 +72,12 @@ public class ImmutableStewTest extends StdOutTest { final Stew stew = new Stew(1, 2, 3, 4); stew.mix(); - verify(getStdOutMock()) - .println("Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers"); + assertEquals("Mixing the stew we find: 1 potatoes, 2 carrots, 3 meat and 4 peppers", appender.getLastMessage()); stew.taste(); - verify(getStdOutMock()).println("Tasting the stew"); + assertEquals("Tasting the stew", appender.getLastMessage()); stew.mix(); - verify(getStdOutMock()) - .println("Mixing the stew we find: 0 potatoes, 1 carrots, 2 meat and 3 peppers"); - + assertEquals("Mixing the stew we find: 0 potatoes, 1 carrots, 2 meat and 3 peppers", appender.getLastMessage()); } -} \ No newline at end of file +} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java deleted file mode 100644 index f90551020..000000000 --- a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.privateclassdata; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java index a894e4ae0..f2e3c5fad 100644 --- a/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/StewTest.java @@ -22,17 +22,31 @@ */ package com.iluwatar.privateclassdata; +import com.iluwatar.privateclassdata.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; -import static org.mockito.Mockito.inOrder; +import static org.junit.Assert.assertEquals; /** * Date: 12/27/15 - 10:46 PM * * @author Jeroen Meulemeester */ -public class StewTest extends StdOutTest { +public class StewTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } /** * Verify if mixing the stew doesn't change the internal state @@ -43,13 +57,12 @@ public class StewTest extends StdOutTest { final String expectedMessage = "Mixing the immutable stew we find: 1 potatoes, " + "2 carrots, 3 meat and 4 peppers"; - final InOrder inOrder = inOrder(getStdOutMock()); for (int i = 0; i < 20; i++) { stew.mix(); - inOrder.verify(getStdOutMock()).println(expectedMessage); + assertEquals(expectedMessage, appender.getLastMessage()); } - inOrder.verifyNoMoreInteractions(); + assertEquals(20, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/private-class-data/src/test/java/com/iluwatar/privateclassdata/utils/InMemoryAppender.java b/private-class-data/src/test/java/com/iluwatar/privateclassdata/utils/InMemoryAppender.java new file mode 100644 index 000000000..4b2def101 --- /dev/null +++ b/private-class-data/src/test/java/com/iluwatar/privateclassdata/utils/InMemoryAppender.java @@ -0,0 +1,53 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.privateclassdata.utils; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } +} diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java index 2ca547a0b..dd725df75 100644 --- a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java +++ b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/ConsumerTest.java @@ -23,18 +23,15 @@ package com.iluwatar.producer.consumer; import org.junit.Test; -import org.mockito.InOrder; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.*; /** * Date: 12/27/15 - 11:01 PM * * @author Jeroen Meulemeester */ -public class ConsumerTest extends StdOutTest { +public class ConsumerTest { private static final int ITEM_COUNT = 5; @@ -48,14 +45,11 @@ public class ConsumerTest extends StdOutTest { reset(queue); // Don't count the preparation above as interactions with the queue final Consumer consumer = new Consumer("consumer", queue); - final InOrder inOrder = inOrder(getStdOutMock()); for (int id = 0; id < ITEM_COUNT; id++) { consumer.consume(); - inOrder.verify(getStdOutMock()) - .println("Consumer [consumer] consume item [" + id + "] produced by [producer]"); } - inOrder.verifyNoMoreInteractions(); + verify(queue, times(ITEM_COUNT)).take(); } } diff --git a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java b/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java deleted file mode 100644 index 1e1c41f75..000000000 --- a/producer-consumer/src/test/java/com/iluwatar/producer/consumer/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.producer.consumer; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java b/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java deleted file mode 100644 index 48831444a..000000000 --- a/proxy/src/test/java/com/iluwatar/proxy/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.proxy; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java index b87b7a0bc..921624f63 100644 --- a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerProxyTest.java @@ -22,17 +22,32 @@ */ package com.iluwatar.proxy; +import com.iluwatar.proxy.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; -import static org.mockito.Mockito.inOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Date: 12/28/15 - 9:18 PM * * @author Jeroen Meulemeester */ -public class WizardTowerProxyTest extends StdOutTest { +public class WizardTowerProxyTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } @Test public void testEnter() throws Exception { @@ -48,13 +63,11 @@ public class WizardTowerProxyTest extends StdOutTest { tower.enter(wizard); } - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Gandalf enters the tower."); - inOrder.verify(getStdOutMock()).println("Dumbledore enters the tower."); - inOrder.verify(getStdOutMock()).println("Oz enters the tower."); - inOrder.verify(getStdOutMock()).println("Merlin is not allowed to enter!"); - inOrder.verifyNoMoreInteractions(); - + assertTrue(appender.logContains("Gandalf enters the tower.")); + assertTrue(appender.logContains("Dumbledore enters the tower.")); + assertTrue(appender.logContains("Oz enters the tower.")); + assertTrue(appender.logContains("Merlin is not allowed to enter!")); + assertEquals(4, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java index 9996434f5..ab56115ca 100644 --- a/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java +++ b/proxy/src/test/java/com/iluwatar/proxy/WizardTowerTest.java @@ -22,17 +22,32 @@ */ package com.iluwatar.proxy; +import com.iluwatar.proxy.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; -import static org.mockito.Mockito.inOrder; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Date: 12/28/15 - 9:18 PM * * @author Jeroen Meulemeester */ -public class WizardTowerTest extends StdOutTest { +public class WizardTowerTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(WizardTower.class); + } + + @After + public void tearDown() { + appender.stop(); + } @Test public void testEnter() throws Exception { @@ -48,13 +63,11 @@ public class WizardTowerTest extends StdOutTest { tower.enter(wizard); } - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Gandalf enters the tower."); - inOrder.verify(getStdOutMock()).println("Dumbledore enters the tower."); - inOrder.verify(getStdOutMock()).println("Oz enters the tower."); - inOrder.verify(getStdOutMock()).println("Merlin enters the tower."); - inOrder.verifyNoMoreInteractions(); - + assertTrue(appender.logContains("Gandalf enters the tower.")); + assertTrue(appender.logContains("Dumbledore enters the tower.")); + assertTrue(appender.logContains("Oz enters the tower.")); + assertTrue(appender.logContains("Merlin enters the tower.")); + assertEquals(4, appender.getLogSize()); } -} \ No newline at end of file +} diff --git a/proxy/src/test/java/com/iluwatar/proxy/utils/InMemoryAppender.java b/proxy/src/test/java/com/iluwatar/proxy/utils/InMemoryAppender.java new file mode 100644 index 000000000..857d3e548 --- /dev/null +++ b/proxy/src/test/java/com/iluwatar/proxy/utils/InMemoryAppender.java @@ -0,0 +1,58 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.proxy.utils; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getFormattedMessage().equals(message)); + } + + public int getLogSize() { + return log.size(); + } +} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java index dc8feb04f..7d0ac70bf 100644 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderAndWriterTest.java @@ -23,21 +23,35 @@ package com.iluwatar.reader.writer.lock; -import static org.mockito.Mockito.inOrder; +import com.iluwatar.reader.writer.lock.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.junit.Test; -import org.mockito.InOrder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertTrue; /** * @author hongshuwei@gmail.com */ -public class ReaderAndWriterTest extends StdOutTest { +public class ReaderAndWriterTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } private static final Logger LOGGER = LoggerFactory.getLogger(ReaderAndWriterTest.class); @@ -65,11 +79,10 @@ public class ReaderAndWriterTest extends StdOutTest { LOGGER.error("Error waiting for ExecutorService shutdown", e); } - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Reader 1 begin"); - inOrder.verify(getStdOutMock()).println("Reader 1 finish"); - inOrder.verify(getStdOutMock()).println("Writer 1 begin"); - inOrder.verify(getStdOutMock()).println("Writer 1 finish"); + assertTrue(appender.logContains("Reader 1 begin")); + assertTrue(appender.logContains("Reader 1 finish")); + assertTrue(appender.logContains("Writer 1 begin")); + assertTrue(appender.logContains("Writer 1 finish")); } /** @@ -96,11 +109,10 @@ public class ReaderAndWriterTest extends StdOutTest { LOGGER.error("Error waiting for ExecutorService shutdown", e); } - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Writer 1 begin"); - inOrder.verify(getStdOutMock()).println("Writer 1 finish"); - inOrder.verify(getStdOutMock()).println("Reader 1 begin"); - inOrder.verify(getStdOutMock()).println("Reader 1 finish"); + assertTrue(appender.logContains("Writer 1 begin")); + assertTrue(appender.logContains("Writer 1 finish")); + assertTrue(appender.logContains("Reader 1 begin")); + assertTrue(appender.logContains("Reader 1 finish")); } } diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java index a51120bf8..9830eda67 100644 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/ReaderTest.java @@ -22,22 +22,36 @@ */ package com.iluwatar.reader.writer.lock; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.spy; +import com.iluwatar.reader.writer.lock.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.junit.Test; -import org.mockito.InOrder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static junit.framework.TestCase.assertTrue; +import static org.mockito.Mockito.spy; /** * @author hongshuwei@gmail.com */ -public class ReaderTest extends StdOutTest { +public class ReaderTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Reader.class); + } + + @After + public void tearDown() { + appender.stop(); + } private static final Logger LOGGER = LoggerFactory.getLogger(ReaderTest.class); @@ -66,11 +80,9 @@ public class ReaderTest extends StdOutTest { // Read operation will hold the read lock 250 milliseconds, so here we prove that multiple reads // can be performed in the same time. - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Reader 1 begin"); - inOrder.verify(getStdOutMock()).println("Reader 2 begin"); - inOrder.verify(getStdOutMock()).println("Reader 1 finish"); - inOrder.verify(getStdOutMock()).println("Reader 2 finish"); - + assertTrue(appender.logContains("Reader 1 begin")); + assertTrue(appender.logContains("Reader 2 begin")); + assertTrue(appender.logContains("Reader 1 finish")); + assertTrue(appender.logContains("Reader 2 finish")); } } diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java deleted file mode 100644 index 7a1af09c0..000000000 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/StdOutTest.java +++ /dev/null @@ -1,76 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ - -package com.iluwatar.reader.writer.lock; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java index 729b5eff3..4bf6d0747 100644 --- a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/WriterTest.java @@ -22,22 +22,36 @@ */ package com.iluwatar.reader.writer.lock; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.spy; +import com.iluwatar.reader.writer.lock.utils.InMemoryAppender; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; -import org.junit.Test; -import org.mockito.InOrder; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.spy; /** * @author hongshuwei@gmail.com */ -public class WriterTest extends StdOutTest { +public class WriterTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(Writer.class); + } + + @After + public void tearDown() { + appender.stop(); + } private static final Logger LOGGER = LoggerFactory.getLogger(WriterTest.class); @@ -67,10 +81,9 @@ public class WriterTest extends StdOutTest { // Write operation will hold the write lock 250 milliseconds, so here we verify that when two // writer execute concurrently, the second writer can only writes only when the first one is // finished. - final InOrder inOrder = inOrder(getStdOutMock()); - inOrder.verify(getStdOutMock()).println("Writer 1 begin"); - inOrder.verify(getStdOutMock()).println("Writer 1 finish"); - inOrder.verify(getStdOutMock()).println("Writer 2 begin"); - inOrder.verify(getStdOutMock()).println("Writer 2 finish"); + assertTrue(appender.logContains("Writer 1 begin")); + assertTrue(appender.logContains("Writer 1 finish")); + assertTrue(appender.logContains("Writer 2 begin")); + assertTrue(appender.logContains("Writer 2 finish")); } } diff --git a/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/utils/InMemoryAppender.java b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/utils/InMemoryAppender.java new file mode 100644 index 000000000..44fcb084b --- /dev/null +++ b/reader-writer-lock/src/test/java/com/iluwatar/reader/writer/lock/utils/InMemoryAppender.java @@ -0,0 +1,54 @@ +/** + * The MIT License + * Copyright (c) 2014 Ilkka Seppälä + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package com.iluwatar.reader.writer.lock.utils; + +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.slf4j.LoggerFactory; + +import java.util.LinkedList; +import java.util.List; + +public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender(Class clazz) { + ((Logger) LoggerFactory.getLogger(clazz)).addAppender(this); + start(); + } + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getFormattedMessage().equals(message)); + } +} diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java index 55bdaf19c..638b06ecc 100644 --- a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java +++ b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/ClosableTest.java @@ -22,28 +22,64 @@ */ package com.iluwatar.resource.acquisition.is.initialization; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; +import org.slf4j.LoggerFactory; -import static org.mockito.Mockito.inOrder; +import java.util.LinkedList; +import java.util.List; + +import static org.junit.Assert.assertTrue; /** * Date: 12/28/15 - 9:31 PM * * @author Jeroen Meulemeester */ -public class ClosableTest extends StdOutTest { +public class ClosableTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } @Test public void testOpenClose() throws Exception { - final InOrder inOrder = inOrder(getStdOutMock()); try (final SlidingDoor door = new SlidingDoor(); final TreasureChest chest = new TreasureChest()) { - inOrder.verify(getStdOutMock()).println("Sliding door opens."); - inOrder.verify(getStdOutMock()).println("Treasure chest opens."); + assertTrue(appender.logContains("Sliding door opens.")); + assertTrue(appender.logContains("Treasure chest opens.")); } - inOrder.verify(getStdOutMock()).println("Treasure chest closes."); - inOrder.verify(getStdOutMock()).println("Sliding door closes."); - inOrder.verifyNoMoreInteractions(); + assertTrue(appender.logContains("Treasure chest closes.")); + assertTrue(appender.logContains("Sliding door closes.")); } -} \ No newline at end of file + public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getMessage().equals(message)); + } + } + +} diff --git a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java b/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java deleted file mode 100644 index 42cb42e6b..000000000 --- a/resource-acquisition-is-initialization/src/test/java/com/iluwatar/resource/acquisition/is/initialization/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.resource.acquisition.is.initialization; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/state/src/test/java/com/iluwatar/state/MammothTest.java b/state/src/test/java/com/iluwatar/state/MammothTest.java index 4fe37bfd1..be4c0d892 100644 --- a/state/src/test/java/com/iluwatar/state/MammothTest.java +++ b/state/src/test/java/com/iluwatar/state/MammothTest.java @@ -22,17 +22,19 @@ */ package com.iluwatar.state; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; -import org.mockito.Mockito; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import static org.mockito.Mockito.mock; /** * Date: 12/29/15 - 8:27 PM @@ -41,31 +43,16 @@ import static org.mockito.Mockito.mock; */ public class MammothTest { - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ @Before public void setUp() { - System.setOut(this.stdOutMock); + appender = new InMemoryAppender(); } - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ @After public void tearDown() { - System.setOut(this.stdOutOrig); + appender.stop(); } /** @@ -74,28 +61,27 @@ public class MammothTest { */ @Test public void testTimePasses() { - final InOrder inOrder = Mockito.inOrder(this.stdOutMock); final Mammoth mammoth = new Mammoth(); mammoth.observe(); - inOrder.verify(this.stdOutMock).println("The mammoth is calm and peaceful."); - inOrder.verifyNoMoreInteractions(); + assertEquals("The mammoth is calm and peaceful.", appender.getLastMessage()); + assertEquals(1 , appender.getLogSize()); mammoth.timePasses(); - inOrder.verify(this.stdOutMock).println("The mammoth gets angry!"); - inOrder.verifyNoMoreInteractions(); + assertEquals("The mammoth gets angry!", appender.getLastMessage()); + assertEquals(2 , appender.getLogSize()); mammoth.observe(); - inOrder.verify(this.stdOutMock).println("The mammoth is furious!"); - inOrder.verifyNoMoreInteractions(); + assertEquals("The mammoth is furious!", appender.getLastMessage()); + assertEquals(3 , appender.getLogSize()); mammoth.timePasses(); - inOrder.verify(this.stdOutMock).println("The mammoth calms down."); - inOrder.verifyNoMoreInteractions(); + assertEquals("The mammoth calms down.", appender.getLastMessage()); + assertEquals(4 , appender.getLogSize()); mammoth.observe(); - inOrder.verify(this.stdOutMock).println("The mammoth is calm and peaceful."); - inOrder.verifyNoMoreInteractions(); + assertEquals("The mammoth is calm and peaceful.", appender.getLastMessage()); + assertEquals(5 , appender.getLogSize()); } @@ -109,4 +95,26 @@ public class MammothTest { assertEquals("The mammoth", toString); } -} \ No newline at end of file + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + } + +} diff --git a/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java index 35f4c1a82..d04e0f5c1 100644 --- a/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java +++ b/strategy/src/test/java/com/iluwatar/strategy/DragonSlayingStrategyTest.java @@ -22,19 +22,22 @@ */ package com.iluwatar.strategy; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; import java.util.Arrays; import java.util.Collection; +import java.util.LinkedList; +import java.util.List; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/29/15 - 10:58 PM @@ -71,20 +74,22 @@ public class DragonSlayingStrategyTest { private final DragonSlayingStrategy strategy; /** - * The expected action on the std-out + * The expected action in the log */ private final String expectedResult; - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; /** * Create a new test instance for the given strategy @@ -97,30 +102,35 @@ public class DragonSlayingStrategyTest { this.expectedResult = expectedResult; } - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - /** * Test if executing the strategy gives the correct response */ @Test public void testExecute() { this.strategy.execute(); - verify(this.stdOutMock).println(this.expectedResult); - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(this.expectedResult, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + } +} diff --git a/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java b/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java index e0cb90d42..0ea337f93 100644 --- a/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java +++ b/template-method/src/test/java/com/iluwatar/templatemethod/StealingMethodTest.java @@ -22,19 +22,19 @@ */ package com.iluwatar.templatemethod; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; import org.junit.After; import org.junit.Before; import org.junit.Test; -import org.mockito.InOrder; +import org.slf4j.LoggerFactory; -import java.io.PrintStream; +import java.util.LinkedList; +import java.util.List; import static org.junit.Assert.assertEquals; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.verifyZeroInteractions; +import static org.junit.Assert.assertTrue; /** * Date: 12/30/15 - 18:12 PM @@ -43,6 +43,18 @@ import static org.mockito.Mockito.verifyZeroInteractions; */ public abstract class StealingMethodTest { + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } + /** * The tested stealing method */ @@ -68,17 +80,6 @@ public abstract class StealingMethodTest { */ private final String expectedStealMethod; - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - /** * Create a new test for the given stealing method, together with the expected results * @@ -98,22 +99,6 @@ public abstract class StealingMethodTest { this.expectedStealMethod = expectedStealMethod; } - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - /** * Verify if the thief picks the correct target */ @@ -127,11 +112,11 @@ public abstract class StealingMethodTest { */ @Test public void testConfuseTarget() { - verifyZeroInteractions(this.stdOutMock); + assertEquals(0, appender.getLogSize()); this.method.confuseTarget(this.expectedTarget); - verify(this.stdOutMock).println(this.expectedConfuseMethod); - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(this.expectedConfuseMethod, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } /** @@ -139,11 +124,11 @@ public abstract class StealingMethodTest { */ @Test public void testStealTheItem() { - verifyZeroInteractions(this.stdOutMock); + assertEquals(0, appender.getLogSize()); this.method.stealTheItem(this.expectedTarget); - verify(this.stdOutMock).println(this.expectedStealMethod); - verifyNoMoreInteractions(this.stdOutMock); + assertEquals(this.expectedStealMethod, appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } /** @@ -151,14 +136,37 @@ public abstract class StealingMethodTest { */ @Test public void testSteal() { - final InOrder inOrder = inOrder(this.stdOutMock); - this.method.steal(); - inOrder.verify(this.stdOutMock).println(this.expectedTargetResult); - inOrder.verify(this.stdOutMock).println(this.expectedConfuseMethod); - inOrder.verify(this.stdOutMock).println(this.expectedStealMethod); - inOrder.verifyNoMoreInteractions(); + assertTrue(appender.logContains(this.expectedTargetResult)); + assertTrue(appender.logContains(this.expectedConfuseMethod)); + assertTrue(appender.logContains(this.expectedStealMethod)); + assertEquals(3, appender.getLogSize()); } -} \ No newline at end of file + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getFormattedMessage().equals(message)); + } + } +} diff --git a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java index 4bb9a2111..439a64da1 100644 --- a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java +++ b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java @@ -22,20 +22,40 @@ */ package com.iluwatar.twin; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; import org.mockito.InOrder; +import org.slf4j.LoggerFactory; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import java.util.LinkedList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.Mockito.*; /** * Date: 12/30/15 - 18:44 PM * * @author Jeroen Meulemeester */ -public class BallItemTest extends StdOutTest { +public class BallItemTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } @Test public void testClick() { @@ -63,10 +83,11 @@ public class BallItemTest extends StdOutTest { ballItem.setTwin(ballThread); ballItem.draw(); - verify(getStdOutMock()).println("draw"); - verify(getStdOutMock()).println("doDraw"); + assertTrue(appender.logContains("draw")); + assertTrue(appender.logContains("doDraw")); - verifyNoMoreInteractions(ballThread, getStdOutMock()); + verifyNoMoreInteractions(ballThread); + assertEquals(2, appender.getLogSize()); } @Test @@ -76,9 +97,32 @@ public class BallItemTest extends StdOutTest { ballItem.setTwin(ballThread); ballItem.move(); - verify(getStdOutMock()).println("move"); + assertTrue(appender.logContains("move")); - verifyNoMoreInteractions(ballThread, getStdOutMock()); + verifyNoMoreInteractions(ballThread); + assertEquals(1, appender.getLogSize()); } -} \ No newline at end of file + public class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public boolean logContains(String message) { + return log.stream().anyMatch(event -> event.getMessage().equals(message)); + } + + public int getLogSize() { + return log.size(); + } + } + +} diff --git a/twin/src/test/java/com/iluwatar/twin/StdOutTest.java b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java deleted file mode 100644 index b3baf8abd..000000000 --- a/twin/src/test/java/com/iluwatar/twin/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.twin; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java b/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java deleted file mode 100644 index 075f235f5..000000000 --- a/visitor/src/test/java/com/iluwatar/visitor/StdOutTest.java +++ /dev/null @@ -1,75 +0,0 @@ -/** - * The MIT License - * Copyright (c) 2014 Ilkka Seppälä - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - */ -package com.iluwatar.visitor; - -import org.junit.After; -import org.junit.Before; - -import java.io.PrintStream; - -import static org.mockito.Mockito.mock; - -/** - * Date: 12/10/15 - 8:37 PM - * - * @author Jeroen Meulemeester - */ -public abstract class StdOutTest { - - /** - * The mocked standard out {@link PrintStream}, required since some actions don't have any - * influence on accessible objects, except for writing to std-out using {@link System#out} - */ - private final PrintStream stdOutMock = mock(PrintStream.class); - - /** - * Keep the original std-out so it can be restored after the test - */ - private final PrintStream stdOutOrig = System.out; - - /** - * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test - */ - @Before - public void setUp() { - System.setOut(this.stdOutMock); - } - - /** - * Removed the mocked std-out {@link PrintStream} again from the {@link System} class - */ - @After - public void tearDown() { - System.setOut(this.stdOutOrig); - } - - /** - * Get the mocked stdOut {@link PrintStream} - * - * @return The stdOut print stream mock, renewed before each test - */ - final PrintStream getStdOutMock() { - return this.stdOutMock; - } - -} diff --git a/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java b/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java index 4a131bbf2..a0c8b4eea 100644 --- a/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java +++ b/visitor/src/test/java/com/iluwatar/visitor/VisitorTest.java @@ -22,19 +22,38 @@ */ package com.iluwatar.visitor; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import org.junit.After; +import org.junit.Before; import org.junit.Test; +import org.slf4j.LoggerFactory; +import java.util.LinkedList; +import java.util.List; import java.util.Optional; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.junit.Assert.assertEquals; /** * Date: 12/30/15 - 18:59 PM * * @author Jeroen Meulemeester */ -public abstract class VisitorTest extends StdOutTest { +public abstract class VisitorTest { + + private InMemoryAppender appender; + + @Before + public void setUp() { + appender = new InMemoryAppender(); + } + + @After + public void tearDown() { + appender.stop(); + } /** * The tested visitor instance @@ -76,27 +95,48 @@ public abstract class VisitorTest extends StdOutTest { public void testVisitCommander() { this.visitor.visitCommander(new Commander()); if (this.commanderResponse.isPresent()) { - verify(getStdOutMock()).println(this.commanderResponse.get()); + assertEquals(this.commanderResponse.get(), appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } - verifyNoMoreInteractions(getStdOutMock()); } @Test public void testVisitSergeant() { this.visitor.visitSergeant(new Sergeant()); if (this.sergeantResponse.isPresent()) { - verify(getStdOutMock()).println(this.sergeantResponse.get()); + assertEquals(this.sergeantResponse.get(), appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } - verifyNoMoreInteractions(getStdOutMock()); } @Test public void testVisitSoldier() { this.visitor.visitSoldier(new Soldier()); if (this.soldierResponse.isPresent()) { - verify(getStdOutMock()).println(this.soldierResponse.get()); + assertEquals(this.soldierResponse.get(), appender.getLastMessage()); + assertEquals(1, appender.getLogSize()); } - verifyNoMoreInteractions(getStdOutMock()); } + private class InMemoryAppender extends AppenderBase { + private List log = new LinkedList<>(); + + public InMemoryAppender() { + ((Logger) LoggerFactory.getLogger("root")).addAppender(this); + start(); + } + + @Override + protected void append(ILoggingEvent eventObject) { + log.add(eventObject); + } + + public int getLogSize() { + return log.size(); + } + + public String getLastMessage() { + return log.get(log.size() - 1).getFormattedMessage(); + } + } } From b37190a2140555d981161bc0e4ad210452cecde4 Mon Sep 17 00:00:00 2001 From: daniel-bryla Date: Fri, 4 Nov 2016 12:19:32 +0100 Subject: [PATCH 106/145] #502 Introduced logging in new example --- .../main/java/com/iluwatar/caching/App.java | 10 +-- .../com/iluwatar/event/asynchronous/App.java | 61 ++++++++++--------- .../iluwatar/event/asynchronous/Event.java | 13 ++-- .../event/asynchronous/EventManager.java | 12 ++-- .../asynchronous/EventAsynchronousTest.java | 18 +++--- 5 files changed, 64 insertions(+), 50 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 8a4ff0b8d..f30b20e05 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -149,9 +149,9 @@ public class App { * Cache-Aside */ public void useCacheAsideStategy() { - System.out.println("# CachingPolicy.ASIDE"); + LOGGER.info("# CachingPolicy.ASIDE"); AppManager.initCachingPolicy(CachingPolicy.ASIDE); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food."); UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats."); @@ -160,10 +160,10 @@ public class App { AppManager.save(userAccount4); AppManager.save(userAccount5); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("003"); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); AppManager.find("004"); - System.out.println(AppManager.printCacheContent()); + LOGGER.info(AppManager.printCacheContent()); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java index 5a2565940..c7c543434 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -16,38 +16,43 @@ */ package com.iluwatar.event.asynchronous; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.io.IOException; import java.io.InputStream; import java.util.Properties; import java.util.Scanner; /** - * + * * This application demonstrates the Event-based Asynchronous pattern. Essentially, users (of the pattern) may * choose to run events in an Asynchronous or Synchronous mode. There can be multiple Asynchronous events running at * once but only one Synchronous event can run at a time. Asynchronous events are synonymous to multi-threads. The key * point here is that the threads run in the background and the user is free to carry on with other processes. Once an * event is complete, the appropriate listener/callback method will be called. The listener then proceeds to carry out * further processing depending on the needs of the user. - * + * * The {@link EventManager} manages the events/threads that the user creates. Currently, the supported event operations * are: start, stop, getStatus. For Synchronous events, the user is unable to * start another (Synchronous) event if one is already running at the time. The running event would have to either be * stopped or completed before a new event can be started. - * + * * The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many * of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:- * (1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without * interrupting your application. (2) Execute multiple operations simultaneously, receiving notifications when each * completes. (3) Wait for resources to become available without stopping ("hanging") your application. (4) Communicate * with pending asynchronous operations using the familiar events-and-delegates model. - * + * * @see EventManager * @see Event * */ public class App { + private static final Logger LOGGER = LoggerFactory.getLogger(App.class); + public static final String PROP_FILE_NAME = "config.properties"; boolean interactiveMode = false; @@ -77,7 +82,7 @@ public class App { try { prop.load(inputStream); } catch (IOException e) { - System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode."); + LOGGER.error("{} was not found. Defaulting to non-interactive mode.", PROP_FILE_NAME, e); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -106,27 +111,27 @@ public class App { try { // Create an Asynchronous event. int aEventId = eventManager.createAsync(60); - System.out.println("Async Event [" + aEventId + "] has been created."); + LOGGER.info("Async Event [{}] has been created.", aEventId); eventManager.start(aEventId); - System.out.println("Async Event [" + aEventId + "] has been started."); + LOGGER.info("Async Event [{}] has been started.", aEventId); // Create a Synchronous event. int sEventId = eventManager.create(60); - System.out.println("Sync Event [" + sEventId + "] has been created."); + LOGGER.info("Sync Event [{}] has been created.", sEventId); eventManager.start(sEventId); - System.out.println("Sync Event [" + sEventId + "] has been started."); + LOGGER.info("Sync Event [{}] has been started.", sEventId); eventManager.status(aEventId); eventManager.status(sEventId); eventManager.cancel(aEventId); - System.out.println("Async Event [" + aEventId + "] has been stopped."); + LOGGER.info("Async Event [{}] has been stopped.", aEventId); eventManager.cancel(sEventId); - System.out.println("Sync Event [" + sEventId + "] has been stopped."); + LOGGER.info("Sync Event [{}] has been stopped.", sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } @@ -139,58 +144,58 @@ public class App { Scanner s = new Scanner(System.in); int option = -1; while (option != 4) { - System.out.println("Hello. Would you like to boil some eggs?"); - System.out.println("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT"); - System.out.print("Choose [1,2,3,4]: "); + LOGGER.info("Hello. Would you like to boil some eggs?"); + LOGGER.info("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT"); + LOGGER.info("Choose [1,2,3,4]: "); option = s.nextInt(); if (option == 1) { s.nextLine(); - System.out.print("Boil multiple eggs at once (A) or boil them one-by-one (S)?: "); + LOGGER.info("Boil multiple eggs at once (A) or boil them one-by-one (S)?: "); String eventType = s.nextLine(); - System.out.print("How long should this egg be boiled for (in seconds)?: "); + LOGGER.info("How long should this egg be boiled for (in seconds)?: "); int eventTime = s.nextInt(); if (eventType.equalsIgnoreCase("A")) { try { int eventId = eventManager.createAsync(eventTime); eventManager.start(eventId); - System.out.println("Egg [" + eventId + "] is being boiled."); + LOGGER.info("Egg [{}] is being boiled.", eventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } else if (eventType.equalsIgnoreCase("S")) { try { int eventId = eventManager.create(eventTime); eventManager.start(eventId); - System.out.println("Egg [" + eventId + "] is being boiled."); + LOGGER.info("Egg [{}] is being boiled.", eventId); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } else { - System.out.println("Unknown event type."); + LOGGER.info("Unknown event type."); } } else if (option == 2) { - System.out.print("Which egg?: "); + LOGGER.info("Which egg?: "); int eventId = s.nextInt(); try { eventManager.cancel(eventId); - System.out.println("Egg [" + eventId + "] is removed from boiler."); + LOGGER.info("Egg [{}] is removed from boiler.", eventId); } catch (EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } else if (option == 3) { s.nextLine(); - System.out.print("Just one egg (O) OR all of them (A) ?: "); + LOGGER.info("Just one egg (O) OR all of them (A) ?: "); String eggChoice = s.nextLine(); if (eggChoice.equalsIgnoreCase("O")) { - System.out.print("Which egg?: "); + LOGGER.info("Which egg?: "); int eventId = s.nextInt(); try { eventManager.status(eventId); } catch (EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } else if (eggChoice.equalsIgnoreCase("A")) { eventManager.statusOfAllEvents(); diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java index 5e557fff2..bd4687dd5 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -16,13 +16,18 @@ */ package com.iluwatar.event.asynchronous; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + /** - * + * * Each Event runs as a separate/individual thread. * */ public class Event implements IEvent, Runnable { + private static final Logger LOGGER = LoggerFactory.getLogger(Event.class); + private int eventId; private int eventTime; private boolean isSynchronous; @@ -31,7 +36,7 @@ public class Event implements IEvent, Runnable { private ThreadCompleteListener eventListener; /** - * + * * @param eventId event ID * @param eventTime event time * @param isSynchronous is of synchronous type @@ -63,9 +68,9 @@ public class Event implements IEvent, Runnable { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventId + "] is not done."); + LOGGER.info("[{}] is not done.", eventId); } else { - System.out.println("[" + eventId + "] is done."); + LOGGER.info("[{}] is done.", eventId); } } diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java index dae995e38..e181e8704 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -22,7 +22,7 @@ import java.util.Random; import java.util.concurrent.ConcurrentHashMap; /** - * + * * EventManager handles and maintains a pool of event threads. {@link Event} threads are created upon user request. Thre * are two types of events; Asynchronous and Synchronous. There can be multiple Asynchronous events running at once but * only one Synchronous event running at a time. Currently supported event operations are: start, stop, and getStatus. @@ -52,7 +52,7 @@ public class EventManager implements ThreadCompleteListener { /** * Create a Synchronous event. - * + * * @param eventTime Time an event should run for. * @return eventId * @throws MaxNumOfEventsAllowedException When too many events are running at a time. @@ -74,7 +74,7 @@ public class EventManager implements ThreadCompleteListener { /** * Create an Asynchronous event. - * + * * @param eventTime Time an event should run for. * @return eventId * @throws MaxNumOfEventsAllowedException When too many events are running at a time. @@ -106,7 +106,7 @@ public class EventManager implements ThreadCompleteListener { /** * Starts event. - * + * * @param eventId The event that needs to be started. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ @@ -120,7 +120,7 @@ public class EventManager implements ThreadCompleteListener { /** * Stops event. - * + * * @param eventId The event that needs to be stopped. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ @@ -139,7 +139,7 @@ public class EventManager implements ThreadCompleteListener { /** * Get status of a running event. - * + * * @param eventId The event to inquire status of. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java index 213439203..f032c8971 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -16,10 +16,12 @@ */ package com.iluwatar.event.asynchronous; -import static org.junit.Assert.assertTrue; - import org.junit.Before; import org.junit.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import static org.junit.Assert.assertTrue; /** * @@ -29,6 +31,8 @@ import org.junit.Test; public class EventAsynchronousTest { App app; + private static final Logger LOGGER = LoggerFactory.getLogger(EventAsynchronousTest.class); + @Before public void setUp() { app = new App(); @@ -46,7 +50,7 @@ public class EventAsynchronousTest { eventManager.cancel(aEventId); assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } @@ -63,7 +67,7 @@ public class EventAsynchronousTest { assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } @@ -76,7 +80,7 @@ public class EventAsynchronousTest { sEventId = eventManager.create(60); eventManager.start(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } @@ -101,7 +105,7 @@ public class EventAsynchronousTest { } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } @@ -129,7 +133,7 @@ public class EventAsynchronousTest { assertTrue(eventManager.getEventPool().size() == 0); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { - System.out.println(e.getMessage()); + LOGGER.error(e.getMessage()); } } } From 6f3e2985a4b19781d843c38c74772aa4d35641de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 6 Nov 2016 12:14:39 +0200 Subject: [PATCH 107/145] Create presentation template --- presentation.html | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 presentation.html diff --git a/presentation.html b/presentation.html new file mode 100644 index 000000000..b19c85a81 --- /dev/null +++ b/presentation.html @@ -0,0 +1,45 @@ + + + + Title + + + + + + + + + From e1ae1067dbf88b365c52d2b4a45ca221136bddfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 6 Nov 2016 12:26:03 +0200 Subject: [PATCH 108/145] Link Hexagonal Architecture pattern to corresponding blog entry --- hexagonal/README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hexagonal/README.md b/hexagonal/README.md index 33c2ba9cb..27d31520c 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -26,6 +26,9 @@ Use Hexagonal Architecture pattern when * it is important that the application is fully testable * you use Domain Driven Design methodology and/or Microservices architectural style +## Tutorials +* [Build Maintainable Systems With Hexagonal Architecture](http://java-design-patterns.com/blog/build-maintainable-systems-with-hexagonal-architecture/) + ## Real world examples * [Apache Isis](https://isis.apache.org/) From 4c24d99414e55fa8cf8fe3c068317f323350f762 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sat, 12 Nov 2016 22:44:08 +0200 Subject: [PATCH 109/145] Work on Hexagonal Architecture presentation --- hexagonal/README.md | 4 +- hexagonal/etc/hexagon.png | Bin 0 -> 51788 bytes hexagonal/etc/layers.png | Bin 0 -> 15887 bytes hexagonal/etc/presentation.html | 95 ++++++++++++++++++++++++++++++++ presentation.html | 45 --------------- 5 files changed, 97 insertions(+), 47 deletions(-) create mode 100644 hexagonal/etc/hexagon.png create mode 100644 hexagonal/etc/layers.png create mode 100644 hexagonal/etc/presentation.html delete mode 100644 presentation.html diff --git a/hexagonal/README.md b/hexagonal/README.md index 33c2ba9cb..30f70f54c 100644 --- a/hexagonal/README.md +++ b/hexagonal/README.md @@ -23,8 +23,8 @@ Allow an application to equally be driven by users, programs, automated test or ## Applicability Use Hexagonal Architecture pattern when -* it is important that the application is fully testable -* you use Domain Driven Design methodology and/or Microservices architectural style +* When the application needs to be independent of any frameworks +* When it is important that the application highly maintainable and fully testable ## Real world examples diff --git a/hexagonal/etc/hexagon.png b/hexagonal/etc/hexagon.png new file mode 100644 index 0000000000000000000000000000000000000000..e2d14e91dd692d888f85998087c881c559191900 GIT binary patch literal 51788 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 literal 0 HcmV?d00001 diff --git a/hexagonal/etc/layers.png b/hexagonal/etc/layers.png new file mode 100644 index 0000000000000000000000000000000000000000..cb5a9c90bbe953ff87197bfb1834c34fac450f3c GIT binary patch literal 15887 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 Kh}Vc32LC_vUU`lH literal 0 HcmV?d00001 diff --git a/hexagonal/etc/presentation.html b/hexagonal/etc/presentation.html new file mode 100644 index 000000000..46e26c550 --- /dev/null +++ b/hexagonal/etc/presentation.html @@ -0,0 +1,95 @@ + + + + Title + + + + + + + + + + diff --git a/presentation.html b/presentation.html deleted file mode 100644 index b19c85a81..000000000 --- a/presentation.html +++ /dev/null @@ -1,45 +0,0 @@ - - - - Title - - - - - - - - - From c850295aab8510779b60faa837697b9478f1eda0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Sun, 13 Nov 2016 11:23:22 +0200 Subject: [PATCH 110/145] Add new diagram --- hexagonal/etc/hexagon.png | Bin 51788 -> 0 bytes hexagonal/etc/ports_and_adapters.png | Bin 0 -> 34860 bytes hexagonal/etc/ports_and_adapters.xml | 1 + hexagonal/etc/presentation.html | 24 ++++++++++++++++-------- 4 files changed, 17 insertions(+), 8 deletions(-) delete mode 100644 hexagonal/etc/hexagon.png create mode 100644 hexagonal/etc/ports_and_adapters.png create mode 100644 hexagonal/etc/ports_and_adapters.xml diff --git a/hexagonal/etc/hexagon.png b/hexagonal/etc/hexagon.png deleted file mode 100644 index e2d14e91dd692d888f85998087c881c559191900..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51788 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 diff --git a/hexagonal/etc/ports_and_adapters.png b/hexagonal/etc/ports_and_adapters.png new file mode 100644 index 0000000000000000000000000000000000000000..d285045dea68721c586bbfae5d303265f738eea0 GIT binary patch literal 34860 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 literal 0 HcmV?d00001 diff --git a/hexagonal/etc/ports_and_adapters.xml b/hexagonal/etc/ports_and_adapters.xml new file mode 100644 index 000000000..0e64414b8 --- /dev/null +++ b/hexagonal/etc/ports_and_adapters.xml @@ -0,0 +1 @@ +7Zpdk6I4FIZ/jbdbJAHEyx7nY/diqrqqd2tnLiOJSDUSK6ZHe3/9BkmUfFiDDqBM2TcNBwLxOe85nBOYoPl6/4XjzeorI7SYwIDsJ+jjBEKA4kj+qyzvtWUag9qQ8Zyok06Gl/w/qoyBsr7lhG6NEwVjhcg3pjFlZUlTYdgw52xnnrZkhXnXDc6oY3hJceFa/82JWNXWJApO9j9pnq30nUGgjixw+ppx9laq+00gWh7+6sNrrK+lzt+uMGG7hgl9mqA5Z0zUW+v9nBYVW42tHvf5zNHjvDktRZsBqB7wAxdvVM/4MC/xrlnIKW6qzRXd44yVE/RhQ3m+poLyk/X5ZPqwW+WCvmxwWo3aSYFI20qsC7kH5Ka6I+WC7s/OGhxZSI1RJi/N3+UpagBMFD4lL6D3dydnIa2lVcNRMFZGrASSHa99giQ3FCc/MzhKZmgaGcyOKBrMAPQwO2r7V5iFDrM549ThJgfJEKc/B7LMi2LOCsYP46oYg2kq7VvB2SttHCHxIo7ibhCGATARJshBqDNfk2AXoot+Lrpr4RFMk6UXXpwmdLHsCF5s6W/qwkMe+aEO4MW/GzwEPQmvJ3jT0cMDMxNeGA0GLxk7PDQ1H7VDKm82fnh2zoODwdPlz3jphdCUHoyGkx5oUeRdTS+iCQl99BK4QHFHxUoUWfQ89V5v9Fq0FXdOz9ZeMCC9Nk/ckjxVfa7cK1lJTWJ0n4tvje3vcjuotks5k2/Nne/6rJJ8zqsJHQ7Vd6PE6ZEtenJG7I2n1KhQBeYZFU0duJAbECMPRG3jtMAi/2HOwkdW3eGZ5XJ+Z7vECFjOqWevRjWbZOtCCJkXCu2Cvv7NzoUOjj7+7Ha+b1MwXOz7P6IefQwfPr7Qx33WNYNkR5s60nXuANkR9lnXDEIPWc8WNBvu2aJn+8v5JfDkF/V0Ac2nS3e5J/Hkntkj91yWe7S7O/U/OO/9/nzfWC5/+L6d70dfldsrOSEcbiXHs4R9ZeTUGdEXO41E2mX0TD3Rc1+Vue2gq6Mn6S963BX4Z8bF1hGBlLcwPW/GhFJGM4CUCRd5VsrdVDrz8FaoCpY8xcWTOrDOCSnOBWcH8YVmoYHzGDgNWYS+12pdxJe7SP/PVlKQZ1Q0ltVvtVEfXrRSojD3/w4NALvudd9hAN97x7ALQO6ywN90WwUDzqiS903h2CvFiZucZ32xcdvmv6RqMi6zFCtvjuZY4et851lN6g2N221+xAIv8Pb28RQHJhcwG04yOrU1uHxl6au0kLvBYz3dYOTi6S3doPF2irpKNAqe2I96mIIHWJ6Mr20XgF059dcuoD5axWEEoOsRQwDThwAuFECbfvE+Ox5dlxkCSB4CuFAAXbW8vpcRvldRHT4CfAK46WrhKAXQ5quzOxUAdAWAbrpkOEoBuE35E8Eb2Y//XuseVh+CPP1ZR+secvf0dXftkdMn9OjT/w== \ No newline at end of file diff --git a/hexagonal/etc/presentation.html b/hexagonal/etc/presentation.html index 46e26c550..74361ad27 100644 --- a/hexagonal/etc/presentation.html +++ b/hexagonal/etc/presentation.html @@ -60,29 +60,37 @@ Use Hexagonal Architecture pattern --- -# Diagram +# Concepts -.center[![Alt text](hexagon.png)] +* Ports are interfaces +* The ports that drive the application are called primary ports +* The ports that are driven by the application are called secondary ports --- # Concepts -* Ports are interfaces -* Adapters are implementations -* Driver ports vs driven ports +* Adapters are interface implementations +* Typically different adapters for testing and production are provided + +--- + +# Diagram + +.center[![Alt text](ports_and_adapters.png)] --- # Real world examples -* [Apache Isis](https://isis.apache.org/) +* [Apache Isis](https://isis.apache.org/) builds generic UI and REST API directly from the underlying domain objects --- -# Implementation example +# Tutorials -* http://java-design-patterns.com/patterns/hexagonal/ +* Blog http://java-design-patterns.com/blog/build-maintainable-systems-with-hexagonal-architecture/ +* Source code http://java-design-patterns.com/patterns/hexagonal/