invalidProperties =
+ null; // Invalid properties, causing NullPointerException
// Throw null pointer exception
- assertThrows(NullPointerException.class, () -> {
- // Attempt to construct a document with invalid properties
- new DocumentImplementation(invalidProperties);
- });
+ assertThrows(
+ NullPointerException.class,
+ () -> {
+ // Attempt to construct a document with invalid properties
+ new DocumentImplementation(invalidProperties);
+ });
}
@Test
@@ -97,11 +98,11 @@ void shouldPutAndGetNestedDocument() {
DocumentImplementation nestedDocument = new DocumentImplementation(new HashMap<>());
nestedDocument.put("nestedKey", "nestedValue");
-
document.put("nested", nestedDocument);
// Retrieving the nested document
- DocumentImplementation retrievedNestedDocument = (DocumentImplementation) document.get("nested");
+ DocumentImplementation retrievedNestedDocument =
+ (DocumentImplementation) document.get("nested");
assertNotNull(retrievedNestedDocument);
assertEquals("nestedValue", retrievedNestedDocument.get("nestedKey"));
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
index 09af6d7b5260..16dcba0db37f 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/AppTest.java
@@ -24,25 +24,21 @@
*/
package com.iluwatar.abstractdocument;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Simple App test
- */
+import org.junit.jupiter.api.Test;
+
+/** Simple App test */
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.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteAppWithoutException() {
assertDoesNotThrow(() -> App.main(null));
}
-
}
diff --git a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
index 4b52fa7a6ac4..fc29dea45c43 100644
--- a/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
+++ b/abstract-document/src/test/java/com/iluwatar/abstractdocument/DomainTest.java
@@ -24,18 +24,16 @@
*/
package com.iluwatar.abstractdocument;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
import com.iluwatar.abstractdocument.domain.Car;
import com.iluwatar.abstractdocument.domain.Part;
import com.iluwatar.abstractdocument.domain.enums.Property;
-import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
+import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-/**
- * Test for Part and Car
- */
+/** Test for Part and Car */
class DomainTest {
private static final String TEST_PART_TYPE = "test-part-type";
@@ -47,11 +45,11 @@ class DomainTest {
@Test
void shouldConstructPart() {
- var partProperties = Map.of(
- Property.TYPE.toString(), TEST_PART_TYPE,
- Property.MODEL.toString(), TEST_PART_MODEL,
- Property.PRICE.toString(), (Object) TEST_PART_PRICE
- );
+ var partProperties =
+ Map.of(
+ Property.TYPE.toString(), TEST_PART_TYPE,
+ Property.MODEL.toString(), TEST_PART_MODEL,
+ Property.PRICE.toString(), (Object) TEST_PART_PRICE);
var part = new Part(partProperties);
assertEquals(TEST_PART_TYPE, part.getType().orElseThrow());
assertEquals(TEST_PART_MODEL, part.getModel().orElseThrow());
@@ -60,15 +58,14 @@ void shouldConstructPart() {
@Test
void shouldConstructCar() {
- var carProperties = Map.of(
- Property.MODEL.toString(), TEST_CAR_MODEL,
- Property.PRICE.toString(), TEST_CAR_PRICE,
- Property.PARTS.toString(), List.of(Map.of(), Map.of())
- );
+ var carProperties =
+ Map.of(
+ Property.MODEL.toString(), TEST_CAR_MODEL,
+ Property.PRICE.toString(), TEST_CAR_PRICE,
+ Property.PARTS.toString(), List.of(Map.of(), Map.of()));
var car = new Car(carProperties);
assertEquals(TEST_CAR_MODEL, car.getModel().orElseThrow());
assertEquals(TEST_CAR_PRICE, car.getPrice().orElseThrow());
assertEquals(2, car.getParts().count());
}
-
}
diff --git a/abstract-factory/README.md b/abstract-factory/README.md
index 7ec0d60bc00b..ee9823a39a5f 100644
--- a/abstract-factory/README.md
+++ b/abstract-factory/README.md
@@ -36,6 +36,10 @@ Wikipedia says
> The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes
+Class diagram
+
+
+
## Programmatic Example of Abstract Factory in Java
To create a kingdom using the Abstract Factory pattern in Java, we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
@@ -165,10 +169,6 @@ The program output:
07:35:46.343 [main] INFO com.iluwatar.abstractfactory.App -- This is the orc king!
```
-## Abstract Factory Pattern Class Diagram
-
-
-
## When to Use the Abstract Factory Pattern in Java
Use the Abstract Factory pattern in Java when:
diff --git a/abstract-factory/pom.xml b/abstract-factory/pom.xml
index 99a92423beae..60fbf72eff54 100644
--- a/abstract-factory/pom.xml
+++ b/abstract-factory/pom.xml
@@ -34,6 +34,14 @@
abstract-factory
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
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 e360822ca147..798cbe4fd118 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/App.java
@@ -74,6 +74,7 @@ public void run() {
/**
* Creates kingdom.
+ *
* @param kingdomType type of Kingdom
*/
public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
@@ -82,4 +83,4 @@ public void createKingdom(final Kingdom.FactoryMaker.KingdomType kingdomType) {
kingdom.setCastle(kingdomFactory.createCastle());
kingdom.setArmy(kingdomFactory.createArmy());
}
-}
\ No newline at end of file
+}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
index 3efec4c87eb1..78c75323f1c0 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Army.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * Army interface.
- */
+/** Army interface. */
public interface Army {
String getDescription();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
index 8fca068199cb..ee1e16f3cd3f 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Castle.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * Castle interface.
- */
+/** Castle interface. */
public interface Castle {
String getDescription();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
index 055d2cc75b0c..d7e46c1456f0 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfArmy.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * ElfArmy.
- */
+/** ElfArmy. */
public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the elven army!";
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
index 5b0c26c4290d..136afb11fd23 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfCastle.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * ElfCastle.
- */
+/** ElfCastle. */
public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the elven castle!";
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
index 0696e1d096f9..9b0d3a6f1a77 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKing.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * ElfKing.
- */
+/** ElfKing. */
public class ElfKing implements King {
static final String DESCRIPTION = "This is the elven king!";
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
index f45d2ee036c1..b09a2f47c252 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/ElfKingdomFactory.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * ElfKingdomFactory concrete factory.
- */
+/** ElfKingdomFactory concrete factory. */
public class ElfKingdomFactory implements KingdomFactory {
@Override
@@ -43,5 +41,4 @@ public King createKing() {
public Army createArmy() {
return new ElfArmy();
}
-
}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
index 01af71a6a293..9f65ed434577 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/King.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * King interface.
- */
+/** King interface. */
public interface King {
String getDescription();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
index db1c65ca46de..d1f85a6a4812 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/Kingdom.java
@@ -27,9 +27,7 @@
import lombok.Getter;
import lombok.Setter;
-/**
- * Helper class to manufacture {@link KingdomFactory} beans.
- */
+/** Helper class to manufacture {@link KingdomFactory} beans. */
@Getter
@Setter
public class Kingdom {
@@ -38,21 +36,16 @@ public class Kingdom {
private Castle castle;
private Army army;
- /**
- * The factory of kingdom factories.
- */
+ /** The factory of kingdom factories. */
public static class FactoryMaker {
- /**
- * Enumeration for the different types of Kingdoms.
- */
+ /** Enumeration for the different types of Kingdoms. */
public enum KingdomType {
- ELF, ORC
+ ELF,
+ ORC
}
- /**
- * The factory method to create KingdomFactory concrete objects.
- */
+ /** The factory method to create KingdomFactory concrete objects. */
public static KingdomFactory makeFactory(KingdomType type) {
return switch (type) {
case ELF -> new ElfKingdomFactory();
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
index fdfe19dc08d2..199c6697dcaa 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/KingdomFactory.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * KingdomFactory factory interface.
- */
+/** KingdomFactory factory interface. */
public interface KingdomFactory {
Castle createCastle();
@@ -34,5 +32,4 @@ public interface KingdomFactory {
King createKing();
Army createArmy();
-
}
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
index d687c32e8c27..31ed6896d921 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcArmy.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * OrcArmy.
- */
+/** OrcArmy. */
public class OrcArmy implements Army {
static final String DESCRIPTION = "This is the orc army!";
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
index f842bb3c62d0..bdae5709a97c 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcCastle.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * OrcCastle.
- */
+/** OrcCastle. */
public class OrcCastle implements Castle {
static final String DESCRIPTION = "This is the orc castle!";
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
index e25d93bfba40..7f106d45a01d 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKing.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * OrcKing.
- */
+/** OrcKing. */
public class OrcKing implements King {
static final String DESCRIPTION = "This is the orc king!";
diff --git a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
index c80728a87fdc..82d258570460 100644
--- a/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
+++ b/abstract-factory/src/main/java/com/iluwatar/abstractfactory/OrcKingdomFactory.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.abstractfactory;
-/**
- * OrcKingdomFactory concrete factory.
- */
+/** OrcKingdomFactory concrete factory. */
public class OrcKingdomFactory implements KingdomFactory {
@Override
diff --git a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
index 0f7708e07a12..b5dde940c464 100644
--- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AbstractFactoryTest.java
@@ -24,14 +24,12 @@
*/
package com.iluwatar.abstractfactory;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Tests for abstract factory.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests for abstract factory. */
class AbstractFactoryTest {
private final App app = new App();
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 736a7f8b740f..9f53691a594d 100644
--- a/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
+++ b/abstract-factory/src/test/java/com/iluwatar/abstractfactory/AppTest.java
@@ -24,18 +24,16 @@
*/
package com.iluwatar.abstractfactory;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Check whether the execution of the main method in {@link App} throws an exception.
- */
+import org.junit.jupiter.api.Test;
+
+/** Check whether the execution of the main method in {@link App} throws an exception. */
class AppTest {
-
+
@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
index c17cbebd79ae..a804ce9e632b 100644
--- a/active-object/README.md
+++ b/active-object/README.md
@@ -41,6 +41,11 @@ Wikipedia says
> * The implementation of the active object method.
> * A callback or variable for the client to receive the result.
+Sequence diagram
+
+
+
+
## Programmatic Example of Active Object in Java
This section explains how the Active Object design pattern works in Java, highlighting its use in asynchronous task management and concurrency control.
diff --git a/active-object/etc/active-object-sequence-diagram.png b/active-object/etc/active-object-sequence-diagram.png
new file mode 100644
index 000000000000..b725d9b07b6d
Binary files /dev/null and b/active-object/etc/active-object-sequence-diagram.png differ
diff --git a/active-object/pom.xml b/active-object/pom.xml
index 08a09e6642e8..aa26dbbe2e48 100644
--- a/active-object/pom.xml
+++ b/active-object/pom.xml
@@ -34,6 +34,14 @@
active-object
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
index c7b661845a00..5a440020c0ac 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/ActiveCreature.java
@@ -29,86 +29,87 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * ActiveCreature class is the base of the active object example.
- *
- */
+/** ActiveCreature class is the base of the active object example. */
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.
- */
+ /** 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 =
+ 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());
- });
+ requests.put(
+ () -> {
+ logger.info("{} is eating!", name());
+ logger.info("{} has finished eating!", name());
+ });
}
/**
* Roam 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())
- );
+ 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() {
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/App.java b/active-object/src/main/java/com/iluwatar/activeobject/App.java
index b88b4c5590a0..ca3a5526ebb8 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/App.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/App.java
@@ -30,17 +30,17 @@
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.
- *
+ * 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;
/**
@@ -48,11 +48,11 @@ public class App implements Runnable {
*
* @param args command line arguments.
*/
- public static void main(String[] args) {
+ public static void main(String[] args) {
var app = new App();
app.run();
}
-
+
@Override
public void run() {
List creatures = new ArrayList<>();
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
index 8f5570a8663b..30adde034de5 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
@@ -24,14 +24,10 @@
*/
package com.iluwatar.activeobject;
-/**
- * An implementation of the ActiveCreature class.
- *
- */
+/** An implementation of the ActiveCreature class. */
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
index 5441ed6b0d9b..be79e2fb5527 100644
--- a/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
+++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
@@ -27,17 +27,16 @@
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);
- }
-
+ @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
index 8ef2f142cf46..559e2a1f58f1 100644
--- a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
+++ b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
@@ -28,11 +28,10 @@
import org.junit.jupiter.api.Test;
-
class AppTest {
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
}
diff --git a/actor-model/README.md b/actor-model/README.md
new file mode 100644
index 000000000000..be8065ffefef
--- /dev/null
+++ b/actor-model/README.md
@@ -0,0 +1,201 @@
+---
+title: "Actor Model Pattern in Java: Building Concurrent Systems with Elegance"
+shortTitle: Actor Model
+description: "Explore the Actor Model pattern in Java with real-world examples and practical implementation. Learn how to build scalable, message-driven systems using actors, messages, and asynchronous communication."
+category: Concurrency
+language: en
+tag:
+ - Concurrency
+ - Messaging
+ - Isolation
+ - Asynchronous
+ - Distributed Systems
+ - Actor Model
+---
+
+## Also Known As
+
+- Message-passing concurrency
+- Actor-based concurrency
+
+---
+
+## Intent of Actor Model Pattern
+
+The Actor Model pattern enables the construction of highly concurrent, distributed, and fault-tolerant systems by using isolated components (actors) that interact exclusively through asynchronous message passing.
+
+---
+
+## Detailed Explanation of Actor Model Pattern with Real-World Examples
+
+### 📦 Real-world Example
+
+Imagine a customer service system:
+- Each **customer support agent** is an **actor**.
+- Customers **send questions (messages)** to agents.
+- Each agent handles one request at a time and can **respond asynchronously** without interfering with other agents.
+
+---
+
+### 🧠 In Plain Words
+
+> "Actors are like independent workers that never share memory and only communicate through messages."
+
+---
+
+### 📖 Wikipedia Says
+
+> [Actor model](https://en.wikipedia.org/wiki/Actor_model) is a mathematical model of concurrent computation that treats "actors" as the universal primitives of concurrent computation.
+
+---
+
+### 🧹 Architecture Diagram
+
+
+
+---
+
+## Programmatic Example of Actor Model Pattern in Java
+
+### Actor.java
+
+```java
+public abstract class Actor implements Runnable {
+
+ @Setter @Getter private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active = true;
+
+
+ public void send(Message message) {
+ mailbox.add(message);
+ }
+
+ public void stop() {
+ active = false;
+ }
+
+ @Override
+ public void run() {
+
+ }
+
+ protected abstract void onReceive(Message message);
+}
+
+```
+
+### Message.java
+
+```java
+
+@AllArgsConstructor
+@Getter
+@Setter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
+```
+
+### ActorSystem.java
+
+```java
+public class ActorSystem {
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
+```
+
+### App.java
+
+```java
+public class App {
+ public static void main(String[] args) {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
+```
+
+---
+
+## When to Use the Actor Model Pattern in Java
+
+- When building **concurrent or distributed systems**
+- When you want **no shared mutable state**
+- When you need **asynchronous, message-driven communication**
+- When components should be **isolated and loosely coupled**
+
+---
+
+## Actor Model Pattern Java Tutorials
+
+- [Baeldung – Akka with Java](https://www.baeldung.com/java-akka)
+- [Vaughn Vernon – Reactive Messaging Patterns](https://vaughnvernon.co/?p=1143)
+
+---
+
+## Real-World Applications of Actor Model Pattern in Java
+
+- [Akka Framework](https://akka.io/)
+- [Erlang and Elixir concurrency](https://www.erlang.org/)
+- [Microsoft Orleans](https://learn.microsoft.com/en-us/dotnet/orleans/)
+- JVM-based game engines and simulators
+
+---
+
+## Benefits and Trade-offs of Actor Model Pattern
+
+### ✅ Benefits
+- High concurrency support
+- Easy scaling across threads or machines
+- Fault isolation and recovery
+- Message ordering within actors
+
+### ⚠️ Trade-offs
+- Harder to debug due to asynchronous behavior
+- Slight performance overhead due to message queues
+- More complex to design than simple method calls
+
+---
+
+## Related Java Design Patterns
+
+- [Command Pattern](../command)
+- [Mediator Pattern](../mediator)
+- [Event-Driven Architecture](../event-driven-architecture)
+- [Observer Pattern](../observer)
+
+---
+
+## References and Credits
+
+- *Programming Erlang*, Joe Armstrong
+- *Reactive Design Patterns*, Roland Kuhn
+- *The Actor Model in 10 Minutes*, [InfoQ Article](https://www.infoq.com/articles/actor-model/)
+- [Akka Documentation](https://doc.akka.io/docs/akka/current/index.html)
+
diff --git a/actor-model/etc/Actor_Model_UML_Class_Diagram.png b/actor-model/etc/Actor_Model_UML_Class_Diagram.png
new file mode 100644
index 000000000000..a4c34d7a75fd
Binary files /dev/null and b/actor-model/etc/Actor_Model_UML_Class_Diagram.png differ
diff --git a/actor-model/etc/actor-model.urm.puml b/actor-model/etc/actor-model.urm.puml
new file mode 100644
index 000000000000..020c1fc735a4
--- /dev/null
+++ b/actor-model/etc/actor-model.urm.puml
@@ -0,0 +1,35 @@
+@startuml actor-model
+
+title Actor Model - UML Class Diagram
+
+class ActorSystem {
+ +actorOf(actor: Actor): Actor
+ +shutdown(): void
+}
+
+class Actor {
+ -mailbox: BlockingQueue
+ -active: boolean
+ +send(message: Message): void
+ +stop(): void
+ +run(): void
+ #onReceive(message: Message): void
+}
+
+class ExampleActor {
+ +onReceive(message: Message): void
+}
+
+class Message {
+ -content: String
+ -sender: Actor
+ +getContent(): String
+ +getSender(): Actor
+}
+
+ActorSystem --> Actor : creates
+Actor <|-- ExampleActor : extends
+Actor --> Message : processes
+ExampleActor --> Message : uses
+
+@enduml
diff --git a/actor-model/pom.xml b/actor-model/pom.xml
new file mode 100644
index 000000000000..76c288829b8d
--- /dev/null
+++ b/actor-model/pom.xml
@@ -0,0 +1,114 @@
+
+
+
+
+ 4.0.0
+
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ actor-model
+ Actor Model
+
+
+
+
+
+ org.junit
+ junit-bom
+ 5.11.0
+ pom
+ import
+
+
+
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.junit.platform
+ junit-platform-launcher
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 3.3.0
+
+
+ jar-with-dependencies
+
+
+
+ com.iluwatar.actormodel.App
+
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
+
+
+
+
+
+
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java
new file mode 100644
index 000000000000..6e2aaccd1937
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Actor.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import lombok.Getter;
+import lombok.Setter;
+
+public abstract class Actor implements Runnable {
+
+ @Setter @Getter private String actorId;
+ private final BlockingQueue mailbox = new LinkedBlockingQueue<>();
+ private volatile boolean active =
+ true; // always read from main memory and written back to main memory,
+
+ // rather than being cached in a thread's local memory. To make it consistent to all Actors
+
+ public void send(Message message) {
+ mailbox.add(message); // Add message to queue
+ }
+
+ public void stop() {
+ active = false; // Stop the actor loop
+ }
+
+ @Override
+ public void run() {
+ while (active) {
+ try {
+ Message message = mailbox.take(); // Wait for a message
+ onReceive(message); // Process it
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ // Child classes must define what to do with a message
+ protected abstract void onReceive(Message message);
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java
new file mode 100644
index 000000000000..db7c21cb6088
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ActorSystem.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.atomic.AtomicInteger;
+
+public class ActorSystem {
+ private final ExecutorService executor = Executors.newCachedThreadPool();
+ private final ConcurrentHashMap actorRegister = new ConcurrentHashMap<>();
+ private final AtomicInteger idCounter = new AtomicInteger(0);
+
+ public void startActor(Actor actor) {
+ String actorId = "actor-" + idCounter.incrementAndGet(); // Generate a new and unique ID
+ actor.setActorId(actorId); // assign the actor it's ID
+ actorRegister.put(actorId, actor); // Register and save the actor with it's ID
+ executor.submit(actor); // Run the actor in a thread
+ }
+
+ public Actor getActorById(String actorId) {
+ return actorRegister.get(actorId); // Find by Id
+ }
+
+ public void shutdown() {
+ executor.shutdownNow(); // Stop all threads
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/App.java b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
new file mode 100644
index 000000000000..79fe79e48a6f
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/App.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.
+ */
+
+/**
+ * The Actor Model is a design pattern used to handle concurrency in a safe, scalable, and
+ * message-driven way.
+ *
+ * In the Actor Model: - An **Actor** is an independent unit that has its own state and behavior.
+ * - Actors **communicate only through messages** — they do not share memory. - An **ActorSystem**
+ * is responsible for creating, starting, and managing the lifecycle of actors. - Messages are
+ * delivered asynchronously, and each actor processes them one at a time.
+ *
+ *
💡 Key benefits: - No shared memory = no need for complex thread-safety - Easy to scale with
+ * many actors - Suitable for highly concurrent or distributed systems
+ *
+ *
🔍 This example demonstrates the Actor Model: - `ActorSystem` starts two actors: `srijan` and
+ * `ansh`. - `ExampleActor` and `ExampleActor2` extend the `Actor` class and override the
+ * `onReceive()` method to handle messages. - Actors communicate using `send()` to pass `Message`
+ * objects that include the message content and sender's ID. - The actors process messages
+ * **asynchronously in separate threads**, and we allow a short delay (`Thread.sleep`) to let them
+ * run. - The system is shut down gracefully at the end.
+ */
+package com.iluwatar.actormodel;
+
+public class App {
+ public static void main(String[] args) throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+ Actor srijan = new ExampleActor(system);
+ Actor ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+ srijan.send(new Message("Hello srijan!", ansh.getActorId()));
+
+ Thread.sleep(1000); // Give time for messages to process
+
+ srijan.stop(); // Stop the actor gracefully
+ ansh.stop();
+ system.shutdown(); // Stop the actor system
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
new file mode 100644
index 000000000000..fd49325f44bd
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ // Logger log = Logger.getLogger(getClass().getName());
+
+ @Override
+ protected void onReceive(Message message) {
+ LOGGER.info(
+ "[{}]Received : {} from : [{}]", getActorId(), message.getContent(), message.getSenderId());
+ Actor sender = actorSystem.getActorById(message.getSenderId()); // sender actor id
+ // Reply of the message
+ if (sender != null && !message.getSenderId().equals(getActorId())) {
+ sender.send(new Message("I got your message ", getActorId()));
+ }
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
new file mode 100644
index 000000000000..037f96716558
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/ExampleActor2.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Getter;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ExampleActor2 extends Actor {
+ private final ActorSystem actorSystem;
+ @Getter private final List receivedMessages = new ArrayList<>();
+
+ public ExampleActor2(ActorSystem actorSystem) {
+ this.actorSystem = actorSystem;
+ }
+
+ @Override
+ protected void onReceive(Message message) {
+ receivedMessages.add(message.getContent());
+ LOGGER.info("[{}]Received : {}", getActorId(), message.getContent());
+ }
+}
diff --git a/actor-model/src/main/java/com/iluwatar/actormodel/Message.java b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
new file mode 100644
index 000000000000..03ca6e02cac0
--- /dev/null
+++ b/actor-model/src/main/java/com/iluwatar/actormodel/Message.java
@@ -0,0 +1,35 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actormodel;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+
+@AllArgsConstructor
+@Getter
+public class Message {
+ private final String content;
+ private final String senderId;
+}
diff --git a/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
new file mode 100644
index 000000000000..a4a0dee569ab
--- /dev/null
+++ b/actor-model/src/test/java/com/iluwatar/actor/ActorModelTest.java
@@ -0,0 +1,63 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.actor;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import com.iluwatar.actormodel.ActorSystem;
+import com.iluwatar.actormodel.App;
+import com.iluwatar.actormodel.ExampleActor;
+import com.iluwatar.actormodel.ExampleActor2;
+import com.iluwatar.actormodel.Message;
+import org.junit.jupiter.api.Test;
+
+public class ActorModelTest {
+ @Test
+ void testMainMethod() throws InterruptedException {
+ App.main(new String[] {});
+ }
+
+ @Test
+ public void testMessagePassing() throws InterruptedException {
+ ActorSystem system = new ActorSystem();
+
+ ExampleActor srijan = new ExampleActor(system);
+ ExampleActor2 ansh = new ExampleActor2(system);
+
+ system.startActor(srijan);
+ system.startActor(ansh);
+
+ // Ansh recieves a message from Srijan
+ ansh.send(new Message("Hello ansh", srijan.getActorId()));
+
+ // Wait briefly to allow async processing
+ Thread.sleep(200);
+
+ // Check that Srijan received the message
+ assertTrue(
+ ansh.getReceivedMessages().contains("Hello ansh"),
+ "ansh should receive the message from Srijan");
+ }
+}
diff --git a/acyclic-visitor/README.md b/acyclic-visitor/README.md
index eef18bbed344..fb57d5681fd4 100644
--- a/acyclic-visitor/README.md
+++ b/acyclic-visitor/README.md
@@ -29,6 +29,11 @@ In plain words
> The Acyclic Visitor pattern allows new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating the dependency cycles that are inherent to the GangOfFour VisitorPattern.
+Sequence diagram
+
+
+
+
## Programmatic Example of Acyclic Visitor in Java
In this Java example, we have a hierarchy of modem classes illustrating the Acyclic Visitor pattern. The modems in this hierarchy need to be visited by an external algorithm based on filtering criteria (is it Unix or DOS compatible modem).
@@ -138,10 +143,6 @@ Program output:
09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
```
-## Acyclic Visitor Pattern Class Diagram
-
-
-
## When to Use the Acyclic Visitor Pattern in Java
This pattern can be used:
diff --git a/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png
new file mode 100644
index 000000000000..a3c2ba56b89f
Binary files /dev/null and b/acyclic-visitor/etc/acyclic-visitor-sequence-diagram.png differ
diff --git a/acyclic-visitor/pom.xml b/acyclic-visitor/pom.xml
index 52604048e127..b4f5646b71c0 100644
--- a/acyclic-visitor/pom.xml
+++ b/acyclic-visitor/pom.xml
@@ -34,6 +34,14 @@
acyclic-visitor
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
index 38da4923a467..a3b1679a2d9f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
@@ -28,6 +28,4 @@
* All ModemVisitor interface extends all visitor interfaces. This interface provides ease of use
* when a visitor needs to visit all modem types.
*/
-public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
-
-}
+public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
index 64d4d039aba3..3b7c6cd61e4b 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
@@ -37,9 +37,7 @@
*/
public class App {
- /**
- * Program's entry point.
- */
+ /** Program's entry point. */
public static void main(String[] args) {
var conUnix = new ConfigureForUnixVisitor();
var conDos = new ConfigureForDosVisitor();
@@ -50,6 +48,6 @@ public static void main(String[] args) {
hayes.accept(conDos); // Hayes modem with Dos configurator
zoom.accept(conDos); // Zoom modem with Dos configurator
hayes.accept(conUnix); // Hayes modem with Unix configurator
- zoom.accept(conUnix); // Zoom modem with Unix configurator
+ zoom.accept(conUnix); // Zoom modem with Unix configurator
}
}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
index 9f9f29187839..267a8d66ac45 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
@@ -27,8 +27,7 @@
import lombok.extern.slf4j.Slf4j;
/**
- * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos
- * manufacturer.
+ * ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos manufacturer.
*/
@Slf4j
public class ConfigureForDosVisitor implements AllModemVisitor {
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
index 097f19c0dbbd..d9fd14f69435 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
@@ -37,4 +37,4 @@ public class ConfigureForUnixVisitor implements ZoomVisitor {
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Unix configurator.");
}
-}
\ No newline at end of file
+}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
index 384df8a4d9c9..e0b2fcc2b530 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
@@ -26,15 +26,11 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * Hayes class implements its accept method.
- */
+/** Hayes class implements its accept method. */
@Slf4j
public class Hayes implements Modem {
- /**
- * Accepts all visitors but honors only HayesVisitor.
- */
+ /** Accepts all visitors but honors only HayesVisitor. */
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof HayesVisitor) {
@@ -42,12 +38,9 @@ public void accept(ModemVisitor modemVisitor) {
} else {
LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
}
-
}
- /**
- * Hayes' modem's toString method.
- */
+ /** Hayes' modem's toString method. */
@Override
public String toString() {
return "Hayes modem";
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
index a33c87cfa645..aad9b970994f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.acyclicvisitor;
-/**
- * HayesVisitor interface.
- */
+/** HayesVisitor interface. */
public interface HayesVisitor extends ModemVisitor {
void visit(Hayes hayes);
}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
index fd15ee422468..8552574453e5 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
@@ -24,10 +24,7 @@
*/
package com.iluwatar.acyclicvisitor;
-/**
- * //Modem abstract class.
- * converted to an interface
- */
+/** //Modem abstract class. converted to an interface */
public interface Modem {
void accept(ModemVisitor modemVisitor);
}
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
index e9f02e1ad6fd..59b50a54a12f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
@@ -26,15 +26,11 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * Zoom class implements its accept method.
- */
+/** Zoom class implements its accept method. */
@Slf4j
public class Zoom implements Modem {
- /**
- * Accepts all visitors but honors only ZoomVisitor.
- */
+ /** Accepts all visitors but honors only ZoomVisitor. */
@Override
public void accept(ModemVisitor modemVisitor) {
if (modemVisitor instanceof ZoomVisitor) {
@@ -44,9 +40,7 @@ public void accept(ModemVisitor modemVisitor) {
}
}
- /**
- * Zoom modem's toString method.
- */
+ /** Zoom modem's toString method. */
@Override
public String toString() {
return "Zoom modem";
diff --git a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
index 639af1c65777..5388ded6f735 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.acyclicvisitor;
-/**
- * ZoomVisitor interface.
- */
+/** ZoomVisitor interface. */
public interface ZoomVisitor extends ModemVisitor {
void visit(Zoom zoom);
}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
index 9cc242d8f7fc..7a21498a63ea 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
@@ -24,25 +24,22 @@
*/
package com.iluwatar.acyclicvisitor;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that the Acyclic Visitor example runs without errors.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests that the Acyclic Visitor example runs without errors. */
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.
+ * 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[] {}));
}
-}
\ No newline at end of file
+}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
index 66640e3ca1ac..a989d9287921 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
@@ -24,14 +24,12 @@
*/
package com.iluwatar.acyclicvisitor;
-import org.junit.jupiter.api.Test;
-
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.*;
-/**
- * Hayes test class
- */
+import org.junit.jupiter.api.Test;
+
+/** Hayes test class */
class HayesTest {
@Test
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
index df7b7e8408a5..d5fe79965d47 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
@@ -24,16 +24,13 @@
*/
package com.iluwatar.acyclicvisitor;
-
-import org.junit.jupiter.api.Test;
-
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-/**
- * Zoom test class
- */
+import org.junit.jupiter.api.Test;
+
+/** Zoom test class */
class ZoomTest {
@Test
diff --git a/adapter/README.md b/adapter/README.md
index 945de2db00e8..489742494709 100644
--- a/adapter/README.md
+++ b/adapter/README.md
@@ -35,6 +35,10 @@ Wikipedia says
> In software engineering, the adapter pattern is a software design pattern that allows the interface of an existing class to be used as another interface. It is often used to make existing classes work with others without modifying their source code.
+Sequence diagram
+
+
+
## Programmatic Example of Adapter Pattern in Java
The Adapter Pattern example in Java shows how a class with an incompatible interface can be adapted to work with another class.
diff --git a/adapter/etc/adapter-sequence-diagram.png b/adapter/etc/adapter-sequence-diagram.png
new file mode 100644
index 000000000000..a9bb557ea61e
Binary files /dev/null and b/adapter/etc/adapter-sequence-diagram.png differ
diff --git a/adapter/pom.xml b/adapter/pom.xml
index d54cbd048708..6e7f45a515b5 100644
--- a/adapter/pom.xml
+++ b/adapter/pom.xml
@@ -34,6 +34,14 @@
adapter
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/adapter/src/main/java/com/iluwatar/adapter/App.java b/adapter/src/main/java/com/iluwatar/adapter/App.java
index 1f572813cef1..a4fa74274f74 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/App.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/App.java
@@ -37,16 +37,15 @@
* The Adapter ({@link FishingBoatAdapter}) converts the interface of the adaptee class ({@link
* FishingBoat}) into a suitable one expected by the client ({@link RowingBoat}).
*
- *
The story of this implementation is this. Pirates are coming! we need a {@link
- * RowingBoat} to flee! We have a {@link FishingBoat} and our captain. We have no time to make up a
- * new ship! we need to reuse this {@link FishingBoat}. The captain needs a rowing boat which he can
- * operate. The spec is in {@link RowingBoat}. We will use the Adapter pattern to reuse {@link
- * FishingBoat}.
+ *
The story of this implementation is this.
+ * Pirates are coming! we need a {@link RowingBoat} to flee! We have a {@link FishingBoat} and our
+ * captain. We have no time to make up a new ship! we need to reuse this {@link FishingBoat}. The
+ * captain needs a rowing boat which he can operate. The spec is in {@link RowingBoat}. We will use
+ * the Adapter pattern to reuse {@link FishingBoat}.
*/
public final class App {
- private App() {
- }
+ private App() {}
/**
* Program entry point.
diff --git a/adapter/src/main/java/com/iluwatar/adapter/Captain.java b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
index 3d6d7746d00a..3b771e9d833e 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/Captain.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/Captain.java
@@ -29,7 +29,8 @@
import lombok.Setter;
/**
- * The Captain uses {@link RowingBoat} to sail. This is the client in the pattern.
+ * The Captain uses {@link RowingBoat} to sail.
+ * This is the client in the pattern.
*/
@Setter
@NoArgsConstructor
@@ -41,5 +42,4 @@ public final class Captain {
void row() {
rowingBoat.row();
}
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
index e692d859873c..dd39f88f1ce0 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/FishingBoat.java
@@ -36,5 +36,4 @@ final class FishingBoat {
void sail() {
LOGGER.info("The fishing boat is sailing");
}
-
}
diff --git a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
index c8714ef91040..55eeeaf4bd42 100644
--- a/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
+++ b/adapter/src/main/java/com/iluwatar/adapter/RowingBoat.java
@@ -25,10 +25,10 @@
package com.iluwatar.adapter;
/**
- * The interface expected by the client. A rowing boat is rowed to move.
+ * The interface expected by the client.
+ * A rowing boat is rowed to move.
*/
public interface RowingBoat {
void row();
-
}
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
index 10024ff0dc46..bc4984da3d6f 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AdapterPatternTest.java
@@ -24,17 +24,15 @@
*/
package com.iluwatar.adapter;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import java.util.HashMap;
-import java.util.Map;
-
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-/**
- * Tests for the adapter pattern.
- */
+import java.util.HashMap;
+import java.util.Map;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Tests for the adapter pattern. */
class AdapterPatternTest {
private Map beans;
@@ -43,9 +41,7 @@ class AdapterPatternTest {
private static final String ROWING_BEAN = "captain";
- /**
- * This method runs before the test execution and sets the bean objects in the beans Map.
- */
+ /** This method runs before the test execution and sets the bean objects in the beans Map. */
@BeforeEach
void setup() {
beans = new HashMap<>();
diff --git a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
index be51d2687548..a2cc4c22bcaa 100644
--- a/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
+++ b/adapter/src/test/java/com/iluwatar/adapter/AppTest.java
@@ -24,23 +24,17 @@
*/
package com.iluwatar.adapter;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that Adapter example runs without errors.
- */
-class AppTest {
+import org.junit.jupiter.api.Test;
- /**
- * Check whether the execution of the main method in {@link App}
- * throws an exception.
- */
+/** Tests that Adapter example runs without errors. */
+class AppTest {
+ /** 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/ambassador/README.md b/ambassador/README.md
index 6f87afae1bf1..fa08223feb75 100644
--- a/ambassador/README.md
+++ b/ambassador/README.md
@@ -33,6 +33,10 @@ Microsoft documentation states
> An ambassador service can be thought of as an out-of-process proxy which is co-located with the client. This pattern can be useful for offloading common client connectivity tasks such as monitoring, logging, routing, security (such as TLS), and resiliency patterns in a language agnostic way. It is often used with legacy applications, or other applications that are difficult to modify, in order to extend their networking capabilities. It can also enable a specialized team to implement those features.
+Sequence diagram
+
+
+
## Programmatic Example of Ambassador Pattern in Java
In this example of the Ambassador Pattern in Java, we demonstrate how to implement latency checks, logging, and retry mechanisms to improve system reliability.
diff --git a/ambassador/etc/ambassador-sequence-diagram.png b/ambassador/etc/ambassador-sequence-diagram.png
new file mode 100644
index 000000000000..71e6a947c5dd
Binary files /dev/null and b/ambassador/etc/ambassador-sequence-diagram.png differ
diff --git a/ambassador/pom.xml b/ambassador/pom.xml
index a6d702426080..15e4a07f0dc3 100644
--- a/ambassador/pom.xml
+++ b/ambassador/pom.xml
@@ -34,6 +34,14 @@
4.0.0
ambassador
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/App.java b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
index ff025d9b2bf6..8de149fe0813 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/App.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/App.java
@@ -28,8 +28,8 @@
* The ambassador pattern creates a helper service that sends network requests on behalf of a
* client. It is often used in cloud-based applications to offload features of a remote service.
*
- * An ambassador service can be thought of as an out-of-process proxy that is co-located with
- * the client. Similar to the proxy design pattern, the ambassador service provides an interface for
+ *
An ambassador service can be thought of as an out-of-process proxy that is co-located with the
+ * client. Similar to the proxy design pattern, the ambassador service provides an interface for
* another remote service. In addition to the interface, the ambassador provides extra functionality
* and features, specifically offloaded common connectivity tasks. This usually consists of
* monitoring, logging, routing, security etc. This is extremely useful in legacy applications where
@@ -37,14 +37,11 @@
* capabilities.
*
*
In this example, we will the ({@link ServiceAmbassador}) class represents the ambassador while
- * the
- * ({@link RemoteService}) class represents a remote application.
+ * the ({@link RemoteService}) class represents a remote application.
*/
public class App {
- /**
- * Entry point.
- */
+ /** Entry point. */
public static void main(String[] args) {
var host1 = new Client();
var host2 = new Client();
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
index d0f81c1dd121..0baabf4ffc06 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/Client.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * A simple Client.
- */
+/** A simple Client. */
@Slf4j
public class Client {
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
index eba634494fc7..d99348040cfe 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteService.java
@@ -29,9 +29,7 @@
import com.iluwatar.ambassador.util.RandomProvider;
import lombok.extern.slf4j.Slf4j;
-/**
- * A remote legacy application represented by a Singleton implementation.
- */
+/** A remote legacy application represented by a Singleton implementation. */
@Slf4j
public class RemoteService implements RemoteServiceInterface {
private static final int THRESHOLD = 200;
@@ -49,9 +47,7 @@ private RemoteService() {
this(Math::random);
}
- /**
- * This constructor is used for testing purposes only.
- */
+ /** This constructor is used for testing purposes only. */
RemoteService(RandomProvider randomProvider) {
this.randomProvider = randomProvider;
}
@@ -75,7 +71,8 @@ public long doRemoteFunction(int value) {
LOGGER.error("Thread sleep state interrupted", e);
Thread.currentThread().interrupt();
}
- return waitTime <= THRESHOLD ? value * 10
+ return waitTime <= THRESHOLD
+ ? value * 10
: RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
}
}
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
index 104d81ec2eff..aa6012bae33f 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceInterface.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.ambassador;
-/**
- * Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}).
- */
+/** Interface shared by ({@link RemoteService}) and ({@link ServiceAmbassador}). */
interface RemoteServiceInterface {
long doRemoteFunction(int value);
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
index 8f1a0a1a4907..8549ed7247f3 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/RemoteServiceStatus.java
@@ -29,17 +29,14 @@
/**
* Holds information regarding the status of the Remote Service.
*
- *
This Enum replaces the integer value previously
- * stored in {@link RemoteServiceInterface} as SonarCloud was identifying
- * it as an issue. All test cases have been checked after changes,
- * without failures.
+ * This Enum replaces the integer value previously stored in {@link RemoteServiceInterface} as
+ * SonarCloud was identifying it as an issue. All test cases have been checked after changes,
+ * without failures.
*/
-
public enum RemoteServiceStatus {
FAILURE(-1);
- @Getter
- private final long remoteServiceStatusValue;
+ @Getter private final long remoteServiceStatusValue;
RemoteServiceStatus(long remoteServiceStatusValue) {
this.remoteServiceStatusValue = remoteServiceStatusValue;
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
index f3f30a09dc9b..4d310169770b 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/ServiceAmbassador.java
@@ -40,8 +40,7 @@ public class ServiceAmbassador implements RemoteServiceInterface {
private static final int RETRIES = 3;
private static final int DELAY_MS = 3000;
- ServiceAmbassador() {
- }
+ ServiceAmbassador() {}
@Override
public long doRemoteFunction(int value) {
diff --git a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
index e8243cdcc7cf..4eba2fada7fa 100644
--- a/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
+++ b/ambassador/src/main/java/com/iluwatar/ambassador/util/RandomProvider.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.ambassador.util;
-/**
- * An interface for randomness. Useful for testing purposes.
- */
+/** An interface for randomness. Useful for testing purposes. */
public interface RandomProvider {
double random();
}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
index cea0eeac7bb9..ddb2d6eff411 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/AppTest.java
@@ -24,25 +24,22 @@
*/
package com.iluwatar.ambassador;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
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.
+ *
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/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
index ff7f027f64f1..24603efff9d4 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ClientTest.java
@@ -24,13 +24,11 @@
*/
package com.iluwatar.ambassador;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Test for {@link Client}
- */
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link Client} */
class ClientTest {
@Test
@@ -38,6 +36,7 @@ void test() {
Client client = new Client();
var result = client.useService(10);
- assertTrue(result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ assertTrue(
+ result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
}
}
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
index 5fed19a169b1..81e4f744128f 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/RemoteServiceTest.java
@@ -29,9 +29,7 @@
import com.iluwatar.ambassador.util.RandomProvider;
import org.junit.jupiter.api.Test;
-/**
- * Test for {@link RemoteService}
- */
+/** Test for {@link RemoteService} */
class RemoteServiceTest {
@Test
diff --git a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
index 50c354c1485a..0543b2e7e370 100644
--- a/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
+++ b/ambassador/src/test/java/com/iluwatar/ambassador/ServiceAmbassadorTest.java
@@ -24,18 +24,17 @@
*/
package com.iluwatar.ambassador;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertTrue;
-/**
- * Test for {@link ServiceAmbassador}
- */
+import org.junit.jupiter.api.Test;
+
+/** Test for {@link ServiceAmbassador} */
class ServiceAmbassadorTest {
@Test
void test() {
long result = new ServiceAmbassador().doRemoteFunction(10);
- assertTrue(result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
+ assertTrue(
+ result == 100 || result == RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue());
}
}
diff --git a/anti-corruption-layer/README.md b/anti-corruption-layer/README.md
index fdc3edd7a337..9b6042850eba 100644
--- a/anti-corruption-layer/README.md
+++ b/anti-corruption-layer/README.md
@@ -44,6 +44,10 @@ In plain words
> Implement a façade or adapter layer between different subsystems that don't share the same semantics. This layer translates requests that one subsystem makes to the other subsystem. Use this pattern to ensure that an application's design is not limited by dependencies on outside subsystems. This pattern was first described by Eric Evans in Domain-Driven Design.
+Sequence diagram
+
+
+
## Programmatic Example of Anti-Corruption Layer Pattern in Java
The ACL design pattern in Java provides an intermediary layer that translates data formats, ensuring that integration between different systems does not lead to data corruption.
@@ -122,7 +126,7 @@ public class LegacyShop {
String id = legacyOrder.getId();
- Optional orderInModernSystem = acl.findOrderInModernSystem(id);
+ Optional orderInModernSystem = acl.findOrderInModernSystem(id);
if (orderInModernSystem.isPresent()) {
// order is already in the modern system
diff --git a/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png b/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png
new file mode 100644
index 000000000000..835ff4e84a0f
Binary files /dev/null and b/anti-corruption-layer/etc/anti-corruption-layer-sequence-diagram.png differ
diff --git a/anti-corruption-layer/pom.xml b/anti-corruption-layer/pom.xml
index 711e006b7076..2fddfd3ecc46 100644
--- a/anti-corruption-layer/pom.xml
+++ b/anti-corruption-layer/pom.xml
@@ -45,8 +45,8 @@
test
- junit
- junit
+ org.junit.jupiter
+ junit-jupiter-engine
test
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
index ce2dbffd6f9a..f7cf8f075f83 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
@@ -28,9 +28,8 @@
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
- * This layer translates communications between the two systems,
- * allowing one system to remain unchanged while the other can avoid compromising
- * its design and technological approach.
+ * This layer translates communications between the two systems, allowing one system to remain
+ * unchanged while the other can avoid compromising its design and technological approach.
*/
@SpringBootApplication
public class App {
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
index c8f72fca49fd..880d98c7d5b0 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
@@ -23,30 +23,26 @@
* THE SOFTWARE.
*/
/**
- * Context and problem
- * Most applications rely on other systems for some data or functionality.
- * For example, when a legacy application is migrated to a modern system,
- * it may still need existing legacy resources. New features must be able to call the legacy system.
- * This is especially true of gradual migrations,
- * where different features of a larger application are moved to a modern system over time.
+ * Context and problem Most applications rely on other systems for some data or functionality. For
+ * example, when a legacy application is migrated to a modern system, it may still need existing
+ * legacy resources. New features must be able to call the legacy system. This is especially true of
+ * gradual migrations, where different features of a larger application are moved to a modern system
+ * over time.
*
- * Often these legacy systems suffer from quality issues such as convoluted data schemas
- * or obsolete APIs.
- * The features and technologies used in legacy systems can vary widely from more modern systems.
- * To interoperate with the legacy system,
- * the new application may need to support outdated infrastructure, protocols, data models, APIs,
- * or other features that you wouldn't otherwise put into a modern application.
+ *
Often these legacy systems suffer from quality issues such as convoluted data schemas or
+ * obsolete APIs. The features and technologies used in legacy systems can vary widely from more
+ * modern systems. To interoperate with the legacy system, the new application may need to support
+ * outdated infrastructure, protocols, data models, APIs, or other features that you wouldn't
+ * otherwise put into a modern application.
*
- *
Maintaining access between new and legacy systems can force the new system to adhere to
- * at least some of the legacy system's APIs or other semantics.
- * When these legacy features have quality issues, supporting them "corrupts" what might
- * otherwise be a cleanly designed modern application.
- * Similar issues can arise with any external system that your development team doesn't control,
- * not just legacy systems.
+ *
Maintaining access between new and legacy systems can force the new system to adhere to at
+ * least some of the legacy system's APIs or other semantics. When these legacy features have
+ * quality issues, supporting them "corrupts" what might otherwise be a cleanly designed modern
+ * application. Similar issues can arise with any external system that your development team doesn't
+ * control, not just legacy systems.
*
*
Solution Isolate the different subsystems by placing an anti-corruption layer between them.
- * This layer translates communications between the two systems,
- * allowing one system to remain unchanged while the other can avoid compromising
- * its design and technological approach.
+ * This layer translates communications between the two systems, allowing one system to remain
+ * unchanged while the other can avoid compromising its design and technological approach.
*/
package com.iluwatar.corruption;
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
index fae658ee5bac..4e8a17fa5d2a 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
@@ -33,36 +33,34 @@
import org.springframework.stereotype.Service;
/**
- * The class represents an anti-corruption layer.
- * The main purpose of the class is to provide a layer between the modern and legacy systems.
- * The class is responsible for converting the data from one system to another
- * decoupling the systems to each other
+ * The class represents an anti-corruption layer. The main purpose of the class is to provide a
+ * layer between the modern and legacy systems. The class is responsible for converting the data
+ * from one system to another decoupling the systems to each other
*
- *
It allows using one system a domain model of the other system
- * without changing the domain model of the system.
+ *
It allows using one system a domain model of the other system without changing the domain
+ * model of the system.
*/
@Service
public class AntiCorruptionLayer {
- @Autowired
- private LegacyShop legacyShop;
-
+ @Autowired private LegacyShop legacyShop;
/**
* The method converts the order from the legacy system to the modern system.
+ *
* @param id the id of the order
* @return the order in the modern system
*/
public Optional findOrderInLegacySystem(String id) {
- return legacyShop.findOrder(id).map(o ->
- new ModernOrder(
- o.getId(),
- new Customer(o.getCustomer()),
- new Shipment(o.getItem(), o.getQty(), o.getPrice()),
- ""
- )
- );
+ return legacyShop
+ .findOrder(id)
+ .map(
+ o ->
+ new ModernOrder(
+ o.getId(),
+ new Customer(o.getCustomer()),
+ new Shipment(o.getItem(), o.getQty(), o.getPrice()),
+ ""));
}
-
}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
index e9fdaa14ec1f..e84578528be7 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.java
@@ -29,6 +29,7 @@
/**
* The class represents a data store for the modern system.
+ *
* @param the type of the value stored in the data store
*/
public abstract class DataStore {
@@ -44,6 +45,5 @@ public Optional get(String key) {
public Optional put(String key, V value) {
return Optional.ofNullable(inner.put(key, value));
-
}
}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
index 103fcfccd9e7..c0acd288ed0c 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.corruption.system;
-/**
- * The class represents a general exception for the shop.
- */
+/** The class represents a general exception for the shop. */
public class ShopException extends Exception {
public ShopException(String message) {
super(message);
@@ -41,9 +39,12 @@ public ShopException(String message) {
* @throws ShopException the exception
*/
public static ShopException throwIncorrectData(String lhs, String rhs) throws ShopException {
- throw new ShopException("The order is already placed but has an incorrect data:\n"
- + "Incoming order: " + lhs + "\n"
- + "Existing order: " + rhs);
+ throw new ShopException(
+ "The order is already placed but has an incorrect data:\n"
+ + "Incoming order: "
+ + lhs
+ + "\n"
+ + "Existing order: "
+ + rhs);
}
-
}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
index 2481d19c8212..45faa06cb26c 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.java
@@ -28,8 +28,8 @@
import lombok.Data;
/**
- * The class represents an order in the legacy system.
- * The class is used by the legacy system to store the data.
+ * The class represents an order in the legacy system. The class is used by the legacy system to
+ * store the data.
*/
@Data
@AllArgsConstructor
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
index b29b71d87e5c..ec1d613a7235 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.java
@@ -28,10 +28,8 @@
import org.springframework.stereotype.Service;
/**
- * The class represents a data store for the legacy system.
- * The class is used by the legacy system to store the data.
+ * The class represents a data store for the legacy system. The class is used by the legacy system
+ * to store the data.
*/
@Service
-public class LegacyStore extends DataStore {
-}
-
+public class LegacyStore extends DataStore {}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
index dd335141579d..130f36d39674 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.java
@@ -27,9 +27,7 @@
import lombok.AllArgsConstructor;
import lombok.Data;
-/**
- * The class represents a customer in the modern system.
- */
+/** The class represents a customer in the modern system. */
@Data
@AllArgsConstructor
public class Customer {
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
index 94c4f57b9a9c..7b62985015d6 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
@@ -27,9 +27,7 @@
import lombok.AllArgsConstructor;
import lombok.Data;
-/**
- * The class represents an order in the modern system.
- */
+/** The class represents an order in the modern system. */
@Data
@AllArgsConstructor
public class ModernOrder {
@@ -39,6 +37,4 @@ public class ModernOrder {
private Shipment shipment;
private String extra;
-
-
}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
index 45cd355bea87..24080abe1533 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
@@ -31,20 +31,18 @@
import org.springframework.stereotype.Service;
/**
- * The class represents a modern shop system.
- * The main purpose of the class is to place orders and find orders.
+ * The class represents a modern shop system. The main purpose of the class is to place orders and
+ * find orders.
*/
@Service
public class ModernShop {
- @Autowired
- private ModernStore store;
+ @Autowired private ModernStore store;
- @Autowired
- private AntiCorruptionLayer acl;
+ @Autowired private AntiCorruptionLayer acl;
/**
- * Places the order in the modern system.
- * If the order is already present in the legacy system, then no need to place it again.
+ * Places the order in the modern system. If the order is already present in the legacy system,
+ * then no need to place it again.
*/
public void placeOrder(ModernOrder order) throws ShopException {
@@ -62,9 +60,7 @@ public void placeOrder(ModernOrder order) throws ShopException {
}
}
- /**
- * Finds the order in the modern system.
- */
+ /** Finds the order in the modern system. */
public Optional findOrder(String orderId) {
return store.get(orderId);
}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
index 4ec4d7dbcfc0..4fb3952fae5e 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
@@ -27,10 +27,6 @@
import com.iluwatar.corruption.system.DataStore;
import org.springframework.stereotype.Service;
-/**
- * The class represents a data store for the modern system.
- */
+/** The class represents a data store for the modern system. */
@Service
-public class ModernStore extends DataStore {
-}
-
+public class ModernStore extends DataStore {}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
index 292c0d8e3771..085a3921ceeb 100644
--- a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
@@ -28,8 +28,8 @@
import lombok.Data;
/**
- * The class represents a shipment in the modern system.
- * The class is used by the modern system to store the data.
+ * The class represents a shipment in the modern system. The class is used by the modern system to
+ * store the data.
*/
@Data
@AllArgsConstructor
diff --git a/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
index ba24c89811db..ee46d124eee6 100644
--- a/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
+++ b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
@@ -24,88 +24,73 @@
*/
package com.iluwatar.corruption.system;
+import static org.junit.jupiter.api.Assertions.*;
+
import com.iluwatar.corruption.system.legacy.LegacyOrder;
import com.iluwatar.corruption.system.legacy.LegacyShop;
import com.iluwatar.corruption.system.modern.Customer;
import com.iluwatar.corruption.system.modern.ModernOrder;
import com.iluwatar.corruption.system.modern.ModernShop;
import com.iluwatar.corruption.system.modern.Shipment;
-import org.junit.Test;
-import org.junit.runner.RunWith;
+import java.util.Optional;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.test.context.junit4.SpringRunner;
-
-import java.util.Optional;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
-import static org.junit.jupiter.api.Assertions.*;
-
-@RunWith(SpringRunner.class)
+@ExtendWith(SpringExtension.class)
@SpringBootTest
public class AntiCorruptionLayerTest {
- @Autowired
- private LegacyShop legacyShop;
-
- @Autowired
- private ModernShop modernShop;
-
-
- /**
- * Test the anti-corruption layer.
- * Main intention is to demonstrate how the anti-corruption layer works.
- *
- * The 2 shops (modern and legacy) should operate independently and in the same time synchronize the data.
- * To avoid corrupting the domain models of the 2 shops, we use an anti-corruption layer
- * that transforms one model to another under the hood.
- *
- */
- @Test
- public void antiCorruptionLayerTest() throws ShopException {
-
- // a new order comes to the legacy shop.
- LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
- // place the order in the legacy shop.
- legacyShop.placeOrder(legacyOrder);
- // the order is placed as usual since there is no other orders with the id in the both systems.
- Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
- assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
-
- // a new order (or maybe just the same order) appears in the modern shop.
- ModernOrder modernOrder = new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), "");
-
- // the system places it, but it checks if there is an order with the same id in the legacy shop.
- modernShop.placeOrder(modernOrder);
-
- Optional modernOrderWithIdOne = modernShop.findOrder("1");
- // there is no new order placed since there is already an order with the same id in the legacy shop.
- assertTrue(modernOrderWithIdOne.isEmpty());
-
- }
- /**
- * Test the anti-corruption layer.
- * Main intention is to demonstrate how the anti-corruption layer works.
- *
- * This test tests the anti-corruption layer from the rule the orders should be the same in the both systems.
- *
- */
- @Test(expected = ShopException.class)
- public void antiCorruptionLayerWithExTest() throws ShopException {
-
- // a new order comes to the legacy shop.
- LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
- // place the order in the legacy shop.
- legacyShop.placeOrder(legacyOrder);
- // the order is placed as usual since there is no other orders with the id in the both systems.
- Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
- assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
-
- // a new order but with the same id and different data appears in the modern shop
- ModernOrder modernOrder = new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), "");
-
- // the system rejects the order since there are 2 orders with contradiction there.
- modernShop.placeOrder(modernOrder);
-
-
- }
-}
\ No newline at end of file
+ @Autowired private LegacyShop legacyShop;
+
+ @Autowired private ModernShop modernShop;
+
+ /**
+ * Test the anti-corruption layer. Main intention is to demonstrate how the anti-corruption layer
+ * works. The 2 shops (modern and legacy) should operate independently and in the same time
+ * synchronize the data.
+ */
+ @Test
+ public void antiCorruptionLayerTest() throws ShopException {
+ // a new order comes to the legacy shop.
+ LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
+ // place the order in the legacy shop.
+ legacyShop.placeOrder(legacyOrder);
+ // the order is placed as usual since there is no other orders with the id in the both systems.
+ Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
+ assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
+
+ // a new order (or maybe just the same order) appears in the modern shop
+ ModernOrder modernOrder =
+ new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 1, 1), "");
+ // the system places it, but it checks if there is an order with the same id in the legacy shop.
+ modernShop.placeOrder(modernOrder);
+
+ Optional modernOrderWithIdOne = modernShop.findOrder("1");
+ // there is no new order placed since there is already an order with the same id in the legacy
+ // shop.
+ assertTrue(modernOrderWithIdOne.isEmpty());
+ }
+
+ /**
+ * Test the anti-corruption layer when a conflict occurs between systems. This test ensures that
+ * an exception is thrown when conflicting orders are placed.
+ */
+ @Test
+ public void antiCorruptionLayerWithExTest() throws ShopException {
+ // a new order comes to the legacy shop.
+ LegacyOrder legacyOrder = new LegacyOrder("1", "addr1", "item1", 1, 1);
+ // place the order in the legacy shop.
+ legacyShop.placeOrder(legacyOrder);
+ // the order is placed as usual since there is no other orders with the id in the both systems.
+ Optional legacyOrderWithIdOne = legacyShop.findOrder("1");
+ assertEquals(Optional.of(legacyOrder), legacyOrderWithIdOne);
+ // a new order but with the same id and different data appears in the modern shop
+ ModernOrder modernOrder =
+ new ModernOrder("1", new Customer("addr1"), new Shipment("item1", 10, 1), "");
+ // the system rejects the order since there are 2 orders with contradiction there.
+ assertThrows(ShopException.class, () -> modernShop.placeOrder(modernOrder));
+ }
+}
diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md
index a4b7ec8deb92..584d8601a23e 100644
--- a/arrange-act-assert/README.md
+++ b/arrange-act-assert/README.md
@@ -38,6 +38,10 @@ WikiWikiWeb says
> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
+Flowchart
+
+
+
## Programmatic Example of Arrange/Act/Assert Pattern in Java
We need to write comprehensive and clear unit test suite for a class. Using the Arrange/Act/Assert pattern in Java testing ensures clarity.
diff --git a/arrange-act-assert/etc/arrange-act-assert-flowchart.png b/arrange-act-assert/etc/arrange-act-assert-flowchart.png
new file mode 100644
index 000000000000..8b7615352523
Binary files /dev/null and b/arrange-act-assert/etc/arrange-act-assert-flowchart.png differ
diff --git a/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
index c3f5e6fe43e1..0c31b1f89f44 100644
--- a/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
+++ b/arrange-act-assert/src/main/java/com/iluwatar/arrangeactassert/Cash.java
@@ -35,12 +35,12 @@ public class Cash {
private int amount;
- //plus
+ // plus
void plus(int addend) {
amount += addend;
}
- //minus
+ // minus
boolean minus(int subtrahend) {
if (amount >= subtrahend) {
amount -= subtrahend;
@@ -50,7 +50,7 @@ boolean minus(int subtrahend) {
}
}
- //count
+ // count
int count() {
return amount;
}
diff --git a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
index b771cb6e7dee..ebb261277080 100644
--- a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAAATest.java
@@ -35,8 +35,11 @@
* tests, so they're easier to read, maintain and enhance.
*
* It breaks tests down into three clear and distinct steps:
+ *
*
1. Arrange: Perform the setup and initialization required for the test.
+ *
*
2. Act: Take action(s) required for the test.
+ *
*
3. Assert: Verify the outcome(s) of the test.
*
*
This pattern has several significant benefits. It creates a clear separation between a test's
@@ -48,53 +51,52 @@
* clearly about the three steps your test will perform. But it makes tests more natural to write at
* the same time since you already have an outline.
*
- *
In ({@link CashAAATest}) we have four test methods. Each of them has only one reason to
- * change and one reason to fail. In a large and complicated code base, tests that honor the single
+ *
In ({@link CashAAATest}) we have four test methods. Each of them has only one reason to change
+ * and one reason to fail. In a large and complicated code base, tests that honor the single
* responsibility principle are much easier to troubleshoot.
*/
-
class CashAAATest {
@Test
void testPlus() {
- //Arrange
+ // Arrange
var cash = new Cash(3);
- //Act
+ // Act
cash.plus(4);
- //Assert
+ // Assert
assertEquals(7, cash.count());
}
@Test
void testMinus() {
- //Arrange
+ // Arrange
var cash = new Cash(8);
- //Act
+ // Act
var result = cash.minus(5);
- //Assert
+ // Assert
assertTrue(result);
assertEquals(3, cash.count());
}
@Test
void testInsufficientMinus() {
- //Arrange
+ // Arrange
var cash = new Cash(1);
- //Act
+ // Act
var result = cash.minus(6);
- //Assert
+ // Assert
assertFalse(result);
assertEquals(1, cash.count());
}
@Test
void testUpdate() {
- //Arrange
+ // Arrange
var cash = new Cash(5);
- //Act
+ // Act
cash.plus(6);
var result = cash.minus(3);
- //Assert
+ // Assert
assertTrue(result);
assertEquals(8, cash.count());
}
diff --git a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
index 142fd623abb8..5756822516b8 100644
--- a/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
+++ b/arrange-act-assert/src/test/java/com/iluwatar/arrangeactassert/CashAntiAAATest.java
@@ -37,23 +37,22 @@
* single responsibility principle. If this test method failed after a small code change, it might
* take some digging to discover why.
*/
-
class CashAntiAAATest {
@Test
void testCash() {
- //initialize
+ // initialize
var cash = new Cash(3);
- //test plus
+ // test plus
cash.plus(4);
assertEquals(7, cash.count());
- //test minus
+ // test minus
cash = new Cash(8);
assertTrue(cash.minus(5));
assertEquals(3, cash.count());
assertFalse(cash.minus(6));
assertEquals(3, cash.count());
- //test update
+ // test update
cash.plus(5);
assertTrue(cash.minus(5));
assertEquals(3, cash.count());
diff --git a/async-method-invocation/README.md b/async-method-invocation/README.md
index 9b0e4e43e13c..519152e1268c 100644
--- a/async-method-invocation/README.md
+++ b/async-method-invocation/README.md
@@ -36,6 +36,10 @@ Wikipedia says
> In multithreaded computer programming, asynchronous method invocation (AMI), also known as asynchronous method calls or the asynchronous pattern is a design pattern in which the call site is not blocked while waiting for the called code to finish. Instead, the calling thread is notified when the reply arrives. Polling for a reply is an undesired option.
+Sequence diagram
+
+
+
## Programmatic Example of Async Method Invocation Pattern in Java
Consider a scenario where multiple tasks need to be executed simultaneously. Using the Async Method Invocation pattern, you can initiate these tasks without waiting for each to complete, thus optimizing resource usage and reducing latency.
diff --git a/async-method-invocation/etc/async-method-invocation-sequence-diagram.png b/async-method-invocation/etc/async-method-invocation-sequence-diagram.png
new file mode 100644
index 000000000000..28420d783379
Binary files /dev/null and b/async-method-invocation/etc/async-method-invocation-sequence-diagram.png differ
diff --git a/async-method-invocation/pom.xml b/async-method-invocation/pom.xml
index 52a03369f47d..d9ddd918c8e9 100644
--- a/async-method-invocation/pom.xml
+++ b/async-method-invocation/pom.xml
@@ -34,6 +34,14 @@
async-method-invocation
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
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 01fd8f1c681a..ec3beed3be4d 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
@@ -30,15 +30,15 @@
/**
* In this example, we are launching space rockets and deploying lunar rovers.
*
- * The application demonstrates the async method invocation pattern. The key parts of the
- * pattern are AsyncResult which is an intermediate container for an asynchronously
- * evaluated value, AsyncCallback which can be provided to be executed on task
- * completion and AsyncExecutor that manages the execution of the async tasks.
+ *
The application demonstrates the async method invocation pattern. The key parts of the pattern
+ * are AsyncResult which is an intermediate container for an asynchronously evaluated
+ * value, AsyncCallback which can be provided to be executed on task completion and
+ * AsyncExecutor that manages the execution of the async tasks.
*
- *
The main method shows example flow of async invocations. The main thread starts multiple
- * tasks with variable durations and then continues its own work. When the main thread has done it's
- * job it collects the results of the async tasks. Two of the tasks are handled with callbacks,
- * meaning the callbacks are executed immediately when the tasks complete.
+ *
The main method shows example flow of async invocations. The main thread starts multiple tasks
+ * with variable durations and then continues its own work. When the main thread has done it's job
+ * it collects the results of the async tasks. Two of the tasks are handled with callbacks, meaning
+ * the callbacks are executed immediately when the tasks complete.
*
*
Noteworthy difference of thread usage between the async results and callbacks is that the
* async results are collected in the main thread but the callbacks are executed within the worker
@@ -62,10 +62,7 @@ public class App {
private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
- /**
- * Program entry point.
- */
-
+ /** Program entry point. */
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor();
@@ -74,8 +71,8 @@ public static void main(String[] args) throws Exception {
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
- final var asyncResult4 = executor.startProcess(lazyval(20, 400),
- callback("Deploying lunar rover"));
+ final var asyncResult4 =
+ executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
final var asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
@@ -99,7 +96,7 @@ public static void main(String[] args) throws Exception {
/**
* Creates a callable that lazily evaluates to given value with artificial delay.
*
- * @param value value to evaluate
+ * @param value value to evaluate
* @param delayMillis artificial delay in milliseconds
* @return new callable for lazy evaluation
*/
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
index fcea6d07190d..3bae90830098 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncExecutor.java
@@ -27,9 +27,7 @@
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
-/**
- * AsyncExecutor interface.
- */
+/** AsyncExecutor interface. */
public interface AsyncExecutor {
/**
@@ -44,7 +42,7 @@ public interface AsyncExecutor {
* Starts processing of an async task. Returns immediately with async result. Executes callback
* when the task is completed.
*
- * @param task task to be executed asynchronously
+ * @param task task to be executed asynchronously
* @param callback callback to be executed on task completion
* @return async result for the task
*/
@@ -56,7 +54,7 @@ public interface AsyncExecutor {
*
* @param asyncResult async result of a task
* @return evaluated value of the completed task
- * @throws ExecutionException if execution has failed, containing the root cause
+ * @throws ExecutionException if execution has failed, containing the root cause
* @throws InterruptedException if the execution is interrupted
*/
T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException;
diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
index d71cf0defcd1..3eebdc4e773d 100644
--- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
+++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/AsyncResult.java
@@ -44,7 +44,7 @@ public interface AsyncResult {
* Gets the value of completed async task.
*
* @return evaluated value or throws ExecutionException if execution has failed
- * @throws ExecutionException if execution has failed, containing the root cause
+ * @throws ExecutionException if execution has failed, containing the root cause
* @throws IllegalStateException if execution is not completed
*/
T getValue() throws ExecutionException;
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 f4a50e0d6c30..a1261f34184c 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
@@ -24,19 +24,14 @@
*/
package com.iluwatar.async.method.invocation;
-import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
-/**
- * Implementation of async executor that creates a new thread for every task.
- */
+/** Implementation of async executor that creates a new thread for every task. */
public class ThreadAsyncExecutor implements AsyncExecutor {
- /**
- * Index for thread naming.
- */
+ /** Index for thread naming. */
private final AtomicInteger idx = new AtomicInteger(0);
@Override
@@ -47,19 +42,22 @@ public AsyncResult startProcess(Callable task) {
@Override
public AsyncResult startProcess(Callable task, AsyncCallback callback) {
var result = new CompletableResult<>(callback);
- new Thread(() -> {
- try {
- result.setValue(task.call());
- } catch (Exception ex) {
- result.setException(ex);
- }
- }, "executor-" + idx.incrementAndGet()).start();
+ new Thread(
+ () -> {
+ try {
+ result.setValue(task.call());
+ } catch (Exception ex) {
+ result.setException(ex);
+ }
+ },
+ "executor-" + idx.incrementAndGet())
+ .start();
return result;
}
@Override
- public T endProcess(AsyncResult asyncResult) throws ExecutionException,
- InterruptedException {
+ public T endProcess(AsyncResult asyncResult)
+ throws ExecutionException, InterruptedException {
if (!asyncResult.isCompleted()) {
asyncResult.await();
}
diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
index f31c5549bdd1..d58a3f6b5c30 100644
--- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
+++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/AppTest.java
@@ -24,26 +24,22 @@
*/
package com.iluwatar.async.method.invocation;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
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.
+ * 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/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 c9f85f48d9c9..d6540ce77309 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
@@ -33,7 +33,6 @@
import static org.mockito.Mockito.when;
import static org.mockito.internal.verification.VerificationModeFactory.times;
-import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import org.junit.jupiter.api.BeforeEach;
@@ -43,49 +42,43 @@
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
-/**
- * ThreadAsyncExecutorTest
- *
- */
+/** ThreadAsyncExecutorTest */
class ThreadAsyncExecutorTest {
- @Captor
- private ArgumentCaptor exceptionCaptor;
+ @Captor private ArgumentCaptor exceptionCaptor;
- @Mock
- private Callable task;
+ @Mock private Callable task;
- @Mock
- private AsyncCallback callback;
+ @Mock private AsyncCallback callback;
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
}
- /**
- * Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)}
- */
+ /** Test used to verify the happy path of {@link ThreadAsyncExecutor#startProcess(Callable)} */
@Test
void testSuccessfulTaskWithoutCallback() {
- assertTimeout(ofMillis(3000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
-
- final var result = new Object();
- when(task.call()).thenReturn(result);
-
- final var asyncResult = executor.startProcess(task);
- assertNotNull(asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- // Our task should only execute once ...
- verify(task, times(1)).call();
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
- });
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call()).thenReturn(result);
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ // Our task should only execute once ...
+ verify(task, times(1)).call();
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
@@ -94,28 +87,30 @@ void testSuccessfulTaskWithoutCallback() {
*/
@Test
void testSuccessfulTaskWithCallback() {
- assertTimeout(ofMillis(3000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
-
- final var result = new Object();
- when(task.call()).thenReturn(result);
-
- final var asyncResult = executor.startProcess(task, callback);
- assertNotNull(asyncResult);
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- // Our task should only execute once ...
- verify(task, times(1)).call();
-
- // ... same for the callback, we expect our object
- verify(callback, times(1)).onComplete(eq(result));
- verify(callback, times(0)).onError(exceptionCaptor.capture());
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
- });
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call()).thenReturn(result);
+
+ final var asyncResult = executor.startProcess(task, callback);
+ assertNotNull(asyncResult);
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ // Our task should only execute once ...
+ verify(task, times(1)).call();
+
+ // ... same for the callback, we expect our object
+ verify(callback, times(1)).onComplete(eq(result));
+ verify(callback, times(0)).onError(exceptionCaptor.capture());
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
@@ -124,38 +119,43 @@ void testSuccessfulTaskWithCallback() {
*/
@Test
void testLongRunningTaskWithoutCallback() {
- assertTimeout(ofMillis(5000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
-
- final var result = new Object();
- when(task.call()).thenAnswer(i -> {
- Thread.sleep(1500);
- return result;
- });
-
- final var asyncResult = executor.startProcess(task);
- assertNotNull(asyncResult);
- assertFalse(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
- } catch (IllegalStateException e) {
- assertNotNull(e.getMessage());
- }
-
- // Our task should only execute once, but it can take a while ...
- verify(task, timeout(3000).times(1)).call();
-
- // Prevent timing issues, and wait until the result is available
- asyncResult.await();
- assertTrue(asyncResult.isCompleted());
- verifyNoMoreInteractions(task);
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
- });
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Our task should only execute once, but it can take a while ...
+ verify(task, timeout(3000).times(1)).call();
+
+ // Prevent timing issues, and wait until the result is available
+ asyncResult.await();
+ assertTrue(asyncResult.isCompleted());
+ verifyNoMoreInteractions(task);
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
@@ -164,42 +164,47 @@ void testLongRunningTaskWithoutCallback() {
*/
@Test
void testLongRunningTaskWithCallback() {
- assertTimeout(ofMillis(5000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
-
- final var result = new Object();
- when(task.call()).thenAnswer(i -> {
- Thread.sleep(1500);
- return result;
- });
-
- final var asyncResult = executor.startProcess(task, callback);
- assertNotNull(asyncResult);
- assertFalse(asyncResult.isCompleted());
-
- verifyNoMoreInteractions(callback);
-
- try {
- asyncResult.getValue();
- fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
- } catch (IllegalStateException e) {
- assertNotNull(e.getMessage());
- }
-
- // Our task should only execute once, but it can take a while ...
- verify(task, timeout(3000).times(1)).call();
- verify(callback, timeout(3000).times(1)).onComplete(eq(result));
- verify(callback, times(0)).onError(isA(Exception.class));
-
- // Prevent timing issues, and wait until the result is available
- asyncResult.await();
- assertTrue(asyncResult.isCompleted());
- verifyNoMoreInteractions(task, callback);
-
- // ... and the result should be exactly the same object
- assertSame(result, asyncResult.getValue());
- });
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task, callback);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ verifyNoMoreInteractions(callback);
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ // Our task should only execute once, but it can take a while ...
+ verify(task, timeout(3000).times(1)).call();
+ verify(callback, timeout(3000).times(1)).onComplete(eq(result));
+ verify(callback, times(0)).onError(isA(Exception.class));
+
+ // Prevent timing issues, and wait until the result is available
+ asyncResult.await();
+ assertTrue(asyncResult.isCompleted());
+ verifyNoMoreInteractions(task, callback);
+
+ // ... and the result should be exactly the same object
+ assertSame(result, asyncResult.getValue());
+ });
}
/**
@@ -209,35 +214,40 @@ void testLongRunningTaskWithCallback() {
*/
@Test
void testEndProcess() {
- assertTimeout(ofMillis(5000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
-
- final var result = new Object();
- when(task.call()).thenAnswer(i -> {
- Thread.sleep(1500);
- return result;
- });
-
- final var asyncResult = executor.startProcess(task);
- assertNotNull(asyncResult);
- assertFalse(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
- } catch (IllegalStateException e) {
- assertNotNull(e.getMessage());
- }
-
- assertSame(result, executor.endProcess(asyncResult));
- verify(task, times(1)).call();
- assertTrue(asyncResult.isCompleted());
-
- // Calling end process a second time while already finished should give the same result
- assertSame(result, executor.endProcess(asyncResult));
- verifyNoMoreInteractions(task);
- });
+ assertTimeout(
+ ofMillis(5000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+
+ final var result = new Object();
+ when(task.call())
+ .thenAnswer(
+ i -> {
+ Thread.sleep(1500);
+ return result;
+ });
+
+ final var asyncResult = executor.startProcess(task);
+ assertNotNull(asyncResult);
+ assertFalse(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail(
+ "Expected IllegalStateException when calling AsyncResult#getValue on a non-completed task");
+ } catch (IllegalStateException e) {
+ assertNotNull(e.getMessage());
+ }
+
+ assertSame(result, executor.endProcess(asyncResult));
+ verify(task, times(1)).call();
+ assertTrue(asyncResult.isCompleted());
+
+ // Calling end process a second time while already finished should give the same result
+ assertSame(result, executor.endProcess(asyncResult));
+ verifyNoMoreInteractions(task);
+ });
}
/**
@@ -246,25 +256,28 @@ void testEndProcess() {
*/
@Test
void testNullTask() {
- assertTimeout(ofMillis(3000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
- final var asyncResult = executor.startProcess(null);
-
- assertNotNull(asyncResult, "The AsyncResult should not be 'null', even though the task was 'null'.");
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected ExecutionException with NPE as cause");
- } catch (final ExecutionException e) {
- assertNotNull(e.getMessage());
- assertNotNull(e.getCause());
- assertEquals(NullPointerException.class, e.getCause().getClass());
- }
- });
-
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task was 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
}
/**
@@ -273,32 +286,35 @@ void testNullTask() {
*/
@Test
void testNullTaskWithCallback() {
- assertTimeout(ofMillis(3000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
- final var asyncResult = executor.startProcess(null, callback);
-
- assertNotNull(asyncResult, "The AsyncResult should not be 'null', even though the task was 'null'.");
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
- verify(callback, times(0)).onComplete(any());
- verify(callback, times(1)).onError(exceptionCaptor.capture());
-
- final var exception = exceptionCaptor.getValue();
- assertNotNull(exception);
-
- assertEquals(NullPointerException.class, exception.getClass());
-
- try {
- asyncResult.getValue();
- fail("Expected ExecutionException with NPE as cause");
- } catch (final ExecutionException e) {
- assertNotNull(e.getMessage());
- assertNotNull(e.getCause());
- assertEquals(NullPointerException.class, e.getCause().getClass());
- }
- });
-
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null, callback);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task was 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+ verify(callback, times(0)).onComplete(any());
+ verify(callback, times(1)).onError(exceptionCaptor.capture());
+
+ final var exception = exceptionCaptor.getValue();
+ assertNotNull(exception);
+
+ assertEquals(NullPointerException.class, exception.getClass());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
}
/**
@@ -307,28 +323,27 @@ void testNullTaskWithCallback() {
*/
@Test
void testNullTaskWithNullCallback() {
- assertTimeout(ofMillis(3000), () -> {
- // Instantiate a new executor and start a new 'null' task ...
- final var executor = new ThreadAsyncExecutor();
- final var asyncResult = executor.startProcess(null, null);
-
- assertNotNull(
- asyncResult,
- "The AsyncResult should not be 'null', even though the task and callback were 'null'."
- );
- asyncResult.await(); // Prevent timing issues, and wait until the result is available
- assertTrue(asyncResult.isCompleted());
-
- try {
- asyncResult.getValue();
- fail("Expected ExecutionException with NPE as cause");
- } catch (final ExecutionException e) {
- assertNotNull(e.getMessage());
- assertNotNull(e.getCause());
- assertEquals(NullPointerException.class, e.getCause().getClass());
- }
- });
-
+ assertTimeout(
+ ofMillis(3000),
+ () -> {
+ // Instantiate a new executor and start a new 'null' task ...
+ final var executor = new ThreadAsyncExecutor();
+ final var asyncResult = executor.startProcess(null, null);
+
+ assertNotNull(
+ asyncResult,
+ "The AsyncResult should not be 'null', even though the task and callback were 'null'.");
+ asyncResult.await(); // Prevent timing issues, and wait until the result is available
+ assertTrue(asyncResult.isCompleted());
+
+ try {
+ asyncResult.getValue();
+ fail("Expected ExecutionException with NPE as cause");
+ } catch (final ExecutionException e) {
+ assertNotNull(e.getMessage());
+ assertNotNull(e.getCause());
+ assertEquals(NullPointerException.class, e.getCause().getClass());
+ }
+ });
}
-
}
diff --git a/backpressure/README.md b/backpressure/README.md
new file mode 100644
index 000000000000..d0734ad1f934
--- /dev/null
+++ b/backpressure/README.md
@@ -0,0 +1,156 @@
+---
+title: "Backpressure Pattern in Java: Gracefully regulate producer-to-consumer data flow to prevent overload."
+shortTitle: Backpressure
+description: "Dive into the Backpressure design pattern in Java through practical examples, discovering how it prevents overload while ensuring stability and peak performance by aligning data flow with consumer capacity."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Event-driven
+ - Reactive
+ - Resilience
+---
+
+## Also known as
+
+* Flow Control
+* Rate Limiting Mechanism
+
+## Intent of the Backpressure Design Pattern
+
+Control the rate of data production so downstream consumers are not overwhelmed by excessive load.
+
+## Detailed Explanation of Backpressure Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a busy coffee shop where multiple baristas brew drinks (producers), and a single barista is responsible for carefully crafting latte art (consumer). If drinks are brewed faster than the latte-art barista can decorate them, they pile up, risking quality issues or discarded drinks. By introducing a pacing system—only sending new cups once the latte-art barista is ready—everyone stays synchronized, ensuring minimal waste and a consistently enjoyable customer experience.
+
+In plain words
+
+> The Backpressure design pattern is a flow control mechanism that prevents overwhelming a system by regulating data production based on the consumer’s processing capacity.
+
+Wikipedia says
+
+> Back pressure (or backpressure) is the term for a resistance to the desired flow of fluid through pipes. Obstructions or tight bends create backpressure via friction loss and pressure drop. In distributed systems in particular event-driven architecture, back pressure is a technique to regulate flow of data, ensuring that components do not become overwhelmed.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Backpressure Pattern in Java
+
+This example demonstrates how backpressure can be implemented using Project Reactor. We begin by creating a simple publisher that emits a stream of integers, introducing a small delay to mimic a slower production rate:
+
+```java
+public class Publisher {
+ public static Flux publish(int start, int count, int delay) {
+ return Flux.range(start, count).delayElements(Duration.ofMillis(delay)).log();
+ }
+}
+```
+
+Next, we define a custom subscriber by extending Reactor’s BaseSubscriber. It simulates slow processing by sleeping for 500ms per item. Initially, the subscriber requests ten items; for every five items processed, it requests five more:
+
+```java
+public class Subscriber extends BaseSubscriber {
+
+ private static final Logger logger = LoggerFactory.getLogger(Subscriber.class);
+
+ @Override
+ protected void hookOnSubscribe(@NonNull Subscription subscription) {
+ logger.info("subscribe()");
+ request(10); //request 10 items initially
+ }
+
+ @Override
+ protected void hookOnNext(@NonNull Integer value) {
+ processItem();
+ logger.info("process({})", value);
+ if (value % 5 == 0) {
+ // request for the next 5 items after processing first 5
+ request(5);
+ }
+ }
+
+ @Override
+ protected void hookOnComplete() {
+ //completed processing.
+ }
+
+ private void processItem() {
+ try {
+ Thread.sleep(500); // simulate slow processing
+ } catch (InterruptedException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+}
+```
+
+Finally, in the `main` method, we publish a range of integers and subscribe using the custom subscriber. A short sleep in the main thread allows the emission, backpressure requests, and processing to be fully observed:
+
+```java
+public static void main(String[] args) throws InterruptedException {
+ Subscriber sub = new Subscriber();
+ Publisher.publish(1, 8, 200).subscribe(sub);
+ Thread.sleep(5000); //wait for execution
+
+}
+```
+
+Below is an example of the program’s output. It shows the subscriber’s log entries, including when it requests more data and when each integer is processed:
+
+```
+23:09:55.746 [main] DEBUG reactor.util.Loggers -- Using Slf4j logging framework
+23:09:55.762 [main] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onSubscribe(FluxConcatMapNoPrefetch.FluxConcatMapNoPrefetchSubscriber)
+23:09:55.762 [main] INFO com.iluwatar.backpressure.Subscriber -- subscribe()
+23:09:55.763 [main] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- request(10)
+23:09:55.969 [parallel-1] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(1)
+23:09:56.475 [parallel-1] INFO com.iluwatar.backpressure.Subscriber -- process(1)
+23:09:56.680 [parallel-2] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(2)
+23:09:57.185 [parallel-2] INFO com.iluwatar.backpressure.Subscriber -- process(2)
+23:09:57.389 [parallel-3] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(3)
+23:09:57.894 [parallel-3] INFO com.iluwatar.backpressure.Subscriber -- process(3)
+23:09:58.099 [parallel-4] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(4)
+23:09:58.599 [parallel-4] INFO com.iluwatar.backpressure.Subscriber -- process(4)
+23:09:58.805 [parallel-5] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(5)
+23:09:59.311 [parallel-5] INFO com.iluwatar.backpressure.Subscriber -- process(5)
+23:09:59.311 [parallel-5] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- request(5)
+23:09:59.516 [parallel-6] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(6)
+23:10:00.018 [parallel-6] INFO com.iluwatar.backpressure.Subscriber -- process(6)
+23:10:00.223 [parallel-7] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(7)
+23:10:00.729 [parallel-7] INFO com.iluwatar.backpressure.Subscriber -- process(7)
+23:10:00.930 [parallel-8] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onNext(8)
+23:10:01.436 [parallel-8] INFO com.iluwatar.backpressure.Subscriber -- process(8)
+23:10:01.437 [parallel-8] INFO reactor.Flux.ConcatMapNoPrefetch.1 -- onComplete()
+```
+
+## When to Use the Backpressure Pattern In Java
+
+* Use in Java systems where data is produced at high velocity and consumers risk overload.
+* Applicable in reactive or event-driven architectures to maintain stability under varying load conditions.
+
+## Benefits and Trade-offs of Backpressure Pattern
+
+Benefits:
+
+* Protects consumers from saturation and resource exhaustion.
+
+Trade-offs:
+
+* Introduces possible delays if production must slow down to match consumer capacity.
+* Requires careful orchestration in complex systems with multiple concurrent data sources.
+
+## Related Java Design Patterns
+
+* [Observer Pattern](https://java-design-patterns.com/patterns/observer/): Both patterns involve a producer notifying consumers. Observer is synchronous and tightly coupled (observers know the subject).
+* [Publish-Subscribe Pattern](https://java-design-patterns.com/patterns/publish-subscribe/): Both patterns deal with asynchronous data flow and can work together to manage message distribution and consumption effectively.
+
+## References and Credits
+
+* [Backpressure Explained (RedHat Developers Blog)](https://developers.redhat.com/articles/backpressure-explained)
+* [Hands-On Reactive Programming in Spring 5](https://amzn.to/3YuYfyO)
+* [Reactive Programming with RxJava: Creating Asynchronous, Event-Based Applications](https://amzn.to/42negbf)
+* [Reactive Streams in Java](https://amzn.to/3RJjUzA)
+* [Reactive Streams Specification](https://www.reactive-streams.org/)
diff --git a/backpressure/etc/backpressure-sequence-diagram.png b/backpressure/etc/backpressure-sequence-diagram.png
new file mode 100644
index 000000000000..b74a5d2a5362
Binary files /dev/null and b/backpressure/etc/backpressure-sequence-diagram.png differ
diff --git a/backpressure/etc/backpressure.png b/backpressure/etc/backpressure.png
new file mode 100644
index 000000000000..0de9ff54b673
Binary files /dev/null and b/backpressure/etc/backpressure.png differ
diff --git a/backpressure/pom.xml b/backpressure/pom.xml
new file mode 100644
index 000000000000..fcc15892fb8a
--- /dev/null
+++ b/backpressure/pom.xml
@@ -0,0 +1,81 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ backpressure
+
+
+
+ io.projectreactor
+ reactor-core
+ 3.8.0-M1
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ io.projectreactor
+ reactor-test
+ 3.8.0-M1
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.backpressure.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/App.java b/backpressure/src/main/java/com/iluwatar/backpressure/App.java
new file mode 100644
index 000000000000..4632c42a467f
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/App.java
@@ -0,0 +1,76 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * The Backpressure pattern is a flow control mechanism. It allows a consumer to signal to a
+ * producer to slow down or stop sending data when it's overwhelmed.
+ * Prevents memory overflow, CPU thrashing, and resource exhaustion.
+ * Ensures fair usage of resources in distributed systems.
+ * Avoids buffer bloat and latency spikes. Key concepts of this design paradigm involves
+ * Publisher/Producer: Generates data.
+ * Subscriber/Consumer: Receives and processes data.
+ *
+ * In this example we will create a {@link Publisher} and a {@link Subscriber}. Publisher
+ * will emit a stream of integer values with a predefined delay. Subscriber takes 500 ms to
+ * process one integer. Since the subscriber can't process the items fast enough we apply
+ * backpressure to the publisher so that it will request 10 items first, process 5 items and
+ * request for the next 5 again. After processing 5 items subscriber will keep requesting for
+ * another 5 until the stream ends.
+ */
+public class App {
+
+ protected static CountDownLatch latch;
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) throws InterruptedException {
+
+ /*
+ * This custom subscriber applies backpressure:
+ * - Has a processing delay of 0.5 milliseconds
+ * - Requests 10 items initially
+ * - Process 5 items and request for the next 5 items
+ */
+ Subscriber sub = new Subscriber();
+ // slow publisher emit 15 numbers with a delay of 200 milliseconds
+ Publisher.publish(1, 17, 200).subscribe(sub);
+
+ latch = new CountDownLatch(1);
+ latch.await();
+
+ sub = new Subscriber();
+ // fast publisher emit 15 numbers with a delay of 1 millisecond
+ Publisher.publish(1, 17, 1).subscribe(sub);
+
+ latch = new CountDownLatch(1);
+ latch.await();
+ }
+}
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java b/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java
new file mode 100644
index 000000000000..1d39a070ae57
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/Publisher.java
@@ -0,0 +1,46 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import java.time.Duration;
+import reactor.core.publisher.Flux;
+
+/** This class is the publisher that generates the data stream. */
+public class Publisher {
+
+ private Publisher() {}
+
+ /**
+ * On message method will trigger when the subscribed event is published.
+ *
+ * @param start starting integer
+ * @param count how many integers to emit
+ * @param delay delay between each item in milliseconds
+ * @return a flux stream of integers
+ */
+ public static Flux publish(int start, int count, int delay) {
+ return Flux.range(start, count).delayElements(Duration.ofMillis(delay)).log();
+ }
+}
diff --git a/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java b/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java
new file mode 100644
index 000000000000..40ff5aebc814
--- /dev/null
+++ b/backpressure/src/main/java/com/iluwatar/backpressure/Subscriber.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import lombok.NonNull;
+import lombok.extern.slf4j.Slf4j;
+import org.reactivestreams.Subscription;
+import reactor.core.publisher.BaseSubscriber;
+
+/** This class is the custom subscriber that subscribes to the data stream. */
+@Slf4j
+public class Subscriber extends BaseSubscriber {
+
+ @Override
+ protected void hookOnSubscribe(@NonNull Subscription subscription) {
+ request(10); // request 10 items initially
+ }
+
+ @Override
+ protected void hookOnNext(@NonNull Integer value) {
+ processItem();
+ LOGGER.info("process({})", value);
+ if (value % 5 == 0) {
+ // request for the next 5 items after processing first 5
+ request(5);
+ }
+ }
+
+ @Override
+ protected void hookOnComplete() {
+ App.latch.countDown();
+ }
+
+ private void processItem() {
+ try {
+ Thread.sleep(500); // simulate slow processing
+ } catch (InterruptedException e) {
+ LOGGER.error(e.getMessage(), e);
+ Thread.currentThread().interrupt();
+ }
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java
new file mode 100644
index 000000000000..8fe2245a97b7
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/AppTest.java
@@ -0,0 +1,37 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+public class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java b/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java
new file mode 100644
index 000000000000..e99926e00a1a
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/LoggerExtension.java
@@ -0,0 +1,60 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.slf4j.LoggerFactory;
+
+public class LoggerExtension implements BeforeEachCallback, AfterEachCallback {
+
+ private final ListAppender listAppender = new ListAppender<>();
+ private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
+
+ @Override
+ public void afterEach(ExtensionContext extensionContext) {
+ listAppender.stop();
+ listAppender.list.clear();
+ logger.detachAppender(listAppender);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext extensionContext) {
+ logger.addAppender(listAppender);
+ listAppender.start();
+ }
+
+ public List getFormattedMessages() {
+ return listAppender.list.stream()
+ .map(ILoggingEvent::getFormattedMessage)
+ .collect(Collectors.toList());
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java
new file mode 100644
index 000000000000..010a80e83959
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/PublisherTest.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import static com.iluwatar.backpressure.Publisher.publish;
+
+import java.time.Duration;
+import org.junit.jupiter.api.Test;
+import reactor.core.publisher.Flux;
+import reactor.test.StepVerifier;
+
+public class PublisherTest {
+
+ @Test
+ public void testPublish() {
+
+ Flux flux = publish(1, 3, 200);
+
+ StepVerifier.withVirtualTime(() -> flux)
+ .expectSubscription()
+ .expectNoEvent(Duration.ofMillis(200))
+ .expectNext(1)
+ .expectNoEvent(Duration.ofSeconds(200))
+ .expectNext(2)
+ .expectNoEvent(Duration.ofSeconds(200))
+ .expectNext(3)
+ .verifyComplete();
+ }
+}
diff --git a/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java b/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java
new file mode 100644
index 000000000000..7f7676612d02
--- /dev/null
+++ b/backpressure/src/test/java/com/iluwatar/backpressure/SubscriberTest.java
@@ -0,0 +1,54 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.backpressure;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.concurrent.CountDownLatch;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+public class SubscriberTest {
+
+ @RegisterExtension public LoggerExtension loggerExtension = new LoggerExtension();
+
+ @Test
+ public void testSubscribe() throws InterruptedException {
+
+ Subscriber sub = new Subscriber();
+ Publisher.publish(1, 8, 100).subscribe(sub);
+
+ App.latch = new CountDownLatch(1);
+ App.latch.await();
+
+ String result = String.join(",", loggerExtension.getFormattedMessages());
+ assertTrue(
+ result.endsWith(
+ "onSubscribe(FluxConcatMapNoPrefetch."
+ + "FluxConcatMapNoPrefetchSubscriber),request(10),onNext(1),process(1),onNext(2),"
+ + "process(2),onNext(3),process(3),onNext(4),process(4),onNext(5),process(5),request(5),"
+ + "onNext(6),process(6),onNext(7),process(7),onNext(8),process(8),onComplete()"));
+ }
+}
diff --git a/balking/README.md b/balking/README.md
index f659d278b349..cdd09cf5b7d1 100644
--- a/balking/README.md
+++ b/balking/README.md
@@ -29,6 +29,10 @@ Wikipedia says
> The balking pattern is a software design pattern that only executes an action on an object when the object is in a particular state. For example, if an object reads ZIP files and a calling method invokes a get method on the object when the ZIP file is not open, the object would "balk" at the request.
+Flowchart
+
+
+
## Programmatic Example of Balking Pattern in Java
This example demonstrates the Balking Pattern in a multithreaded Java application, highlighting state management and concurrency control. The Balking Pattern is exemplified by a washing machine's start button that initiates washing only if the machine is idle. This ensures state management and prevents concurrent issues.
diff --git a/balking/etc/balking-flowchart.png b/balking/etc/balking-flowchart.png
new file mode 100644
index 000000000000..400a1ce8d3e3
Binary files /dev/null and b/balking/etc/balking-flowchart.png differ
diff --git a/balking/pom.xml b/balking/pom.xml
index b9902e671351..818f7549c330 100644
--- a/balking/pom.xml
+++ b/balking/pom.xml
@@ -34,6 +34,14 @@
4.0.0
balking
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
index f142c8c7bf72..f27922219ea6 100644
--- a/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
+++ b/balking/src/main/java/com/iluwatar/balking/DelayProvider.java
@@ -26,9 +26,7 @@
import java.util.concurrent.TimeUnit;
-/**
- * An interface to simulate delay while executing some work.
- */
+/** An interface to simulate delay while executing some work. */
public interface DelayProvider {
void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
}
diff --git a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
index 794cfbd8ae15..52ce7c593b6b 100644
--- a/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
+++ b/balking/src/main/java/com/iluwatar/balking/WashingMachine.java
@@ -28,30 +28,26 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-/**
- * Washing machine class.
- */
+/** Washing machine class. */
@Slf4j
public class WashingMachine {
private final DelayProvider delayProvider;
- @Getter
- private WashingMachineState washingMachineState;
+ @Getter private WashingMachineState washingMachineState;
- /**
- * Creates a new instance of WashingMachine.
- */
+ /** Creates a new instance of WashingMachine. */
public WashingMachine() {
- this((interval, timeUnit, task) -> {
- try {
- Thread.sleep(timeUnit.toMillis(interval));
- } catch (InterruptedException ie) {
- LOGGER.error("", ie);
- Thread.currentThread().interrupt();
- }
- task.run();
- });
+ this(
+ (interval, timeUnit, task) -> {
+ try {
+ Thread.sleep(timeUnit.toMillis(interval));
+ } catch (InterruptedException ie) {
+ LOGGER.error("", ie);
+ Thread.currentThread().interrupt();
+ }
+ task.run();
+ });
}
/**
@@ -63,9 +59,7 @@ public WashingMachine(DelayProvider delayProvider) {
this.washingMachineState = WashingMachineState.ENABLED;
}
- /**
- * Method responsible for washing if the object is in appropriate state.
- */
+ /** Method responsible for washing if the object is in appropriate state. */
public void wash() {
synchronized (this) {
var machineState = getWashingMachineState();
@@ -81,12 +75,9 @@ public void wash() {
this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
}
- /**
- * Method is responsible for ending the washing by changing machine state.
- */
+ /** Method is responsible for ending the washing by changing machine state. */
public synchronized void endOfWashing() {
washingMachineState = WashingMachineState.ENABLED;
LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
}
-
}
diff --git a/balking/src/test/java/com/iluwatar/balking/AppTest.java b/balking/src/test/java/com/iluwatar/balking/AppTest.java
index b744b6786613..40beabf553d0 100644
--- a/balking/src/test/java/com/iluwatar/balking/AppTest.java
+++ b/balking/src/test/java/com/iluwatar/balking/AppTest.java
@@ -24,27 +24,22 @@
*/
package com.iluwatar.balking;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.function.Executable;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
-/**
- * Application test
- */
+/** Application test */
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.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App} throws an exception.
*/
-
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow((Executable) App::main);
}
-
-}
\ No newline at end of file
+}
diff --git a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
index f5c1e2de60f3..9bf7ac2548a0 100644
--- a/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
+++ b/balking/src/test/java/com/iluwatar/balking/WashingMachineTest.java
@@ -29,9 +29,7 @@
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
-/**
- * Tests for {@link WashingMachine}
- */
+/** Tests for {@link WashingMachine} */
class WashingMachineTest {
private final FakeDelayProvider fakeDelayProvider = new FakeDelayProvider();
@@ -69,4 +67,4 @@ public void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task) {
this.task = task;
}
}
-}
\ No newline at end of file
+}
diff --git a/bloc/README.md b/bloc/README.md
new file mode 100644
index 000000000000..76a4a0917485
--- /dev/null
+++ b/bloc/README.md
@@ -0,0 +1,230 @@
+---
+title: "Bloc Pattern in Java: State Management Simplified"
+shortTitle: Bloc
+description: "Learn how the Bloc pattern helps manage state changes in Java applications. This guide covers dynamic listener management, real-world examples, and clean code practices for state management."
+category: Architectural
+language: en
+tag:
+ - Abstraction
+ - Data binding
+ - Decoupling
+ - Event-driven
+ - Presentation
+ - Reactive
+ - Reusability
+ - State tracking
+---
+
+## Also known as
+
+* Business Logic Component
+* Business Logic Controller
+
+## Intent of the Bloc Pattern
+
+The Bloc pattern manages the state of an object and allows for dynamically notifying interested listeners about state changes. It separates state management logic from the rest of the application, improving code organization and flexibility.
+
+## Detailed explanation of the Bloc pattern with real-World examples
+
+Real-world example
+
+> Consider a digital counter application where multiple parts of the UI need to be updated whenever the counter changes. For example, a label displaying the counter value and an activity log showing changes. Instead of directly modifying these UI components, the Bloc pattern manages the counter state and notifies all registered listeners about the state change. Listeners can dynamically subscribe or unsubscribe from receiving updates.
+
+In plain words
+
+> The Bloc pattern manages a single state object and dynamically notifies registered listeners whenever the state changes.
+
+Wikipedia says
+
+> While not a formalized "Gang of Four" design pattern, Bloc is widely used in state-driven applications. It centralizes state management and propagates state changes to registered observers, following principles of separation of concerns.
+
+Sequence diagram
+
+
+
+## Programmatic Example of the Bloc Pattern in Java
+
+This example demonstrates how to implement the Bloc pattern using Java and Swing. The pattern separates the state of the application from UI components, and provides a reactive, event-driven approach to managing updates.
+
+Core components of the Bloc Pattern include a `State` object, a `Bloc` class responsible for managing and updating that state, and interfaces (`StateListener` and `ListenerManager`) for subscribing to changes.
+
+The `State` class represents the application's data at any given time.
+
+```java
+public record State(int value) {}
+```
+
+The `ListenerManager` interface declares methods to add and remove listeners, as well as retrieve them.
+
+```java
+public interface ListenerManager {
+ void addListener(StateListener listener);
+ void removeListener(StateListener listener);
+ List> getListeners();
+}
+```
+
+The `StateListener` interface defines how a listener reacts to state changes.
+
+```java
+public interface StateListener {
+ void onStateChange(T state);
+}
+```
+
+The `Bloc` class maintains the current state and notifies listeners of updates. It provides `increment` and `decrement` methods to update state and automatically notify registered listeners.
+
+```java
+public class Bloc implements ListenerManager {
+
+ private State currentState;
+ private final List> listeners = new ArrayList<>();
+
+ public Bloc() {
+ this.currentState = new State(0);
+ }
+
+ @Override
+ public void addListener(StateListener listener) {
+ listeners.add(listener);
+ listener.onStateChange(currentState);
+ }
+
+ @Override
+ public void removeListener(StateListener listener) {
+ listeners.remove(listener);
+ }
+
+ @Override
+ public List> getListeners() {
+ return Collections.unmodifiableList(listeners);
+ }
+
+ private void emitState(State newState) {
+ currentState = newState;
+ for (StateListener listener : listeners) {
+ listener.onStateChange(currentState);
+ }
+ }
+
+ public void increment() {
+ emitState(new State(currentState.value() + 1));
+ }
+
+ public void decrement() {
+ emitState(new State(currentState.value() - 1));
+ }
+}
+```
+
+This class demonstrates how to integrate the Bloc pattern with a simple Swing GUI. It sets up a counter, buttons to change the state, and a toggle to enable or disable the listener dynamically.
+
+```java
+public class Main {
+ public static void main(String[] args) {
+ BlocUi blocUi = new BlocUi();
+ blocUi.createAndShowUi();
+ }
+}
+
+public class BlocUi {
+
+ public void createAndShowUi() {
+ final Bloc bloc = new Bloc();
+
+ JFrame frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ JButton decrementButton = new JButton("Decrement");
+ JButton toggleListenerButton = new JButton("Disable Listener");
+ JButton incrementButton = new JButton("Increment");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value());
+
+ bloc.addListener(stateListener);
+
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+
+ frame.setVisible(true);
+ }
+}
+```
+
+### Program Output
+
+- **On Increment**
+ `Counter: 1`
+
+- **On Decrement**
+ `Counter: 0`
+
+- **Dynamic Listener Toggle**
+ - Listener disabled: Counter stops updating.
+ - Listener enabled: Counter updates again.
+
+## When to Use the Bloc Pattern
+
+Use the Bloc pattern when:
+
+* When you want a clean separation of business logic and UI in Java applications
+* When you need a reactive approach to updating UI based on state changes
+* When you want to avoid coupling controllers or presenters directly to data manipulation
+* When multiple UI elements need access to the same business logic
+
+## Real-World Applications of Bloc Pattern
+
+* Java-based desktop applications that require real-time UI updates
+* Backend-driven Java frameworks that separate service layers from presentation
+* Cross-platform applications where the logic must remain consistent regardless of the UI technology
+
+## Benefits and Trade-offs of Bloc Pattern
+
+Benefits:
+
+* Simplifies UI components by removing direct business logic
+* Improves testability by isolating state and behavior
+* Encourages code reuse by centralizing data flows
+* Enhances maintainability through clear separation of concerns
+
+Trade-offs:
+
+* May introduce additional boilerplate code for managing streams or observers
+* Requires careful design to avoid a monolithic “god” component
+* Demands consistent reactive programming practices to be effective
+
+## Related Patterns
+
+- [Observer](https://java-design-patterns.com/patterns/observer/): Bloc is a specialized implementation of the Observer pattern.
+- [Mediator](https://java-design-patterns.com/patterns/mediator/): Orchestrates interactions among multiple objects through a central component
+- [MVC](https://java-design-patterns.com/patterns/model-view-controller/): Shares the idea of separating concerns between layers
+
+## References and Credits
+
+* [Bloc architecture(bloclibrary.dev)](https://bloclibrary.dev/architecture/)
+* [Clean Architecture: A Craftsman's Guide to Software Structure and Design](https://amzn.to/3UoKkaR)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Event-Driven Programming in Java (Oracle)](https://www.oracle.com/java/)
+* [Java Swing Documentation (Oracle)](https://docs.oracle.com/javase/tutorial/uiswing/)
diff --git a/bloc/etc/bloc-sequence-diagram.png b/bloc/etc/bloc-sequence-diagram.png
new file mode 100644
index 000000000000..588863985019
Binary files /dev/null and b/bloc/etc/bloc-sequence-diagram.png differ
diff --git a/bloc/etc/bloc.png b/bloc/etc/bloc.png
new file mode 100644
index 000000000000..60d6eb77c8fc
Binary files /dev/null and b/bloc/etc/bloc.png differ
diff --git a/bloc/etc/bloc.puml b/bloc/etc/bloc.puml
new file mode 100644
index 000000000000..5991f533ae70
--- /dev/null
+++ b/bloc/etc/bloc.puml
@@ -0,0 +1,41 @@
+@startuml
+package com.iluwatar.bloc {
+
+ class State {
+ - value : int
+ + State(value : int)
+ + getValue() : int
+ }
+
+ interface StateListener {
+ + onStateChange(state : T)
+ }
+
+ interface ListenerManager {
+ + addListener(listener : StateListener)
+ + removeListener(listener : StateListener)
+ + getListeners() : List>
+ }
+
+ class BloC {
+ - currentState : State
+ - listeners : List>
+ + BloC()
+ + addListener(listener : StateListener)
+ + removeListener(listener : StateListener)
+ + getListeners() : List>
+ - emitState(newState : State)
+ + increment()
+ + decrement()
+ }
+
+ class Main {
+ + main(args : String[])
+ }
+
+ ListenerManager <|.. BloC
+ StateListener <|.. BloC
+ BloC o-- State
+ BloC *-- StateListener
+}
+@enduml
diff --git a/bloc/etc/bloc.urm.puml b/bloc/etc/bloc.urm.puml
new file mode 100644
index 000000000000..6408aa76e6a4
--- /dev/null
+++ b/bloc/etc/bloc.urm.puml
@@ -0,0 +1,32 @@
+@startuml
+package com.iluwatar.bloc {
+ class Bloc {
+ - currentState : State
+ - listeners : List>
+ + Bloc()
+ + addListener(listener : StateListener)
+ + decrement()
+ - emitState(newState : State)
+ + getListeners() : List>
+ + increment()
+ + removeListener(listener : StateListener)
+ }
+ class BlocUi {
+ + BlocUi()
+ + createAndShowUi()
+ }
+ interface ListenerManager {
+ + addListener(StateListener) {abstract}
+ + getListeners() : List> {abstract}
+ + removeListener(StateListener) {abstract}
+ }
+ class Main {
+ + Main()
+ + main(args : String[]) {static}
+ }
+ interface StateListener {
+ + onStateChange(T) {abstract}
+ }
+}
+Bloc ..|> ListenerManager
+@enduml
\ No newline at end of file
diff --git a/bloc/pom.xml b/bloc/pom.xml
new file mode 100644
index 000000000000..cc52a3b99dc2
--- /dev/null
+++ b/bloc/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ bloc
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.testng
+ testng
+ 7.11.0
+ test
+
+
+ org.assertj
+ assertj-core
+ 3.27.3
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.bloc.Main
+
+
+
+
+
+
+
+
+
diff --git a/bloc/src/main/java/com/iluwatar/bloc/Bloc.java b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java
new file mode 100644
index 000000000000..f6ab0a61cdbf
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/Bloc.java
@@ -0,0 +1,98 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The Bloc class is responsible for managing the current state and notifying registered listeners
+ * whenever the state changes. It implements the ListenerManager interface, allowing listeners to be
+ * added, removed, and notified of state changes.
+ */
+public class Bloc implements ListenerManager {
+
+ private State currentState;
+ private final List> listeners = new ArrayList<>();
+
+ /** Constructs a new Bloc instance with an initial state of value 0. */
+ public Bloc() {
+ this.currentState = new State(0);
+ }
+
+ /**
+ * Adds a listener to receive state change notifications.
+ *
+ * @param listener the listener to add
+ */
+ @Override
+ public void addListener(StateListener listener) {
+ listeners.add(listener);
+ listener.onStateChange(currentState);
+ }
+
+ /**
+ * Removes a listener from receiving state change notifications.
+ *
+ * @param listener the listener to remove
+ */
+ @Override
+ public void removeListener(StateListener listener) {
+ listeners.remove(listener);
+ }
+
+ /**
+ * Returns an unmodifiable list of all registered listeners.
+ *
+ * @return an unmodifiable list of listeners
+ */
+ @Override
+ public List> getListeners() {
+ return Collections.unmodifiableList(listeners);
+ }
+
+ /**
+ * Emits a new state and notifies all registered listeners of the change.
+ *
+ * @param newState the new state to emit
+ */
+ private void emitState(State newState) {
+ currentState = newState;
+ for (StateListener listener : listeners) {
+ listener.onStateChange(currentState);
+ }
+ }
+
+ /** Increments the current state value by 1 and notifies listeners of the change. */
+ public void increment() {
+ emitState(new State(currentState.value() + 1));
+ }
+
+ /** Decrements the current state value by 1 and notifies listeners of the change. */
+ public void decrement() {
+ emitState(new State(currentState.value() - 1));
+ }
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java
new file mode 100644
index 000000000000..500d455d82e1
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/BlocUi.java
@@ -0,0 +1,85 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+import java.awt.BorderLayout;
+import java.awt.Font;
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.SwingConstants;
+import javax.swing.WindowConstants;
+
+/** The BlocUI class handles the creation and management of the UI components. */
+public class BlocUi {
+
+ /** Creates and shows the UI. */
+ public void createAndShowUi() {
+ // Create a Bloc instance to manage the state
+ final Bloc bloc = new Bloc();
+
+ // setting up a frame window with a title
+ JFrame frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ // label to display the counter value
+ JLabel counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ // buttons for increment, decrement, and toggling listener
+ JButton decrementButton = new JButton("Decrement");
+ JButton toggleListenerButton = new JButton("Disable Listener");
+ JButton incrementButton = new JButton("Increment");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ // making a state listener to update the counter label when the state changes
+ StateListener stateListener = state -> counterLabel.setText("Counter: " + state.value());
+
+ // adding the listener to the Bloc instance
+ bloc.addListener(stateListener);
+
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+
+ frame.setVisible(true);
+ }
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java
new file mode 100644
index 000000000000..cd55b0fb320d
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/ListenerManager.java
@@ -0,0 +1,56 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+import java.util.List;
+
+/**
+ * Interface for managing listeners for state changes.
+ *
+ * @param The type of state to be handled by the listeners.
+ */
+public interface ListenerManager {
+
+ /**
+ * Adds a listener that will be notified of state changes.
+ *
+ * @param listener the listener to be added
+ */
+ void addListener(StateListener listener);
+
+ /**
+ * Removes a listener so that it no longer receives state change notifications.
+ *
+ * @param listener the listener to be removed
+ */
+ void removeListener(StateListener listener);
+
+ /**
+ * Returns a list of all listeners currently registered for state changes.
+ *
+ * @return a list of registered listeners
+ */
+ List> getListeners();
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/Main.java b/bloc/src/main/java/com/iluwatar/bloc/Main.java
new file mode 100644
index 000000000000..b7a929bcf2bd
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/Main.java
@@ -0,0 +1,51 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+/**
+ * The BLoC (Business Logic Component) pattern is a software design pattern primarily used in
+ * Flutter applications. It facilitates the separation of business logic from UI code, making the
+ * application more modular, testable, and scalable. The BLoC pattern uses streams to manage the
+ * flow of data and state changes, allowing widgets to react to new states as they arrive. In the
+ * BLoC pattern, the application is divided into three key components: - Input streams: Represent
+ * user interactions or external events fed into the BLoC. - Business logic: Processes the input and
+ * determines the resulting state or actions. - Output streams: Emit the updated state for the UI to
+ * consume. The BLoC pattern is especially useful in reactive programming scenarios and aligns well
+ * with the declarative nature of Flutter. By using this pattern, developers can ensure a clear
+ * separation of concerns, enhance reusability, and maintain consistent state management throughout
+ * the application.
+ */
+public class Main {
+
+ /**
+ * The entry point of the application. Initializes the GUI.
+ *
+ * @param args command-line arguments (not used in this example)
+ */
+ public static void main(String[] args) {
+ BlocUi blocUi = new BlocUi();
+ blocUi.createAndShowUi();
+ }
+}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/State.java b/bloc/src/main/java/com/iluwatar/bloc/State.java
new file mode 100644
index 000000000000..430747548cd3
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/State.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+/**
+ * The {@code State} class represents a state with an integer value. This class encapsulates the
+ * value and provides methods to retrieve it.
+ */
+public record State(int value) {}
diff --git a/bloc/src/main/java/com/iluwatar/bloc/StateListener.java b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java
new file mode 100644
index 000000000000..77aac172e4e3
--- /dev/null
+++ b/bloc/src/main/java/com/iluwatar/bloc/StateListener.java
@@ -0,0 +1,42 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+/**
+ * The {@code StateListener} interface defines the contract for listening to state changes.
+ * Implementations of this interface should handle state changes and define actions to take when the
+ * state changes.
+ *
+ * @param the type of state that this listener will handle
+ */
+public interface StateListener {
+
+ /**
+ * This method is called when the state has changed.
+ *
+ * @param state the updated state
+ */
+ void onStateChange(T state);
+}
diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java
new file mode 100644
index 000000000000..98e34b8d4a22
--- /dev/null
+++ b/bloc/src/test/java/com/iluwatar/bloc/BlocTest.java
@@ -0,0 +1,85 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class BlocTest {
+ private Bloc bloc;
+ private AtomicInteger stateValue;
+
+ @BeforeEach
+ void setUp() {
+ bloc = new Bloc();
+ stateValue = new AtomicInteger(0);
+ }
+
+ @Test
+ void initialState() {
+ assertTrue(bloc.getListeners().isEmpty(), "No listeners should be present initially.");
+ }
+
+ @Test
+ void IncrementUpdateState() {
+ bloc.addListener(state -> stateValue.set(state.value()));
+ bloc.increment();
+ assertEquals(1, stateValue.get(), "State should increment to 1");
+ }
+
+ @Test
+ void DecrementUpdateState() {
+ bloc.addListener(state -> stateValue.set(state.value()));
+ bloc.decrement();
+ assertEquals(-1, stateValue.get(), "State should decrement to -1");
+ }
+
+ @Test
+ void addingListener() {
+ bloc.addListener(state -> {});
+ assertEquals(1, bloc.getListeners().size(), "Listener count should be 1.");
+ }
+
+ @Test
+ void removingListener() {
+ StateListener listener = state -> {};
+ bloc.addListener(listener);
+ bloc.removeListener(listener);
+ assertTrue(bloc.getListeners().isEmpty(), "Listener count should be 0 after removal.");
+ }
+
+ @Test
+ void multipleListeners() {
+ AtomicInteger secondValue = new AtomicInteger();
+ bloc.addListener(state -> stateValue.set(state.value()));
+ bloc.addListener(state -> secondValue.set(state.value()));
+ bloc.increment();
+ assertEquals(1, stateValue.get(), "First listener should receive state 1.");
+ assertEquals(1, secondValue.get(), "Second listener should receive state 1.");
+ }
+}
diff --git a/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java
new file mode 100644
index 000000000000..f1fc73947896
--- /dev/null
+++ b/bloc/src/test/java/com/iluwatar/bloc/BlocUiTest.java
@@ -0,0 +1,121 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.bloc;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.awt.*;
+import javax.swing.*;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class BlocUiTest {
+
+ private JFrame frame;
+ private JLabel counterLabel;
+ private JButton incrementButton;
+ private JButton decrementButton;
+ private JButton toggleListenerButton;
+ private Bloc bloc;
+ private StateListener stateListener;
+
+ @BeforeEach
+ void setUp() {
+ bloc = new Bloc(); // Re-initialize the Bloc for each test
+
+ frame = new JFrame("BloC example");
+ frame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
+ frame.setSize(400, 300);
+
+ counterLabel = new JLabel("Counter: 0", SwingConstants.CENTER);
+ counterLabel.setFont(new Font("Arial", Font.BOLD, 20));
+
+ incrementButton = new JButton("Increment");
+ decrementButton = new JButton("Decrement");
+ toggleListenerButton = new JButton("Disable Listener");
+
+ frame.setLayout(new BorderLayout());
+ frame.add(counterLabel, BorderLayout.CENTER);
+ frame.add(incrementButton, BorderLayout.NORTH);
+ frame.add(decrementButton, BorderLayout.SOUTH);
+ frame.add(toggleListenerButton, BorderLayout.EAST);
+
+ stateListener = state -> counterLabel.setText("Counter: " + state.value());
+ bloc.addListener(stateListener);
+
+ incrementButton.addActionListener(e -> bloc.increment());
+ decrementButton.addActionListener(e -> bloc.decrement());
+ toggleListenerButton.addActionListener(
+ e -> {
+ if (bloc.getListeners().contains(stateListener)) {
+ bloc.removeListener(stateListener);
+ toggleListenerButton.setText("Enable Listener");
+ } else {
+ bloc.addListener(stateListener);
+ toggleListenerButton.setText("Disable Listener");
+ }
+ });
+
+ frame.setVisible(true);
+ }
+
+ @AfterEach
+ void tearDown() {
+ frame.dispose();
+ bloc = new Bloc(); // Reset Bloc state after each test to avoid state carryover
+ }
+
+ @Test
+ void testIncrementButton() {
+ simulateButtonClick(incrementButton);
+ assertEquals("Counter: 1", counterLabel.getText());
+ }
+
+ @Test
+ void testDecrementButton() {
+ simulateButtonClick(decrementButton);
+ assertEquals("Counter: -1", counterLabel.getText());
+ }
+
+ @Test
+ void testToggleListenerButton() {
+ // Disable listener
+ simulateButtonClick(toggleListenerButton);
+ simulateButtonClick(incrementButton);
+ assertEquals("Counter: 0", counterLabel.getText()); // Listener is disabled
+
+ // Enable listener
+ simulateButtonClick(toggleListenerButton);
+ simulateButtonClick(incrementButton);
+ assertEquals("Counter: 2", counterLabel.getText()); // Listener is re-enabled
+ }
+
+ private void simulateButtonClick(JButton button) {
+ for (var listener : button.getActionListeners()) {
+ listener.actionPerformed(null);
+ }
+ }
+}
diff --git a/bridge/README.md b/bridge/README.md
index 9c45078d2d64..26567b292c81 100644
--- a/bridge/README.md
+++ b/bridge/README.md
@@ -36,6 +36,10 @@ Wikipedia says
> The bridge pattern is a design pattern used in software engineering that is meant to "decouple an abstraction from its implementation so that the two can vary independently"
+Sequence diagram
+
+
+
## Programmatic Example of Bridge Pattern in Java
Imagine you have a weapon that can have various enchantments, and you need to combine different weapons with different enchantments. How would you handle this? Would you create multiple copies of each weapon, each with a different enchantment, or would you create separate enchantments and apply them to the weapon as needed? The Bridge pattern enables you to do the latter.
@@ -203,10 +207,6 @@ The hammer is unwielded.
The item's glow fades.
```
-## Bridge Pattern Class Diagram
-
-
-
## When to Use the Bridge Pattern in Java
Consider using the Bridge pattern when:
diff --git a/bridge/etc/bridge-sequence-diagram.png b/bridge/etc/bridge-sequence-diagram.png
new file mode 100644
index 000000000000..b797827628bc
Binary files /dev/null and b/bridge/etc/bridge-sequence-diagram.png differ
diff --git a/bridge/pom.xml b/bridge/pom.xml
index 915aee30a3ed..3cfd33997c05 100644
--- a/bridge/pom.xml
+++ b/bridge/pom.xml
@@ -34,6 +34,14 @@
bridge
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/bridge/src/main/java/com/iluwatar/bridge/App.java b/bridge/src/main/java/com/iluwatar/bridge/App.java
index 1550782a6f23..c3ea3d50c35a 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/App.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/App.java
@@ -35,9 +35,9 @@
* have their own class hierarchies. The interface of the implementations can be changed without
* affecting the clients.
*
- * In this example we have two class hierarchies. One of weapons and another one of
- * enchantments. We can easily combine any weapon with any enchantment using composition instead of
- * creating deep class hierarchy.
+ *
In this example we have two class hierarchies. One of weapons and another one of enchantments.
+ * We can easily combine any weapon with any enchantment using composition instead of creating deep
+ * class hierarchy.
*/
@Slf4j
public class App {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
index 95f4cc351a7f..4bdd4502fd47 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Enchantment.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.bridge;
-/**
- * Enchantment.
- */
+/** Enchantment. */
public interface Enchantment {
void onActivate();
diff --git a/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
index 530fb3e5eb37..42da3523fb31 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/FlyingEnchantment.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * FlyingEnchantment.
- */
+/** FlyingEnchantment. */
@Slf4j
public class FlyingEnchantment implements Enchantment {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
index a7a237c14ebb..328f3b79e9d6 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Hammer.java
@@ -27,9 +27,7 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Hammer.
- */
+/** Hammer. */
@Slf4j
@AllArgsConstructor
public class Hammer implements Weapon {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
index 3c311ddbbdc5..ed22174673d3 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/SoulEatingEnchantment.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * SoulEatingEnchantment.
- */
+/** SoulEatingEnchantment. */
@Slf4j
public class SoulEatingEnchantment implements Enchantment {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Sword.java b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
index 7b75ead21e78..417bc9334046 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Sword.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Sword.java
@@ -27,9 +27,7 @@
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Sword.
- */
+/** Sword. */
@Slf4j
@AllArgsConstructor
public class Sword implements Weapon {
diff --git a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
index e9452343dcc2..a9f0ab4bbf52 100644
--- a/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
+++ b/bridge/src/main/java/com/iluwatar/bridge/Weapon.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.bridge;
-/**
- * Weapon.
- */
+/** Weapon. */
public interface Weapon {
void wield();
diff --git a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
index 5db5f3eb1eb9..d1136fc90f41 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/AppTest.java
@@ -24,24 +24,21 @@
*/
package com.iluwatar.bridge;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
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.
+ *
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/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
index d6c38300c46c..d8853647cb84 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/HammerTest.java
@@ -29,9 +29,7 @@
import org.junit.jupiter.api.Test;
-/**
- * Tests for hammer
- */
+/** Tests for hammer */
class HammerTest extends WeaponTest {
/**
@@ -43,4 +41,4 @@ void testHammer() {
final var hammer = spy(new Hammer(mock(FlyingEnchantment.class)));
testBasicWeaponActions(hammer);
}
-}
\ No newline at end of file
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
index d5849e78a3d5..b021cd08d00c 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/SwordTest.java
@@ -29,9 +29,7 @@
import org.junit.jupiter.api.Test;
-/**
- * Tests for sword
- */
+/** Tests for sword */
class SwordTest extends WeaponTest {
/**
@@ -43,4 +41,4 @@ void testSword() {
final var sword = spy(new Sword(mock(FlyingEnchantment.class)));
testBasicWeaponActions(sword);
}
-}
\ No newline at end of file
+}
diff --git a/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
index 47515f9b1b2a..67648ea6391e 100644
--- a/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
+++ b/bridge/src/test/java/com/iluwatar/bridge/WeaponTest.java
@@ -28,9 +28,7 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
-/**
- * Base class for weapon tests
- */
+/** Base class for weapon tests */
abstract class WeaponTest {
/**
@@ -54,6 +52,5 @@ final void testBasicWeaponActions(final Weapon weapon) {
weapon.unwield();
verify(enchantment).onDeactivate();
verifyNoMoreInteractions(enchantment);
-
}
}
diff --git a/builder/README.md b/builder/README.md
index 6a36c334c644..cf6e3242dd51 100644
--- a/builder/README.md
+++ b/builder/README.md
@@ -40,6 +40,10 @@ public Hero(Profession profession,String name,HairType hairType,HairColor hairCo
As you can see, the number of constructor parameters can quickly become overwhelming, making it difficult to understand their arrangement. Additionally, this list of parameters might continue to grow if you decide to add more options in the future. This is known as the telescoping constructor antipattern.
+Sequence diagram
+
+
+
## Programmatic Example of Builder Pattern in Java
In this Java Builder pattern example, we construct different types of `Hero` objects with varying attributes.
@@ -146,10 +150,6 @@ Program output:
16:28:06.060 [main] INFO com.iluwatar.builder.App -- This is a thief named Desmond with bald head and wielding a bow.
```
-## Builder Pattern Class Diagram
-
-
-
## When to Use the Builder Pattern in Java
Use the Builder pattern when
diff --git a/builder/etc/builder-sequence-diagram.png b/builder/etc/builder-sequence-diagram.png
new file mode 100644
index 000000000000..355556637870
Binary files /dev/null and b/builder/etc/builder-sequence-diagram.png differ
diff --git a/builder/pom.xml b/builder/pom.xml
index 37176ce89207..3677c187d5e6 100644
--- a/builder/pom.xml
+++ b/builder/pom.xml
@@ -34,6 +34,14 @@
builder
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/builder/src/main/java/com/iluwatar/builder/App.java b/builder/src/main/java/com/iluwatar/builder/App.java
index 347798cc105c..acec73a48ee1 100644
--- a/builder/src/main/java/com/iluwatar/builder/App.java
+++ b/builder/src/main/java/com/iluwatar/builder/App.java
@@ -58,22 +58,27 @@ public class App {
*/
public static void main(String[] args) {
- var mage = new Hero.Builder(Profession.MAGE, "Riobard")
- .withHairColor(HairColor.BLACK)
- .withWeapon(Weapon.DAGGER)
- .build();
+ var mage =
+ new Hero.Builder(Profession.MAGE, "Riobard")
+ .withHairColor(HairColor.BLACK)
+ .withWeapon(Weapon.DAGGER)
+ .build();
LOGGER.info(mage.toString());
- var warrior = new Hero.Builder(Profession.WARRIOR, "Amberjill")
- .withHairColor(HairColor.BLOND)
- .withHairType(HairType.LONG_CURLY).withArmor(Armor.CHAIN_MAIL).withWeapon(Weapon.SWORD)
- .build();
+ var warrior =
+ new Hero.Builder(Profession.WARRIOR, "Amberjill")
+ .withHairColor(HairColor.BLOND)
+ .withHairType(HairType.LONG_CURLY)
+ .withArmor(Armor.CHAIN_MAIL)
+ .withWeapon(Weapon.SWORD)
+ .build();
LOGGER.info(warrior.toString());
- var thief = new Hero.Builder(Profession.THIEF, "Desmond")
- .withHairType(HairType.BALD)
- .withWeapon(Weapon.BOW)
- .build();
+ var thief =
+ new Hero.Builder(Profession.THIEF, "Desmond")
+ .withHairType(HairType.BALD)
+ .withWeapon(Weapon.BOW)
+ .build();
LOGGER.info(thief.toString());
}
}
diff --git a/builder/src/main/java/com/iluwatar/builder/Armor.java b/builder/src/main/java/com/iluwatar/builder/Armor.java
index 52cbe1a06a88..1710f569af5e 100644
--- a/builder/src/main/java/com/iluwatar/builder/Armor.java
+++ b/builder/src/main/java/com/iluwatar/builder/Armor.java
@@ -26,12 +26,9 @@
import lombok.AllArgsConstructor;
-/**
- * Armor enumeration.
- */
+/** Armor enumeration. */
@AllArgsConstructor
public enum Armor {
-
CLOTHES("clothes"),
LEATHER("leather"),
CHAIN_MAIL("chain mail"),
diff --git a/builder/src/main/java/com/iluwatar/builder/HairColor.java b/builder/src/main/java/com/iluwatar/builder/HairColor.java
index 47fa9b4a9e15..7f767c98d661 100644
--- a/builder/src/main/java/com/iluwatar/builder/HairColor.java
+++ b/builder/src/main/java/com/iluwatar/builder/HairColor.java
@@ -24,11 +24,8 @@
*/
package com.iluwatar.builder;
-/**
- * HairColor enumeration.
- */
+/** HairColor enumeration. */
public enum HairColor {
-
WHITE,
BLOND,
RED,
@@ -39,5 +36,4 @@ public enum HairColor {
public String toString() {
return name().toLowerCase();
}
-
}
diff --git a/builder/src/main/java/com/iluwatar/builder/HairType.java b/builder/src/main/java/com/iluwatar/builder/HairType.java
index 45b514fb39aa..7ac31d0fa03e 100644
--- a/builder/src/main/java/com/iluwatar/builder/HairType.java
+++ b/builder/src/main/java/com/iluwatar/builder/HairType.java
@@ -26,12 +26,9 @@
import lombok.AllArgsConstructor;
-/**
- * HairType enumeration.
- */
+/** HairType enumeration. */
@AllArgsConstructor
public enum HairType {
-
BALD("bald"),
SHORT("short"),
CURLY("curly"),
diff --git a/builder/src/main/java/com/iluwatar/builder/Hero.java b/builder/src/main/java/com/iluwatar/builder/Hero.java
index 1d15ac2f0a18..a87137e51fe0 100644
--- a/builder/src/main/java/com/iluwatar/builder/Hero.java
+++ b/builder/src/main/java/com/iluwatar/builder/Hero.java
@@ -24,24 +24,30 @@
*/
package com.iluwatar.builder;
-/**
- * Hero,the record class.
- */
-
-public record Hero(Profession profession, String name, HairType hairType, HairColor hairColor, Armor armor, Weapon weapon) {
+/** Hero,the record class. */
+public record Hero(
+ Profession profession,
+ String name,
+ HairType hairType,
+ HairColor hairColor,
+ Armor armor,
+ Weapon weapon) {
private Hero(Builder builder) {
- this(builder.profession, builder.name, builder.hairType, builder.hairColor, builder.armor, builder.weapon);
+ this(
+ builder.profession,
+ builder.name,
+ builder.hairType,
+ builder.hairColor,
+ builder.armor,
+ builder.weapon);
}
@Override
public String toString() {
var sb = new StringBuilder();
- sb.append("This is a ")
- .append(profession)
- .append(" named ")
- .append(name);
+ sb.append("This is a ").append(profession).append(" named ").append(name);
if (hairColor != null || hairType != null) {
sb.append(" with ");
if (hairColor != null) {
@@ -62,9 +68,7 @@ public String toString() {
return sb.toString();
}
- /**
- * The builder class.
- */
+ /** The builder class. */
public static class Builder {
private final Profession profession;
@@ -74,9 +78,7 @@ public static class Builder {
private Armor armor;
private Weapon weapon;
- /**
- * Constructor.
- */
+ /** Constructor. */
public Builder(Profession profession, String name) {
if (profession == null || name == null) {
throw new IllegalArgumentException("profession and name can not be null");
diff --git a/builder/src/main/java/com/iluwatar/builder/Profession.java b/builder/src/main/java/com/iluwatar/builder/Profession.java
index 9ab05467703c..c1be949840f8 100644
--- a/builder/src/main/java/com/iluwatar/builder/Profession.java
+++ b/builder/src/main/java/com/iluwatar/builder/Profession.java
@@ -24,12 +24,12 @@
*/
package com.iluwatar.builder;
-/**
- * Profession enumeration.
- */
+/** Profession enumeration. */
public enum Profession {
-
- WARRIOR, THIEF, MAGE, PRIEST;
+ WARRIOR,
+ THIEF,
+ MAGE,
+ PRIEST;
@Override
public String toString() {
diff --git a/builder/src/main/java/com/iluwatar/builder/Weapon.java b/builder/src/main/java/com/iluwatar/builder/Weapon.java
index 060482705661..03a9565d0cbe 100644
--- a/builder/src/main/java/com/iluwatar/builder/Weapon.java
+++ b/builder/src/main/java/com/iluwatar/builder/Weapon.java
@@ -24,12 +24,13 @@
*/
package com.iluwatar.builder;
-/**
- * Weapon enumeration.
- */
+/** Weapon enumeration. */
public enum Weapon {
-
- DAGGER, SWORD, AXE, WARHAMMER, BOW;
+ DAGGER,
+ SWORD,
+ AXE,
+ WARHAMMER,
+ BOW;
@Override
public String toString() {
diff --git a/builder/src/test/java/com/iluwatar/builder/AppTest.java b/builder/src/test/java/com/iluwatar/builder/AppTest.java
index d7b8c2579333..367d3da1c1ac 100644
--- a/builder/src/test/java/com/iluwatar/builder/AppTest.java
+++ b/builder/src/test/java/com/iluwatar/builder/AppTest.java
@@ -24,24 +24,21 @@
*/
package com.iluwatar.builder;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
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.
+ * 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/builder/src/test/java/com/iluwatar/builder/HeroTest.java b/builder/src/test/java/com/iluwatar/builder/HeroTest.java
index 48ec7db2a1d5..5f67a56aa999 100644
--- a/builder/src/test/java/com/iluwatar/builder/HeroTest.java
+++ b/builder/src/test/java/com/iluwatar/builder/HeroTest.java
@@ -30,41 +30,33 @@
import org.junit.jupiter.api.Test;
-/**
- * HeroTest
- *
- */
+/** HeroTest */
class HeroTest {
- /**
- * Test if we get the expected exception when trying to create a hero without a profession
- */
+ /** Test if we get the expected exception when trying to create a hero without a profession */
@Test
void testMissingProfession() {
assertThrows(IllegalArgumentException.class, () -> new Hero.Builder(null, "Sir without a job"));
}
- /**
- * Test if we get the expected exception when trying to create a hero without a name
- */
+ /** Test if we get the expected exception when trying to create a hero without a name */
@Test
void testMissingName() {
assertThrows(IllegalArgumentException.class, () -> new Hero.Builder(Profession.THIEF, null));
}
- /**
- * Test if the hero build by the builder has the correct attributes, as requested
- */
+ /** Test if the hero build by the builder has the correct attributes, as requested */
@Test
void testBuildHero() {
final String heroName = "Sir Lancelot";
- final var hero = new Hero.Builder(Profession.WARRIOR, heroName)
- .withArmor(Armor.CHAIN_MAIL)
- .withWeapon(Weapon.SWORD)
- .withHairType(HairType.LONG_CURLY)
- .withHairColor(HairColor.BLOND)
- .build();
+ final var hero =
+ new Hero.Builder(Profession.WARRIOR, heroName)
+ .withArmor(Armor.CHAIN_MAIL)
+ .withWeapon(Weapon.SWORD)
+ .withHairType(HairType.LONG_CURLY)
+ .withHairColor(HairColor.BLOND)
+ .build();
assertNotNull(hero);
assertNotNull(hero.toString());
@@ -74,7 +66,5 @@ void testBuildHero() {
assertEquals(Weapon.SWORD, hero.weapon());
assertEquals(HairType.LONG_CURLY, hero.hairType());
assertEquals(HairColor.BLOND, hero.hairColor());
-
}
-
-}
\ No newline at end of file
+}
diff --git a/business-delegate/README.md b/business-delegate/README.md
index 3a3203b230dc..9f83e1a1748a 100644
--- a/business-delegate/README.md
+++ b/business-delegate/README.md
@@ -36,6 +36,10 @@ Wikipedia says
> Business Delegate is a Java EE design pattern. This pattern is directing to reduce the coupling in between business services and the connected presentation tier, and to hide the implementation details of services (including lookup and accessibility of EJB architecture). Business Delegates acts as an adaptor to invoke business objects from the presentation tier.
+Sequence diagram
+
+
+
## Programmatic Example of Business Delegate Pattern in Java
The following Java code demonstrates how to implement the Business Delegate pattern. This pattern is particularly useful in applications requiring loose coupling and efficient service interaction.
@@ -145,10 +149,6 @@ Here is the console output.
21:15:33.794 [main] INFO com.iluwatar.business.delegate.YouTubeService - YouTubeService is now processing
```
-## Business Delegate Pattern Class Diagram
-
-
-
## When to Use the Business Delegate Pattern in Java
Use the Business Delegate pattern when
diff --git a/business-delegate/etc/business-delegate-sequence-diagram.png b/business-delegate/etc/business-delegate-sequence-diagram.png
new file mode 100644
index 000000000000..a6279e9d1888
Binary files /dev/null and b/business-delegate/etc/business-delegate-sequence-diagram.png differ
diff --git a/business-delegate/pom.xml b/business-delegate/pom.xml
index 21046526f17c..a5bccb1529e7 100644
--- a/business-delegate/pom.xml
+++ b/business-delegate/pom.xml
@@ -34,6 +34,14 @@
business-delegate
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
index 682bf68cc41f..c23ed42caecd 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/App.java
@@ -34,9 +34,9 @@
* retrieved through service lookups. The Business Delegate itself may contain business logic too
* potentially tying together multiple service calls, exception handling, retrying etc.
*
- * In this example the client ({@link MobileClient}) utilizes a business delegate (
- * {@link BusinessDelegate}) to search for movies in video streaming services. The Business Delegate
- * then selects the appropriate service and makes the service call.
+ *
In this example the client ({@link MobileClient}) utilizes a business delegate ( {@link
+ * BusinessDelegate}) to search for movies in video streaming services. The Business Delegate then
+ * selects the appropriate service and makes the service call.
*/
public class App {
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
index 388407fb81ff..81920c857cd8 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessDelegate.java
@@ -26,9 +26,7 @@
import lombok.Setter;
-/**
- * BusinessDelegate separates the presentation and business tiers.
- */
+/** BusinessDelegate separates the presentation and business tiers. */
@Setter
public class BusinessDelegate {
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
index b2d45b9e6975..81a1b2f18fd0 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/BusinessLookup.java
@@ -27,9 +27,7 @@
import java.util.Locale;
import lombok.Setter;
-/**
- * Class for performing service lookups.
- */
+/** Class for performing service lookups. */
@Setter
public class BusinessLookup {
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java
index cffc464d389a..01b5b642735b 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/MobileClient.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.business.delegate;
-/**
- * MobileClient utilizes BusinessDelegate to call the business tier.
- */
+/** MobileClient utilizes BusinessDelegate to call the business tier. */
public class MobileClient {
private final BusinessDelegate businessDelegate;
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java
index 83b7cd46ef38..696480e678f3 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/NetflixService.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * NetflixService implementation.
- */
+/** NetflixService implementation. */
@Slf4j
public class NetflixService implements VideoStreamingService {
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java
index 9ac09f265b76..594b51850efb 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/VideoStreamingService.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.business.delegate;
-/**
- * Interface for video streaming service implementations.
- */
+/** Interface for video streaming service implementations. */
public interface VideoStreamingService {
void doProcessing();
diff --git a/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java
index 9b239d585c6f..65c2e55ff6fa 100644
--- a/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java
+++ b/business-delegate/src/main/java/com/iluwatar/business/delegate/YouTubeService.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * YouTubeService implementation.
- */
+/** YouTubeService implementation. */
@Slf4j
public class YouTubeService implements VideoStreamingService {
diff --git a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
index 1449f81bd6af..5f862bf6e5ff 100644
--- a/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
+++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/AppTest.java
@@ -24,25 +24,22 @@
*/
package com.iluwatar.business.delegate;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that Business Delegate example runs without errors.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests that Business Delegate example runs without errors. */
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.
+ *
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/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
index 8e8c8eddc73d..4b8e0ee77d5c 100644
--- a/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
+++ b/business-delegate/src/test/java/com/iluwatar/business/delegate/BusinessDelegateTest.java
@@ -24,18 +24,15 @@
*/
package com.iluwatar.business.delegate;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
-/**
- * Tests for the {@link BusinessDelegate}
- */
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Tests for the {@link BusinessDelegate} */
class BusinessDelegateTest {
private NetflixService netflixService;
@@ -62,8 +59,8 @@ void setup() {
}
/**
- * In this example the client ({@link MobileClient}) utilizes a business delegate (
- * {@link BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
+ * In this example the client ({@link MobileClient}) utilizes a business delegate ( {@link
+ * BusinessDelegate}) to execute a task. The Business Delegate then selects the appropriate
* service and makes the service call.
*/
@Test
diff --git a/bytecode/README.md b/bytecode/README.md
index 889354cf3d04..793eecffcc0e 100644
--- a/bytecode/README.md
+++ b/bytecode/README.md
@@ -31,6 +31,10 @@ In plain words
> An instruction set defines the low-level operations that can be performed. A series of instructions is encoded as a sequence of bytes. A virtual machine executes these instructions one at a time, using a stack for intermediate values. By combining instructions, complex high-level behavior can be defined.
+Sequence diagram
+
+
+
## Programmatic Example of Bytecode Pattern in Java
In this programmatic example, we show how the Bytecode pattern in Java can simplify the execution of complex virtual machine instructions through a well-defined set of operations. This real-world example demonstrates how the Bytecode design pattern in Java can streamline game programming by allowing wizards' behavior to be easily adjusted through bytecode instructions.
diff --git a/bytecode/etc/bytecode-sequence-diagram.png b/bytecode/etc/bytecode-sequence-diagram.png
new file mode 100644
index 000000000000..c863a0e0b5cd
Binary files /dev/null and b/bytecode/etc/bytecode-sequence-diagram.png differ
diff --git a/bytecode/pom.xml b/bytecode/pom.xml
index eabd294e4ced..2fb01fe2f914 100644
--- a/bytecode/pom.xml
+++ b/bytecode/pom.xml
@@ -34,6 +34,14 @@
4.0.0
bytecode
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/App.java b/bytecode/src/main/java/com/iluwatar/bytecode/App.java
index c2e3a3b10f86..9293b5876ef5 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/App.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/App.java
@@ -58,9 +58,7 @@ public class App {
*/
public static void main(String[] args) {
- var vm = new VirtualMachine(
- new Wizard(45, 7, 11, 0, 0),
- new Wizard(36, 18, 8, 0, 0));
+ var vm = new VirtualMachine(new Wizard(45, 7, 11, 0, 0), new Wizard(36, 18, 8, 0, 0));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
index 91d394234191..25330e73fd78 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/Instruction.java
@@ -27,24 +27,21 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
-/**
- * Representation of instructions understandable by virtual machine.
- */
+/** Representation of instructions understandable by virtual machine. */
@AllArgsConstructor
@Getter
public enum Instruction {
-
- LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
- SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
- SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
- SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
- PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
+ LITERAL(1), // e.g. "LITERAL 0", push 0 to stack
+ SET_HEALTH(2), // e.g. "SET_HEALTH", pop health and wizard number, call set health
+ SET_WISDOM(3), // e.g. "SET_WISDOM", pop wisdom and wizard number, call set wisdom
+ SET_AGILITY(4), // e.g. "SET_AGILITY", pop agility and wizard number, call set agility
+ PLAY_SOUND(5), // e.g. "PLAY_SOUND", pop value as wizard number, call play sound
SPAWN_PARTICLES(6), // e.g. "SPAWN_PARTICLES", pop value as wizard number, call spawn particles
- GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
- GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
- GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
- ADD(10), // e.g. "ADD", pop 2 values, push their sum
- DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
+ GET_HEALTH(7), // e.g. "GET_HEALTH", pop value as wizard number, push wizard's health
+ GET_AGILITY(8), // e.g. "GET_AGILITY", pop value as wizard number, push wizard's agility
+ GET_WISDOM(9), // e.g. "GET_WISDOM", pop value as wizard number, push wizard's wisdom
+ ADD(10), // e.g. "ADD", pop 2 values, push their sum
+ DIVIDE(11); // e.g. "DIVIDE", pop 2 values, push their division
private final int intValue;
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
index 2133296ed155..7f835d402f15 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/VirtualMachine.java
@@ -29,9 +29,7 @@
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
-/**
- * Implementation of virtual machine.
- */
+/** Implementation of virtual machine. */
@Getter
@Slf4j
public class VirtualMachine {
@@ -40,19 +38,13 @@ public class VirtualMachine {
private final Wizard[] wizards = new Wizard[2];
- /**
- * No-args constructor.
- */
+ /** No-args constructor. */
public VirtualMachine() {
- wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
- 0, 0);
- wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32),
- 0, 0);
+ wizards[0] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), 0, 0);
+ wizards[1] = new Wizard(randomInt(3, 32), randomInt(3, 32), randomInt(3, 32), 0, 0);
}
- /**
- * Constructor taking the wizards as arguments.
- */
+ /** Constructor taking the wizards as arguments. */
public VirtualMachine(Wizard wizard1, Wizard wizard2) {
wizards[0] = wizard1;
wizards[1] = wizard2;
@@ -112,7 +104,6 @@ public void execute(int[] bytecode) {
case PLAY_SOUND -> {
var wizard = stack.pop();
getWizards()[wizard].playSound();
-
}
case SPAWN_PARTICLES -> {
var wizard = stack.pop();
diff --git a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
index 6501ac9a38f5..d45a2aa55787 100644
--- a/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
+++ b/bytecode/src/main/java/com/iluwatar/bytecode/util/InstructionConverterUtil.java
@@ -26,9 +26,7 @@
import com.iluwatar.bytecode.Instruction;
-/**
- * Utility class used for instruction validation and conversion.
- */
+/** Utility class used for instruction validation and conversion. */
public class InstructionConverterUtil {
/**
* Converts instructions represented as String.
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
index c56e849399b0..72d00eb34fb3 100644
--- a/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/AppTest.java
@@ -24,24 +24,21 @@
*/
package com.iluwatar.bytecode;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
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.
+ * 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/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
index b6ad5dfe6925..1d9a5539f51b 100644
--- a/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/VirtualMachineTest.java
@@ -30,9 +30,7 @@
import org.junit.jupiter.api.Test;
-/**
- * Test for {@link VirtualMachine}
- */
+/** Test for {@link VirtualMachine} */
class VirtualMachineTest {
@Test
@@ -55,7 +53,7 @@ void testSetHealth() {
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
- bytecode[3] = 50; // health amount
+ bytecode[3] = 50; // health amount
bytecode[4] = SET_HEALTH.getIntValue();
var vm = new VirtualMachine();
@@ -71,7 +69,7 @@ void testSetAgility() {
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
- bytecode[3] = 50; // agility amount
+ bytecode[3] = 50; // agility amount
bytecode[4] = SET_AGILITY.getIntValue();
var vm = new VirtualMachine();
@@ -87,7 +85,7 @@ void testSetWisdom() {
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
- bytecode[3] = 50; // wisdom amount
+ bytecode[3] = 50; // wisdom amount
bytecode[4] = SET_WISDOM.getIntValue();
var vm = new VirtualMachine();
@@ -103,7 +101,7 @@ void testGetHealth() {
bytecode[0] = LITERAL.getIntValue();
bytecode[1] = wizardNumber;
bytecode[2] = LITERAL.getIntValue();
- bytecode[3] = 50; // health amount
+ bytecode[3] = 50; // health amount
bytecode[4] = SET_HEALTH.getIntValue();
bytecode[5] = LITERAL.getIntValue();
bytecode[6] = wizardNumber;
diff --git a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
index 494fc9a2e8c4..9dadba1eaf21 100644
--- a/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
+++ b/bytecode/src/test/java/com/iluwatar/bytecode/util/InstructionConverterUtilTest.java
@@ -28,9 +28,7 @@
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
-/**
- * Test for {@link InstructionConverterUtil}
- */
+/** Test for {@link InstructionConverterUtil} */
class InstructionConverterUtilTest {
@Test
@@ -44,8 +42,9 @@ void testEmptyInstruction() {
@Test
void testInstructions() {
- var instructions = "LITERAL 35 SET_HEALTH SET_WISDOM SET_AGILITY PLAY_SOUND"
- + " SPAWN_PARTICLES GET_HEALTH ADD DIVIDE";
+ var instructions =
+ "LITERAL 35 SET_HEALTH SET_WISDOM SET_AGILITY PLAY_SOUND"
+ + " SPAWN_PARTICLES GET_HEALTH ADD DIVIDE";
var bytecode = InstructionConverterUtil.convertToByteCode(instructions);
@@ -61,5 +60,4 @@ void testInstructions() {
Assertions.assertEquals(Instruction.ADD.getIntValue(), bytecode[8]);
Assertions.assertEquals(Instruction.DIVIDE.getIntValue(), bytecode[9]);
}
-
}
diff --git a/caching/README.md b/caching/README.md
index b923da87b3da..2e9c3761ef8d 100644
--- a/caching/README.md
+++ b/caching/README.md
@@ -34,6 +34,10 @@ Wikipedia says
> In computing, a cache is a hardware or software component that stores data so that future requests for that data can be served faster; the data stored in a cache might be the result of an earlier computation or a copy of data stored elsewhere. A cache hit occurs when the requested data can be found in a cache, while a cache miss occurs when it cannot. Cache hits are served by reading data from the cache, which is faster than recomputing a result or reading from a slower data store; thus, the more requests that can be served from the cache, the faster the system performs.
+Flowchart
+
+
+
## Programmatic Example of Caching Pattern in Java
In this programmatic example, we demonstrate different Java caching strategies, including write-through, write-around, and write-behind, using a user account management system.
diff --git a/caching/etc/caching-flowchart.png b/caching/etc/caching-flowchart.png
new file mode 100644
index 000000000000..8068392cd9bd
Binary files /dev/null and b/caching/etc/caching-flowchart.png differ
diff --git a/caching/etc/caching.urm.puml b/caching/etc/caching.urm.puml
index a9dae801eb20..f6f2e4732005 100644
--- a/caching/etc/caching.urm.puml
+++ b/caching/etc/caching.urm.puml
@@ -13,7 +13,7 @@ package com.iluwatar.caching {
- LOGGER : Logger {static}
+ App()
+ main(args : String[]) {static}
- + useCacheAsideStategy()
+ + useCacheAsideStrategy()
+ useReadAndWriteThroughStrategy()
+ useReadThroughAndWriteAroundStrategy()
+ useReadThroughAndWriteBehindStrategy()
@@ -116,4 +116,4 @@ Node --> "-previous" Node
AppManager --> "-cachingPolicy" CachingPolicy
Node --> "-userAccount" UserAccount
CacheStore --> "-cache" LruCache
-@enduml
\ No newline at end of file
+@enduml
diff --git a/caching/pom.xml b/caching/pom.xml
index d7470b5e46d8..3ffce74af493 100644
--- a/caching/pom.xml
+++ b/caching/pom.xml
@@ -34,6 +34,14 @@
caching
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java
index 3adf7e681f9a..8d6af6c090d8 100644
--- a/caching/src/main/java/com/iluwatar/caching/App.java
+++ b/caching/src/main/java/com/iluwatar/caching/App.java
@@ -29,59 +29,44 @@
import lombok.extern.slf4j.Slf4j;
/**
- * 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 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, write-behind
- * which writes data into the cache initially whilst the data is only
- * 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 maintainconsistency between data held in the cache and the data in
- * the underlying data store.
+ * 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 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,
+ * write-behind which writes data into the cache initially whilst the data is only 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
+ * maintainconsistency 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 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 whether the data is coming from the cache or the
- * DB (i.e. separation of concern). The AppManager ({@link AppManager}) handles
- * the transaction of data to-and-from the underlying data store (depending on
- * the preferred caching policy/strategy).
- *
- * {@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy -->
- * DBManager}
- *
+ * 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 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
+ * whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
+ * ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
+ * (depending on the preferred caching policy/strategy).
*
- *
- * There are 2 ways to launch the application.
- * - to use "in Memory" database.
- * - to use the MongoDb as a database
+ *
{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager}
*
- * To run the application with "in Memory" database, just launch it without parameters
- * Example: 'java -jar app.jar'
+ *
There are 2 ways to launch the application. - to use "in Memory" database. - to use the
+ * MongoDb as a database
*
- * To run the application with MongoDb you need to be installed the MongoDb
- * in your system, or to launch it in the docker container.
- * You may launch docker container from the root of current module with command:
- * 'docker-compose up'
- * Then you can start the application with parameter --mongo
- * Example: 'java -jar app.jar --mongo'
- *
+ * To run the application with "in Memory" database, just launch it without parameters Example:
+ * 'java -jar app.jar'
+ *
+ *
To run the application with MongoDb you need to be installed the MongoDb in your system, or to
+ * launch it in the docker container. You may launch docker container from the root of current
+ * module with command: 'docker-compose up' Then you can start the application with parameter
+ * --mongo Example: 'java -jar app.jar --mongo'
*
* @see CacheStore
* @see LruCache
@@ -89,13 +74,10 @@
*/
@Slf4j
public class App {
- /**
- * Constant parameter name to use mongoDB.
- */
+ /** Constant parameter name to use mongoDB. */
private static final String USE_MONGO_DB = "--mongo";
- /**
- * Application manager.
- */
+
+ /** Application manager. */
private final AppManager appManager;
/**
@@ -133,7 +115,7 @@ public static void main(final String[] args) {
LOGGER.info(splitLine);
app.useReadThroughAndWriteBehindStrategy();
LOGGER.info(splitLine);
- app.useCacheAsideStategy();
+ app.useCacheAsideStrategy();
LOGGER.info(splitLine);
}
@@ -152,9 +134,7 @@ private static boolean isDbMongo(final String[] args) {
return false;
}
- /**
- * Read-through and write-through.
- */
+ /** Read-through and write-through. */
public void useReadAndWriteThroughStrategy() {
LOGGER.info("# CachingPolicy.THROUGH");
appManager.initCachingPolicy(CachingPolicy.THROUGH);
@@ -167,9 +147,7 @@ public void useReadAndWriteThroughStrategy() {
appManager.find("001");
}
- /**
- * Read-through and write-around.
- */
+ /** Read-through and write-around. */
public void useReadThroughAndWriteAroundStrategy() {
LOGGER.info("# CachingPolicy.AROUND");
appManager.initCachingPolicy(CachingPolicy.AROUND);
@@ -189,22 +167,14 @@ public void useReadThroughAndWriteAroundStrategy() {
appManager.find("002");
}
- /**
- * Read-through and write-behind.
- */
+ /** Read-through and write-behind. */
public void useReadThroughAndWriteBehindStrategy() {
LOGGER.info("# CachingPolicy.BEHIND");
appManager.initCachingPolicy(CachingPolicy.BEHIND);
- var userAccount3 = new UserAccount("003",
- "Adam",
- "He likes food.");
- var userAccount4 = new UserAccount("004",
- "Rita",
- "She hates cats.");
- var userAccount5 = new UserAccount("005",
- "Isaac",
- "He is allergic to mustard.");
+ var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
+ var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
+ var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
appManager.save(userAccount3);
appManager.save(userAccount4);
@@ -212,32 +182,22 @@ public void useReadThroughAndWriteBehindStrategy() {
LOGGER.info(appManager.printCacheContent());
appManager.find("003");
LOGGER.info(appManager.printCacheContent());
- UserAccount userAccount6 = new UserAccount("006",
- "Yasha",
- "She is an only child.");
+ UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
appManager.save(userAccount6);
LOGGER.info(appManager.printCacheContent());
appManager.find("004");
LOGGER.info(appManager.printCacheContent());
}
- /**
- * Cache-Aside.
- */
- public void useCacheAsideStategy() {
+ /** Cache-Aside. */
+ public void useCacheAsideStrategy() {
LOGGER.info("# CachingPolicy.ASIDE");
appManager.initCachingPolicy(CachingPolicy.ASIDE);
LOGGER.info(appManager.printCacheContent());
- var userAccount3 = new UserAccount("003",
- "Adam",
- "He likes food.");
- var userAccount4 = new UserAccount("004",
- "Rita",
- "She hates cats.");
- var userAccount5 = new UserAccount("005",
- "Isaac",
- "He is allergic to mustard.");
+ var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
+ var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
+ var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
appManager.save(userAccount3);
appManager.save(userAccount4);
appManager.save(userAccount5);
diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java
index 3d298537521f..c1d21fea33fe 100644
--- a/caching/src/main/java/com/iluwatar/caching/AppManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java
@@ -29,26 +29,21 @@
import lombok.extern.slf4j.Slf4j;
/**
- * AppManager helps to bridge the gap in communication between the main class
- * and the application's back-end. DB connection is initialized through this
- * class. The chosen caching strategy/policy is also initialized here.
- * Before the cache can be used, the size of the cache has to be set.
- * Depending on the chosen caching policy, AppManager will call the
- * appropriate function in the CacheStore class.
+ * AppManager helps to bridge the gap in communication between the main class and the application's
+ * back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
+ * also initialized here. Before the cache can be used, the size of the cache has to be set.
+ * Depending on the chosen caching policy, AppManager will call the appropriate function in the
+ * CacheStore class.
*/
@Slf4j
public class AppManager {
- /**
- * Caching Policy.
- */
+ /** Caching Policy. */
private CachingPolicy cachingPolicy;
- /**
- * Database Manager.
- */
+
+ /** Database Manager. */
private final DbManager dbManager;
- /**
- * Cache Store.
- */
+
+ /** Cache Store. */
private final CacheStore cacheStore;
/**
@@ -62,9 +57,9 @@ public AppManager(final DbManager newDbManager) {
}
/**
- * Developer/Tester is able to choose whether the application should use
- * MongoDB as its underlying data storage or a simple Java data structure
- * to (temporarily) store the data/objects during runtime.
+ * Developer/Tester is able to choose whether the application should use MongoDB as its underlying
+ * data storage or a simple Java data structure to (temporarily) store the data/objects during
+ * runtime.
*/
public void initDb() {
dbManager.connect();
@@ -91,8 +86,7 @@ public void initCachingPolicy(final CachingPolicy policy) {
*/
public UserAccount find(final String userId) {
LOGGER.info("Trying to find {} in cache", userId);
- if (cachingPolicy == CachingPolicy.THROUGH
- || cachingPolicy == CachingPolicy.AROUND) {
+ if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return cacheStore.readThroughWithWriteBackPolicy(userId);
@@ -147,12 +141,12 @@ private void saveAside(final UserAccount userAccount) {
*/
private UserAccount findAside(final String userId) {
return Optional.ofNullable(cacheStore.get(userId))
- .or(() -> {
- Optional userAccount =
- Optional.ofNullable(dbManager.readFromDb(userId));
+ .or(
+ () -> {
+ Optional userAccount = Optional.ofNullable(dbManager.readFromDb(userId));
userAccount.ifPresent(account -> cacheStore.set(userId, account));
return userAccount;
})
- .orElse(null);
+ .orElse(null);
}
}
diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
index 2c7184f57b3f..b26c52b22159 100644
--- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java
+++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
@@ -30,27 +30,21 @@
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
-/**
- * The caching strategies are implemented in this class.
- */
+/** The caching strategies are implemented in this class. */
@Slf4j
public class CacheStore {
- /**
- * Cache capacity.
- */
+ /** Cache capacity. */
private static final int CAPACITY = 3;
- /**
- * Lru cache see {@link LruCache}.
- */
+ /** Lru cache see {@link LruCache}. */
private LruCache cache;
- /**
- * DbManager.
- */
+
+ /** DbManager. */
private final DbManager dbManager;
/**
* Cache Store.
+ *
* @param dataBaseManager {@link DbManager}
*/
public CacheStore(final DbManager dataBaseManager) {
@@ -60,6 +54,7 @@ public CacheStore(final DbManager dataBaseManager) {
/**
* Init cache capacity.
+ *
* @param capacity int
*/
public void initCapacity(final int capacity) {
@@ -72,6 +67,7 @@ public void initCapacity(final int capacity) {
/**
* Get user account using read-through cache.
+ *
* @param userId {@link String}
* @return {@link UserAccount}
*/
@@ -88,6 +84,7 @@ public UserAccount readThrough(final String userId) {
/**
* Get user account using write-through cache.
+ *
* @param userAccount {@link UserAccount}
*/
public void writeThrough(final UserAccount userAccount) {
@@ -101,6 +98,7 @@ public void writeThrough(final UserAccount userAccount) {
/**
* Get user account using write-around cache.
+ *
* @param userAccount {@link UserAccount}
*/
public void writeAround(final UserAccount userAccount) {
@@ -116,6 +114,7 @@ public void writeAround(final UserAccount userAccount) {
/**
* Get user account using read-through cache with write-back policy.
+ *
* @param userId {@link String}
* @return {@link UserAccount}
*/
@@ -137,6 +136,7 @@ public UserAccount readThroughWithWriteBackPolicy(final String userId) {
/**
* Set user account.
+ *
* @param userAccount {@link UserAccount}
*/
public void writeBehind(final UserAccount userAccount) {
@@ -148,18 +148,14 @@ public void writeBehind(final UserAccount userAccount) {
cache.set(userAccount.getUserId(), userAccount);
}
- /**
- * Clears cache.
- */
+ /** Clears cache. */
public void clearCache() {
if (cache != null) {
cache.clear();
}
}
- /**
- * Writes remaining content in the cache into the DB.
- */
+ /** Writes remaining content in the cache into the DB. */
public void flushCache() {
LOGGER.info("# flushCache...");
Optional.ofNullable(cache)
@@ -171,6 +167,7 @@ public void flushCache() {
/**
* Print user accounts.
+ *
* @return {@link String}
*/
public String print() {
@@ -184,6 +181,7 @@ public String print() {
/**
* Delegate to backing cache store.
+ *
* @param userId {@link String}
* @return {@link UserAccount}
*/
@@ -193,6 +191,7 @@ public UserAccount get(final String userId) {
/**
* Delegate to backing cache store.
+ *
* @param userId {@link String}
* @param userAccount {@link UserAccount}
*/
@@ -202,6 +201,7 @@ public void set(final String userId, final UserAccount userAccount) {
/**
* Delegate to backing cache store.
+ *
* @param userId {@link String}
*/
public void invalidate(final String userId) {
diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
index dcd5711dbf6e..0ec07ced7b8f 100644
--- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
+++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
@@ -27,31 +27,19 @@
import lombok.AllArgsConstructor;
import lombok.Getter;
-/**
- * Enum class containing the four caching strategies implemented in the pattern.
- */
+/** Enum class containing the four caching strategies implemented in the pattern. */
@AllArgsConstructor
@Getter
public enum CachingPolicy {
- /**
- * Through.
- */
+ /** Through. */
THROUGH("through"),
- /**
- * AROUND.
- */
+ /** AROUND. */
AROUND("around"),
- /**
- * BEHIND.
- */
+ /** BEHIND. */
BEHIND("behind"),
- /**
- * ASIDE.
- */
+ /** ASIDE. */
ASIDE("aside");
- /**
- * Policy value.
- */
+ /** Policy value. */
private final String policy;
}
diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java
index ef94bf482161..9c9107de6f88 100644
--- a/caching/src/main/java/com/iluwatar/caching/LruCache.java
+++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java
@@ -30,42 +30,33 @@
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
-
/**
- * Data structure/implementation of the application's cache. The data structure
- * consists of a hash table attached with a doubly linked-list. The linked-list
- * helps in capturing and maintaining the LRU data in the cache. When a data is
- * queried (from the cache), added (to the cache), or updated, the data is
- * moved to the front of the list to depict itself as the most-recently-used
- * data. The LRU data is always at the end of the list.
+ * Data structure/implementation of the application's cache. The data structure consists of a hash
+ * table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
+ * LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
+ * the data is moved to the front of the list to depict itself as the most-recently-used data. The
+ * LRU data is always at the end of the list.
*/
@Slf4j
public class LruCache {
- /**
- * Static class Node.
- */
+ /** Static class Node. */
static class Node {
- /**
- * user id.
- */
+ /** user id. */
private final String userId;
- /**
- * User Account.
- */
+
+ /** User Account. */
private UserAccount userAccount;
- /**
- * previous.
- */
+
+ /** previous. */
private Node previous;
- /**
- * next.
- */
+
+ /** next. */
private Node next;
/**
* Node definition.
*
- * @param id String
+ * @param id String
* @param account {@link UserAccount}
*/
Node(final String id, final UserAccount account) {
@@ -74,21 +65,16 @@ static class Node {
}
}
- /**
- * Capacity of Cache.
- */
+ /** Capacity of Cache. */
private int capacity;
- /**
- * Cache {@link HashMap}.
- */
+
+ /** Cache {@link HashMap}. */
private Map cache = new HashMap<>();
- /**
- * Head.
- */
+
+ /** Head. */
private Node head;
- /**
- * End.
- */
+
+ /** End. */
private Node end;
/**
@@ -155,7 +141,7 @@ public void setHead(final Node node) {
* Set user account.
*
* @param userAccount {@link UserAccount}
- * @param userId {@link String}
+ * @param userId {@link String}
*/
public void set(final String userId, final UserAccount userAccount) {
if (cache.containsKey(userId)) {
@@ -195,14 +181,14 @@ public boolean contains(final String userId) {
public void invalidate(final String userId) {
var toBeRemoved = cache.remove(userId);
if (toBeRemoved != null) {
- LOGGER.info("# {} has been updated! "
- + "Removing older version from cache...", userId);
+ LOGGER.info("# {} has been updated! " + "Removing older version from cache...", userId);
remove(toBeRemoved);
}
}
/**
* Check if the cache is full.
+ *
* @return boolean
*/
public boolean isFull() {
@@ -218,9 +204,7 @@ public UserAccount getLruData() {
return end.userAccount;
}
- /**
- * Clear cache.
- */
+ /** Clear cache. */
public void clear() {
head = null;
end = null;
diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
index 73100c0a6c24..561c942ac781 100644
--- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java
+++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
@@ -29,24 +29,18 @@
import lombok.EqualsAndHashCode;
import lombok.ToString;
-/**
- * Entity class (stored in cache and DB) used in the application.
- */
+/** Entity class (stored in cache and DB) used in the application. */
@Data
@AllArgsConstructor
@ToString
@EqualsAndHashCode
public class UserAccount {
- /**
- * User Id.
- */
+ /** User Id. */
private String userId;
- /**
- * User Name.
- */
+
+ /** User Name. */
private String userName;
- /**
- * Additional Info.
- */
+
+ /** Additional Info. */
private String additionalInfo;
}
diff --git a/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java b/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
index 908fca8ea0e3..5e8fc415df4c 100644
--- a/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
+++ b/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
@@ -24,30 +24,20 @@
*/
package com.iluwatar.caching.constants;
-/**
- * Constant class for defining constants.
- */
+/** Constant class for defining constants. */
public final class CachingConstants {
- /**
- * User Account.
- */
+ /** User Account. */
public static final String USER_ACCOUNT = "user_accounts";
- /**
- * User ID.
- */
+
+ /** User ID. */
public static final String USER_ID = "userID";
- /**
- * User Name.
- */
+
+ /** User Name. */
public static final String USER_NAME = "userName";
- /**
- * Additional Info.
- */
+
+ /** Additional Info. */
public static final String ADD_INFO = "additionalInfo";
- /**
- * Constructor.
- */
- private CachingConstants() {
- }
+ /** Constructor. */
+ private CachingConstants() {}
}
diff --git a/caching/src/main/java/com/iluwatar/caching/constants/package-info.java b/caching/src/main/java/com/iluwatar/caching/constants/package-info.java
index 9356e7e7b6c3..b94476cbabeb 100644
--- a/caching/src/main/java/com/iluwatar/caching/constants/package-info.java
+++ b/caching/src/main/java/com/iluwatar/caching/constants/package-info.java
@@ -22,7 +22,5 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-/**
- * Constants.
- */
+/** Constants. */
package com.iluwatar.caching.constants;
diff --git a/caching/src/main/java/com/iluwatar/caching/database/DbManager.java b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
index 16b60fc82d63..14b98a66b52b 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
@@ -27,19 +27,15 @@
import com.iluwatar.caching.UserAccount;
/**
- * DBManager handles the communication with the underlying data store i.e.
- * Database. It contains the implemented methods for querying, inserting,
- * and updating data. MongoDB was used as the database for the application.
+ * DBManager handles the communication with the underlying data store i.e. Database. It contains the
+ * implemented methods for querying, inserting, and updating data. MongoDB was used as the database
+ * for the application.
*/
public interface DbManager {
- /**
- * Connect to DB.
- */
+ /** Connect to DB. */
void connect();
- /**
- * Disconnect from DB.
- */
+ /** Disconnect from DB. */
void disconnect();
/**
diff --git a/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
index ee7a4a04b936..92031b7c95b0 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
@@ -24,15 +24,10 @@
*/
package com.iluwatar.caching.database;
-/**
- * Creates the database connection according the input parameter.
- */
+/** Creates the database connection according the input parameter. */
public final class DbManagerFactory {
- /**
- * Private constructor.
- */
- private DbManagerFactory() {
- }
+ /** Private constructor. */
+ private DbManagerFactory() {}
/**
* Init database.
diff --git a/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
index f2fa696cca5e..e47eef55cd8c 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
@@ -40,10 +40,7 @@
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
-/**
- * Implementation of DatabaseManager.
- * implements base methods to work with MongoDb.
- */
+/** Implementation of DatabaseManager. implements base methods to work with MongoDb. */
@Slf4j
public class MongoDb implements DbManager {
private static final String DATABASE_NAME = "admin";
@@ -56,14 +53,11 @@ void setDb(MongoDatabase db) {
this.db = db;
}
- /**
- * Connect to Db. Check th connection
- */
+ /** Connect to Db. Check th connection */
@Override
public void connect() {
- MongoCredential mongoCredential = MongoCredential.createCredential(MONGO_USER,
- DATABASE_NAME,
- MONGO_PASSWORD.toCharArray());
+ MongoCredential mongoCredential =
+ MongoCredential.createCredential(MONGO_USER, DATABASE_NAME, MONGO_PASSWORD.toCharArray());
MongoClientOptions options = MongoClientOptions.builder().build();
client = new MongoClient(new ServerAddress(), mongoCredential, options);
db = client.getDatabase(DATABASE_NAME);
@@ -82,9 +76,8 @@ public void disconnect() {
*/
@Override
public UserAccount readFromDb(final String userId) {
- var iterable = db
- .getCollection(CachingConstants.USER_ACCOUNT)
- .find(new Document(USER_ID, userId));
+ var iterable =
+ db.getCollection(CachingConstants.USER_ACCOUNT).find(new Document(USER_ID, userId));
if (iterable.first() == null) {
return null;
}
@@ -106,11 +99,11 @@ public UserAccount readFromDb(final String userId) {
*/
@Override
public UserAccount writeToDb(final UserAccount userAccount) {
- db.getCollection(USER_ACCOUNT).insertOne(
+ db.getCollection(USER_ACCOUNT)
+ .insertOne(
new Document(USER_ID, userAccount.getUserId())
- .append(USER_NAME, userAccount.getUserName())
- .append(ADD_INFO, userAccount.getAdditionalInfo())
- );
+ .append(USER_NAME, userAccount.getUserName())
+ .append(ADD_INFO, userAccount.getAdditionalInfo()));
return userAccount;
}
@@ -123,10 +116,10 @@ public UserAccount writeToDb(final UserAccount userAccount) {
@Override
public UserAccount updateDb(final UserAccount userAccount) {
Document id = new Document(USER_ID, userAccount.getUserId());
- Document dataSet = new Document(USER_NAME, userAccount.getUserName())
+ Document dataSet =
+ new Document(USER_NAME, userAccount.getUserName())
.append(ADD_INFO, userAccount.getAdditionalInfo());
- db.getCollection(CachingConstants.USER_ACCOUNT)
- .updateOne(id, new Document("$set", dataSet));
+ db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(id, new Document("$set", dataSet));
return userAccount;
}
@@ -141,15 +134,15 @@ public UserAccount upsertDb(final UserAccount userAccount) {
String userId = userAccount.getUserId();
String userName = userAccount.getUserName();
String additionalInfo = userAccount.getAdditionalInfo();
- db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
+ db.getCollection(CachingConstants.USER_ACCOUNT)
+ .updateOne(
new Document(USER_ID, userId),
- new Document("$set",
- new Document(USER_ID, userId)
- .append(USER_NAME, userName)
- .append(ADD_INFO, additionalInfo)
- ),
- new UpdateOptions().upsert(true)
- );
+ new Document(
+ "$set",
+ new Document(USER_ID, userId)
+ .append(USER_NAME, userName)
+ .append(ADD_INFO, additionalInfo)),
+ new UpdateOptions().upsert(true));
return userAccount;
}
}
diff --git a/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
index 6155e1d69ee6..6040ca174a21 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
@@ -28,19 +28,12 @@
import java.util.HashMap;
import java.util.Map;
-/**
- * Implementation of DatabaseManager.
- * implements base methods to work with hashMap as database.
- */
+/** Implementation of DatabaseManager. implements base methods to work with hashMap as database. */
public class VirtualDb implements DbManager {
- /**
- * Virtual DataBase.
- */
+ /** Virtual DataBase. */
private Map db;
- /**
- * Creates new HashMap.
- */
+ /** Creates new HashMap. */
@Override
public void connect() {
db = new HashMap<>();
diff --git a/caching/src/main/java/com/iluwatar/caching/database/package-info.java b/caching/src/main/java/com/iluwatar/caching/database/package-info.java
index 535771a7d275..631cb4c584cd 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/package-info.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/package-info.java
@@ -22,7 +22,5 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-/**
- * Database classes.
- */
+/** Database classes. */
package com.iluwatar.caching.database;
diff --git a/caching/src/test/java/com/iluwatar/caching/AppTest.java b/caching/src/test/java/com/iluwatar/caching/AppTest.java
index 510b3a256f5f..35e01edbc37e 100644
--- a/caching/src/test/java/com/iluwatar/caching/AppTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/AppTest.java
@@ -24,23 +24,20 @@
*/
package com.iluwatar.caching;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that Caching example runs without errors.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests that Caching example runs without errors. */
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.
+ *
+ *
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/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
index 0bcb83897d59..d17cff5bd2ef 100644
--- a/caching/src/test/java/com/iluwatar/caching/CachingTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
@@ -24,20 +24,16 @@
*/
package com.iluwatar.caching;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-
-/**
- * Application test
- */
+/** Application test */
class CachingTest {
private App app;
- /**
- * Setup of application test includes: initializing DB connection and cache size/capacity.
- */
+ /** Setup of application test includes: initializing DB connection and cache size/capacity. */
@BeforeEach
void setUp() {
// VirtualDB (instead of MongoDB) was used in running the JUnit tests
@@ -68,6 +64,6 @@ void testReadThroughAndWriteBehindStrategy() {
@Test
void testCacheAsideStrategy() {
assertNotNull(app);
- app.useCacheAsideStategy();
+ app.useCacheAsideStrategy();
}
}
diff --git a/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java b/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
index 5cb130a34e33..87cc1ed6f587 100644
--- a/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
@@ -24,6 +24,13 @@
*/
package com.iluwatar.caching.database;
+import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
+import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
+import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.mockito.Mockito.*;
+
import com.iluwatar.caching.UserAccount;
import com.iluwatar.caching.constants.CachingConstants;
import com.mongodb.client.FindIterable;
@@ -34,20 +41,12 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
-import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
-import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
-import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.mockito.Mockito.*;
-
class MongoDbTest {
private static final String ID = "123";
private static final String NAME = "Some user";
private static final String ADDITIONAL_INFO = "Some app Info";
- @Mock
- MongoDatabase db;
+ @Mock MongoDatabase db;
private MongoDb mongoDb = new MongoDb();
private UserAccount userAccount;
@@ -66,9 +65,8 @@ void connect() {
@Test
void readFromDb() {
- Document document = new Document(USER_ID, ID)
- .append(USER_NAME, NAME)
- .append(ADD_INFO, ADDITIONAL_INFO);
+ Document document =
+ new Document(USER_ID, ID).append(USER_NAME, NAME).append(ADD_INFO, ADDITIONAL_INFO);
MongoCollection mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
@@ -77,27 +75,36 @@ void readFromDb() {
when(findIterable.first()).thenReturn(document);
- assertEquals(mongoDb.readFromDb(ID),userAccount);
+ assertEquals(mongoDb.readFromDb(ID), userAccount);
}
@Test
void writeToDb() {
MongoCollection mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
- assertDoesNotThrow(()-> {mongoDb.writeToDb(userAccount);});
+ assertDoesNotThrow(
+ () -> {
+ mongoDb.writeToDb(userAccount);
+ });
}
@Test
void updateDb() {
MongoCollection mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
- assertDoesNotThrow(()-> {mongoDb.updateDb(userAccount);});
+ assertDoesNotThrow(
+ () -> {
+ mongoDb.updateDb(userAccount);
+ });
}
@Test
void upsertDb() {
MongoCollection mongoCollection = mock(MongoCollection.class);
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
- assertDoesNotThrow(()-> {mongoDb.upsertDb(userAccount);});
+ assertDoesNotThrow(
+ () -> {
+ mongoDb.upsertDb(userAccount);
+ });
}
-}
\ No newline at end of file
+}
diff --git a/callback/README.md b/callback/README.md
index 1c90cdcea5f2..923132ac5b6a 100644
--- a/callback/README.md
+++ b/callback/README.md
@@ -37,6 +37,10 @@ Wikipedia says
> In computer programming, a callback, also known as a "call-after" function, is any executable code that is passed as an argument to other code; that other code is expected to call back (execute) the argument at a given time.
+Sequence diagram
+
+
+
## Programmatic Example of Callback Pattern in Java
We need to be notified after the executing task has finished. We pass a callback method for the executor and wait for it to call back on us.
diff --git a/callback/etc/callback-sequence-diagram.png b/callback/etc/callback-sequence-diagram.png
new file mode 100644
index 000000000000..5922734d8fd2
Binary files /dev/null and b/callback/etc/callback-sequence-diagram.png differ
diff --git a/callback/pom.xml b/callback/pom.xml
index cdae8e87044e..772615f457f9 100644
--- a/callback/pom.xml
+++ b/callback/pom.xml
@@ -34,6 +34,14 @@
callback
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/callback/src/main/java/com/iluwatar/callback/App.java b/callback/src/main/java/com/iluwatar/callback/App.java
index a574126c4cc9..7b630f8da247 100644
--- a/callback/src/main/java/com/iluwatar/callback/App.java
+++ b/callback/src/main/java/com/iluwatar/callback/App.java
@@ -34,12 +34,9 @@
@Slf4j
public final class App {
- private App() {
- }
+ private App() {}
- /**
- * Program entry point.
- */
+ /** Program entry point. */
public static void main(final String[] args) {
var task = new SimpleTask();
task.executeWith(() -> LOGGER.info("I'm done now."));
diff --git a/callback/src/main/java/com/iluwatar/callback/Callback.java b/callback/src/main/java/com/iluwatar/callback/Callback.java
index 14de46b21386..7b75b5c71077 100644
--- a/callback/src/main/java/com/iluwatar/callback/Callback.java
+++ b/callback/src/main/java/com/iluwatar/callback/Callback.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.callback;
-/**
- * Callback interface.
- */
+/** Callback interface. */
public interface Callback {
void call();
diff --git a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java
index a7ac0a9394e5..bbf060a6fc9f 100644
--- a/callback/src/main/java/com/iluwatar/callback/SimpleTask.java
+++ b/callback/src/main/java/com/iluwatar/callback/SimpleTask.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * Implementation of task that need to be executed.
- */
+/** Implementation of task that need to be executed. */
@Slf4j
public final class SimpleTask extends Task {
diff --git a/callback/src/main/java/com/iluwatar/callback/Task.java b/callback/src/main/java/com/iluwatar/callback/Task.java
index 30481f747cde..d69697454dff 100644
--- a/callback/src/main/java/com/iluwatar/callback/Task.java
+++ b/callback/src/main/java/com/iluwatar/callback/Task.java
@@ -26,14 +26,10 @@
import java.util.Optional;
-/**
- * Template-method class for callback hook execution.
- */
+/** Template-method class for callback hook execution. */
public abstract class Task {
- /**
- * Execute with callback.
- */
+ /** Execute with callback. */
final void executeWith(Callback callback) {
execute();
Optional.ofNullable(callback).ifPresent(Callback::call);
diff --git a/callback/src/test/java/com/iluwatar/callback/AppTest.java b/callback/src/test/java/com/iluwatar/callback/AppTest.java
index 26c5df95f698..ca0e93072f0f 100644
--- a/callback/src/test/java/com/iluwatar/callback/AppTest.java
+++ b/callback/src/test/java/com/iluwatar/callback/AppTest.java
@@ -24,25 +24,22 @@
*/
package com.iluwatar.callback;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that Callback example runs without errors.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests that Callback example runs without errors. */
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.
+ * 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/callback/src/test/java/com/iluwatar/callback/CallbackTest.java b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
index d8dab98e676d..99939d491f4e 100644
--- a/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
+++ b/callback/src/test/java/com/iluwatar/callback/CallbackTest.java
@@ -31,8 +31,8 @@
/**
* Add a field as a counter. Every time the callback method is called increment this field. Unit
* test checks that the field is being incremented.
- *
- * Could be done with mock objects as well where the call method call is verified.
+ *
+ *
Could be done with mock objects as well where the call method call is verified.
*/
class CallbackTest {
@@ -53,6 +53,5 @@ void test() {
task.executeWith(callback);
assertEquals(Integer.valueOf(2), callingCount, "Callback called twice");
-
}
}
diff --git a/chain-of-responsibility/README.md b/chain-of-responsibility/README.md
index f8c926973f67..454147dcd5d3 100644
--- a/chain-of-responsibility/README.md
+++ b/chain-of-responsibility/README.md
@@ -35,6 +35,10 @@ Wikipedia says
> In object-oriented design, the chain-of-responsibility pattern is a design pattern consisting of a source of command objects and a series of processing objects. Each processing object contains logic that defines the types of command objects that it can handle; the rest are passed to the next processing object in the chain.
+Flowchart
+
+
+
## Programmatic Example of Chain of Responsibility Pattern
In this Java example, the Orc King gives orders which are processed by a chain of command representing the Chain of Responsibility pattern. Learn how to implement this design pattern in Java with the following code snippet.
@@ -159,10 +163,6 @@ Orc officer handling request "torture prisoner"
Orc soldier handling request "collect tax"
```
-## Chain of Responsibility Pattern Class Diagram
-
-
-
## When to Use the Chain of Responsibility Pattern in Java
Use Chain of Responsibility when
diff --git a/chain-of-responsibility/etc/chain-of-responsibility-flowchart.png b/chain-of-responsibility/etc/chain-of-responsibility-flowchart.png
new file mode 100644
index 000000000000..875507374086
Binary files /dev/null and b/chain-of-responsibility/etc/chain-of-responsibility-flowchart.png differ
diff --git a/chain-of-responsibility/pom.xml b/chain-of-responsibility/pom.xml
index f72502045af9..e6a7fb974be7 100644
--- a/chain-of-responsibility/pom.xml
+++ b/chain-of-responsibility/pom.xml
@@ -34,6 +34,14 @@
chain-of-responsibility
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcCommander.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcCommander.java
index 70ea09463507..ad3749c985ca 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcCommander.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcCommander.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * OrcCommander.
- */
+/** OrcCommander. */
@Slf4j
public class OrcCommander implements RequestHandler {
@Override
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcKing.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcKing.java
index c01aa151a5be..7500ebf3af77 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcKing.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcKing.java
@@ -28,9 +28,7 @@
import java.util.Comparator;
import java.util.List;
-/**
- * OrcKing makes requests that are handled by the chain.
- */
+/** OrcKing makes requests that are handled by the chain. */
public class OrcKing {
private List handlers;
@@ -43,12 +41,9 @@ private void buildChain() {
handlers = Arrays.asList(new OrcCommander(), new OrcOfficer(), new OrcSoldier());
}
- /**
- * Handle request by the chain.
- */
+ /** Handle request by the chain. */
public void makeRequest(Request req) {
- handlers
- .stream()
+ handlers.stream()
.sorted(Comparator.comparing(RequestHandler::getPriority))
.filter(handler -> handler.canHandleRequest(req))
.findFirst()
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcOfficer.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcOfficer.java
index 7138a001ce55..0edb579119e1 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcOfficer.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcOfficer.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * OrcOfficer.
- */
+/** OrcOfficer. */
@Slf4j
public class OrcOfficer implements RequestHandler {
@Override
@@ -52,4 +50,3 @@ public String name() {
return "Orc officer";
}
}
-
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcSoldier.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcSoldier.java
index 6650b34746ca..7398844cd0e2 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcSoldier.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/OrcSoldier.java
@@ -26,9 +26,7 @@
import lombok.extern.slf4j.Slf4j;
-/**
- * OrcSoldier.
- */
+/** OrcSoldier. */
@Slf4j
public class OrcSoldier implements RequestHandler {
@Override
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/Request.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/Request.java
index 05c0d88d5961..2f94422647a4 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/Request.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/Request.java
@@ -27,9 +27,7 @@
import java.util.Objects;
import lombok.Getter;
-/**
- * Request.
- */
+/** Request. */
@Getter
public class Request {
@@ -39,9 +37,7 @@ public class Request {
*/
private final RequestType requestType;
- /**
- * A description of the request.
- */
+ /** A description of the request. */
private final String requestDescription;
/**
@@ -53,7 +49,7 @@ public class Request {
/**
* Create a new request of the given type and accompanied description.
*
- * @param requestType The type of request
+ * @param requestType The type of request
* @param requestDescription The description of the request
*/
public Request(final RequestType requestType, final String requestDescription) {
@@ -61,9 +57,7 @@ public Request(final RequestType requestType, final String requestDescription) {
this.requestDescription = Objects.requireNonNull(requestDescription);
}
- /**
- * Mark the request as handled.
- */
+ /** Mark the request as handled. */
public void markHandled() {
this.handled = true;
}
@@ -72,5 +66,4 @@ public void markHandled() {
public String toString() {
return getRequestDescription();
}
-
}
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestHandler.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestHandler.java
index ca46c44bbc2d..89eafbdcb43c 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestHandler.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestHandler.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.chain;
-/**
- * RequestHandler.
- */
+/** RequestHandler. */
public interface RequestHandler {
boolean canHandleRequest(Request req);
diff --git a/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestType.java b/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestType.java
index 17277cad36a2..f3cc73c97a08 100644
--- a/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestType.java
+++ b/chain-of-responsibility/src/main/java/com/iluwatar/chain/RequestType.java
@@ -24,13 +24,9 @@
*/
package com.iluwatar.chain;
-/**
- * RequestType enumeration.
- */
+/** RequestType enumeration. */
public enum RequestType {
-
DEFEND_CASTLE,
TORTURE_PRISONER,
COLLECT_TAX
-
}
diff --git a/chain-of-responsibility/src/test/java/com/iluwatar/chain/AppTest.java b/chain-of-responsibility/src/test/java/com/iluwatar/chain/AppTest.java
index 702d58dcb3fd..4d2cd68991d9 100644
--- a/chain-of-responsibility/src/test/java/com/iluwatar/chain/AppTest.java
+++ b/chain-of-responsibility/src/test/java/com/iluwatar/chain/AppTest.java
@@ -24,25 +24,22 @@
*/
package com.iluwatar.chain;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Application test
- */
+import org.junit.jupiter.api.Test;
+
+/** Application test */
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.
+ * 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/chain-of-responsibility/src/test/java/com/iluwatar/chain/OrcKingTest.java b/chain-of-responsibility/src/test/java/com/iluwatar/chain/OrcKingTest.java
index 7bb9de901640..ec53a411787a 100644
--- a/chain-of-responsibility/src/test/java/com/iluwatar/chain/OrcKingTest.java
+++ b/chain-of-responsibility/src/test/java/com/iluwatar/chain/OrcKingTest.java
@@ -29,32 +29,26 @@
import java.util.List;
import org.junit.jupiter.api.Test;
-/**
- * OrcKingTest
- *
- */
+/** OrcKingTest */
class OrcKingTest {
- /**
- * All possible requests
- */
- private static final List REQUESTS = List.of(
- new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"),
- new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"),
- new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ...")
- );
+ /** All possible requests */
+ private static final List REQUESTS =
+ List.of(
+ new Request(RequestType.DEFEND_CASTLE, "Don't let the barbarians enter my castle!!"),
+ new Request(RequestType.TORTURE_PRISONER, "Don't just stand there, tickle him!"),
+ new Request(RequestType.COLLECT_TAX, "Don't steal, the King hates competition ..."));
@Test
void testMakeRequest() {
final var king = new OrcKing();
- REQUESTS.forEach(request -> {
- king.makeRequest(request);
- assertTrue(
- request.isHandled(),
- "Expected all requests from King to be handled, but [" + request + "] was not!"
- );
- });
+ REQUESTS.forEach(
+ request -> {
+ king.makeRequest(request);
+ assertTrue(
+ request.isHandled(),
+ "Expected all requests from King to be handled, but [" + request + "] was not!");
+ });
}
-
-}
\ No newline at end of file
+}
diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md
index b8e0ff248122..99c1b7b4d398 100644
--- a/circuit-breaker/README.md
+++ b/circuit-breaker/README.md
@@ -33,6 +33,10 @@ Wikipedia says
> Circuit breaker is a design pattern used in modern software development. It is used to detect failures and encapsulates the logic of preventing a failure from constantly recurring, during maintenance, temporary external system failure or unexpected system difficulties.
+Flowchart
+
+
+
## Programmatic Example of Circuit Breaker Pattern in Java
This Java example demonstrates how the Circuit Breaker pattern can manage remote service failures and maintain system stability.
diff --git a/circuit-breaker/etc/circuit-breaker-flowchart.png b/circuit-breaker/etc/circuit-breaker-flowchart.png
new file mode 100644
index 000000000000..6e0af83e0eee
Binary files /dev/null and b/circuit-breaker/etc/circuit-breaker-flowchart.png differ
diff --git a/circuit-breaker/pom.xml b/circuit-breaker/pom.xml
index eda24a4f4d5a..5b2be0c5605a 100644
--- a/circuit-breaker/pom.xml
+++ b/circuit-breaker/pom.xml
@@ -34,6 +34,14 @@
circuit-breaker
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
index a29b6d769826..6011aa9d10e6 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/App.java
@@ -27,33 +27,29 @@
import lombok.extern.slf4j.Slf4j;
/**
- *
* The intention of the Circuit Builder pattern is to handle remote failures robustly, which is to
* mean that if a service is dependent on n number of other services, and m of them fail, we should
* be able to recover from that failure by ensuring that the user can still use the services that
* are actually functional, and resources are not tied up by uselessly by the services which are not
* working. However, we should also be able to detect when any of the m failing services become
* operational again, so that we can use it
- *
- *
- * In this example, the circuit breaker pattern is demonstrated by using three services: {@link
+ *
+ *
In this example, the circuit breaker pattern is demonstrated by using three services: {@link
* DelayedRemoteService}, {@link QuickRemoteService} and {@link MonitoringService}. The monitoring
- * service is responsible for calling three services: a local service, a quick remove service
- * {@link QuickRemoteService} and a delayed remote service {@link DelayedRemoteService} , and by
- * using the circuit breaker construction we ensure that if the call to remote service is going to
- * fail, we are going to save our resources and not make the function call at all, by wrapping our
- * call to the remote services in the {@link DefaultCircuitBreaker} implementation object.
- *
- *
- * This works as follows: The {@link DefaultCircuitBreaker} object can be in one of three states:
- * Open , Closed and Half-Open , which represents the real world circuits. If
- * the state is closed (initial), we assume everything is alright and perform the function call.
+ * service is responsible for calling three services: a local service, a quick remove service {@link
+ * QuickRemoteService} and a delayed remote service {@link DelayedRemoteService} , and by using the
+ * circuit breaker construction we ensure that if the call to remote service is going to fail, we
+ * are going to save our resources and not make the function call at all, by wrapping our call to
+ * the remote services in the {@link DefaultCircuitBreaker} implementation object.
+ *
+ *
This works as follows: The {@link DefaultCircuitBreaker} object can be in one of three states:
+ * Open , Closed and Half-Open , which represents the real world circuits. If the
+ * state is closed (initial), we assume everything is alright and perform the function call.
* However, every time the call fails, we note it and once it crosses a threshold, we set the state
* to Open, preventing any further calls to the remote server. Then, after a certain retry period
* (during which we expect thee service to recover), we make another call to the remote server and
* this state is called the Half-Open state, where it stays till the service is down, and once it
* recovers, it goes back to the closed state and the cycle continues.
- *
*/
@Slf4j
public class App {
@@ -68,45 +64,45 @@ public static void main(String[] args) {
var serverStartTime = System.nanoTime();
var delayedService = new DelayedRemoteService(serverStartTime, 5);
- var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2,
- 2000 * 1000 * 1000);
+ var delayedServiceCircuitBreaker =
+ new DefaultCircuitBreaker(delayedService, 3000, 2, 2000 * 1000 * 1000);
var quickService = new QuickRemoteService();
- var quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, 2,
- 2000 * 1000 * 1000);
+ var quickServiceCircuitBreaker =
+ new DefaultCircuitBreaker(quickService, 3000, 2, 2000 * 1000 * 1000);
- //Create an object of monitoring service which makes both local and remote calls
- var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
- quickServiceCircuitBreaker);
+ // Create an object of monitoring service which makes both local and remote calls
+ var monitoringService =
+ new MonitoringService(delayedServiceCircuitBreaker, quickServiceCircuitBreaker);
- //Fetch response from local resource
+ // Fetch response from local resource
LOGGER.info(monitoringService.localResourceResponse());
- //Fetch response from delayed service 2 times, to meet the failure threshold
+ // Fetch response from delayed service 2 times, to meet the failure threshold
LOGGER.info(monitoringService.delayedServiceResponse());
LOGGER.info(monitoringService.delayedServiceResponse());
- //Fetch current state of delayed service circuit breaker after crossing failure threshold limit
- //which is OPEN now
+ // Fetch current state of delayed service circuit breaker after crossing failure threshold limit
+ // which is OPEN now
LOGGER.info(delayedServiceCircuitBreaker.getState());
- //Meanwhile, the delayed service is down, fetch response from the healthy quick service
+ // Meanwhile, the delayed service is down, fetch response from the healthy quick service
LOGGER.info(monitoringService.quickServiceResponse());
LOGGER.info(quickServiceCircuitBreaker.getState());
- //Wait for the delayed service to become responsive
+ // Wait for the delayed service to become responsive
try {
LOGGER.info("Waiting for delayed service to become responsive");
Thread.sleep(5000);
} catch (InterruptedException e) {
LOGGER.error("An error occurred: ", e);
}
- //Check the state of delayed circuit breaker, should be HALF_OPEN
+ // Check the state of delayed circuit breaker, should be HALF_OPEN
LOGGER.info(delayedServiceCircuitBreaker.getState());
- //Fetch response from delayed service, which should be healthy by now
+ // Fetch response from delayed service, which should be healthy by now
LOGGER.info(monitoringService.delayedServiceResponse());
- //As successful response is fetched, it should be CLOSED again.
+ // As successful response is fetched, it should be CLOSED again.
LOGGER.info(delayedServiceCircuitBreaker.getState());
}
}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java
index aaccd65b1dce..31e11751a4ea 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/CircuitBreaker.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.circuitbreaker;
-/**
- * The Circuit breaker interface.
- */
+/** The Circuit breaker interface. */
public interface CircuitBreaker {
// Success response. Reset everything to defaults
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java
index 18febbb6b1b7..762c04d6b589 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DefaultCircuitBreaker.java
@@ -45,14 +45,14 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
/**
* Constructor to create an instance of Circuit Breaker.
*
- * @param timeout Timeout for the API request. Not necessary for this simple example
+ * @param timeout Timeout for the API request. Not necessary for this simple example
* @param failureThreshold Number of failures we receive from the depended on service before
- * changing state to 'OPEN'
- * @param retryTimePeriod Time, in nanoseconds, period after which a new request is made to
- * remote service for status check.
+ * changing state to 'OPEN'
+ * @param retryTimePeriod Time, in nanoseconds, period after which a new request is made to remote
+ * service for status check.
*/
- DefaultCircuitBreaker(RemoteService serviceToCall, long timeout, int failureThreshold,
- long retryTimePeriod) {
+ DefaultCircuitBreaker(
+ RemoteService serviceToCall, long timeout, int failureThreshold, long retryTimePeriod) {
this.service = serviceToCall;
// We start in a closed state hoping that everything is fine
this.state = State.CLOSED;
@@ -61,7 +61,7 @@ public class DefaultCircuitBreaker implements CircuitBreaker {
// Used to break the calls made to remote resource if it exceeds the limit
this.timeout = timeout;
this.retryTimePeriod = retryTimePeriod;
- //An absurd amount of time in future which basically indicates the last failure never happened
+ // An absurd amount of time in future which basically indicates the last failure never happened
this.lastFailureTime = System.nanoTime() + futureTime;
this.failureCount = 0;
}
@@ -84,16 +84,16 @@ public void recordFailure(String response) {
// Evaluate the current state based on failureThreshold, failureCount and lastFailureTime.
protected void evaluateState() {
- if (failureCount >= failureThreshold) { //Then something is wrong with remote service
+ if (failureCount >= failureThreshold) { // Then something is wrong with remote service
if ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
- //We have waited long enough and should try checking if service is up
+ // We have waited long enough and should try checking if service is up
state = State.HALF_OPEN;
} else {
- //Service would still probably be down
+ // Service would still probably be down
state = State.OPEN;
}
} else {
- //Everything is working fine
+ // Everything is working fine
state = State.CLOSED;
}
}
@@ -140,9 +140,9 @@ public String attemptRequest() throws RemoteServiceException {
} else {
// Make the API request if the circuit is not OPEN
try {
- //In a real application, this would be run in a thread and the timeout
- //parameter of the circuit breaker would be utilized to know if service
- //is working. Here, we simulate that based on server response itself
+ // In a real application, this would be run in a thread and the timeout
+ // parameter of the circuit breaker would be utilized to know if service
+ // is working. Here, we simulate that based on server response itself
var response = service.call();
// Yay!! the API responded fine. Let's reset everything.
recordSuccess();
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java
index 4db2988146f4..ad87f1a6e71d 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/DelayedRemoteService.java
@@ -56,12 +56,12 @@ public DelayedRemoteService() {
@Override
public String call() throws RemoteServiceException {
var currentTime = System.nanoTime();
- //Since currentTime and serverStartTime are both in nanoseconds, we convert it to
- //seconds by diving by 10e9 and ensure floating point division by multiplying it
- //with 1.0 first. We then check if it is greater or less than specified delay and then
- //send the reply
+ // Since currentTime and serverStartTime are both in nanoseconds, we convert it to
+ // seconds by diving by 10e9 and ensure floating point division by multiplying it
+ // with 1.0 first. We then check if it is greater or less than specified delay and then
+ // send the reply
if ((currentTime - serverStartTime) * 1.0 / (1000 * 1000 * 1000) < delay) {
- //Can use Thread.sleep() here to block and simulate a hung server
+ // Can use Thread.sleep() here to block and simulate a hung server
throw new RemoteServiceException("Delayed service is down");
}
return "Delayed service is working";
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java
index 33564acc16ff..3fa5cd776d8c 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/MonitoringService.java
@@ -39,7 +39,7 @@ public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickServ
this.quickService = quickService;
}
- //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
+ // Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
public String localResourceResponse() {
return "Local Service is working";
}
@@ -69,4 +69,4 @@ public String quickServiceResponse() {
return e.getMessage();
}
}
-}
\ No newline at end of file
+}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java
index 404f1c05b4c7..2367e49233ae 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/QuickRemoteService.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.circuitbreaker;
-/**
- * A quick response remote service, that responds healthy without any delay or failure.
- */
+/** A quick response remote service, that responds healthy without any delay or failure. */
public class QuickRemoteService implements RemoteService {
@Override
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java
index dac616f03414..ced5d3ac9d47 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteService.java
@@ -30,6 +30,6 @@
*/
public interface RemoteService {
- //Fetch response from remote service.
+ // Fetch response from remote service.
String call() throws RemoteServiceException;
}
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java
index eb033cd8ebfa..48deec756b75 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/RemoteServiceException.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.circuitbreaker;
-/**
- * Exception thrown when {@link RemoteService} does not respond successfully.
- */
+/** Exception thrown when {@link RemoteService} does not respond successfully. */
public class RemoteServiceException extends Exception {
public RemoteServiceException(String message) {
diff --git a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java
index e59b6ce8b41f..f2668281eb57 100644
--- a/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java
+++ b/circuit-breaker/src/main/java/com/iluwatar/circuitbreaker/State.java
@@ -24,11 +24,9 @@
*/
package com.iluwatar.circuitbreaker;
-/**
- * Enumeration for states the circuit breaker could be in.
- */
+/** Enumeration for states the circuit breaker could be in. */
public enum State {
CLOSED,
OPEN,
HALF_OPEN
-}
\ No newline at end of file
+}
diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java
index 108695706a07..3cbeadcd13de 100644
--- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java
+++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/AppTest.java
@@ -31,20 +31,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-/**
- * App Test showing usage of circuit breaker.
- */
+/** App Test showing usage of circuit breaker. */
class AppTest {
private static final Logger LOGGER = LoggerFactory.getLogger(AppTest.class);
- //Startup delay for delayed service (in seconds)
+ // Startup delay for delayed service (in seconds)
private static final int STARTUP_DELAY = 4;
- //Number of failed requests for circuit breaker to open
+ // Number of failed requests for circuit breaker to open
private static final int FAILURE_THRESHOLD = 1;
- //Time period in seconds for circuit breaker to retry service
+ // Time period in seconds for circuit breaker to retry service
private static final int RETRY_PERIOD = 2;
private MonitoringService monitoringService;
@@ -62,75 +60,75 @@ class AppTest {
@BeforeEach
void setupCircuitBreakers() {
var delayedService = new DelayedRemoteService(System.nanoTime(), STARTUP_DELAY);
- //Set the circuit Breaker parameters
- delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
- FAILURE_THRESHOLD,
- RETRY_PERIOD * 1000 * 1000 * 1000);
+ // Set the circuit Breaker parameters
+ delayedServiceCircuitBreaker =
+ new DefaultCircuitBreaker(
+ delayedService, 3000, FAILURE_THRESHOLD, RETRY_PERIOD * 1000 * 1000 * 1000);
var quickService = new QuickRemoteService();
- //Set the circuit Breaker parameters
- quickServiceCircuitBreaker = new DefaultCircuitBreaker(quickService, 3000, FAILURE_THRESHOLD,
- RETRY_PERIOD * 1000 * 1000 * 1000);
-
- monitoringService = new MonitoringService(delayedServiceCircuitBreaker,
- quickServiceCircuitBreaker);
+ // Set the circuit Breaker parameters
+ quickServiceCircuitBreaker =
+ new DefaultCircuitBreaker(
+ quickService, 3000, FAILURE_THRESHOLD, RETRY_PERIOD * 1000 * 1000 * 1000);
+ monitoringService =
+ new MonitoringService(delayedServiceCircuitBreaker, quickServiceCircuitBreaker);
}
@Test
void testFailure_OpenStateTransition() {
- //Calling delayed service, which will be unhealthy till 4 seconds
+ // Calling delayed service, which will be unhealthy till 4 seconds
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
- //As failure threshold is "1", the circuit breaker is changed to OPEN
+ // As failure threshold is "1", the circuit breaker is changed to OPEN
assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
- //As circuit state is OPEN, we expect a quick fallback response from circuit breaker.
+ // As circuit state is OPEN, we expect a quick fallback response from circuit breaker.
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
- //Meanwhile, the quick service is responding and the circuit state is CLOSED
+ // Meanwhile, the quick service is responding and the circuit state is CLOSED
assertEquals("Quick Service is working", monitoringService.quickServiceResponse());
assertEquals("CLOSED", quickServiceCircuitBreaker.getState());
-
}
@Test
void testFailure_HalfOpenStateTransition() {
- //Calling delayed service, which will be unhealthy till 4 seconds
+ // Calling delayed service, which will be unhealthy till 4 seconds
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
- //As failure threshold is "1", the circuit breaker is changed to OPEN
+ // As failure threshold is "1", the circuit breaker is changed to OPEN
assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
- //Waiting for recovery period of 2 seconds for circuit breaker to retry service.
+ // Waiting for recovery period of 2 seconds for circuit breaker to retry service.
try {
LOGGER.info("Waiting 2s for delayed service to become responsive");
Thread.sleep(2000);
} catch (InterruptedException e) {
LOGGER.error("An error occurred: ", e);
}
- //After 2 seconds, the circuit breaker should move to "HALF_OPEN" state and retry fetching response from service again
+ // After 2 seconds, the circuit breaker should move to "HALF_OPEN" state and retry fetching
+ // response from service again
assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState());
-
}
@Test
void testRecovery_ClosedStateTransition() {
- //Calling delayed service, which will be unhealthy till 4 seconds
+ // Calling delayed service, which will be unhealthy till 4 seconds
assertEquals("Delayed service is down", monitoringService.delayedServiceResponse());
- //As failure threshold is "1", the circuit breaker is changed to OPEN
+ // As failure threshold is "1", the circuit breaker is changed to OPEN
assertEquals("OPEN", delayedServiceCircuitBreaker.getState());
- //Waiting for 4 seconds, which is enough for DelayedService to become healthy and respond successfully.
+ // Waiting for 4 seconds, which is enough for DelayedService to become healthy and respond
+ // successfully.
try {
LOGGER.info("Waiting 4s for delayed service to become responsive");
Thread.sleep(4000);
} catch (InterruptedException e) {
LOGGER.error("An error occurred: ", e);
}
- //As retry period is 2 seconds (<4 seconds of wait), hence the circuit breaker should be back in HALF_OPEN state.
+ // As retry period is 2 seconds (<4 seconds of wait), hence the circuit breaker should be back
+ // in HALF_OPEN state.
assertEquals("HALF_OPEN", delayedServiceCircuitBreaker.getState());
- //Check the success response from delayed service.
+ // Check the success response from delayed service.
assertEquals("Delayed service is working", monitoringService.delayedServiceResponse());
- //As the response is success, the state should be CLOSED
+ // As the response is success, the state should be CLOSED
assertEquals("CLOSED", delayedServiceCircuitBreaker.getState());
}
-
}
diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java
index 465371a3aad3..c184a8376220 100644
--- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java
+++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/DefaultCircuitBreakerTest.java
@@ -28,29 +28,27 @@
import org.junit.jupiter.api.Test;
-/**
- * Circuit Breaker test
- */
+/** Circuit Breaker test */
class DefaultCircuitBreakerTest {
- //long timeout, int failureThreshold, long retryTimePeriod
+ // long timeout, int failureThreshold, long retryTimePeriod
@Test
void testEvaluateState() {
var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 100);
- //Right now, failureCountfailureThreshold, and lastFailureTime is nearly equal to current time,
- //state should be half-open
+ // Since failureCount>failureThreshold, and lastFailureTime is nearly equal to current time,
+ // state should be half-open
assertEquals(circuitBreaker.getState(), "HALF_OPEN");
- //Since failureCount>failureThreshold, and lastFailureTime is much lesser current time,
- //state should be open
+ // Since failureCount>failureThreshold, and lastFailureTime is much lesser current time,
+ // state should be open
circuitBreaker.lastFailureTime = System.nanoTime() - 1000 * 1000 * 1000 * 1000;
circuitBreaker.evaluateState();
assertEquals(circuitBreaker.getState(), "OPEN");
- //Now set it back again to closed to test idempotency
+ // Now set it back again to closed to test idempotency
circuitBreaker.failureCount = 0;
circuitBreaker.evaluateState();
assertEquals(circuitBreaker.getState(), "CLOSED");
@@ -59,23 +57,24 @@ void testEvaluateState() {
@Test
void testSetStateForBypass() {
var circuitBreaker = new DefaultCircuitBreaker(null, 1, 1, 2000 * 1000 * 1000);
- //Right now, failureCount {
- var obj = new DelayedRemoteService();
- obj.call();
- });
+ Assertions.assertThrows(
+ RemoteServiceException.class,
+ () -> {
+ var obj = new DelayedRemoteService();
+ obj.call();
+ });
}
/**
@@ -54,7 +54,7 @@ void testDefaultConstructor() throws RemoteServiceException {
*/
@Test
void testParameterizedConstructor() throws RemoteServiceException {
- var obj = new DelayedRemoteService(System.nanoTime()-2000*1000*1000,1);
- assertEquals("Delayed service is working",obj.call());
+ var obj = new DelayedRemoteService(System.nanoTime() - 2000 * 1000 * 1000, 1);
+ assertEquals("Delayed service is working", obj.call());
}
}
diff --git a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java
index 51dde10ece8b..f7781fd1c58d 100644
--- a/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java
+++ b/circuit-breaker/src/test/java/com/iluwatar/circuitbreaker/MonitoringServiceTest.java
@@ -28,28 +28,25 @@
import org.junit.jupiter.api.Test;
-/**
- * Monitoring Service test
- */
+/** Monitoring Service test */
class MonitoringServiceTest {
- //long timeout, int failureThreshold, long retryTimePeriod
+ // long timeout, int failureThreshold, long retryTimePeriod
@Test
void testLocalResponse() {
- var monitoringService = new MonitoringService(null,null);
+ var monitoringService = new MonitoringService(null, null);
var response = monitoringService.localResourceResponse();
assertEquals(response, "Local Service is working");
}
@Test
void testDelayedRemoteResponseSuccess() {
- var delayedService = new DelayedRemoteService(System.nanoTime()-2*1000*1000*1000, 2);
- var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
- 1,
- 2 * 1000 * 1000 * 1000);
+ var delayedService = new DelayedRemoteService(System.nanoTime() - 2 * 1000 * 1000 * 1000, 2);
+ var delayedServiceCircuitBreaker =
+ new DefaultCircuitBreaker(delayedService, 3000, 1, 2 * 1000 * 1000 * 1000);
- var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
- //Set time in past to make the server work
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, null);
+ // Set time in past to make the server work
var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Delayed service is working");
}
@@ -57,11 +54,10 @@ void testDelayedRemoteResponseSuccess() {
@Test
void testDelayedRemoteResponseFailure() {
var delayedService = new DelayedRemoteService(System.nanoTime(), 2);
- var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
- 1,
- 2 * 1000 * 1000 * 1000);
- var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
- //Set time as current time as initially server fails
+ var delayedServiceCircuitBreaker =
+ new DefaultCircuitBreaker(delayedService, 3000, 1, 2 * 1000 * 1000 * 1000);
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, null);
+ // Set time as current time as initially server fails
var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Delayed service is down");
}
@@ -69,11 +65,10 @@ void testDelayedRemoteResponseFailure() {
@Test
void testQuickRemoteServiceResponse() {
var delayedService = new QuickRemoteService();
- var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000,
- 1,
- 2 * 1000 * 1000 * 1000);
- var monitoringService = new MonitoringService(delayedServiceCircuitBreaker,null);
- //Set time as current time as initially server fails
+ var delayedServiceCircuitBreaker =
+ new DefaultCircuitBreaker(delayedService, 3000, 1, 2 * 1000 * 1000 * 1000);
+ var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, null);
+ // Set time as current time as initially server fails
var response = monitoringService.delayedServiceResponse();
assertEquals(response, "Quick Service is working");
}
diff --git a/clean-architecture/README.md b/clean-architecture/README.md
new file mode 100644
index 000000000000..599ae19ce13e
--- /dev/null
+++ b/clean-architecture/README.md
@@ -0,0 +1,291 @@
+---
+title: "Clean Architecture - A Software Maintainable Architectural style."
+shortTitle: Clean Architecture
+description: "Learn the Clean Architecture Style in Java with real-world examples, code snippets, and class diagrams. Enhance your coding skills with our detailed explanations."
+category: Architectural
+language: en
+tag:
+ - Architecture
+ - Decoupling
+ - Domain
+ - Inversion of control
+ - Layered architecture
+ - Modularity
+ - Testing
+---
+
+## Intent of Clean Architecture.
+
+To organize the system so that the core business logic remains independent from external concerns and frameworks.
+
+## Detailed Explanation of Clean Architecture Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a large pizza chain with multiple ordering channels—web, mobile app, phone calls, and in-store kiosks. The core “pizza domain” logic (calculating prices, preparing orders, managing loyalty points) is kept entirely separate from the user interfaces and storage mechanisms. As a result, the chain can add or change the ordering channel (for example, introducing a chatbot or swapping out the database) without altering the fundamental pizza-ordering rules, thanks to the layered boundaries and strict dependency rules of Clean Architecture.
+
+In plain words
+
+> Clean Architecture is a software design approach that isolates the core business logic from external concerns (like databases, frameworks, or UI) through strict layering and clear boundaries, ensuring that changes in one layer don't ripple through the entire system.
+
+Wikipedia says
+
+> The clean architecture proposed by Robert C. Martin in 2012 combines the principles of the hexagonal architecture, the onion architecture and several other variants. It provides additional levels of detail of the component, which are presented as concentric rings. It isolates adapters and interfaces (user interface, databases, external systems, devices) in the outer rings of the architecture and leaves the inner rings for use cases and entities. The clean architecture uses the principle of dependency inversion with the strict rule that dependencies shall only exist between an outer ring to an inner ring and never the contrary.
+
+Mind map
+
+
+
+Flowchart
+
+
+
+## Programmatic Example of Clean Architecture Pattern
+
+First, we define the core domain entities: `Product`, `Order`, and `Cart`. These classes capture the fundamental business logic and state.
+
+```java
+public class Product {
+ private String id;
+ private String name;
+ private double price;
+
+ public Product(String id, String name, double price) {
+ this.id = id;
+ this.name = name;
+ this.price = price;
+ }
+}
+```
+
+```java
+public class Cart {
+ private Product product;
+ private int quantity;
+
+ public CartItem(Product product, int quantity) {
+ this.product = product;
+ this.quantity = quantity;
+ }
+
+ public double getTotalPrice() {
+ return product.getPrice() * quantity;
+ }
+}
+```
+
+```java
+public class Order {
+ private String orderId;
+ private List items;
+ private double totalPrice;
+
+ public Order(String orderId, List items) {
+ this.orderId = orderId;
+ this.items = items;
+ this.totalPrice = items.stream().mapToDouble(CartItem::getTotalPrice).sum();
+ }
+}
+```
+
+The repository interfaces are created to abstract data operations for each domain object, allowing us to switch out storage or persistence mechanisms without changing higher-level logic.
+
+```java
+public interface CartRepository {
+ void addItemToCart(String userId, Product product, int quantity);
+ void removeItemFromCart(String userId, String productId);
+ List getItemsInCart(String userId);
+ double calculateTotal(String userId);
+ void clearCart(String userId);
+}
+```
+```java
+public interface ProductRepository {
+ Product getProductById(String productId);
+}
+```
+```java
+public interface OrderRepository {
+ void saveOrder(Order order);
+}
+```
+
+The in-memory data store implementations use simple collections to hold state. They demonstrate how easily we can replace or extend the data layer (e.g., swapping in a database) without affecting the domain logic.
+
+```java
+public class InMemoryCartRepository implements CartRepository {
+ private final Map> userCarts = new HashMap<>();
+
+ @Override
+ public void addItemToCart(String userId, Product product, int quantity) {
+ List cart = userCarts.getOrDefault(userId, new ArrayList<>());
+ cart.add(new Cart(product, quantity));
+ userCarts.put(userId, cart);
+ }
+
+ @Override
+ public void removeItemFromCart(String userId, String productId) {
+ List cart = userCarts.get(userId);
+ if (cart != null) {
+ cart.removeIf(item -> item.getProduct().getId().equals(productId));
+ }
+ }
+
+ @Override
+ public List getItemsInCart(String userId) {
+ return userCarts.getOrDefault(userId, new ArrayList<>());
+ }
+
+ @Override
+ public double calculateTotal(String userId) {
+ return userCarts.getOrDefault(userId, new ArrayList<>())
+ .stream()
+ .mapToDouble(Cart::getTotalPrice)
+ .sum();
+ }
+
+ @Override
+ public void clearCart(String userId) {
+ userCarts.remove(userId);
+ }
+}
+```
+```java
+public class InMemoryOrderRepository implements OrderRepository {
+ private final List orders = new ArrayList<>();
+
+ @Override
+ public void saveOrder(Order order) {
+ orders.add(order);
+ }
+}
+```
+
+```java
+public class InMemoryProductRepository implements ProductRepository {
+ private final Map products = new HashMap<>();
+
+ public InMemoryProductRepository() {
+ products.put("1", new Product("1", "Laptop", 1000.0));
+ products.put("2", new Product("2", "Smartphone", 500.0));
+ }
+
+ @Override
+ public Product getProductById(String productId) {
+ return products.get(productId);
+ }
+}
+```
+
+The order controller coordinates the checkout process by using the use-case or service layer (`ShoppingCartService`).
+
+```java
+public class OrderController{
+ private final ShoppingCartService shoppingCartUseCase;
+
+ public OrderController(ShoppingCartService shoppingCartUseCase) {
+ this.shoppingCartUseCase = shoppingCartUseCase;
+ }
+
+ public Order checkout(String userId) {
+ return shoppingCartUseCase.checkout(userId);
+ }
+}
+```
+
+The cart controller focuses on cart-related actions like adding or removing items and calculating totals.
+
+
+```java
+public class CartController {
+ private final ShoppingCartService shoppingCartUseCase;
+
+ public CartController(ShoppingCartService shoppingCartUseCase) {
+ this.shoppingCartUseCase = shoppingCartUseCase;
+ }
+
+ public void addItemToCart(String userId, String productId, int quantity) {
+ shoppingCartUseCase.addItemToCart(userId, productId, quantity);
+ }
+
+ public void removeItemFromCart(String userId, String productId) {
+ shoppingCartUseCase.removeItemFromCart(userId, productId);
+ }
+
+ public double calculateTotal(String userId) {
+ return shoppingCartUseCase.calculateTotal(userId);
+ }
+}
+```
+
+The clean architecture in action. In the `main` method, we wire up everything, simulating a typical user flow: items are added to the cart, the total is calculated, and finally an order is placed.
+
+```java
+public static void main(String[] args) {
+
+ ProductRepository productRepository = new InMemoryProductRepository();
+ CartRepository cartRepository = new InMemoryCartRepository();
+ OrderRepository orderRepository = new InMemoryOrderRepository();
+
+ ShoppingCartService shoppingCartUseCase =
+ new ShoppingCartService(productRepository, cartRepository, orderRepository);
+
+ CartController cartController = new CartController(shoppingCartUseCase);
+ OrderController orderController = new OrderController(shoppingCartUseCase);
+
+ String userId = "user123";
+ cartController.addItemToCart(userId, "1", 1);
+ cartController.addItemToCart(userId, "2", 2);
+
+ System.out.println("Total: $" + cartController.calculateTotal(userId));
+
+ Order order = orderController.checkout(userId);
+ System.out.println(
+ "Order placed! Order ID: " + order.getOrderId() + ", Total: $" + order.getTotalPrice());
+ }
+```
+
+The output of the code.
+
+```md
+Total: $2000.0
+Order placed! Order ID: ORDER-1743349969254, Total: $2000.0
+```
+
+## When to Use the Clean Architecture Pattern in Java
+
+* When you want to keep business rules independent of UI, database, or any other external agency
+* When you need a high level of maintainability and testability in large Java applications
+* When you aim to enforce clear boundaries among application layers
+
+## Real-World Applications of Clean Architecture Pattern in Java
+
+* Large-scale enterprise systems in finance and insurance domains
+* Microservices-based architectures that prioritize decoupling and modular design
+* Java systems requiring stringent separation of concerns and domain-centered design
+
+## Benefits and Trade-offs of Clean Architecture Pattern
+
+Benefits:
+
+* High maintainability by isolating core logic from infrastructure details
+* Enhanced testability through clear boundaries around the domain model
+* Flexibility in replacing or upgrading external components without affecting core logic
+
+Trade-Offs:
+
+* Initial complexity from enforcing strict layers and boundaries
+* Potential overhead in smaller projects not requiring such rigid separation
+* Requires disciplined team adherence to architecture rules
+
+## Related Java Design Patterns
+
+* [Dependency Injection](https://java-design-patterns.com/patterns/dependency-injection/): Facilitates decoupling layers by injecting dependencies rather than hard-coding them
+* [Layered Architecture](https://java-design-patterns.com/patterns/layered-architecture/): Both separate concerns into distinct tiers but Clean Architecture emphasizes strict dependency rules
+* [Hexagonal Architecture](https://java-design-patterns.com/patterns/hexagonal-architecture/): Similar focus on isolating core logic with ports and adapters
+
+## References and Credits
+
+* [Clean Architecture: A Craftsman's Guide to Software Structure and Design](https://amzn.to/3UoKkaR)
+* [Clean Code: A Handbook of Agile Software Craftsmanship](https://amzn.to/3wRnjp5)
+* [Domain-Driven Design: Tackling Complexity in the Heart of Software](https://amzn.to/3wlDrze)
diff --git a/clean-architecture/etc/clean-architecture-flowchart.png b/clean-architecture/etc/clean-architecture-flowchart.png
new file mode 100644
index 000000000000..043827f08844
Binary files /dev/null and b/clean-architecture/etc/clean-architecture-flowchart.png differ
diff --git a/clean-architecture/etc/clean-architecture-mind-map.png b/clean-architecture/etc/clean-architecture-mind-map.png
new file mode 100644
index 000000000000..59786d4a7c3e
Binary files /dev/null and b/clean-architecture/etc/clean-architecture-mind-map.png differ
diff --git a/clean-architecture/etc/cleanArchitectureUMLDiagram.PNG b/clean-architecture/etc/cleanArchitectureUMLDiagram.PNG
new file mode 100644
index 000000000000..f107d34a14d8
Binary files /dev/null and b/clean-architecture/etc/cleanArchitectureUMLDiagram.PNG differ
diff --git a/clean-architecture/etc/cleanArchitectureUMLDiagram.png b/clean-architecture/etc/cleanArchitectureUMLDiagram.png
new file mode 100644
index 000000000000..f107d34a14d8
Binary files /dev/null and b/clean-architecture/etc/cleanArchitectureUMLDiagram.png differ
diff --git a/clean-architecture/pom.xml b/clean-architecture/pom.xml
new file mode 100644
index 000000000000..8222c6d9d80d
--- /dev/null
+++ b/clean-architecture/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ clean-architecture
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.cleanarchitecture.App
+
+
+
+
+
+
+
+
+
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/App.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/App.java
new file mode 100644
index 000000000000..4b56dcbbfd0f
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/App.java
@@ -0,0 +1,71 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Clean Architecture ensures separation of concerns by organizing code, into layers and making it
+ * scalable and maintainable.
+ *
+ * In the example there are Entities (Core Models) – Product, Cart, Order handle business logic.
+ * Use Cases (Application Logic) – ShoppingCartService manages operations like adding items and
+ * checkout. Interfaces & Adapters – Repositories (CartRepository, OrderRepository) abstract data
+ * handling, while controllers (CartController, OrderController) manage interactions.
+ */
+@Slf4j
+public final class App {
+
+ private App() {
+ throw new UnsupportedOperationException("Utility class");
+ }
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(final String[] args) {
+ ProductRepository productRepository = new InMemoryProductRepository();
+ CartRepository cartRepository = new InMemoryCartRepository();
+ OrderRepository orderRepository = new InMemoryOrderRepository();
+
+ ShoppingCartService shoppingCartUseCase =
+ new ShoppingCartService(productRepository, cartRepository, orderRepository);
+
+ CartController cartController = new CartController(shoppingCartUseCase);
+ OrderController orderController = new OrderController(shoppingCartUseCase);
+
+ String userId = "user123";
+ cartController.addItemToCart(userId, "1", 1);
+ cartController.addItemToCart(userId, "2", 2);
+
+ Order order = orderController.checkout(userId);
+ LOGGER.info("Total: ${}", cartController.calculateTotal(userId));
+
+ LOGGER.info(
+ "Order placed! Order ID: {}, Total: ${}", order.getOrderId(), order.getTotalPrice());
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Cart.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Cart.java
new file mode 100644
index 000000000000..c4e65df94845
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Cart.java
@@ -0,0 +1,64 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import lombok.Getter;
+
+/**
+ * Represents a shopping cart containing a product and its quantity. This class calculates the total
+ * price of the product based on its price and quantity.
+ */
+@Getter
+public class Cart {
+ /** The product in the cart. It holds the product details such as name, price, and description. */
+ private final Product product;
+
+ /**
+ * The quantity of the product in the cart. It represents how many units of the product are added
+ * to the cart.
+ */
+ private final int quantity;
+
+ /**
+ * Constructs a new Cart instance with a specified product and quantity.
+ *
+ * @param prod the product to be added to the cart.
+ * @param qty the quantity of the product in the cart.
+ */
+ public Cart(final Product prod, final int qty) {
+ this.product = prod;
+ this.quantity = qty;
+ }
+
+ /**
+ * Calculates the total price of the products in the cart. The total price is the product's price
+ * multiplied by the quantity.
+ *
+ * @return the total price of the products in the cart.
+ */
+ public double getTotalPrice() {
+ return product.getPrice() * quantity;
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/CartController.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/CartController.java
new file mode 100644
index 000000000000..da93cc2a6d93
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/CartController.java
@@ -0,0 +1,77 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+/**
+ * Controller class for handling shopping cart operations.
+ *
+ *
This class provides methods to add, remove, and calculate the total price of items in a user's
+ * shopping cart.
+ */
+public class CartController {
+
+ /** Service layer responsible for cart operations. */
+ private final ShoppingCartService shoppingCartUseCase;
+
+ /**
+ * Constructs a CartController with the specified shopping cart service.
+ *
+ * @param shoppingCart The shopping cart service to handle cart operations.
+ */
+ public CartController(final ShoppingCartService shoppingCart) {
+ this.shoppingCartUseCase = shoppingCart;
+ }
+
+ /**
+ * Adds an item to the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @param productId The ID of the product to be added.
+ * @param quantity The quantity of the product.
+ */
+ public void addItemToCart(final String userId, final String productId, final int quantity) {
+ shoppingCartUseCase.addItemToCart(userId, productId, quantity);
+ }
+
+ /**
+ * Removes an item from the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @param productId The ID of the product to be removed.
+ */
+ public void removeItemFromCart(final String userId, final String productId) {
+ shoppingCartUseCase.removeItemFromCart(userId, productId);
+ }
+
+ /**
+ * Calculates the total cost of items in the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @return The total price of all items in the cart.
+ */
+ public double calculateTotal(final String userId) {
+ return shoppingCartUseCase.calculateTotal(userId);
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/CartRepository.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/CartRepository.java
new file mode 100644
index 000000000000..844bc48345b4
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/CartRepository.java
@@ -0,0 +1,70 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import java.util.List;
+
+/** CartRepository. */
+public interface CartRepository {
+ /**
+ * Adds an item to the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @param product The product to be added.
+ * @param quantity The quantity of the product.
+ */
+ void addItemToCart(String userId, Product product, int quantity);
+
+ /**
+ * Removes an item from the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @param productId The ID of the product to be removed.
+ */
+ void removeItemFromCart(String userId, String productId);
+
+ /**
+ * Retrieves the list of items in the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @return A list of items in the cart.
+ */
+ List getItemsInCart(String userId);
+
+ /**
+ * Calculates the total price of the items in the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @return The total price of all items in the cart.
+ */
+ double calculateTotal(String userId);
+
+ /**
+ * Clears all items from the user's cart.
+ *
+ * @param userId The ID of the user.
+ */
+ void clearCart(String userId);
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryCartRepository.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryCartRepository.java
new file mode 100644
index 000000000000..2965cddd57f3
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryCartRepository.java
@@ -0,0 +1,102 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of {@link CartRepository} that stores cart items in memory.
+ *
+ * This class maintains a map of user carts where each user has a list of cart items.
+ */
+public class InMemoryCartRepository implements CartRepository {
+ /** A map storing user carts with their respective cart items. */
+ private final Map> userCarts = new HashMap<>();
+
+ /**
+ * Adds an item to the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @param product The product to be added.
+ * @param quantity The quantity of the product.
+ */
+ @Override
+ public void addItemToCart(final String userId, final Product product, final int quantity) {
+ List cart = userCarts.getOrDefault(userId, new ArrayList<>());
+ cart.add(new Cart(product, quantity));
+ userCarts.put(userId, cart);
+ }
+
+ /**
+ * Removes an item from the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @param productId The ID of the product to be removed.
+ */
+ @Override
+ public void removeItemFromCart(final String userId, final String productId) {
+ List cart = userCarts.get(userId);
+ if (cart != null) {
+ cart.removeIf(item -> item.getProduct().getId().equals(productId));
+ }
+ }
+
+ /**
+ * Retrieves all items in the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @return A list of {@link Cart} items in the user's cart.
+ */
+ @Override
+ public List getItemsInCart(final String userId) {
+ return userCarts.getOrDefault(userId, new ArrayList<>());
+ }
+
+ /**
+ * Calculates the total price of items in the user's cart.
+ *
+ * @param userId The ID of the user.
+ * @return The total price of the cart.
+ */
+ @Override
+ public double calculateTotal(final String userId) {
+ return userCarts.getOrDefault(userId, new ArrayList<>()).stream()
+ .mapToDouble(Cart::getTotalPrice)
+ .sum();
+ }
+
+ /**
+ * Clears all items from the user's cart.
+ *
+ * @param userId The ID of the user.
+ */
+ @Override
+ public void clearCart(final String userId) {
+ userCarts.remove(userId);
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryOrderRepository.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryOrderRepository.java
new file mode 100644
index 000000000000..b8a17cd6045a
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryOrderRepository.java
@@ -0,0 +1,49 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An in-memory implementation of the {@link OrderRepository}.
+ *
+ * This class stores orders in a list, allowing orders to be saved but not persisted beyond the
+ * application's runtime.
+ */
+public class InMemoryOrderRepository implements OrderRepository {
+ /** A list to store orders in memory. */
+ private final List orders = new ArrayList<>();
+
+ /**
+ * Saves an order to the in-memory repository.
+ *
+ * @param order The order to be saved.
+ */
+ @Override
+ public void saveOrder(final Order order) {
+ orders.add(order);
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryProductRepository.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryProductRepository.java
new file mode 100644
index 000000000000..c91677feeff5
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/InMemoryProductRepository.java
@@ -0,0 +1,71 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * In-memory implementation of the {@link ProductRepository} interface.
+ *
+ * This repository stores products in memory allowing retrieval by product ID.
+ */
+public class InMemoryProductRepository implements ProductRepository {
+ /** A map to store products by their unique product ID. */
+ private final Map products = new HashMap<>();
+
+ /**
+ * The price of the Laptop in USD.
+ *
+ * Used in the in-memory product repository to define the cost of a Laptop.
+ */
+ private static final double LAPTOP_PRICE = 1000.0;
+
+ /**
+ * The price of the Smartphone in USD.
+ *
+ *
Used in the in-memory product repository to define the cost of a Smartphone.
+ */
+ private static final double SMARTPHONE_PRICE = 500.0;
+
+ /**
+ * Constructs an {@code InMemoryProductRepository} and initializes it with some example products.
+ */
+ public InMemoryProductRepository() {
+ products.put("1", new Product("1", "Laptop", LAPTOP_PRICE));
+ products.put("2", new Product("2", "Smartphone", SMARTPHONE_PRICE));
+ }
+
+ /**
+ * Retrieves a product by its unique ID.
+ *
+ * @param productId The ID of the product to retrieve.
+ * @return The {@link Product} corresponding to the given ID {@code null} if not found.
+ */
+ @Override
+ public Product getProductById(final String productId) {
+ return products.get(productId);
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Order.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Order.java
new file mode 100644
index 000000000000..70bf058dc2eb
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Order.java
@@ -0,0 +1,58 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import java.util.List;
+import lombok.Getter;
+
+/**
+ * Represents an order placed by a user containing the ordered items and total price.
+ *
+ *
An order includes a unique order ID, a list of cart items and the total price of the order.
+ */
+@Getter
+public class Order {
+ /** The unique identifier for this order. */
+ private final String orderId;
+
+ /** The list of items included in this order. */
+ private final List items;
+
+ /** The list of items included in this order. */
+ private final double totalPrice;
+
+ /**
+ * Constructs an {@code Order} with the given order ID and list of cart items. The total price is
+ * based on the individual item prices in the cart.
+ *
+ * @param id The unique identifier for the order.
+ * @param item The list of cart items included in the order.
+ */
+ public Order(final String id, final List item) {
+ this.orderId = id;
+ this.items = item;
+ this.totalPrice = items.stream().mapToDouble(Cart::getTotalPrice).sum();
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/OrderController.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/OrderController.java
new file mode 100644
index 000000000000..d61dad322750
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/OrderController.java
@@ -0,0 +1,54 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+/**
+ * Controller for handling order-related operations.
+ *
+ * This class provides an endpoint for users to checkout their cart and place an order.
+ */
+public class OrderController {
+ /** Service for managing shopping cart operations. */
+ private final ShoppingCartService shoppingCartUseCase;
+
+ /**
+ * Constructs an {@code OrderController} with the given shopping cart service.
+ *
+ * @param shoppingCartUse The shopping cart service used to process orders.
+ */
+ public OrderController(final ShoppingCartService shoppingCartUse) {
+ this.shoppingCartUseCase = shoppingCartUse;
+ }
+
+ /**
+ * Processes the checkout for a given user and creates an order.
+ *
+ * @param userId The ID of the user checking out.
+ * @return The created {@link Order} after checkout.
+ */
+ public Order checkout(final String userId) {
+ return shoppingCartUseCase.checkout(userId);
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/OrderRepository.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/OrderRepository.java
new file mode 100644
index 000000000000..4c7276fcb53f
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/OrderRepository.java
@@ -0,0 +1,39 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+/**
+ * Repository interface for managing order persistence.
+ *
+ *
This interface defines the contract for storing orders in the system.
+ */
+public interface OrderRepository {
+ /**
+ * Saves an order to the repository.
+ *
+ * @param order The order to be saved.
+ */
+ void saveOrder(Order order);
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Product.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Product.java
new file mode 100644
index 000000000000..100613872865
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/Product.java
@@ -0,0 +1,53 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import lombok.Getter;
+
+/** Represents a product in the system. */
+@Getter
+public class Product {
+ /** The unique identifier for the product. */
+ private final String id;
+
+ /** The name of the product. */
+ private final String name;
+
+ /** The price of the product. */
+ private final double price;
+
+ /**
+ * Constructs a new Product with the given details.
+ *
+ * @param pdtId The unique identifier of the product.
+ * @param firstName The name of the product.
+ * @param p The price of the product.
+ */
+ public Product(final String pdtId, final String firstName, final double p) {
+ this.id = pdtId;
+ this.name = firstName;
+ this.price = p;
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/ProductRepository.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/ProductRepository.java
new file mode 100644
index 000000000000..713b62e799bc
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/ProductRepository.java
@@ -0,0 +1,36 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+/** Repository interface for handling product-related operations. */
+public interface ProductRepository {
+ /**
+ * Retrieves a product by its unique identifier.
+ *
+ * @param productId The unique ID of the product.
+ * @return The product corresponding to the given ID.
+ */
+ Product getProductById(String productId);
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/ShoppingCartService.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/ShoppingCartService.java
new file mode 100644
index 000000000000..cd74aa3145cf
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/ShoppingCartService.java
@@ -0,0 +1,112 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import java.util.List;
+
+/**
+ * Service class for managing shopping cart operations.
+ *
+ *
This class provides functionalities to add and remove items from the cart, calculate the total
+ * price, and handle checkout operations.
+ */
+public class ShoppingCartService {
+ /** Repository for managing product data. */
+ private final ProductRepository productRepository;
+
+ /** Repository for managing cart data. */
+ private final CartRepository cartRepository;
+
+ /** Repository for managing order data. */
+ private final OrderRepository orderRepository;
+
+ /**
+ * Constructs a ShoppingCartService with the required repositories.
+ *
+ * @param pdtRepository The repository to fetch product details.
+ * @param repository The repository to manage cart operations.
+ * @param ordRepository The repository to handle order persistence.
+ */
+ public ShoppingCartService(
+ final ProductRepository pdtRepository,
+ final CartRepository repository,
+ final OrderRepository ordRepository) {
+ this.productRepository = pdtRepository;
+ this.cartRepository = repository;
+ this.orderRepository = ordRepository;
+ }
+
+ /**
+ * Adds an item to the user's shopping cart.
+ *
+ * @param userId The ID of the user.
+ * @param productId The ID of the product to be added.
+ * @param quantity The quantity of the product.
+ */
+ public void addItemToCart(final String userId, final String productId, final int quantity) {
+ Product product = productRepository.getProductById(productId);
+ if (product != null) {
+ cartRepository.addItemToCart(userId, product, quantity);
+ }
+ }
+
+ /**
+ * Removes an item from the user's shopping cart.
+ *
+ * @param userId The ID of the user.
+ * @param productId The ID of the product to be removed.
+ */
+ public void removeItemFromCart(final String userId, final String productId) {
+ cartRepository.removeItemFromCart(userId, productId);
+ }
+
+ /**
+ * Calculates the total cost of items in the user's shopping cart.
+ *
+ * @param userId The ID of the user.
+ * @return The total price of all items in the cart.
+ */
+ public double calculateTotal(final String userId) {
+ return cartRepository.calculateTotal(userId);
+ }
+
+ /**
+ * Checks out the user's cart and creates an order.
+ *
+ *
This method retrieves the cart items, generates an order ID, creates a new order, saves it,
+ * and clears the cart.
+ *
+ * @param userId The ID of the user.
+ * @return The created order containing purchased items.
+ */
+ public Order checkout(final String userId) {
+ List items = cartRepository.getItemsInCart(userId);
+ String orderId = "ORDER-" + System.currentTimeMillis();
+ Order order = new Order(orderId, items);
+ orderRepository.saveOrder(order);
+ cartRepository.clearCart(userId);
+ return order;
+ }
+}
diff --git a/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/package-info.java b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/package-info.java
new file mode 100644
index 000000000000..7b8142f436ae
--- /dev/null
+++ b/clean-architecture/src/main/java/com/iluwatar/cleanarchitecture/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.
+ */
+/**
+ * Provides classes and interfaces for the clean architecture pattern implementation.
+ *
+ * This package includes classes for managing products, carts, orders, repositories, and services
+ * for a shopping cart system, following the clean architecture principles.
+ */
+package com.iluwatar.cleanarchitecture;
diff --git a/clean-architecture/src/test/java/com/iluwatar/cleanarchitecture/AppTest.java b/clean-architecture/src/test/java/com/iluwatar/cleanarchitecture/AppTest.java
new file mode 100644
index 000000000000..86265d2886b7
--- /dev/null
+++ b/clean-architecture/src/test/java/com/iluwatar/cleanarchitecture/AppTest.java
@@ -0,0 +1,43 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+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[] {}));
+ }
+}
diff --git a/clean-architecture/src/test/java/com/iluwatar/cleanarchitecture/CartControllerTest.java b/clean-architecture/src/test/java/com/iluwatar/cleanarchitecture/CartControllerTest.java
new file mode 100644
index 000000000000..c015c54c139c
--- /dev/null
+++ b/clean-architecture/src/test/java/com/iluwatar/cleanarchitecture/CartControllerTest.java
@@ -0,0 +1,65 @@
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.cleanarchitecture;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+class CartControllerTest {
+
+ private CartController cartController;
+
+ @BeforeEach
+ void setUp() {
+ ProductRepository productRepository = new InMemoryProductRepository();
+ CartRepository cartRepository = new InMemoryCartRepository();
+ OrderRepository orderRepository = new InMemoryOrderRepository();
+ ShoppingCartService shoppingCartUseCase =
+ new ShoppingCartService(productRepository, cartRepository, orderRepository);
+ cartController = new CartController(shoppingCartUseCase);
+ }
+
+ @Test
+ void testRemoveItemFromCart() {
+ cartController.addItemToCart("user123", "1", 1);
+ cartController.addItemToCart("user123", "2", 2);
+
+ assertEquals(2000.0, cartController.calculateTotal("user123"));
+
+ cartController.removeItemFromCart("user123", "1");
+
+ assertEquals(1000.0, cartController.calculateTotal("user123"));
+ }
+
+ @Test
+ void testRemoveNonExistentItem() {
+ cartController.addItemToCart("user123", "2", 2);
+ cartController.removeItemFromCart("user123", "999");
+
+ assertEquals(1000.0, cartController.calculateTotal("user123"));
+ }
+}
diff --git a/client-session/README.md b/client-session/README.md
index c49e53c7174b..e693ef7db977 100644
--- a/client-session/README.md
+++ b/client-session/README.md
@@ -33,6 +33,10 @@ Wikipedia says
> The client-server model on Wikipedia describes a system where client devices request services and resources from centralized servers. This model is crucial in web applications where client sessions are used to manage user-specific data across multiple requests. For example, when a bank customer accesses online banking services, their login credentials and session state are managed by the web server to maintain continuity of their interactions.
+Sequence diagram
+
+
+
## Programmatic Example of Client Session Pattern in Java
The Client Session design pattern is a behavioral design pattern that maintains a user's state and data across multiple requests within a web application, ensuring a continuous and personalized user experience. This pattern is commonly used in web applications where user-specific data needs to be managed across multiple requests.
diff --git a/client-session/etc/client-session-sequence-diagram.png b/client-session/etc/client-session-sequence-diagram.png
new file mode 100644
index 000000000000..7d1282a9bbd9
Binary files /dev/null and b/client-session/etc/client-session-sequence-diagram.png differ
diff --git a/client-session/pom.xml b/client-session/pom.xml
index b7ff08637091..1b2ea4564ff9 100644
--- a/client-session/pom.xml
+++ b/client-session/pom.xml
@@ -34,6 +34,14 @@
client-session
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/client-session/src/main/java/com/iluwatar/client/session/App.java b/client-session/src/main/java/com/iluwatar/client/session/App.java
index 282d8a7f3b28..8f744353ed1b 100644
--- a/client-session/src/main/java/com/iluwatar/client/session/App.java
+++ b/client-session/src/main/java/com/iluwatar/client/session/App.java
@@ -29,12 +29,11 @@
* The Client-Session pattern allows the session data to be stored on the client side and send this
* data to the server with each request.
*
- * In this example, The {@link Server} class represents the server that would process the
+ *
In this example, The {@link Server} class represents the server that would process the
* incoming {@link Request} and also assign {@link Session} to a client. Here one instance of Server
* is created. The we create two sessions for two different clients. These sessions are then passed
* on to the server in the request along with the data. The server is then able to interpret the
* client based on the session associated with it.
- *
*/
public class App {
diff --git a/client-session/src/main/java/com/iluwatar/client/session/Request.java b/client-session/src/main/java/com/iluwatar/client/session/Request.java
index 008011f180f7..e47ed2775ac4 100644
--- a/client-session/src/main/java/com/iluwatar/client/session/Request.java
+++ b/client-session/src/main/java/com/iluwatar/client/session/Request.java
@@ -28,9 +28,7 @@
import lombok.AllArgsConstructor;
import lombok.Data;
-/**
- * The Request class which contains the Session details and data.
- */
+/** The Request class which contains the Session details and data. */
@Data
@AllArgsConstructor
public class Request {
@@ -38,5 +36,4 @@ public class Request {
private String data;
private Session session;
-
}
diff --git a/client-session/src/main/java/com/iluwatar/client/session/Server.java b/client-session/src/main/java/com/iluwatar/client/session/Server.java
index 6d9dc3dbc80a..13a43a2dda81 100644
--- a/client-session/src/main/java/com/iluwatar/client/session/Server.java
+++ b/client-session/src/main/java/com/iluwatar/client/session/Server.java
@@ -31,7 +31,8 @@
import lombok.extern.slf4j.Slf4j;
/**
- * The Server class. The client communicates with the server and request processing and getting a new session.
+ * The Server class. The client communicates with the server and request processing and getting a
+ * new session.
*/
@Slf4j
@Data
@@ -41,12 +42,10 @@ public class Server {
private int port;
-
/**
* Creates a new session.
*
* @param name name of the client
- *
* @return Session Object
*/
public Session getSession(String name) {
@@ -59,7 +58,10 @@ public Session getSession(String name) {
* @param request Request object with data and Session
*/
public void process(Request request) {
- LOGGER.info("Processing Request with client: " + request.getSession().getClientName() + " data: " + request.getData());
+ LOGGER.info(
+ "Processing Request with client: "
+ + request.getSession().getClientName()
+ + " data: "
+ + request.getData());
}
-
}
diff --git a/client-session/src/main/java/com/iluwatar/client/session/Session.java b/client-session/src/main/java/com/iluwatar/client/session/Session.java
index bb9f7246cb4e..a7639485b83c 100644
--- a/client-session/src/main/java/com/iluwatar/client/session/Session.java
+++ b/client-session/src/main/java/com/iluwatar/client/session/Session.java
@@ -29,20 +29,16 @@
import lombok.Data;
/**
- * The Session class. Each client get assigned a Session which is then used for further communications.
+ * The Session class. Each client get assigned a Session which is then used for further
+ * communications.
*/
@Data
@AllArgsConstructor
public class Session {
- /**
- * Session id.
- */
+ /** Session id. */
private String id;
- /**
- * Client name.
- */
+ /** Client name. */
private String clientName;
-
}
diff --git a/client-session/src/test/java/com/iluwatar/client/session/AppTest.java b/client-session/src/test/java/com/iluwatar/client/session/AppTest.java
index 0e33f74f460b..63951ae48ae3 100644
--- a/client-session/src/test/java/com/iluwatar/client/session/AppTest.java
+++ b/client-session/src/test/java/com/iluwatar/client/session/AppTest.java
@@ -33,6 +33,6 @@ class AppTest {
@Test
void appStartsWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java b/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java
index 72f960332a2f..0037ca6339fb 100644
--- a/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java
+++ b/client-session/src/test/java/com/iluwatar/client/session/ServerTest.java
@@ -24,9 +24,10 @@
*/
package com.iluwatar.client.session;
-import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
+import org.junit.jupiter.api.Test;
+
class ServerTest {
@Test
diff --git a/collecting-parameter/README.md b/collecting-parameter/README.md
index 55609aebdcb9..89c37e79bfb8 100644
--- a/collecting-parameter/README.md
+++ b/collecting-parameter/README.md
@@ -36,6 +36,10 @@ Wikipedia says
> In the Collecting Parameter idiom a collection (list, map, etc.) is passed repeatedly as a parameter to a method which adds items to the collection.
+Flowchart
+
+
+
## Programmatic Example of Collecting Parameter Pattern in Java
Within a large corporate building, there exists a global printer queue that is a collection of all the printing jobs that are currently pending. Various floors contain different models of printers, each having a different printing policy. We must construct a program that can continually add appropriate printing jobs to a collection, which is called the collecting parameter.
diff --git a/collecting-parameter/etc/collecting-parameter-flowchart.png b/collecting-parameter/etc/collecting-parameter-flowchart.png
new file mode 100644
index 000000000000..dcab61c2eb5d
Binary files /dev/null and b/collecting-parameter/etc/collecting-parameter-flowchart.png differ
diff --git a/collecting-parameter/pom.xml b/collecting-parameter/pom.xml
index 8f7fdc2b52db..8c5c015468a4 100644
--- a/collecting-parameter/pom.xml
+++ b/collecting-parameter/pom.xml
@@ -39,11 +39,6 @@
junit-jupiter-engine
test
-
- junit
- junit
- test
-
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java
index 93ead620da95..d505970fb84f 100644
--- a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java
@@ -28,23 +28,23 @@
import java.util.Queue;
/**
- * The Collecting Parameter Design Pattern aims to return a result that is the collaborative result of several
- * methods. This design pattern uses a 'collecting parameter' that is passed to several functions, accumulating results
- * as it travels from method-to-method. This is different to the Composed Method design pattern, where a single
- * collection is modified via several methods.
+ * The Collecting Parameter Design Pattern aims to return a result that is the collaborative result
+ * of several methods. This design pattern uses a 'collecting parameter' that is passed to several
+ * functions, accumulating results as it travels from method-to-method. This is different to the
+ * Composed Method design pattern, where a single collection is modified via several methods.
*
- * This example is inspired by Kent Beck's example in his book, 'Smalltalk Best Practice Patterns'. The context for this
- * situation is that there is a single printer queue {@link PrinterQueue} that holds numerous print jobs
- * {@link PrinterItem} that must be distributed to various print centers.
- * Each print center has its own requirements and printing limitations. In this example, the following requirements are:
- * If an A4 document is coloured, it must also be single-sided. All other non-coloured A4 documents are accepted.
- * All A3 documents must be non-coloured and single sided. All A2 documents must be a single page, single sided, and
+ *
This example is inspired by Kent Beck's example in his book, 'Smalltalk Best Practice
+ * Patterns'. The context for this situation is that there is a single printer queue {@link
+ * PrinterQueue} that holds numerous print jobs {@link PrinterItem} that must be distributed to
+ * various print centers. Each print center has its own requirements and printing limitations. In
+ * this example, the following requirements are: If an A4 document is coloured, it must also be
+ * single-sided. All other non-coloured A4 documents are accepted. All A3 documents must be
+ * non-coloured and single sided. All A2 documents must be a single page, single sided, and
* non-coloured.
*
- *
A collecting parameter (the result variable) is used to filter the global printer queue so that it meets the
- * requirements for this centre,
- **/
-
+ *
A collecting parameter (the result variable) is used to filter the global printer queue so
+ * that it meets the requirements for this centre,
+ */
public class App {
static PrinterQueue printerQueue = PrinterQueue.getInstance();
@@ -75,16 +75,16 @@ public static void main(String[] args) {
}
/**
- * Adds A4 document jobs to the collecting parameter according to some policy that can be whatever the client
- * (the print center) wants.
+ * Adds A4 document jobs to the collecting parameter according to some policy that can be whatever
+ * the client (the print center) wants.
*
* @param printerItemsCollection the collecting parameter
*/
public static void addValidA4Papers(Queue printerItemsCollection) {
/*
- Iterate through the printer queue, and add A4 papers according to the correct policy to the collecting parameter,
- which is 'printerItemsCollection' in this case.
- */
+ Iterate through the printer queue, and add A4 papers according to the correct policy to the collecting parameter,
+ which is 'printerItemsCollection' in this case.
+ */
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A4)) {
var isColouredAndSingleSided = nextItem.isColour && !nextItem.isDoubleSided;
@@ -96,9 +96,9 @@ public static void addValidA4Papers(Queue printerItemsCollection) {
}
/**
- * Adds A3 document jobs to the collecting parameter according to some policy that can be whatever the client
- * (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate
- * the wants of the client.
+ * Adds A3 document jobs to the collecting parameter according to some policy that can be whatever
+ * the client (the print center) wants. The code is similar to the 'addA4Papers' method. The code
+ * can be changed to accommodate the wants of the client.
*
* @param printerItemsCollection the collecting parameter
*/
@@ -106,7 +106,8 @@ public static void addValidA3Papers(Queue printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A3)) {
- // Encoding the policy into a Boolean: the A3 paper cannot be coloured and double-sided at the same time
+ // Encoding the policy into a Boolean: the A3 paper cannot be coloured and double-sided at
+ // the same time
var isNotColouredAndSingleSided = !nextItem.isColour && !nextItem.isDoubleSided;
if (isNotColouredAndSingleSided) {
printerItemsCollection.add(nextItem);
@@ -116,9 +117,9 @@ public static void addValidA3Papers(Queue printerItemsCollection) {
}
/**
- * Adds A2 document jobs to the collecting parameter according to some policy that can be whatever the client
- * (the print center) wants. The code is similar to the 'addA4Papers' method. The code can be changed to accommodate
- * the wants of the client.
+ * Adds A2 document jobs to the collecting parameter according to some policy that can be whatever
+ * the client (the print center) wants. The code is similar to the 'addA4Papers' method. The code
+ * can be changed to accommodate the wants of the client.
*
* @param printerItemsCollection the collecting parameter
*/
@@ -126,9 +127,10 @@ public static void addValidA2Papers(Queue printerItemsCollection) {
for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
if (nextItem.paperSize.equals(PaperSizes.A2)) {
- // Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and non-coloured.
- var isNotColouredSingleSidedAndOnePage = nextItem.pageCount == 1 && !nextItem.isDoubleSided
- && !nextItem.isColour;
+ // Encoding the policy into a Boolean: the A2 paper must be single page, single-sided, and
+ // non-coloured.
+ var isNotColouredSingleSidedAndOnePage =
+ nextItem.pageCount == 1 && !nextItem.isDoubleSided && !nextItem.isColour;
if (isNotColouredSingleSidedAndOnePage) {
printerItemsCollection.add(nextItem);
}
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.java
index 3f0a24854f02..5669f744dd84 100644
--- a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.java
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.java
@@ -26,18 +26,14 @@
import java.util.Objects;
-/**
- * This class represents a Print Item, that should be added to the queue.
- **/
+/** This class represents a Print Item, that should be added to the queue. */
public class PrinterItem {
PaperSizes paperSize;
int pageCount;
boolean isDoubleSided;
boolean isColour;
- /**
- * The {@link PrinterItem} constructor.
- **/
+ /** The {@link PrinterItem} constructor. */
public PrinterItem(PaperSizes paperSize, int pageCount, boolean isDoubleSided, boolean isColour) {
if (!Objects.isNull(paperSize)) {
this.paperSize = paperSize;
@@ -53,6 +49,5 @@ public PrinterItem(PaperSizes paperSize, int pageCount, boolean isDoubleSided, b
this.isColour = isColour;
this.isDoubleSided = isDoubleSided;
-
}
-}
\ No newline at end of file
+}
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java
index 882fc5277f71..8cdfc84107a3 100644
--- a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java
@@ -29,15 +29,17 @@
import java.util.Queue;
/**
- * This class represents a singleton Printer Queue. It contains a queue that can be filled up with {@link PrinterItem}.
- **/
+ * This class represents a singleton Printer Queue. It contains a queue that can be filled up with
+ * {@link PrinterItem}.
+ */
public class PrinterQueue {
static PrinterQueue currentInstance = null;
private final Queue printerItemQueue;
/**
- * This class is a singleton. The getInstance method will ensure that only one instance exists at a time.
+ * This class is a singleton. The getInstance method will ensure that only one instance exists at
+ * a time.
*/
public static PrinterQueue getInstance() {
if (Objects.isNull(currentInstance)) {
@@ -46,16 +48,12 @@ public static PrinterQueue getInstance() {
return currentInstance;
}
- /**
- * Empty the printer queue.
- */
+ /** Empty the printer queue. */
public void emptyQueue() {
currentInstance.getPrinterQueue().clear();
}
- /**
- * Private constructor prevents instantiation, unless using the getInstance() method.
- */
+ /** Private constructor prevents instantiation, unless using the getInstance() method. */
private PrinterQueue() {
printerItemQueue = new LinkedList<>();
}
@@ -72,5 +70,4 @@ public Queue getPrinterQueue() {
public void addPrinterItem(PrinterItem printerItem) {
currentInstance.getPrinterQueue().add(printerItem);
}
-
}
diff --git a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/AppTest.java b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/AppTest.java
index 57f9037778ae..8597c4d6f2cb 100644
--- a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/AppTest.java
+++ b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/AppTest.java
@@ -24,16 +24,14 @@
*/
package com.iluwatar.collectingparameter;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import org.junit.jupiter.api.Test;
+
class AppTest {
- /**
- * Checks whether {@link App} executes without throwing exception
- */
+ /** Checks whether {@link App} executes without throwing exception */
@Test
void executesWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/CollectingParameterTest.java b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/CollectingParameterTest.java
index 9818dbb1809e..c53c5ac2c0ed 100644
--- a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/CollectingParameterTest.java
+++ b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/CollectingParameterTest.java
@@ -24,11 +24,11 @@
*/
package com.iluwatar.collectingparameter;
+import java.util.LinkedList;
+import java.util.Queue;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
-import java.util.LinkedList;
-import java.util.Queue;
class CollectingParameterTest {
diff --git a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.java b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.java
index fc633108be76..8c03eea90c95 100644
--- a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.java
+++ b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.java
@@ -24,12 +24,12 @@
*/
package com.iluwatar.collectingparameter;
+import static org.junit.jupiter.api.Assertions.*;
+
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.Timeout;
-import static org.junit.jupiter.api.Assertions.*;
-
class PrinterQueueTest {
@Test
@@ -43,13 +43,14 @@ void singletonTest() {
@Test()
@Timeout(1000)
void negativePageCount() throws IllegalArgumentException {
- Assertions.assertThrows(IllegalArgumentException.class, () -> new PrinterItem(PaperSizes.A4, -1, true, true));
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new PrinterItem(PaperSizes.A4, -1, true, true));
}
@Test()
@Timeout(1000)
void nullPageSize() throws IllegalArgumentException {
- Assertions.assertThrows(IllegalArgumentException.class, () -> new PrinterItem(null, 1, true, true));
+ Assertions.assertThrows(
+ IllegalArgumentException.class, () -> new PrinterItem(null, 1, true, true));
}
-
-}
\ No newline at end of file
+}
diff --git a/collection-pipeline/README.md b/collection-pipeline/README.md
index 8f8764be2b28..33d207922828 100644
--- a/collection-pipeline/README.md
+++ b/collection-pipeline/README.md
@@ -29,6 +29,10 @@ Wikipedia says
> In software engineering, a pipeline consists of a chain of processing elements (processes, threads, coroutines, functions, etc.), arranged so that the output of each element is the input of the next; the name is by analogy to a physical pipeline. Usually some amount of buffering is provided between consecutive elements. The information that flows in these pipelines is often a stream of records, bytes, or bits, and the elements of a pipeline may be called filters; this is also called the pipe(s) and filters design pattern. Connecting elements into a pipeline is analogous to function composition.
+Flowchart
+
+
+
## Programmatic Example of Collection Pipeline Pattern in Java
The Collection Pipeline is a programming pattern where you organize some computation as a sequence of operations which compose by taking a collection as output of one operation and feeding it into the next.
diff --git a/collection-pipeline/etc/collection-pipeline-flowchart.png b/collection-pipeline/etc/collection-pipeline-flowchart.png
new file mode 100644
index 000000000000..48ee51002129
Binary files /dev/null and b/collection-pipeline/etc/collection-pipeline-flowchart.png differ
diff --git a/collection-pipeline/pom.xml b/collection-pipeline/pom.xml
index 63de6d75f523..b3c36cf06ef6 100644
--- a/collection-pipeline/pom.xml
+++ b/collection-pipeline/pom.xml
@@ -34,6 +34,14 @@
collection-pipeline
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java
index 2cfeb963ba49..7b4537800437 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Car.java
@@ -25,8 +25,5 @@
package com.iluwatar.collectionpipeline;
-/**
- * A Car class that has the properties of make, model, year and category.
- */
-public record Car(String make, String model, int year, Category category) {
-}
+/** A Car class that has the properties of make, model, year and category. */
+public record Car(String make, String model, int year, Category category) {}
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/CarFactory.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/CarFactory.java
index b96af01dbe53..2bfaa676060e 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/CarFactory.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/CarFactory.java
@@ -26,12 +26,9 @@
import java.util.List;
-/**
- * A factory class to create a collection of {@link Car} instances.
- */
+/** A factory class to create a collection of {@link Car} instances. */
public class CarFactory {
- private CarFactory() {
- }
+ private CarFactory() {}
/**
* Factory method to create a {@link List} of {@link Car} instances.
@@ -39,11 +36,12 @@ private CarFactory() {
* @return {@link List} of {@link Car}
*/
public static List createCars() {
- return List.of(new Car("Jeep", "Wrangler", 2011, Category.JEEP),
+ return List.of(
+ new Car("Jeep", "Wrangler", 2011, Category.JEEP),
new Car("Jeep", "Comanche", 1990, Category.JEEP),
new Car("Dodge", "Avenger", 2010, Category.SEDAN),
new Car("Buick", "Cascada", 2016, Category.CONVERTIBLE),
new Car("Ford", "Focus", 2012, Category.SEDAN),
new Car("Chevrolet", "Geo Metro", 1992, Category.CONVERTIBLE));
}
-}
\ No newline at end of file
+}
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Category.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Category.java
index c7b3dfcdccd3..9de7e4da3b50 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Category.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Category.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.collectionpipeline;
-/**
- * Enum for the category of car.
- */
+/** Enum for the category of car. */
public enum Category {
JEEP,
SEDAN,
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java
index 20bf77d29a2f..dfe2d9ea5697 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/FunctionalProgramming.java
@@ -33,20 +33,19 @@
/**
* Iterating and sorting with a collection pipeline
*
- * In functional programming, it's common to sequence complex operations through
- * a series of smaller modular functions or operations. The series is called a composition of
- * functions, or a function composition. When a collection of data flows through a function
- * composition, it becomes a collection pipeline. Function Composition and Collection Pipeline are
- * two design patterns frequently used in functional-style programming.
+ *
In functional programming, it's common to sequence complex operations through a series of
+ * smaller modular functions or operations. The series is called a composition of functions, or a
+ * function composition. When a collection of data flows through a function composition, it becomes
+ * a collection pipeline. Function Composition and Collection Pipeline are two design patterns
+ * frequently used in functional-style programming.
*
- *
Instead of passing a lambda expression to the map method, we passed the
- * method reference Car::getModel. Likewise, instead of passing the lambda expression car ->
- * car.getYear() to the comparing method, we passed the method reference Car::getYear. Method
- * references are short, concise, and expressive. It is best to use them wherever possible.
+ *
Instead of passing a lambda expression to the map method, we passed the method reference
+ * Car::getModel. Likewise, instead of passing the lambda expression car -> car.getYear() to the
+ * comparing method, we passed the method reference Car::getYear. Method references are short,
+ * concise, and expressive. It is best to use them wherever possible.
*/
public class FunctionalProgramming {
- private FunctionalProgramming() {
- }
+ private FunctionalProgramming() {}
/**
* Method to get models using for collection pipeline.
@@ -55,8 +54,11 @@ private FunctionalProgramming() {
* @return {@link List} of {@link String} representing models built after year 2000
*/
public static List getModelsAfter2000(List cars) {
- return cars.stream().filter(car -> car.year() > 2000).sorted(Comparator.comparing(Car::year))
- .map(Car::model).toList();
+ return cars.stream()
+ .filter(car -> car.year() > 2000)
+ .sorted(Comparator.comparing(Car::year))
+ .map(Car::model)
+ .toList();
}
/**
@@ -76,8 +78,11 @@ public static Map> getGroupingOfCarsByCategory(List car
* @return {@link List} of {@link Car} to belonging to the group
*/
public static List getSedanCarsOwnedSortedByDate(List persons) {
- return persons.stream().map(Person::cars).flatMap(List::stream)
+ return persons.stream()
+ .map(Person::cars)
+ .flatMap(List::stream)
.filter(car -> Category.SEDAN.equals(car.category()))
- .sorted(Comparator.comparing(Car::year)).toList();
+ .sorted(Comparator.comparing(Car::year))
+ .toList();
}
}
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java
index 2a85334c6643..24c0b965e4da 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/ImperativeProgramming.java
@@ -35,21 +35,20 @@
* Imperative-style programming to iterate over the list and get the names of cars made later than
* the year 2000. We then sort the models in ascending order by year.
*
- * As you can see, there's a lot of looping in this code. First, the
- * getModelsAfter2000UsingFor method takes a list of cars as its parameter. It extracts or filters
- * out cars made after the year 2000, putting them into a new list named carsSortedByYear. Next, it
- * sorts that list in ascending order by year-of-make. Finally, it loops through the list
- * carsSortedByYear to get the model names and returns them in a list.
+ *
As you can see, there's a lot of looping in this code. First, the getModelsAfter2000UsingFor
+ * method takes a list of cars as its parameter. It extracts or filters out cars made after the year
+ * 2000, putting them into a new list named carsSortedByYear. Next, it sorts that list in ascending
+ * order by year-of-make. Finally, it loops through the list carsSortedByYear to get the model names
+ * and returns them in a list.
*
- *
This short example demonstrates what I call the effect of statements. While
- * functions and methods in general can be used as expressions, the {@link Collections} sort method
- * doesn't return a result. Because it is used as a statement, it mutates the list given as
- * argument. Both of the for loops also mutate lists as they iterate. Being statements, that's just
- * how these elements work. As a result, the code contains unnecessary garbage variables
+ *
This short example demonstrates what I call the effect of statements. While functions and
+ * methods in general can be used as expressions, the {@link Collections} sort method doesn't return
+ * a result. Because it is used as a statement, it mutates the list given as argument. Both of the
+ * for loops also mutate lists as they iterate. Being statements, that's just how these elements
+ * work. As a result, the code contains unnecessary garbage variables
*/
public class ImperativeProgramming {
- private ImperativeProgramming() {
- }
+ private ImperativeProgramming() {}
/**
* Method to return the car models built after year 2000 using for loops.
@@ -66,12 +65,14 @@ public static List getModelsAfter2000(List cars) {
}
}
- Collections.sort(carsSortedByYear, new Comparator() {
- @Override
- public int compare(Car car1, Car car2) {
- return car1.year() - car2.year();
- }
- });
+ Collections.sort(
+ carsSortedByYear,
+ new Comparator() {
+ @Override
+ public int compare(Car car1, Car car2) {
+ return car1.year() - car2.year();
+ }
+ });
List models = new ArrayList<>();
for (Car car : carsSortedByYear) {
@@ -121,12 +122,13 @@ public static List getSedanCarsOwnedSortedByDate(List persons) {
}
}
- sedanCars.sort(new Comparator() {
- @Override
- public int compare(Car o1, Car o2) {
- return o1.year() - o2.year();
- }
- });
+ sedanCars.sort(
+ new Comparator() {
+ @Override
+ public int compare(Car o1, Car o2) {
+ return o1.year() - o2.year();
+ }
+ });
return sedanCars;
}
diff --git a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java
index 992596125423..a84dc7f72a31 100644
--- a/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java
+++ b/collection-pipeline/src/main/java/com/iluwatar/collectionpipeline/Person.java
@@ -26,7 +26,5 @@
import java.util.List;
-/**
- * A Person class that has the list of cars that the person owns and use.
- */
+/** A Person class that has the list of cars that the person owns and use. */
public record Person(List cars) {}
diff --git a/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java b/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java
index 959d0c6b5b83..9a990b09ec15 100644
--- a/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java
+++ b/collection-pipeline/src/test/java/com/iluwatar/collectionpipeline/AppTest.java
@@ -31,9 +31,7 @@
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
-/**
- * Tests that Collection Pipeline methods work as expected.
- */
+/** Tests that Collection Pipeline methods work as expected. */
@Slf4j
class AppTest {
@@ -53,19 +51,20 @@ void testGetModelsAfter2000UsingPipeline() {
@Test
void testGetGroupingOfCarsByCategory() {
- var modelsExpected = Map.of(
- Category.CONVERTIBLE, List.of(
- new Car("Buick", "Cascada", 2016, Category.CONVERTIBLE),
- new Car("Chevrolet", "Geo Metro", 1992, Category.CONVERTIBLE)
- ),
- Category.SEDAN, List.of(
- new Car("Dodge", "Avenger", 2010, Category.SEDAN),
- new Car("Ford", "Focus", 2012, Category.SEDAN)
- ),
- Category.JEEP, List.of(
- new Car("Jeep", "Wrangler", 2011, Category.JEEP),
- new Car("Jeep", "Comanche", 1990, Category.JEEP))
- );
+ var modelsExpected =
+ Map.of(
+ Category.CONVERTIBLE,
+ List.of(
+ new Car("Buick", "Cascada", 2016, Category.CONVERTIBLE),
+ new Car("Chevrolet", "Geo Metro", 1992, Category.CONVERTIBLE)),
+ Category.SEDAN,
+ List.of(
+ new Car("Dodge", "Avenger", 2010, Category.SEDAN),
+ new Car("Ford", "Focus", 2012, Category.SEDAN)),
+ Category.JEEP,
+ List.of(
+ new Car("Jeep", "Wrangler", 2011, Category.JEEP),
+ new Car("Jeep", "Comanche", 1990, Category.JEEP)));
var modelsFunctional = FunctionalProgramming.getGroupingOfCarsByCategory(cars);
var modelsImperative = ImperativeProgramming.getGroupingOfCarsByCategory(cars);
LOGGER.info("Category " + modelsFunctional);
@@ -76,10 +75,10 @@ void testGetGroupingOfCarsByCategory() {
@Test
void testGetSedanCarsOwnedSortedByDate() {
var john = new Person(cars);
- var modelsExpected = List.of(
- new Car("Dodge", "Avenger", 2010, Category.SEDAN),
- new Car("Ford", "Focus", 2012, Category.SEDAN)
- );
+ var modelsExpected =
+ List.of(
+ new Car("Dodge", "Avenger", 2010, Category.SEDAN),
+ new Car("Ford", "Focus", 2012, Category.SEDAN));
var modelsFunctional = FunctionalProgramming.getSedanCarsOwnedSortedByDate(List.of(john));
var modelsImperative = ImperativeProgramming.getSedanCarsOwnedSortedByDate(List.of(john));
assertEquals(modelsExpected, modelsFunctional);
diff --git a/combinator/README.md b/combinator/README.md
index 0ebe7deea67a..93aa7792fb58 100644
--- a/combinator/README.md
+++ b/combinator/README.md
@@ -35,6 +35,10 @@ Wikipedia says
> A combinator is a higher-order function that uses only function application and earlier defined combinators to define a result from its arguments.
+Flowchart
+
+
+
## Programmatic Example of Combinator Pattern in Java
In software design, combinatory logic is pivotal for creating reusable and modular code components. By leveraging higher-order functions, the Combinator pattern promotes code reuse and maintainability in Java applications.
diff --git a/combinator/etc/combinator-flowchart.png b/combinator/etc/combinator-flowchart.png
new file mode 100644
index 000000000000..9cc8b341395d
Binary files /dev/null and b/combinator/etc/combinator-flowchart.png differ
diff --git a/combinator/pom.xml b/combinator/pom.xml
index 0e2f41962a5b..d366b383a187 100644
--- a/combinator/pom.xml
+++ b/combinator/pom.xml
@@ -34,10 +34,37 @@
combinator
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
test
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.combinator.CombinatorApp
+
+
+
+
+
+
+
+
diff --git a/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java b/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java
index 2ca6e16d774e..bf1ec4a017de 100644
--- a/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java
+++ b/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java
@@ -26,25 +26,20 @@
import lombok.extern.slf4j.Slf4j;
-
/**
- * The functional pattern representing a style of organizing libraries
- * centered around the idea of combining functions.
- * Putting it simply, there is some type T, some functions
- * for constructing "primitive" values of type T,
- * and some "combinators" which can combine values of type T
- * in various ways to build up more complex values of type T.
- * The class {@link Finder} defines a simple function {@link Finder#find(String)}
- * and connected functions
- * {@link Finder#or(Finder)},
- * {@link Finder#not(Finder)},
- * {@link Finder#and(Finder)}
- * Using them the became possible to get more complex functions {@link Finders}
+ * The functional pattern representing a style of organizing libraries centered around the idea of
+ * combining functions. Putting it simply, there is some type T, some functions for constructing
+ * "primitive" values of type T, and some "combinators" which can combine values of type T in
+ * various ways to build up more complex values of type T. The class {@link Finder} defines a simple
+ * function {@link Finder#find(String)} and connected functions {@link Finder#or(Finder)}, {@link
+ * Finder#not(Finder)}, {@link Finder#and(Finder)} Using them the became possible to get more
+ * complex functions {@link Finders}
*/
@Slf4j
public class CombinatorApp {
- private static final String TEXT = """
+ private static final String TEXT =
+ """
It was many and many a year ago,
In a kingdom by the sea,
That a maiden there lived whom you may know
@@ -60,15 +55,16 @@ public class CombinatorApp {
/**
* main.
+ *
* @param args args
*/
public static void main(String[] args) {
- var queriesOr = new String[]{"many", "Annabel"};
+ var queriesOr = new String[] {"many", "Annabel"};
var finder = Finders.expandedFinder(queriesOr);
var res = finder.find(text());
LOGGER.info("the result of expanded(or) query[{}] is {}", queriesOr, res);
- var queriesAnd = new String[]{"Annabel", "my"};
+ var queriesAnd = new String[] {"Annabel", "my"};
finder = Finders.specializedFinder(queriesAnd);
res = finder.find(text());
LOGGER.info("the result of specialized(and) query[{}] is {}", queriesAnd, res);
@@ -79,12 +75,10 @@ public static void main(String[] args) {
res = Finders.filteredFinder(" was ", "many", "child").find(text());
LOGGER.info("the result of filtered query is {}", res);
-
}
private static String text() {
return TEXT;
}
-
}
diff --git a/combinator/src/main/java/com/iluwatar/combinator/Finder.java b/combinator/src/main/java/com/iluwatar/combinator/Finder.java
index 1189367dedb1..cc2d5ce84918 100644
--- a/combinator/src/main/java/com/iluwatar/combinator/Finder.java
+++ b/combinator/src/main/java/com/iluwatar/combinator/Finder.java
@@ -28,13 +28,12 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-/**
- * Functional interface to find lines in text.
- */
+/** Functional interface to find lines in text. */
public interface Finder {
/**
* The function to find lines in text.
+ *
* @param text full tet
* @return result of searching
*/
@@ -42,17 +41,20 @@ public interface Finder {
/**
* Simple implementation of function {@link #find(String)}.
+ *
* @param word for searching
* @return this
*/
static Finder contains(String word) {
- return txt -> Stream.of(txt.split("\n"))
- .filter(line -> line.toLowerCase().contains(word.toLowerCase()))
- .collect(Collectors.toList());
+ return txt ->
+ Stream.of(txt.split("\n"))
+ .filter(line -> line.toLowerCase().contains(word.toLowerCase()))
+ .collect(Collectors.toList());
}
/**
* combinator not.
+ *
* @param notFinder finder to combine
* @return new finder including previous finders
*/
@@ -66,6 +68,7 @@ default Finder not(Finder notFinder) {
/**
* combinator or.
+ *
* @param orFinder finder to combine
* @return new finder including previous finders
*/
@@ -79,16 +82,14 @@ default Finder or(Finder orFinder) {
/**
* combinator and.
+ *
* @param andFinder finder to combine
* @return new finder including previous finders
*/
default Finder and(Finder andFinder) {
- return
- txt -> this
- .find(txt)
- .stream()
+ return txt ->
+ this.find(txt).stream()
.flatMap(line -> andFinder.find(line).stream())
.collect(Collectors.toList());
}
-
}
diff --git a/combinator/src/main/java/com/iluwatar/combinator/Finders.java b/combinator/src/main/java/com/iluwatar/combinator/Finders.java
index 1920c85d4260..a6f8dd7cac79 100644
--- a/combinator/src/main/java/com/iluwatar/combinator/Finders.java
+++ b/combinator/src/main/java/com/iluwatar/combinator/Finders.java
@@ -28,30 +28,25 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
-/**
- * Complex finders consisting of simple finder.
- */
+/** Complex finders consisting of simple finder. */
public class Finders {
- private Finders() {
- }
-
+ private Finders() {}
/**
* Finder to find a complex query.
+ *
* @param query to find
* @param orQuery alternative to find
* @param notQuery exclude from search
* @return new finder
*/
public static Finder advancedFinder(String query, String orQuery, String notQuery) {
- return
- Finder.contains(query)
- .or(Finder.contains(orQuery))
- .not(Finder.contains(notQuery));
+ return Finder.contains(query).or(Finder.contains(orQuery)).not(Finder.contains(notQuery));
}
/**
* Filtered finder looking a query with excluded queries as well.
+ *
* @param query to find
* @param excludeQueries to exclude
* @return new finder
@@ -63,11 +58,11 @@ public static Finder filteredFinder(String query, String... excludeQueries) {
finder = finder.not(Finder.contains(q));
}
return finder;
-
}
/**
* Specialized query. Every next query is looked in previous result.
+ *
* @param queries array with queries
* @return new finder
*/
@@ -82,6 +77,7 @@ public static Finder specializedFinder(String... queries) {
/**
* Expanded query. Looking for alternatives.
+ *
* @param queries array with queries.
* @return new finder
*/
diff --git a/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
index 02f8581df62a..4a7a6fff7a4e 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
@@ -32,12 +32,12 @@ class CombinatorAppTest {
/**
* Issue: Add at least one assertion to this test case.
- *
- * Solution: Inserted assertion to check whether the execution of the main method in {@link CombinatorApp#main(String[])}
- * throws an exception.
+ *
+ *
Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * CombinatorApp#main(String[])} throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> CombinatorApp.main(new String[]{}));
+ assertDoesNotThrow(() -> CombinatorApp.main(new String[] {}));
}
}
diff --git a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
index aa84b9350d5b..f8a23927c33e 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
@@ -65,7 +65,6 @@ void expandedFinderTest() {
assertEquals("In this kingdom by the sea;", res.get(2));
}
-
private String text() {
return """
It was many and many a year ago,
@@ -81,5 +80,4 @@ private String text() {
With a love that the winged seraphs of heaven
Coveted her and me.""";
}
-
}
diff --git a/command-query-responsibility-segregation/README.md b/command-query-responsibility-segregation/README.md
index 4c9749d97c06..bc1de83fc681 100644
--- a/command-query-responsibility-segregation/README.md
+++ b/command-query-responsibility-segregation/README.md
@@ -32,6 +32,10 @@ Microsoft's documentation says
> CQRS separates reads and writes into different models, using commands to update data, and queries to read data.
+Architecture diagram
+
+
+
## Programmatic Example of CQRS Pattern in Java
One way to implement the Command Query Responsibility Segregation (CQRS) pattern is to separate the read and write operations into different services.
diff --git a/command-query-responsibility-segregation/etc/cqrs-architecture-diagram.png b/command-query-responsibility-segregation/etc/cqrs-architecture-diagram.png
new file mode 100644
index 000000000000..7391582c245c
Binary files /dev/null and b/command-query-responsibility-segregation/etc/cqrs-architecture-diagram.png differ
diff --git a/command-query-responsibility-segregation/pom.xml b/command-query-responsibility-segregation/pom.xml
index bc2659cdcd51..c3c277dd0b80 100644
--- a/command-query-responsibility-segregation/pom.xml
+++ b/command-query-responsibility-segregation/pom.xml
@@ -34,6 +34,14 @@
command-query-responsibility-segregation
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
@@ -46,14 +54,17 @@
org.hibernate
hibernate-core
+ 5.6.15.Final
org.glassfish.jaxb
jaxb-runtime
+ 2.3.3
javax.xml.bind
jaxb-api
+ 2.3.1
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java
index 52b8ead45a74..62317638955c 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java
@@ -62,8 +62,8 @@ public static void main(String[] args) {
commands.bookAddedToAuthor("Effective Java", 40.54, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Java Puzzlers", 39.99, AppConstants.J_BLOCH);
commands.bookAddedToAuthor("Java Concurrency in Practice", 29.40, AppConstants.J_BLOCH);
- commands.bookAddedToAuthor("Patterns of Enterprise"
- + " Application Architecture", 54.01, AppConstants.M_FOWLER);
+ commands.bookAddedToAuthor(
+ "Patterns of Enterprise" + " Application Architecture", 54.01, AppConstants.M_FOWLER);
commands.bookAddedToAuthor("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
@@ -86,5 +86,4 @@ public static void main(String[] args) {
HibernateUtil.getSessionFactory().close();
}
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.java
index 114280afb65f..9cf8c52cbb09 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.cqrs.commandes;
-/**
- * This interface represents the commands of the CQRS pattern.
- */
+/** This interface represents the commands of the CQRS pattern. */
public interface CommandService {
void authorCreated(String username, String name, String email);
@@ -42,5 +40,4 @@ public interface CommandService {
void bookTitleUpdated(String oldTitle, String newTitle);
void bookPriceUpdated(String title, double price);
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
index 52b32dc45f85..b4a368c98319 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
@@ -140,5 +140,4 @@ public void bookPriceUpdated(String title, double price) {
session.getTransaction().commit();
}
}
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
index 5753f8c2bd3a..71d266f43c35 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
@@ -24,14 +24,11 @@
*/
package com.iluwatar.cqrs.constants;
-/**
- * Class to define the constants.
- */
+/** Class to define the constants. */
public class AppConstants {
public static final String E_EVANS = "eEvans";
public static final String J_BLOCH = "jBloch";
public static final String M_FOWLER = "mFowler";
public static final String USER_NAME = "username";
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Author.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
index ff9192717e8e..03155d67a30b 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
@@ -32,9 +32,7 @@
import lombok.Setter;
import lombok.ToString;
-/**
- * This is an Author entity. It is used by Hibernate for persistence.
- */
+/** This is an Author entity. It is used by Hibernate for persistence. */
@ToString
@Getter
@Setter
@@ -43,6 +41,7 @@ public class Author {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
+
private String username;
private String name;
private String email;
@@ -51,8 +50,8 @@ public class Author {
* Constructor.
*
* @param username username of the author
- * @param name name of the author
- * @param email email of the author
+ * @param name name of the author
+ * @param email email of the author
*/
public Author(String username, String name, String email) {
this.username = username;
@@ -60,7 +59,5 @@ public Author(String username, String name, String email) {
this.email = email;
}
- protected Author() {
- }
-
+ protected Author() {}
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Book.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
index 171c91d4da83..2e2c356528e9 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
@@ -45,16 +45,16 @@ public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
+
private String title;
private double price;
- @ManyToOne
- private Author author;
+ @ManyToOne private Author author;
/**
* Constructor.
*
- * @param title title of the book
- * @param price price of the book
+ * @param title title of the book
+ * @param price price of the book
* @param author author of the book
*/
public Book(String title, double price, Author author) {
@@ -63,7 +63,5 @@ public Book(String title, double price, Author author) {
this.author = author;
}
- protected Book() {
- }
-
+ protected Book() {}
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Author.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Author.java
index 35a565cfa332..58074e6dafe2 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Author.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Author.java
@@ -30,9 +30,7 @@
import lombok.NoArgsConstructor;
import lombok.ToString;
-/**
- * This is a DTO (Data Transfer Object) author, contains only useful information to be returned.
- */
+/** This is a DTO (Data Transfer Object) author, contains only useful information to be returned. */
@ToString
@EqualsAndHashCode
@Getter
@@ -43,5 +41,4 @@ public class Author {
private String name;
private String email;
private String username;
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Book.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Book.java
index a938c65d39a9..72ce5b8c249b 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Book.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Book.java
@@ -30,9 +30,7 @@
import lombok.NoArgsConstructor;
import lombok.ToString;
-/**
- * This is a DTO (Data Transfer Object) book, contains only useful information to be returned.
- */
+/** This is a DTO (Data Transfer Object) book, contains only useful information to be returned. */
@ToString
@EqualsAndHashCode
@Getter
@@ -42,5 +40,4 @@ public class Book {
private String title;
private double price;
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java
index c226c6f213a6..b37c1dad05a9 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java
@@ -29,9 +29,7 @@
import java.math.BigInteger;
import java.util.List;
-/**
- * This interface represents the query methods of the CQRS pattern.
- */
+/** This interface represents the query methods of the CQRS pattern. */
public interface QueryService {
Author getAuthorByUsername(String username);
@@ -43,5 +41,4 @@ public interface QueryService {
BigInteger getAuthorBooksCount(String username);
BigInteger getAuthorsCount();
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
index ed6db0b500cb..60847d1c6db6 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
@@ -45,9 +45,10 @@ public class QueryServiceImpl implements QueryService {
public Author getAuthorByUsername(String username) {
Author authorDto;
try (var session = sessionFactory.openSession()) {
- Query sqlQuery = session.createQuery(
+ Query sqlQuery =
+ session.createQuery(
"select new com.iluwatar.cqrs.dto.Author(a.name, a.email, a.username)"
- + " from com.iluwatar.cqrs.domain.model.Author a where a.username=:username");
+ + " from com.iluwatar.cqrs.domain.model.Author a where a.username=:username");
sqlQuery.setParameter(AppConstants.USER_NAME, username);
authorDto = sqlQuery.uniqueResult();
}
@@ -58,9 +59,10 @@ public Author getAuthorByUsername(String username) {
public Book getBook(String title) {
Book bookDto;
try (var session = sessionFactory.openSession()) {
- Query sqlQuery = session.createQuery(
+ Query sqlQuery =
+ session.createQuery(
"select new com.iluwatar.cqrs.dto.Book(b.title, b.price)"
- + " from com.iluwatar.cqrs.domain.model.Book b where b.title=:title");
+ + " from com.iluwatar.cqrs.domain.model.Book b where b.title=:title");
sqlQuery.setParameter("title", title);
bookDto = sqlQuery.uniqueResult();
}
@@ -71,10 +73,11 @@ public Book getBook(String title) {
public List getAuthorBooks(String username) {
List bookDtos;
try (var session = sessionFactory.openSession()) {
- Query sqlQuery = session.createQuery(
+ Query sqlQuery =
+ session.createQuery(
"select new com.iluwatar.cqrs.dto.Book(b.title, b.price)"
- + " from com.iluwatar.cqrs.domain.model.Author a, com.iluwatar.cqrs.domain.model.Book b "
- + "where b.author.id = a.id and a.username=:username");
+ + " from com.iluwatar.cqrs.domain.model.Author a, com.iluwatar.cqrs.domain.model.Book b "
+ + "where b.author.id = a.id and a.username=:username");
sqlQuery.setParameter(AppConstants.USER_NAME, username);
bookDtos = sqlQuery.list();
}
@@ -85,9 +88,11 @@ public List getAuthorBooks(String username) {
public BigInteger getAuthorBooksCount(String username) {
BigInteger bookcount;
try (var session = sessionFactory.openSession()) {
- var sqlQuery = session.createNativeQuery(
- "SELECT count(b.title)" + " FROM Book b, Author a"
- + " where b.author_id = a.id and a.username=:username");
+ var sqlQuery =
+ session.createNativeQuery(
+ "SELECT count(b.title)"
+ + " FROM Book b, Author a"
+ + " where b.author_id = a.id and a.username=:username");
sqlQuery.setParameter(AppConstants.USER_NAME, username);
bookcount = (BigInteger) sqlQuery.uniqueResult();
}
@@ -103,5 +108,4 @@ public BigInteger getAuthorsCount() {
}
return authorcount;
}
-
}
diff --git a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
index b7f6e76f3faa..0b777fc59b1f 100644
--- a/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
@@ -54,5 +54,4 @@ private static SessionFactory buildSessionFactory() {
public static SessionFactory getSessionFactory() {
return SESSIONFACTORY;
}
-
}
diff --git a/command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java b/command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
index d7df072aa05f..a4fb9ed3bb7a 100644
--- a/command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
+++ b/command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
@@ -36,9 +36,7 @@
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
-/**
- * Integration test of IQueryService and ICommandService with h2 data
- */
+/** Integration test of IQueryService and ICommandService with h2 data */
class IntegrationTest {
private static QueryService queryService;
@@ -64,7 +62,6 @@ static void initializeAndPopulateDatabase() {
commandService.bookAddedToAuthor("title2", 20, "username1");
commandService.bookPriceUpdated("title2", 30);
commandService.bookTitleUpdated("title2", "new_title2");
-
}
@Test
@@ -80,7 +77,6 @@ void testGetUpdatedAuthorByUsername() {
var author = queryService.getAuthorByUsername("new_username2");
var expectedAuthor = new Author("new_name2", "new_email2", "new_username2");
assertEquals(expectedAuthor, author);
-
}
@Test
@@ -109,5 +105,4 @@ void testGetAuthorsCount() {
var authorCount = queryService.getAuthorsCount();
assertEquals(new BigInteger("2"), authorCount);
}
-
}
diff --git a/command/README.md b/command/README.md
index 8877f722e4b9..761267c04019 100644
--- a/command/README.md
+++ b/command/README.md
@@ -34,6 +34,10 @@ Wikipedia says
> In object-oriented programming, the command pattern is a behavioral design pattern in which an object is used to encapsulate all information needed to perform an action or trigger an event at a later time.
+Sequence diagram
+
+
+
## Programmatic Example of Command Pattern in Java
In the Command pattern, objects are used to encapsulate all information needed to perform an action or trigger an event at a later time. This pattern is particularly useful for implementing undo functionality in applications.
diff --git a/command/etc/command-sequence-diagram.png b/command/etc/command-sequence-diagram.png
new file mode 100644
index 000000000000..845d0a2fde0c
Binary files /dev/null and b/command/etc/command-sequence-diagram.png differ
diff --git a/command/pom.xml b/command/pom.xml
index d6532a8db700..83fb68cbf8e3 100644
--- a/command/pom.xml
+++ b/command/pom.xml
@@ -34,6 +34,14 @@
command
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/command/src/main/java/com/iluwatar/command/App.java b/command/src/main/java/com/iluwatar/command/App.java
index 18dd6eee3596..a8e0edcdf45c 100644
--- a/command/src/main/java/com/iluwatar/command/App.java
+++ b/command/src/main/java/com/iluwatar/command/App.java
@@ -32,9 +32,9 @@
* Four terms always associated with the command pattern are command, receiver, invoker and
* client. A command object (spell) knows about the receiver (target) and invokes a method of the
* receiver. An invoker object (wizard) receives a reference to the command to be executed and
- * optionally does bookkeeping about the command execution. The invoker does not know anything
- * about how the command is executed. The client decides which commands to execute at which
- * points. To execute a command, it passes a reference of the function to the invoker object.
+ * optionally does bookkeeping about the command execution. The invoker does not know anything about
+ * how the command is executed. The client decides which commands to execute at which points. To
+ * execute a command, it passes a reference of the function to the invoker object.
*
*
In other words, in this example the wizard casts spells on the goblin. The wizard keeps track
* of the previous spells cast, so it is easy to undo them. In addition, the wizard keeps track of
diff --git a/command/src/main/java/com/iluwatar/command/Goblin.java b/command/src/main/java/com/iluwatar/command/Goblin.java
index ed9ee49b85ea..911983c7b71d 100644
--- a/command/src/main/java/com/iluwatar/command/Goblin.java
+++ b/command/src/main/java/com/iluwatar/command/Goblin.java
@@ -24,9 +24,7 @@
*/
package com.iluwatar.command;
-/**
- * Goblin is the target of the spells.
- */
+/** Goblin is the target of the spells. */
public class Goblin extends Target {
public Goblin() {
diff --git a/command/src/main/java/com/iluwatar/command/Size.java b/command/src/main/java/com/iluwatar/command/Size.java
index d16149c1d81e..203f190a89c4 100644
--- a/command/src/main/java/com/iluwatar/command/Size.java
+++ b/command/src/main/java/com/iluwatar/command/Size.java
@@ -26,12 +26,9 @@
import lombok.RequiredArgsConstructor;
-/**
- * Enumeration for target size.
- */
+/** Enumeration for target size. */
@RequiredArgsConstructor
public enum Size {
-
SMALL("small"),
NORMAL("normal");
diff --git a/command/src/main/java/com/iluwatar/command/Target.java b/command/src/main/java/com/iluwatar/command/Target.java
index 7afd847b2a90..5885e0c4a9a6 100644
--- a/command/src/main/java/com/iluwatar/command/Target.java
+++ b/command/src/main/java/com/iluwatar/command/Target.java
@@ -1,66 +1,58 @@
-/*
- * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
- *
- * The MIT License
- * Copyright © 2014-2022 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.command;
-
-import lombok.Getter;
-import lombok.Setter;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * Base class for spell targets.
- */
-@Slf4j
-@Getter
-@Setter
-public abstract class Target {
-
- private Size size;
-
- private Visibility visibility;
-
- /**
- * Print status.
- */
- public void printStatus() {
- LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
- }
-
- /**
- * Changes the size of the target.
- */
- public void changeSize() {
- var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
- setSize(oldSize);
- }
-
- /**
- * Changes the visibility of the target.
- */
- public void changeVisibility() {
- var visible = getVisibility() == Visibility.INVISIBLE
- ? Visibility.VISIBLE : Visibility.INVISIBLE;
- setVisibility(visible);
- }
-}
+/*
+ * This project is licensed under the MIT license. Module model-view-viewmodel is using ZK framework licensed under LGPL (see lgpl-3.0.txt).
+ *
+ * The MIT License
+ * Copyright © 2014-2022 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.command;
+
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.slf4j.Slf4j;
+
+/** Base class for spell targets. */
+@Slf4j
+@Getter
+@Setter
+public abstract class Target {
+
+ private Size size;
+
+ private Visibility visibility;
+
+ /** Print status. */
+ public void printStatus() {
+ LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
+ }
+
+ /** Changes the size of the target. */
+ public void changeSize() {
+ var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
+ setSize(oldSize);
+ }
+
+ /** Changes the visibility of the target. */
+ public void changeVisibility() {
+ var visible =
+ getVisibility() == Visibility.INVISIBLE ? Visibility.VISIBLE : Visibility.INVISIBLE;
+ setVisibility(visible);
+ }
+}
diff --git a/command/src/main/java/com/iluwatar/command/Visibility.java b/command/src/main/java/com/iluwatar/command/Visibility.java
index d1539482e0e8..a07e7876b609 100644
--- a/command/src/main/java/com/iluwatar/command/Visibility.java
+++ b/command/src/main/java/com/iluwatar/command/Visibility.java
@@ -26,12 +26,9 @@
import lombok.RequiredArgsConstructor;
-/**
- * Enumeration for target visibility.
- */
+/** Enumeration for target visibility. */
@RequiredArgsConstructor
public enum Visibility {
-
VISIBLE("visible"),
INVISIBLE("invisible");
diff --git a/command/src/main/java/com/iluwatar/command/Wizard.java b/command/src/main/java/com/iluwatar/command/Wizard.java
index 93d23cd68eff..797c41ec04ad 100644
--- a/command/src/main/java/com/iluwatar/command/Wizard.java
+++ b/command/src/main/java/com/iluwatar/command/Wizard.java
@@ -28,26 +28,20 @@
import java.util.LinkedList;
import lombok.extern.slf4j.Slf4j;
-/**
- * Wizard is the invoker of the commands.
- */
+/** Wizard is the invoker of the commands. */
@Slf4j
public class Wizard {
private final Deque undoStack = new LinkedList<>();
private final Deque redoStack = new LinkedList<>();
- /**
- * Cast spell.
- */
+ /** Cast spell. */
public void castSpell(Runnable runnable) {
runnable.run();
undoStack.offerLast(runnable);
}
- /**
- * Undo last spell.
- */
+ /** Undo last spell. */
public void undoLastSpell() {
if (!undoStack.isEmpty()) {
var previousSpell = undoStack.pollLast();
@@ -56,9 +50,7 @@ public void undoLastSpell() {
}
}
- /**
- * Redo last spell.
- */
+ /** Redo last spell. */
public void redoLastSpell() {
if (!redoStack.isEmpty()) {
var previousSpell = redoStack.pollLast();
diff --git a/command/src/test/java/com/iluwatar/command/AppTest.java b/command/src/test/java/com/iluwatar/command/AppTest.java
index 830fee8fb4dc..874bc3609d91 100644
--- a/command/src/test/java/com/iluwatar/command/AppTest.java
+++ b/command/src/test/java/com/iluwatar/command/AppTest.java
@@ -24,23 +24,21 @@
*/
package com.iluwatar.command;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-/**
- * Tests that Command example runs without errors.
- */
+import org.junit.jupiter.api.Test;
+
+/** Tests that Command example runs without errors. */
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#main(String[])}
- * throws an exception.
+ * Solution: Inserted assertion to check whether the execution of the main method in {@link
+ * App#main(String[])} throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
+ assertDoesNotThrow(() -> App.main(new String[] {}));
}
}
diff --git a/command/src/test/java/com/iluwatar/command/CommandTest.java b/command/src/test/java/com/iluwatar/command/CommandTest.java
index 766cede95b43..5e41c884697b 100644
--- a/command/src/test/java/com/iluwatar/command/CommandTest.java
+++ b/command/src/test/java/com/iluwatar/command/CommandTest.java
@@ -80,17 +80,18 @@ void testCommand() {
* This method asserts that the passed goblin object has the name as expectedName, size as
* expectedSize and visibility as expectedVisibility.
*
- * @param goblin a goblin object whose state is to be verified against other
- * parameters
- * @param expectedName expectedName of the goblin
- * @param expectedSize expected size of the goblin
+ * @param goblin a goblin object whose state is to be verified against other parameters
+ * @param expectedName expectedName of the goblin
+ * @param expectedSize expected size of the goblin
* @param expectedVisibility expected visibility of the goblin
*/
- private void verifyGoblin(Goblin goblin, String expectedName, Size expectedSize,
- Visibility expectedVisibility) {
+ private void verifyGoblin(
+ Goblin goblin, String expectedName, Size expectedSize, Visibility expectedVisibility) {
assertEquals(expectedName, goblin.toString(), "Goblin's name must be same as expectedName");
assertEquals(expectedSize, goblin.getSize(), "Goblin's size must be same as expectedSize");
- assertEquals(expectedVisibility, goblin.getVisibility(),
+ assertEquals(
+ expectedVisibility,
+ goblin.getVisibility(),
"Goblin's visibility must be same as expectedVisibility");
}
}
diff --git a/commander/README.md b/commander/README.md
index 902c6af1bcad..cb5215296bcf 100644
--- a/commander/README.md
+++ b/commander/README.md
@@ -33,6 +33,10 @@ In plain words
> The Commander pattern turns a request into a stand-alone object, allowing for the parameterization of commands, queueing of actions, and the implementation of undo operations.
+Sequence diagram
+
+
+
## Programmatic Example of Commander Pattern in Java
Managing transactions across different services in a distributed system, such as an e-commerce platform with separate `Payment` and `Shipping` microservices, requires careful coordination. Using the Commander pattern in Java for transaction coordination helps ensure data consistency and reliability, even when services experience partial failures.
diff --git a/commander/etc/commander-sequence-diagram.png b/commander/etc/commander-sequence-diagram.png
new file mode 100644
index 000000000000..00d6eee0779c
Binary files /dev/null and b/commander/etc/commander-sequence-diagram.png differ
diff --git a/commander/pom.xml b/commander/pom.xml
index edaeef3e8117..1dbe2dc89de5 100644
--- a/commander/pom.xml
+++ b/commander/pom.xml
@@ -33,6 +33,10 @@
1.26.0-SNAPSHOT
commander
+
+ 2.0.17
+ 1.5.6
+
org.junit.jupiter
@@ -50,7 +54,7 @@