From c8a2ef01d3f2d57998d631cba71e8c5c5a9d4d62 Mon Sep 17 00:00:00 2001 From: Noam Greenshtain <31648669+noamgrinch@users.noreply.github.com> Date: Sun, 14 Mar 2021 08:23:41 +0200 Subject: [PATCH] pattern: Active-Object pattern. (#1660) * Closes #65. * Removed * Removed unnecessary files. Added logging. Closes Fixes #1660. * Added Terminition condition. * Logger implemented. Removed maven wrapper. * Added module to parent POM. removed .gitignore * Replaced tabs with whitespaces, added Javadocs. * Fixed more whitespaces problems. * Fixed more checkstyle errors * More checkstyle errors. * Checkstyle errors. * Final checkstyle cleanup * Added UML file. Changed System.exit() to Runtime. * Changed buisiness logic and readme.md file * Changed typos and readme.md file * Fixed checkstyle errors * Fixed grammer errors and CircleCI bugs. * Wrong readme.md * Added Thread.interrupt() for after catching exception. * Fixed SonarCloud code smells. * Removed unused brackets. * Changed main program exit logic. Added tests. * Reverted abstract-factory * Cleaned code * Added static to loggers. cleaned code smells. * Checkstyle errors. * Code Smells. Co-authored-by: Subhrodip Mohanta --- .../com/iluwatar/abstractfactory/AppTest.java | 16 +-- active-object/README.md | 124 ++++++++++++++++++ active-object/etc/active-object.urm.PNG | Bin 0 -> 19466 bytes active-object/etc/active-object.urm.puml | 25 ++++ active-object/pom.xml | 65 +++++++++ .../iluwatar/activeobject/ActiveCreature.java | 95 ++++++++++++++ .../java/com/iluwatar/activeobject/App.java | 75 +++++++++++ .../java/com/iluwatar/activeobject/Orc.java | 14 ++ .../activeobject/ActiveCreatureTest.java | 19 +++ .../com/iluwatar/activeobject/AppTest.java | 14 ++ pom.xml | 1 + 11 files changed, 438 insertions(+), 10 deletions(-) create mode 100644 active-object/README.md create mode 100644 active-object/etc/active-object.urm.PNG create mode 100644 active-object/etc/active-object.urm.puml create mode 100644 active-object/pom.xml create mode 100644 active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java create mode 100644 active-object/src/main/java/com/iluwatar/activeobject/App.java create mode 100644 active-object/src/main/java/com/iluwatar/activeobject/Orc.java create mode 100644 active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java create mode 100644 active-object/src/test/java/com/iluwatar/activeobject/AppTest.java diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java index 02e4e93f4..c5589895d 100644 --- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java +++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java @@ -28,20 +28,16 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; /** - * Tests that Abstract Factory example runs without errors. + * Issue: Add at least one assertion to this test case. + * + * Solution: Inserted assertion to check whether the execution of the main method in {@link App} + * throws an exception. */ class AppTest { - - /** - * Issue: Add at least one assertion to this test case. - * - * Solution: Inserted assertion to check whether the execution of the main method in {@link App} - * throws an exception. - */ - + @Test void shouldExecuteApplicationWithoutException() { - assertDoesNotThrow(() -> App.main(new String[]{})); + assertDoesNotThrow(() -> App.main(new String[]{})); } } diff --git a/active-object/README.md b/active-object/README.md new file mode 100644 index 000000000..108caebe4 --- /dev/null +++ b/active-object/README.md @@ -0,0 +1,124 @@ +--- +layout: pattern +title: Active Object +folder: active-object +permalink: /patterns/active-object/ +categories: Concurrency +tags: + - Performance +--- + + +## Intent +The active object design pattern decouples method execution from method invocation for objects that each reside in their thread of control. The goal is to introduce concurrency, by using asynchronous method invocation and a scheduler for handling requests. + +## Explanation + +The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods. + +Real-world example + +>The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior. + +To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern. + + +**Programmatic Example** + +```java +public abstract class ActiveCreature{ + private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); + + private BlockingQueue requests; + + private String name; + + private Thread thread; + + public ActiveCreature(String name) { + this.name = name; + this.requests = new LinkedBlockingQueue(); + thread = new Thread(new Runnable() { + @Override + public void run() { + while (true) { + try { + requests.take().run(); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + } + } + } + ); + thread.start(); + } + + public void eat() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} is eating!",name()); + logger.info("{} has finished eating!",name()); + } + } + ); + } + + public void roam() throws InterruptedException { + requests.put(new Runnable() { + @Override + public void run() { + logger.info("{} has started to roam and the wastelands.",name()); + } + } + ); + } + + public String name() { + return this.name; + } +} + +``` + +We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods. + +For example, the Orc class: +```java +public class Orc extends ActiveCreature { + + public Orc(String name) { + super(name); + } + +} +``` + +Now, we can create multiple creatures such as Orcs, tell them to eat and roam and they will execute it on their own thread of control: +```java + public static void main(String[] args) { + var app = new App(); + app.run(); + } + + @Override + public void run() { + ActiveCreature creature; + try { + for (int i = 0;i < creatures;i++) { + creature = new Orc(Orc.class.getSimpleName().toString() + i); + creature.eat(); + creature.roam(); + } + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + } + Runtime.getRuntime().exit(1); + } +``` + +## Class diagram + +![alt text](./etc/active-object.urm.png "Active Object class diagram") \ No newline at end of file diff --git a/active-object/etc/active-object.urm.PNG b/active-object/etc/active-object.urm.PNG new file mode 100644 index 0000000000000000000000000000000000000000..c14f66144ee297261111c1c05529c5d00ebe393e GIT binary patch literal 19466 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 literal 0 HcmV?d00001 diff --git a/active-object/etc/active-object.urm.puml b/active-object/etc/active-object.urm.puml new file mode 100644 index 000000000..3fc3c8e1e --- /dev/null +++ b/active-object/etc/active-object.urm.puml @@ -0,0 +1,25 @@ +@startuml +package com.iluwatar.activeobject { + abstract class ActiveCreature { + - logger : Logger + - name : String + - requests : BlockingQueue + - thread : Thread + + ActiveCreature(name : String) + + eat() + + name() : String + + roam() + } + class App { + - creatures : Integer + - logger : Logger + + App() + + main(args : String[]) {static} + + run() + } + class Orc { + + Orc(name : String) + } +} +Orc --|> ActiveCreature +@enduml \ No newline at end of file diff --git a/active-object/pom.xml b/active-object/pom.xml new file mode 100644 index 000000000..3ea22d4e2 --- /dev/null +++ b/active-object/pom.xml @@ -0,0 +1,65 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.24.0-SNAPSHOT + + active-object + + + org.junit.jupiter + junit-jupiter-engine + test + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + + + com.iluwatar.activeobject.App + + + + + + + + + diff --git a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java new file mode 100644 index 000000000..993a26eec --- /dev/null +++ b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java @@ -0,0 +1,95 @@ +package com.iluwatar.activeobject; + +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * ActiveCreature class is the base of the active object example. + * @author Noam Greenshtain + * + */ +public abstract class ActiveCreature { + + private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName()); + + private BlockingQueue requests; + + private String name; + + private Thread thread; // Thread of execution. + + private int status; // status of the thread of execution. + + /** + * Constructor and initialization. + */ + protected ActiveCreature(String name) { + this.name = name; + this.status = 0; + this.requests = new LinkedBlockingQueue<>(); + thread = new Thread(() -> { + boolean infinite = true; + while (infinite) { + try { + requests.take().run(); + } catch (InterruptedException e) { + if (this.status != 0) { + logger.error("Thread was interrupted. --> {}", e.getMessage()); + } + infinite = false; + Thread.currentThread().interrupt(); + } + } + }); + thread.start(); + } + + /** + * Eats the porridge. + * @throws InterruptedException due to firing a new Runnable. + */ + public void eat() throws InterruptedException { + requests.put(() -> { + logger.info("{} is eating!",name()); + logger.info("{} has finished eating!",name()); + }); + } + + /** + * Roam in the wastelands. + * @throws InterruptedException due to firing a new Runnable. + */ + public void roam() throws InterruptedException { + requests.put(() -> + logger.info("{} has started to roam in the wastelands.",name()) + ); + } + + /** + * Returns the name of the creature. + * @return the name of the creature. + */ + public String name() { + return this.name; + } + + /** + * Kills the thread of execution. + * @param status of the thread of execution. 0 == OK, the rest is logging an error. + */ + public void kill(int status) { + this.status = status; + this.thread.interrupt(); + } + + /** + * Returns the status of the thread of execution. + * @return the status of the thread of execution. + */ + public int getStatus() { + return this.status; + } +} diff --git a/active-object/src/main/java/com/iluwatar/activeobject/App.java b/active-object/src/main/java/com/iluwatar/activeobject/App.java new file mode 100644 index 000000000..d36d1023d --- /dev/null +++ b/active-object/src/main/java/com/iluwatar/activeobject/App.java @@ -0,0 +1,75 @@ +/* + * The MIT License + * Copyright © 2014-2021 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.activeobject; + +import java.util.ArrayList; +import java.util.List; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The Active Object pattern helps to solve synchronization difficulties without using + * 'synchronized' methods. The active object will contain a thread-safe data structure + * (such as BlockingQueue) and use to synchronize method calls by moving the logic of the method + * into an invocator(usually a Runnable) and store it in the DSA. + * + *

In this example, we fire 20 threads to modify a value in the target class. + */ +public class App implements Runnable { + + private static final Logger logger = LoggerFactory.getLogger(App.class.getName()); + + private static final int NUM_CREATURES = 3; + + /** + * Program entry point. + * + * @param args command line arguments. + */ + public static void main(String[] args) { + var app = new App(); + app.run(); + } + + @Override + public void run() { + List creatures = new ArrayList<>(); + try { + for (int i = 0;i < NUM_CREATURES;i++) { + creatures.add(new Orc(Orc.class.getSimpleName() + i)); + creatures.get(i).eat(); + creatures.get(i).roam(); + } + Thread.sleep(1000); + } catch (InterruptedException e) { + logger.error(e.getMessage()); + Thread.currentThread().interrupt(); + } finally { + for (int i = 0;i < NUM_CREATURES;i++) { + creatures.get(i).kill(0); + } + } + } +} diff --git a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java new file mode 100644 index 000000000..3aaa986a3 --- /dev/null +++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java @@ -0,0 +1,14 @@ +package com.iluwatar.activeobject; + +/** + * An implementation of the ActiveCreature class. + * @author Noam Greenshtain + * + */ +public class Orc extends ActiveCreature { + + public Orc(String name) { + super(name); + } + +} diff --git a/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java new file mode 100644 index 000000000..1561b4e2f --- /dev/null +++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java @@ -0,0 +1,19 @@ +package com.iluwatar.activeobject; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +class ActiveCreatureTest { + + @Test + void executionTest() throws InterruptedException { + ActiveCreature orc = new Orc("orc1"); + assertEquals("orc1",orc.name()); + assertEquals(0,orc.getStatus()); + orc.eat(); + orc.roam(); + orc.kill(0); + } + + +} diff --git a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java new file mode 100644 index 000000000..e868a4c71 --- /dev/null +++ b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java @@ -0,0 +1,14 @@ +package com.iluwatar.activeobject; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +import org.junit.jupiter.api.Test; + + +class AppTest { + + @Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } +} diff --git a/pom.xml b/pom.xml index 7e332a436..60dae1679 100644 --- a/pom.xml +++ b/pom.xml @@ -221,6 +221,7 @@ separated-interface special-case parameter-object + active-object