properties) {
+ super(properties);
+ }
}
```
And finally here's how we construct and use the `Car` in a full example.
```java
+ public static void main(String[] args) {
LOGGER.info("Constructing parts and car");
var wheelProperties = Map.of(
- Property.TYPE.toString(), "wheel",
- Property.MODEL.toString(), "15C",
- Property.PRICE.toString(), 100L);
+ Property.TYPE.toString(), "wheel",
+ Property.MODEL.toString(), "15C",
+ Property.PRICE.toString(), 100L);
var doorProperties = Map.of(
- Property.TYPE.toString(), "door",
- Property.MODEL.toString(), "Lambo",
- Property.PRICE.toString(), 300L);
+ Property.TYPE.toString(), "door",
+ Property.MODEL.toString(), "Lambo",
+ Property.PRICE.toString(), 300L);
var carProperties = Map.of(
- Property.MODEL.toString(), "300SL",
- Property.PRICE.toString(), 10000L,
- Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
+ Property.MODEL.toString(), "300SL",
+ Property.PRICE.toString(), 10000L,
+ Property.PARTS.toString(), List.of(wheelProperties, doorProperties));
var car = new Car(carProperties);
@@ -158,34 +172,76 @@ And finally here's how we construct and use the `Car` in a full example.
LOGGER.info("-> price: {}", car.getPrice().orElseThrow());
LOGGER.info("-> parts: ");
car.getParts().forEach(p -> LOGGER.info("\t{}/{}/{}",
- p.getType().orElse(null),
- p.getModel().orElse(null),
- p.getPrice().orElse(null))
+ p.getType().orElse(null),
+ p.getModel().orElse(null),
+ p.getPrice().orElse(null))
);
+}
+```
+
+The program output:
- // Constructing parts and car
- // Here is our car:
- // model: 300SL
- // price: 10000
- // parts:
- // wheel/15C/100
- // door/Lambo/300
```
+07:21:57.391 [main] INFO com.iluwatar.abstractdocument.App -- Constructing parts and car
+07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- Here is our car:
+07:21:57.393 [main] INFO com.iluwatar.abstractdocument.App -- -> model: 300SL
+07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> price: 10000
+07:21:57.394 [main] INFO com.iluwatar.abstractdocument.App -- -> parts:
+07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- wheel/15C/100
+07:21:57.395 [main] INFO com.iluwatar.abstractdocument.App -- door/Lambo/300
+```
+
+## When to Use the Abstract Document Pattern in Java
+
+The Abstract Document design pattern is especially beneficial in scenarios requiring management of different document types in Java that share some common attributes or behaviors, but also have unique attributes or behaviors specific to their individual types. Here are some scenarios where the Abstract Document design pattern can be applicable:
+
+* Content Management Systems (CMS): In a CMS, you might have various types of content such as articles, images, videos, etc. Each type of content could have shared attributes like creation date, author, and tags, while also having specific attributes like image dimensions for images or video duration for videos.
+
+* File Systems: If you're designing a file system where different types of files need to be managed, such as documents, images, audio files, and directories, the Abstract Document pattern can help provide a consistent way to access attributes like file size, creation date, etc., while allowing for specific attributes like image resolution or audio duration.
+
+* E-commerce Systems: An e-commerce platform might have different product types such as physical products, digital downloads, and subscriptions. Each type could share common attributes like name, price, and description, while having unique attributes like shipping weight for physical products or download link for digital products.
+
+* Medical Records Systems: In healthcare, patient records might include various types of data such as demographics, medical history, test results, and prescriptions. The Abstract Document pattern can help manage shared attributes like patient ID and date of birth, while accommodating specialized attributes like test results or prescribed medications.
+
+* Configuration Management: When dealing with configuration settings for software applications, there can be different types of configuration elements, each with its own set of attributes. The Abstract Document pattern can be used to manage these configuration elements while ensuring a consistent way to access and manipulate their attributes.
+
+* Educational Platforms: Educational systems might have various types of learning materials such as text-based content, videos, quizzes, and assignments. Common attributes like title, author, and publication date can be shared, while unique attributes like video duration or assignment due dates can be specific to each type.
+
+* Project Management Tools: In project management applications, you could have different types of tasks like to-do items, milestones, and issues. The Abstract Document pattern could be used to handle general attributes like task name and assignee, while allowing for specific attributes like milestone date or issue priority.
+
+* Documents have diverse and evolving attribute structures.
+
+* Dynamically adding new properties is a common requirement.
+
+* Decoupling data access from specific formats is crucial.
+
+* Maintainability and flexibility are critical for the codebase.
+
+The key idea behind the Abstract Document design pattern is to provide a flexible and extensible way to manage different types of documents or entities with shared and distinct attributes. By defining a common interface and implementing it across various document types, you can achieve a more organized and consistent approach to handling complex data structures.
+
+## Benefits and Trade-offs of Abstract Document Pattern
+
+Benefits:
+
+* Flexibility: Accommodates varied document structures and properties.
+
+* Extensibility: Dynamically add new attributes without breaking existing code.
-## Class diagram
+* Maintainability: Promotes clean and adaptable code due to separation of concerns.
-
+* Reusability: Typed views enable code reuse for accessing specific attribute types.
-## Applicability
+Trade-offs:
-Use the Abstract Document Pattern when
+* Complexity: Requires defining interfaces and views, adding implementation overhead.
-* There is a need to add new properties on the fly
-* You want a flexible way to organize domain in tree like structure
-* You want more loosely coupled system
+* Performance: Might introduce slight performance overhead compared to direct data access.
-## Credits
+## References and Credits
-* [Wikipedia: Abstract Document Pattern](https://en.wikipedia.org/wiki/Abstract_Document_Pattern)
-* [Martin Fowler: Dealing with properties](http://martinfowler.com/apsupp/properties.pdf)
-* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)](https://www.amazon.com/gp/product/0470059028/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0470059028&linkId=e3aacaea7017258acf184f9f3283b492)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Pattern-Oriented Software Architecture Volume 4: A Pattern Language for Distributed Computing (v. 4)](https://amzn.to/49zRP4R)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
+* [Abstract Document Pattern (Wikipedia)](https://en.wikipedia.org/wiki/Abstract_Document_Pattern)
+* [Dealing with Properties (Martin Fowler)](http://martinfowler.com/apsupp/properties.pdf)
diff --git a/abstract-document/pom.xml b/abstract-document/pom.xml
index 63ab70c9c176..ef190e088d5e 100644
--- a/abstract-document/pom.xml
+++ b/abstract-document/pom.xml
@@ -1,8 +1,10 @@
{}", 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 d36d1023d2ce..ca3a5526ebb8 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/App.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,27 +22,25 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.activeobject;
import java.util.ArrayList;
import java.util.List;
-
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
- * The Active Object pattern helps to solve synchronization difficulties without using
- * 'synchronized' methods. The active object will contain a thread-safe data structure
- * (such as BlockingQueue) and use to synchronize method calls by moving the logic of the method
- * into an invocator(usually a Runnable) and store it in the DSA.
- *
+ * 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,16 +48,16 @@ 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<>();
try {
- for (int i = 0;i < NUM_CREATURES;i++) {
+ for (int i = 0; i < NUM_CREATURES; i++) {
creatures.add(new Orc(Orc.class.getSimpleName() + i));
creatures.get(i).eat();
creatures.get(i).roam();
@@ -67,7 +67,7 @@ public void run() {
logger.error(e.getMessage());
Thread.currentThread().interrupt();
} finally {
- for (int i = 0;i < NUM_CREATURES;i++) {
+ for (int i = 0; i < NUM_CREATURES; i++) {
creatures.get(i).kill(0);
}
}
diff --git a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
index 2bce5b2b04a9..30adde034de5 100644
--- a/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
+++ b/active-object/src/main/java/com/iluwatar/activeobject/Orc.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,18 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.activeobject;
-/**
- * An implementation of the ActiveCreature class.
- * @author Noam Greenshtain
- *
- */
+/** 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 8a4296a1f3e6..be79e2fb5527 100644
--- a/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
+++ b/active-object/src/test/java/com/iluwatar/activeobject/ActiveCreatureTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,23 +22,21 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.activeobject;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
+
class ActiveCreatureTest {
-
- @Test
- void executionTest() throws InterruptedException {
- ActiveCreature orc = new Orc("orc1");
- assertEquals("orc1",orc.name());
- assertEquals(0,orc.getStatus());
- orc.eat();
- orc.roam();
- orc.kill(0);
- }
-
+ @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 c7b223d677c6..559e2a1f58f1 100644
--- a/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
+++ b/active-object/src/test/java/com/iluwatar/activeobject/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,18 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.activeobject;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
-
class AppTest {
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
+ @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 0a90836ab256..fb57d5681fd4 100644
--- a/acyclic-visitor/README.md
+++ b/acyclic-visitor/README.md
@@ -1,25 +1,25 @@
---
-layout: pattern
-title: Acyclic Visitor
-folder: acyclic-visitor
-permalink: /patterns/acyclic-visitor/
-categories: Behavioral
+title: "Acyclic Visitor Pattern in Java: Streamlining Object Interactions"
+shortTitle: Acyclic Visitor
+description: "Learn about the Acyclic Visitor pattern in Java. This guide explains how it decouples operations from object hierarchies, providing examples and real-world applications."
+category: Behavioral
language: en
-tags:
- - Extensibility
+tag:
+ - Decoupling
+ - Extensibility
+ - Interface
+ - Object composition
---
-## Intent
+## Intent of Acyclic Visitor Design Pattern
-Allow new functions to be added to existing class hierarchies without affecting those hierarchies, and without creating
-the troublesome dependency cycles that are inherent to the GoF Visitor Pattern.
+The Acyclic Visitor pattern in Java decouples operations from an object hierarchy, providing a flexible design for various applications.
-## Explanation
+## Detailed Explanation of Acyclic Visitor Pattern with Real-World Examples
-Real world example
+Real-world example
-> We have a hierarchy of modem classes. The modems in this hierarchy need to be visited by an external algorithm based
-> on filtering criteria (is it Unix or DOS compatible modem).
+> An analogous real-world example of the Acyclic Visitor pattern in Java is a museum guide system, demonstrating the practical application of this design pattern. Imagine a museum with various exhibits like paintings, sculptures, and historical artifacts. The museum has different types of guides (audio guide, human guide, virtual reality guide) that provide information about each exhibit. Instead of modifying the exhibits every time a new guide type is introduced, each guide implements an interface to visit different exhibit types. This way, the museum can add new types of guides without altering the existing exhibits, ensuring that the system remains extensible and maintainable without forming any dependency cycles.
In plain words
@@ -27,108 +27,123 @@ In plain words
[WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor) says
-> 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.
+> 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.
-**Programmatic Example**
+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).
Here's the `Modem` hierarchy.
```java
public abstract class Modem {
- public abstract void accept(ModemVisitor modemVisitor);
+ public abstract void accept(ModemVisitor modemVisitor);
}
public class Zoom extends Modem {
- ...
- @Override
- public void accept(ModemVisitor modemVisitor) {
- if (modemVisitor instanceof ZoomVisitor) {
- ((ZoomVisitor) modemVisitor).visit(this);
- } else {
- LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+
+ // Other properties and methods...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof ZoomVisitor) {
+ ((ZoomVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only ZoomVisitor is allowed to visit Zoom modem");
+ }
}
- }
}
public class Hayes extends Modem {
- ...
- @Override
- public void accept(ModemVisitor modemVisitor) {
- if (modemVisitor instanceof HayesVisitor) {
- ((HayesVisitor) modemVisitor).visit(this);
- } else {
- LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+
+ // Other properties and methods...
+
+ @Override
+ public void accept(ModemVisitor modemVisitor) {
+ if (modemVisitor instanceof HayesVisitor) {
+ ((HayesVisitor) modemVisitor).visit(this);
+ } else {
+ LOGGER.info("Only HayesVisitor is allowed to visit Hayes modem");
+ }
}
- }
}
```
-Next we introduce the `ModemVisitor` hierarchy.
+Next, we introduce the `ModemVisitor` hierarchy.
```java
public interface ModemVisitor {
}
public interface HayesVisitor extends ModemVisitor {
- void visit(Hayes hayes);
+ void visit(Hayes hayes);
}
public interface ZoomVisitor extends ModemVisitor {
- void visit(Zoom zoom);
+ void visit(Zoom zoom);
}
public interface AllModemVisitor extends ZoomVisitor, HayesVisitor {
}
public class ConfigureForDosVisitor implements AllModemVisitor {
- ...
- @Override
- public void visit(Hayes hayes) {
- LOGGER.info(hayes + " used with Dos configurator.");
- }
- @Override
- public void visit(Zoom zoom) {
- LOGGER.info(zoom + " used with Dos configurator.");
- }
+
+ // Other properties and methods...
+
+ @Override
+ public void visit(Hayes hayes) {
+ LOGGER.info(hayes + " used with Dos configurator.");
+ }
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Dos configurator.");
+ }
}
public class ConfigureForUnixVisitor implements ZoomVisitor {
- ...
- @Override
- public void visit(Zoom zoom) {
- LOGGER.info(zoom + " used with Unix configurator.");
- }
+
+ // Other properties and methods...
+
+ @Override
+ public void visit(Zoom zoom) {
+ LOGGER.info(zoom + " used with Unix configurator.");
+ }
}
```
Finally, here are the visitors in action.
```java
+public static void main(String[] args) {
var conUnix = new ConfigureForUnixVisitor();
var conDos = new ConfigureForDosVisitor();
+
var zoom = new Zoom();
var hayes = new Hayes();
- hayes.accept(conDos);
- zoom.accept(conDos);
- hayes.accept(conUnix);
- zoom.accept(conUnix);
+
+ 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
+}
```
Program output:
```
- // Hayes modem used with Dos configurator.
- // Zoom modem used with Dos configurator.
- // Only HayesVisitor is allowed to visit Hayes modem
- // Zoom modem used with Unix configurator.
+09:15:11.125 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Hayes modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForDosVisitor -- Zoom modem used with Dos configurator.
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.Hayes -- Only HayesVisitor is allowed to visit Hayes modem
+09:15:11.127 [main] INFO com.iluwatar.acyclicvisitor.ConfigureForUnixVisitor -- Zoom modem used with Unix configurator.
```
-## Class diagram
-
-
-
-## Applicability
+## When to Use the Acyclic Visitor Pattern in Java
This pattern can be used:
@@ -138,28 +153,34 @@ This pattern can be used:
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
-## Tutorial
+## Acyclic Visitor Pattern Java Tutorials
-* [Acyclic Visitor Pattern Example](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
+* [The Acyclic Visitor Pattern (Code Crafter)](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
-## Consequences
+## Benefits and Trade-offs of Acyclic Visitor Pattern
-The good:
+Benefits:
-* No dependency cycles between class hierarchies.
-* No need to recompile all the visitors if a new one is added.
-* Does not cause compilation failure in existing visitors if class hierarchy has a new member.
+* Extensible: New operations can be added easily without changing the object structure.
+* Decoupled: Reduces coupling between the objects and the operations on them.
+* No dependency cycles: Ensures acyclic dependencies, improving maintainability and reducing complexity.
-The bad:
+Trade-offs:
-* Violates [Liskov's Substitution Principle](https://java-design-patterns.com/principles/#liskov-substitution-principle) by showing that it can accept all visitors but actually only being interested in particular visitors.
-* Parallel hierarchy of visitors has to be created for all members in visitable class hierarchy.
+* Increased complexity: Can introduce additional complexity with the need for multiple visitor interfaces.
+* Maintenance overhead: Modifying the object hierarchy requires updating all visitors.
-## Related patterns
+## Related Java Design Patterns
-* [Visitor Pattern](https://java-design-patterns.com/patterns/visitor/)
+* [Composite](https://java-design-patterns.com/patterns/composite/): Often used in conjunction with Acyclic Visitor to allow treating individual objects and compositions uniformly.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): Can be used alongside to add responsibilities to objects dynamically.
+* [Visitor](https://java-design-patterns.com/patterns/visitor/): The Acyclic Visitor pattern is a variation of the Visitor pattern that avoids cyclic dependencies.
-## Credits
+## References and Credits
-* [Acyclic Visitor by Robert C. Martin](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
-* [Acyclic Visitor in WikiWikiWeb](https://wiki.c2.com/?AcyclicVisitor)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
+* [Acyclic Visitor (Robert C. Martin)](http://condor.depaul.edu/dmumaugh/OOT/Design-Principles/acv.pdf)
+* [Acyclic Visitor (WikiWikiWeb)](https://wiki.c2.com/?AcyclicVisitor)
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 ee447b35c05b..b4f5646b71c0 100644
--- a/acyclic-visitor/pom.xml
+++ b/acyclic-visitor/pom.xml
@@ -1,8 +1,10 @@
- org.assertj
- assertj-core
- 3.9.1
- test
+ org.slf4j
+ slf4j-api
-
- uk.org.lidalia
- slf4j-test
- 1.2.0
- test
+ ch.qos.logback
+ logback-classic
org.junit.jupiter
@@ -58,8 +49,7 @@
org.mockito
- mockito-all
- 1.10.19
+ mockito-core
test
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 28345c505315..a3b1679a2d9f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/AllModemVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,13 +22,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
/**
* 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 b151019c298d..3b7c6cd61e4b 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
/**
@@ -36,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();
@@ -49,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 bfe7f6646b3b..267a8d66ac45 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,14 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
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 1f465967f053..d9fd14f69435 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
@@ -36,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 3e30258f5844..e0b2fcc2b530 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Hayes.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,20 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Hayes class implements its accept method.
- */
+/** Hayes class implements its accept method. */
@Slf4j
-public class Hayes extends Modem {
+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) {
@@ -41,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 06ee5300df5f..aad9b970994f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/HayesVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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 04018d543135..8552574453e5 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Modem.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-/**
- * Modem abstract class.
- */
-public abstract class Modem {
- public abstract void accept(ModemVisitor modemVisitor);
+/** //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/ModemVisitor.java b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
index b2181fe7c9a9..9391a980cd29 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ModemVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
/**
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 01e7e7aca08b..59b50a54a12f 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/Zoom.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,20 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
-/**
- * Zoom class implements its accept method.
- */
+/** Zoom class implements its accept method. */
@Slf4j
-public class Zoom extends Modem {
+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) {
@@ -43,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 e86a64fa6d42..5388ded6f735 100644
--- a/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
+++ b/acyclic-visitor/src/main/java/com/iluwatar/acyclicvisitor/ZoomVisitor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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 12f0ba6cedd0..7a21498a63ea 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,28 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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/ConfigureForDosVisitorTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java
deleted file mode 100644
index b41fc956cd62..000000000000
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForDosVisitorTest.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.acyclicvisitor;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.groups.Tuple.tuple;
-import static uk.org.lidalia.slf4jext.Level.INFO;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import uk.org.lidalia.slf4jtest.TestLogger;
-import uk.org.lidalia.slf4jtest.TestLoggerFactory;
-
-/**
- * ConfigureForDosVisitor test class
- */
-class ConfigureForDosVisitorTest {
-
- private final TestLogger logger = TestLoggerFactory.getTestLogger(ConfigureForDosVisitor.class);
-
- @Test
- void testVisitForZoom() {
- var conDos = new ConfigureForDosVisitor();
- var zoom = new Zoom();
-
- conDos.visit(zoom);
-
- assertThat(logger.getLoggingEvents())
- .extracting("level", "message")
- .contains(tuple(INFO, zoom + " used with Dos configurator."));
- }
-
- @Test
- void testVisitForHayes() {
- var conDos = new ConfigureForDosVisitor();
- var hayes = new Hayes();
-
- conDos.visit(hayes);
-
- assertThat(logger.getLoggingEvents())
- .extracting("level", "message")
- .contains(tuple(INFO, hayes + " used with Dos configurator."));
- }
-
- @AfterEach
- public void clearLoggers() {
- TestLoggerFactory.clear();
- }
-}
diff --git a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java
deleted file mode 100644
index d2ea55809aa3..000000000000
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ConfigureForUnixVisitorTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.acyclicvisitor;
-
-import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Test;
-import uk.org.lidalia.slf4jtest.TestLogger;
-import uk.org.lidalia.slf4jtest.TestLoggerFactory;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.groups.Tuple.tuple;
-import static uk.org.lidalia.slf4jext.Level.INFO;
-
-/**
- * ConfigureForUnixVisitor test class
- */
-class ConfigureForUnixVisitorTest {
-
- private static final TestLogger LOGGER = TestLoggerFactory.getTestLogger(ConfigureForUnixVisitor.class);
-
- @AfterEach
- public void clearLoggers() {
- TestLoggerFactory.clear();
- }
-
- @Test
- void testVisitForZoom() {
- var conUnix = new ConfigureForUnixVisitor();
- var zoom = new Zoom();
-
- conUnix.visit(zoom);
-
- assertThat(LOGGER.getLoggingEvents())
- .extracting("level", "message")
- .contains(tuple(INFO, zoom + " used with Unix configurator."));
- }
-}
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 eaf9fb152cc2..a989d9287921 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/HayesTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,17 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-import org.junit.jupiter.api.Test;
-
-import static org.mockito.Matchers.eq;
+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
@@ -49,6 +48,6 @@ void testAcceptForUnix() {
hayes.accept(mockVisitor);
- verifyZeroInteractions(mockVisitor);
+ verifyNoMoreInteractions(mockVisitor);
}
}
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 4373fe818fa5..d5fe79965d47 100644
--- a/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
+++ b/acyclic-visitor/src/test/java/com/iluwatar/acyclicvisitor/ZoomTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,19 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.acyclicvisitor;
-
-import org.junit.jupiter.api.Test;
-
-import static org.mockito.Matchers.eq;
+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 df8efe1b7367..489742494709 100644
--- a/adapter/README.md
+++ b/adapter/README.md
@@ -1,28 +1,31 @@
---
-layout: pattern
-title: Adapter
-folder: adapter
-permalink: /patterns/adapter/
-categories: Structural
+title: "Adapter Pattern in Java: Seamless Integration of Incompatible Systems"
+shortTitle: Adapter
+description: "Learn how the Adapter Design Pattern works in Java with detailed examples and use cases. Understand how it enables compatibility between incompatible interfaces."
+category: Structural
language: en
-tags:
- - Gang of Four
+tag:
+ - Compatibility
+ - Decoupling
+ - Gang of Four
+ - Interface
+ - Object composition
+ - Wrapping
---
## Also known as
-Wrapper
-## Intent
-Convert the interface of a class into another interface the clients expect. Adapter lets classes work together that
-couldn't otherwise because of incompatible interfaces.
+* Wrapper
-## Explanation
+## Intent of Adapter Design Pattern
-Real world example
+The Adapter Design Pattern in Java converts the interface of a class into another interface that clients expect, enabling compatibility.
-> Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter.
-> Another example would be the famous power adapter; a three legged plug can't be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet.
-> Yet another example would be a translator translating words spoken by one person to another
+## Detailed Explanation of Adapter Pattern with Real-World Examples
+
+Real-world example
+
+> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter. Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets. Yet another example would be a translator translating words spoken by one person to another
In plain words
@@ -32,109 +35,120 @@ 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.
-**Programmatic Example**
+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.
-Consider a captain that can only use rowing boats and cannot sail at all.
+Consider a wannabe captain that can only use rowing boats but can't sail at all.
-First we have interfaces `RowingBoat` and `FishingBoat`
+First, we have interfaces `RowingBoat` and `FishingBoat`
```java
public interface RowingBoat {
- void row();
+ void row();
}
@Slf4j
public class FishingBoat {
- public void sail() {
- LOGGER.info("The fishing boat is sailing");
- }
+ public void sail() {
+ LOGGER.info("The fishing boat is sailing");
+ }
}
```
-And captain expects an implementation of `RowingBoat` interface to be able to move
+The captain expects an implementation of `RowingBoat` interface to be able to move.
```java
public class Captain {
- private final RowingBoat rowingBoat;
- // default constructor and setter for rowingBoat
- public Captain(RowingBoat rowingBoat) {
- this.rowingBoat = rowingBoat;
- }
+ private final RowingBoat rowingBoat;
- public void row() {
- rowingBoat.row();
- }
+ // default constructor and setter for rowingBoat
+ public Captain(RowingBoat rowingBoat) {
+ this.rowingBoat = rowingBoat;
+ }
+
+ public void row() {
+ rowingBoat.row();
+ }
}
```
-Now let's say the pirates are coming and our captain needs to escape but there is only fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
+Now, let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
```java
@Slf4j
public class FishingBoatAdapter implements RowingBoat {
- private final FishingBoat boat;
+ private final FishingBoat boat;
- public FishingBoatAdapter() {
- boat = new FishingBoat();
- }
+ public FishingBoatAdapter() {
+ boat = new FishingBoat();
+ }
- @Override
- public void row() {
- boat.sail();
- }
+ @Override
+ public void row() {
+ boat.sail();
+ }
}
```
-And now the `Captain` can use the `FishingBoat` to escape the pirates.
+Now the `Captain` can use the `FishingBoat` to escape the pirates.
```java
-var captain = new Captain(new FishingBoatAdapter());
-captain.row();
+ public static void main(final String[] args) {
+ // The captain can only operate rowing boats but with adapter he is able to
+ // use fishing boats as well
+ var captain = new Captain(new FishingBoatAdapter());
+ captain.row();
+}
```
-## Class diagram
-
+The program outputs:
-## Applicability
-Use the Adapter pattern when
+```
+10:25:08.074 [main] INFO com.iluwatar.adapter.FishingBoat -- The fishing boat is sailing
+```
-* you want to use an existing class, and its interface does not match the one you need
-* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
-* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
-* most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
+## When to Use the Adapter Pattern in Java
-## Tutorials
+Use the Adapter pattern in Java when
-* [Dzone](https://dzone.com/articles/adapter-design-pattern-in-java)
-* [Refactoring Guru](https://refactoring.guru/design-patterns/adapter/java/example)
-* [Baeldung](https://www.baeldung.com/java-adapter-pattern)
+* You want to use an existing class, and its interface does not match the one you need
+* You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
+* You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class.
+* Most of the applications using third-party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
-## Consequences
-Class and object adapters have different trade-offs. A class adapter
+## Adapter Pattern Java Tutorials
-* adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter won’t work when we want to adapt a class and all its subclasses.
-* let’s Adapter override some of Adaptee’s behavior, since Adapter is a subclass of Adaptee.
-* introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
+* [Using the Adapter Design Pattern in Java (Dzone)](https://dzone.com/articles/adapter-design-pattern-in-java)
+* [Adapter in Java (Refactoring Guru)](https://refactoring.guru/design-patterns/adapter/java/example)
+* [The Adapter Pattern in Java (Baeldung)](https://www.baeldung.com/java-adapter-pattern)
+* [Adapter Design Pattern (GeeksForGeeks)](https://www.geeksforgeeks.org/adapter-pattern/)
-An object adapter
+## Benefits and Trade-offs of Adapter Pattern
-* let’s a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
-* makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself.
+Class and object adapters offer different benefits and drawbacks. A class adapter adapts the Adaptee to the Target by binding to a specific Adaptee class, which means it cannot adapt a class and all its subclasses. This type of adapter allows the Adapter to override some of the Adaptee’s behavior because the Adapter is a subclass of the Adaptee. Additionally, it introduces only one object without needing extra pointer indirection to reach the Adaptee.
+On the other hand, an object adapter allows a single Adapter to work with multiple Adaptees, including the Adaptee and all its subclasses. This type of adapter can add functionality to all Adaptees simultaneously. However, it makes overriding the Adaptee’s behavior more difficult, as it requires subclassing the Adaptee and having the Adapter refer to this subclass instead of the Adaptee itself.
-## Known uses
+## Real-World Applications of Adapter Pattern in Java
+* `java.io.InputStreamReader` and `java.io.OutputStreamWriter` in the Java IO library.
+* GUI component libraries that allow for plug-ins or adapters to convert between different GUI component interfaces.
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
* [java.util.Collections#enumeration()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#enumeration-java.util.Collection-)
* [javax.xml.bind.annotation.adapters.XMLAdapter](http://docs.oracle.com/javase/8/docs/api/javax/xml/bind/annotation/adapters/XmlAdapter.html#marshal-BoundType-)
+## References and Credits
-## Credits
-
-* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
-* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
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 a629110b91e1..6e7f45a515b5 100644
--- a/adapter/pom.xml
+++ b/adapter/pom.xml
@@ -1,8 +1,10 @@
-
-
- aggregator-microservices
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- aggregator-service
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.aggregator.microservices.App
-
-
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java
deleted file mode 100644
index 791df87ba635..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/App.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.aggregator.microservices;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * Spring Boot EntryPoint Class.
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-}
diff --git a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java b/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
deleted file mode 100644
index f451728b9e0d..000000000000
--- a/aggregator-microservices/aggregator-service/src/main/java/com/iluwatar/aggregator/microservices/Product.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.aggregator.microservices;
-
-import lombok.Getter;
-import lombok.Setter;
-
-/**
- * Encapsulates all the data for a Product that clients will request.
- */
-@Getter
-@Setter
-public class Product {
-
- /**
- * The title of the product.
- */
- private String title;
-
-
- /**
- * The inventories of the product.
- */
- private int productInventories;
-
-}
diff --git a/aggregator-microservices/information-microservice/pom.xml b/aggregator-microservices/information-microservice/pom.xml
deleted file mode 100644
index 796e58e058c8..000000000000
--- a/aggregator-microservices/information-microservice/pom.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- information-microservice
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.information.microservices.InformationApplication
-
-
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/inventory-microservice/pom.xml b/aggregator-microservices/inventory-microservice/pom.xml
deleted file mode 100644
index ddd55c55daf0..000000000000
--- a/aggregator-microservices/inventory-microservice/pom.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- aggregator-microservices
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- inventory-microservice
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.inventory.microservices.InventoryApplication
-
-
-
-
-
-
-
-
-
diff --git a/aggregator-microservices/pom.xml b/aggregator-microservices/pom.xml
deleted file mode 100644
index 413bf3cdd41e..000000000000
--- a/aggregator-microservices/pom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- aggregator-microservices
- pom
-
- information-microservice
- aggregator-service
- inventory-microservice
-
-
diff --git a/ambassador/README.md b/ambassador/README.md
index 4d304b67f14f..fa08223feb75 100644
--- a/ambassador/README.md
+++ b/ambassador/README.md
@@ -1,44 +1,49 @@
---
-layout: pattern
-title: Ambassador
-folder: ambassador
-permalink: /patterns/ambassador/
-categories: Structural
+title: "Ambassador Pattern in Java: Simplifying Remote Resource Management"
+shortTitle: Ambassador
+description: "Explore the Ambassador Pattern in Java, its benefits, use cases, and practical examples. Learn how to decouple and offload common functionalities to improve system performance and maintainability."
+category: Integration
language: en
-tags:
+tag:
+ - API design
- Decoupling
- - Cloud distributed
+ - Fault tolerance
+ - Proxy
+ - Resilience
+ - Scalability
---
-## Intent
+## Intent of Ambassador Design Pattern
-Provide a helper service instance on a client and offload common functionality away from a shared resource.
+The Ambassador Pattern in Java helps offload common functionalities such as monitoring, logging, and routing from a shared resource to a helper service instance, enhancing performance and maintainability in distributed systems.
-## Explanation
+## Detailed Explanation of Ambassador Pattern with Real-World Examples
-Real world example
+Real-world example
-> A remote service has many clients accessing a function it provides. The service is a legacy application and is
-> impossible to update. Large numbers of requests from users are causing connectivity issues. New rules for request
-> frequency should be implemented along with latency checks and client-side logging.
+> Imagine a busy hotel where guests frequently request restaurant reservations, event tickets, or transportation arrangements. Instead of each guest individually contacting these services, the hotel provides a concierge. The concierge handles these tasks on behalf of the guests, ensuring that reservations are made smoothly, tickets are booked on time, and transportation is scheduled efficiently.
+>
+> In this analogy, the guests are the client services, the external providers (restaurants, ticket vendors, transportation) are the remote services, and the concierge represents the ambassador service. This setup allows the guests to focus on enjoying their stay while the concierge manages the complexities of external interactions, providing a seamless and enhanced experience.
In plain words
-> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and
-> logging.
+> With the Ambassador pattern, we can implement less-frequent polling from clients along with latency checks and logging.
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.
+> 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.
-**Programmatic Example**
+Sequence diagram
-With the above introduction in mind we will imitate the functionality in this example. We have an interface implemented
-by the remote service as well as the ambassador service:
+
+
+## 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.
+
+A remote service has many clients accessing a function it provides. The service is a legacy application and is impossible to update. Large numbers of requests from users are causing connectivity issues. New rules for request frequency should be implemented along with latency checks and client-side logging.
+
+With the above introduction in mind we will imitate the functionality in this example. We have an interface implemented by the remote service as well as the ambassador service.
```java
interface RemoteServiceInterface {
@@ -49,6 +54,7 @@ interface RemoteServiceInterface {
A remote services represented as a singleton.
```java
+
@Slf4j
public class RemoteService implements RemoteServiceInterface {
private static RemoteService service = null;
@@ -60,7 +66,8 @@ public class RemoteService implements RemoteServiceInterface {
return service;
}
- private RemoteService() {}
+ private RemoteService() {
+ }
@Override
public long doRemoteFunction(int value) {
@@ -77,69 +84,71 @@ public class RemoteService implements RemoteServiceInterface {
}
```
-A service ambassador adding additional features such as logging, latency checks
+A service ambassador adds additional features such as logging and latency checks.
```java
+
@Slf4j
public class ServiceAmbassador implements RemoteServiceInterface {
- private static final int RETRIES = 3;
- private static final int DELAY_MS = 3000;
-
- ServiceAmbassador() {
- }
-
- @Override
- public long doRemoteFunction(int value) {
- return safeCall(value);
- }
-
- private long checkLatency(int value) {
- var startTime = System.currentTimeMillis();
- var result = RemoteService.getRemoteService().doRemoteFunction(value);
- var timeTaken = System.currentTimeMillis() - startTime;
-
- LOGGER.info("Time taken (ms): " + timeTaken);
- return result;
- }
-
- private long safeCall(int value) {
- var retries = 0;
- var result = (long) FAILURE;
-
- for (int i = 0; i < RETRIES; i++) {
- if (retries >= RETRIES) {
- return FAILURE;
- }
-
- if ((result = checkLatency(value)) == FAILURE) {
- LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
- retries++;
- try {
- sleep(DELAY_MS);
- } catch (InterruptedException e) {
- LOGGER.error("Thread sleep state interrupted", e);
+ private static final int RETRIES = 3;
+ private static final int DELAY_MS = 3000;
+
+ ServiceAmbassador() {
+ }
+
+ @Override
+ public long doRemoteFunction(int value) {
+ return safeCall(value);
+ }
+
+ private long checkLatency(int value) {
+ var startTime = System.currentTimeMillis();
+ var result = RemoteService.getRemoteService().doRemoteFunction(value);
+ var timeTaken = System.currentTimeMillis() - startTime;
+
+ LOGGER.info("Time taken (ms): " + timeTaken);
+ return result;
+ }
+
+ private long safeCall(int value) {
+ var retries = 0;
+ var result = (long) FAILURE;
+
+ for (int i = 0; i < RETRIES; i++) {
+ if (retries >= RETRIES) {
+ return FAILURE;
+ }
+
+ if ((result = checkLatency(value)) == FAILURE) {
+ LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
+ retries++;
+ try {
+ sleep(DELAY_MS);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread sleep state interrupted", e);
+ }
+ } else {
+ break;
+ }
}
- } else {
- break;
- }
+ return result;
}
- return result;
- }
}
```
-A client has a local service ambassador used to interact with the remote service:
+A client has a local service ambassador used to interact with the remote service.
```java
+
@Slf4j
public class Client {
- private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
+ private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
- long useService(int value) {
- var result = serviceAmbassador.doRemoteFunction(value);
- LOGGER.info("Service result: " + result);
- return result;
- }
+ long useService(int value) {
+ var result = serviceAmbassador.doRemoteFunction(value);
+ LOGGER.info("Service result: " + result);
+ return result;
+ }
}
```
@@ -147,43 +156,36 @@ Here are two clients using the service.
```java
public class App {
- public static void main(String[] args) {
- var host1 = new Client();
- var host2 = new Client();
- host1.useService(12);
- host2.useService(73);
- }
+ public static void main(String[] args) {
+ var host1 = new Client();
+ var host2 = new Client();
+ host1.useService(12);
+ host2.useService(73);
+ }
}
```
Here's the output for running the example:
```java
-Time taken (ms): 111
-Service result: 120
-Time taken (ms): 931
-Failed to reach remote: (1)
-Time taken (ms): 665
-Failed to reach remote: (2)
-Time taken (ms): 538
-Failed to reach remote: (3)
-Service result: -1
+Time taken(ms):111
+Service result:120
+Time taken(ms):931
+Failed to reach remote:(1)
+Time taken(ms):665
+Failed to reach remote:(2)
+Time taken(ms):538
+Failed to reach remote:(3)
+Service result:-1
```
-## Class diagram
-
-
+## When to Use the Ambassador Pattern in Java
-## Applicability
+* The Ambassador Pattern is particularly beneficial for Cloud Native and Microservices Architectures in Java. It helps in monitoring, logging, and securing inter-service communication, making it ideal for distributed systems.
+* Legacy System Integration: Facilitates communication with newer services by handling necessary but non-core functionalities.
+* Performance Enhancement: Can be used to cache results or compress data to improve communication efficiency.
-Ambassador is applicable when working with a legacy remote service which cannot be modified or would be extremely
-difficult to modify. Connectivity features can be implemented on the client avoiding the need for changes on the remote
-service.
-
-* Ambassador provides a local interface for a remote service.
-* Ambassador provides logging, circuit breaking, retries and security on the client.
-
-## Typical Use Case
+Typical use cases include:
* Control access to another object
* Implement logging
@@ -191,15 +193,44 @@ service.
* Offload remote service tasks
* Facilitate network connection
-## Known uses
+## Benefits and Trade-offs of Ambassador Pattern
+
+Benefits:
+
+* Separation of Concerns: Offloads cross-cutting concerns from the service logic, leading to cleaner, more maintainable code.
+* Reusable Infrastructure Logic: The ambassador pattern allows the same logic (e.g., logging, monitoring) to be reused across multiple services.
+* Improved Security: Centralizes security features like SSL termination or authentication, reducing the risk of misconfiguration.
+* Flexibility: Makes it easier to update or replace infrastructure concerns without modifying the service code.
+
+Trade-offs:
+
+* Increased Complexity: Adds another layer to the architecture, which can complicate the system design and debugging.
+* Potential Performance Overhead: The additional network hop can introduce latency and overhead, particularly if not optimized.
+* Deployment Overhead: Requires additional resources and management for deploying and scaling ambassador services.
+
+## Real-World Applications of Ambassador Pattern in Java
+* Service Mesh Implementations: In a service mesh architecture, like Istio or Linkerd, the Ambassador pattern is often employed as a sidecar proxy that handles inter-service communications. This includes tasks such as service discovery, routing, load balancing, telemetry (metrics and tracing), and security (authentication and authorization).
+* API Gateways: API gateways can use the Ambassador pattern to encapsulate common functionalities like rate limiting, caching, request shaping, and authentication. This allows backend services to focus on their core business logic without being burdened by these cross-cutting concerns.
+* Logging and Monitoring: An Ambassador can aggregate logs and metrics from various services and forward them to centralized monitoring tools like Prometheus or ELK Stack (Elasticsearch, Logstash, Kibana). This simplifies the logging and monitoring setup for each service and provides a unified view of the system's health.
+* Security: Security-related functionalities such as SSL/TLS termination, identity verification, and encryption can be managed by an Ambassador. This ensures consistent security practices across services and reduces the likelihood of security breaches due to misconfigurations.
+* Resilience: The Ambassador can implement resilience patterns like circuit breakers, retries, and timeouts. For instance, Netflix's Hystrix library can be used within an Ambassador to prevent cascading failures in a microservices ecosystem.
+* Database Proxy: Ambassadors can act as proxies for database connections, providing functionalities like connection pooling, read/write splitting for replicas, and query caching. This offloads significant complexity from the application services.
+* Legacy System Integration: In scenarios where modern microservices need to communicate with legacy systems, an Ambassador can serve as an intermediary that translates protocols, handles necessary transformations, and implements modern security practices, easing the integration process.
+* Network Optimization: For services deployed across different geographical locations or cloud regions, Ambassadors can optimize communication by compressing data, batching requests, or even implementing smart routing to reduce latency and costs.
* [Kubernetes-native API gateway for microservices](https://github.com/datawire/ambassador)
-## Related patterns
+## Related Java Design Patterns
-* [Proxy](https://java-design-patterns.com/patterns/proxy/)
+* [Circuit Breaker](https://java-design-patterns.com/patterns/circuit-breaker/): Often used in conjunction to manage fault tolerance by stopping calls to an unresponsive service.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): The decorator pattern is used to add functionality to an object dynamically, while the ambassador pattern is used to offload functionality to a separate object.
+* [Proxy](https://java-design-patterns.com/patterns/proxy/): Shares similarities with the proxy pattern, but the ambassador pattern specifically focuses on offloading ancillary functionalities.
+* Sidecar: A similar pattern used in the context of containerized applications, where a sidecar container provides additional functionality to the main application container.
-## Credits
+## References and Credits
-* [Ambassador pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
-* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://books.google.co.uk/books?id=6BJNDwAAQBAJ&pg=PT35&lpg=PT35&dq=ambassador+pattern+in+real+world&source=bl&ots=d2e7GhYdHi&sig=Lfl_MDnCgn6lUcjzOg4GXrN13bQ&hl=en&sa=X&ved=0ahUKEwjk9L_18rrbAhVpKcAKHX_KA7EQ6AEIWTAI#v=onepage&q=ambassador%20pattern%20in%20real%20world&f=false)
+* [Building Microservices: Designing Fine-Grained Systems](https://amzn.to/43aGpSR)
+* [Cloud Native Patterns: Designing Change-tolerant Software](https://amzn.to/3wUAl4O)
+* [Designing Distributed Systems: Patterns and Paradigms for Scalable, Reliable Services](https://amzn.to/3T9g9Uj)
+* [Microservices Patterns: With examples in Java](https://amzn.to/3UyWD5O)
+* [Ambassador pattern (Microsoft)](https://docs.microsoft.com/en-us/azure/architecture/patterns/ambassador)
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 573ebe16f937..15e4a07f0dc3 100644
--- a/ambassador/pom.xml
+++ b/ambassador/pom.xml
@@ -1,8 +1,10 @@
"findOrderInModernSystem" AntiCorruptionLayer
+ModernShop ---> "findOrderInLegacySystem" AntiCorruptionLayer
+AntiCorruptionLayer ..|> ModernShop
+AntiCorruptionLayer ..|> LegacyShop
+}
+
+@enduml
\ No newline at end of file
diff --git a/anti-corruption-layer/pom.xml b/anti-corruption-layer/pom.xml
new file mode 100644
index 000000000000..2fddfd3ecc46
--- /dev/null
+++ b/anti-corruption-layer/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ anti-corruption-layer
+ jar
+
+
+ org.springframework.boot
+ spring-boot-starter
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.corruption.App
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000000..f7cf8f075f83
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/App.java
@@ -0,0 +1,40 @@
+/*
+ * 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.corruption;
+
+import org.springframework.boot.SpringApplication;
+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.
+ */
+@SpringBootApplication
+public class App {
+
+ public static void main(String[] args) {
+ SpringApplication.run(App.class, args);
+ }
+}
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
new file mode 100644
index 000000000000..880d98c7d5b0
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/package-info.java
@@ -0,0 +1,48 @@
+/*
+ * 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.
+ */
+/**
+ * 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.
+ *
+ *
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.
+ */
+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
new file mode 100644
index 000000000000..4e8a17fa5d2a
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/AntiCorruptionLayer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.corruption.system;
+
+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.Shipment;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+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
+ *
+ *
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;
+
+ /**
+ * 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()),
+ ""));
+ }
+}
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
new file mode 100644
index 000000000000..e84578528be7
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/DataStore.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.corruption.system;
+
+import java.util.HashMap;
+import java.util.Optional;
+
+/**
+ * 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 {
+ private final HashMap inner;
+
+ public DataStore() {
+ inner = new HashMap<>();
+ }
+
+ public Optional get(String key) {
+ return Optional.ofNullable(inner.get(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
new file mode 100644
index 000000000000..c0acd288ed0c
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/ShopException.java
@@ -0,0 +1,50 @@
+/*
+ * 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.corruption.system;
+
+/** The class represents a general exception for the shop. */
+public class ShopException extends Exception {
+ public ShopException(String message) {
+ super(message);
+ }
+
+ /**
+ * Throws an exception that the order is already placed but has an incorrect data.
+ *
+ * @param lhs the incoming order
+ * @param rhs the existing order
+ * @return the exception
+ * @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);
+ }
+}
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
new file mode 100644
index 000000000000..45faa06cb26c
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyOrder.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.corruption.system.legacy;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The class represents an order in the legacy system. The class is used by the legacy system to
+ * store the data.
+ */
+@Data
+@AllArgsConstructor
+public class LegacyOrder {
+ private String id;
+ private String customer;
+
+ private String item;
+ private int qty;
+ private int price;
+}
diff --git a/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java
new file mode 100644
index 000000000000..b74eb1c29718
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyShop.java
@@ -0,0 +1,52 @@
+/*
+ * 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.corruption.system.legacy;
+
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+/**
+ * The class represents a legacy shop system. The main purpose is to place the order from the
+ * customers.
+ */
+@Service
+public class LegacyShop {
+ @Autowired private LegacyStore store;
+
+ /**
+ * Places the order in the legacy system. If the order is already present in the modern system,
+ * then the order is placed only if the data is the same. If the order is not present in the
+ * modern system, then the order is placed in the legacy system.
+ */
+ public void placeOrder(LegacyOrder legacyOrder) {
+ store.put(legacyOrder.getId(), legacyOrder);
+ }
+
+ /** Finds the order in the legacy system. */
+ public Optional findOrder(String orderId) {
+ return store.get(orderId);
+ }
+}
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
new file mode 100644
index 000000000000..ec1d613a7235
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/legacy/LegacyStore.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.corruption.system.legacy;
+
+import com.iluwatar.corruption.system.DataStore;
+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.
+ */
+@Service
+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
new file mode 100644
index 000000000000..130f36d39674
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Customer.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.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The class represents a customer in the modern system. */
+@Data
+@AllArgsConstructor
+public class Customer {
+ private String address;
+}
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
new file mode 100644
index 000000000000..7b62985015d6
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernOrder.java
@@ -0,0 +1,40 @@
+/*
+ * 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.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The class represents an order in the modern system. */
+@Data
+@AllArgsConstructor
+public class ModernOrder {
+ private String id;
+ private Customer customer;
+
+ 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
new file mode 100644
index 000000000000..24080abe1533
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernShop.java
@@ -0,0 +1,67 @@
+/*
+ * 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.corruption.system.modern;
+
+import com.iluwatar.corruption.system.AntiCorruptionLayer;
+import com.iluwatar.corruption.system.ShopException;
+import java.util.Optional;
+import org.springframework.beans.factory.annotation.Autowired;
+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.
+ */
+@Service
+public class ModernShop {
+ @Autowired private ModernStore store;
+
+ @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.
+ */
+ public void placeOrder(ModernOrder order) throws ShopException {
+
+ String id = order.getId();
+ // check if the order is already present in the legacy system
+ Optional orderInObsoleteSystem = acl.findOrderInLegacySystem(id);
+
+ if (orderInObsoleteSystem.isPresent()) {
+ var legacyOrder = orderInObsoleteSystem.get();
+ if (!order.equals(legacyOrder)) {
+ throw ShopException.throwIncorrectData(legacyOrder.toString(), order.toString());
+ }
+ } else {
+ store.put(id, order);
+ }
+ }
+
+ /** 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
new file mode 100644
index 000000000000..4fb3952fae5e
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/ModernStore.java
@@ -0,0 +1,32 @@
+/*
+ * 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.corruption.system.modern;
+
+import com.iluwatar.corruption.system.DataStore;
+import org.springframework.stereotype.Service;
+
+/** The class represents a data store for the modern system. */
+@Service
+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
new file mode 100644
index 000000000000..085a3921ceeb
--- /dev/null
+++ b/anti-corruption-layer/src/main/java/com/iluwatar/corruption/system/modern/Shipment.java
@@ -0,0 +1,40 @@
+/*
+ * 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.corruption.system.modern;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The class represents a shipment in the modern system. The class is used by the modern system to
+ * store the data.
+ */
+@Data
+@AllArgsConstructor
+public class Shipment {
+ private String item;
+ private int qty;
+ private int price;
+}
diff --git a/anti-corruption-layer/src/main/resources/application.properties b/anti-corruption-layer/src/main/resources/application.properties
new file mode 100644
index 000000000000..e69de29bb2d1
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
new file mode 100644
index 000000000000..ee46d124eee6
--- /dev/null
+++ b/anti-corruption-layer/src/test/java/com/iluwatar/corruption/system/AntiCorruptionLayerTest.java
@@ -0,0 +1,96 @@
+/*
+ * 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.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 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.junit.jupiter.SpringExtension;
+
+@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.
+ */
+ @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/api-gateway/README.md b/api-gateway/README.md
deleted file mode 100644
index 4df96d5d5952..000000000000
--- a/api-gateway/README.md
+++ /dev/null
@@ -1,163 +0,0 @@
----
-layout: pattern
-title: API Gateway
-folder: api-gateway
-permalink: /patterns/api-gateway/
-categories: Architectural
-language: en
-tags:
- - Cloud distributed
- - Decoupling
- - Microservices
----
-
-## Intent
-
-Aggregate calls to microservices in a single location, the API Gateway. The user makes a single call
-to the API Gateway, and the API Gateway then calls each relevant microservice.
-
-## Explanation
-
-With the Microservices pattern, a client may need data from multiple different microservices. If the
-client called each microservice directly, that could contribute to longer load times, since the
-client would have to make a network request for each microservice called. Moreover, having the
-client call each microservice directly ties the client to that microservice - if the internal
-implementations of the microservices change (for example, if two microservices are combined sometime
-in the future) or if the location (host and port) of a microservice changes, then every client that
-makes use of those microservices must be updated.
-
-The intent of the API Gateway pattern is to alleviate some of these issues. In the API Gateway
-pattern, an additional entity (the API Gateway) is placed between the client and the microservices.
-The job of the API Gateway is to aggregate the calls to the microservices. Rather than the client
-calling each microservice individually, the client calls the API Gateway a single time. The API
-Gateway then calls each of the microservices that the client needs.
-
-Real world example
-
-> We are implementing microservices and API Gateway pattern for an e-commerce site. In this system
-> the API Gateway makes calls to the Image and Price microservices.
-
-In plain words
-
-> For a system implemented using microservices architecture, API Gateway is the single entry point
-> that aggregates the calls to the individual microservices.
-
-Wikipedia says
-
-> API Gateway is a server that acts as an API front-end, receives API requests, enforces throttling
-> and security policies, passes requests to the back-end service and then passes the response back
-> to the requester. A gateway often includes a transformation engine to orchestrate and modify the
-> requests and responses on the fly. A gateway can also provide functionality such as collecting
-> analytics data and providing caching. The gateway can provide functionality to support
-> authentication, authorization, security, audit and regulatory compliance.
-
-**Programmatic Example**
-
-This implementation shows what the API Gateway pattern could look like for an e-commerce site. The
-`ApiGateway` makes calls to the Image and Price microservices using the `ImageClientImpl` and
-`PriceClientImpl` respectively. Customers viewing the site on a desktop device can see both price
-information and an image of a product, so the `ApiGateway` calls both of the microservices and
-aggregates the data in the `DesktopProduct` model. However, mobile users only see price information;
-they do not see a product image. For mobile users, the `ApiGateway` only retrieves price
-information, which it uses to populate the `MobileProduct`.
-
-Here's the Image microservice implementation.
-
-```java
-public interface ImageClient {
- String getImagePath();
-}
-
-public class ImageClientImpl implements ImageClient {
- @Override
- public String getImagePath() {
- var httpClient = HttpClient.newHttpClient();
- var httpGet = HttpRequest.newBuilder()
- .GET()
- .uri(URI.create("http://localhost:50005/image-path"))
- .build();
-
- try {
- var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
- return httpResponse.body();
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-}
-```
-
-Here's the Price microservice implementation.
-
-```java
-public interface PriceClient {
- String getPrice();
-}
-
-public class PriceClientImpl implements PriceClient {
-
- @Override
- public String getPrice() {
- var httpClient = HttpClient.newHttpClient();
- var httpGet = HttpRequest.newBuilder()
- .GET()
- .uri(URI.create("http://localhost:50006/price"))
- .build();
-
- try {
- var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
- return httpResponse.body();
- } catch (IOException | InterruptedException e) {
- e.printStackTrace();
- }
-
- return null;
- }
-}
-```
-
-Here we can see how API Gateway maps the requests to the microservices.
-
-```java
-public class ApiGateway {
-
- @Resource
- private ImageClient imageClient;
-
- @Resource
- private PriceClient priceClient;
-
- @RequestMapping(path = "/desktop", method = RequestMethod.GET)
- public DesktopProduct getProductDesktop() {
- var desktopProduct = new DesktopProduct();
- desktopProduct.setImagePath(imageClient.getImagePath());
- desktopProduct.setPrice(priceClient.getPrice());
- return desktopProduct;
- }
-
- @RequestMapping(path = "/mobile", method = RequestMethod.GET)
- public MobileProduct getProductMobile() {
- var mobileProduct = new MobileProduct();
- mobileProduct.setPrice(priceClient.getPrice());
- return mobileProduct;
- }
-}
-```
-
-## Class diagram
-
-
-## Applicability
-
-Use the API Gateway pattern when
-
-* You're using microservices architecture and need a single point of aggregation for your microservice calls.
-
-## Credits
-
-* [microservices.io - API Gateway](http://microservices.io/patterns/apigateway.html)
-* [NGINX - Building Microservices: Using an API Gateway](https://www.nginx.com/blog/building-microservices-using-an-api-gateway/)
-* [Microservices Patterns: With examples in Java](https://www.amazon.com/gp/product/1617294543/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617294543&linkId=ac7b6a57f866ac006a309d9086e8cfbd)
-* [Building Microservices: Designing Fine-Grained Systems](https://www.amazon.com/gp/product/1491950358/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1491950358&linkId=4c95ca9831e05e3f0dadb08841d77bf1)
diff --git a/api-gateway/api-gateway-service/pom.xml b/api-gateway/api-gateway-service/pom.xml
deleted file mode 100644
index a741b70dd588..000000000000
--- a/api-gateway/api-gateway-service/pom.xml
+++ /dev/null
@@ -1,85 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- api-gateway-service
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.mockito
- mockito-core
- test
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.api.gateway.App
-
-
-
-
-
-
-
-
-
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
deleted file mode 100644
index df060b5627c2..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/ApiGateway.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.api.gateway;
-
-import javax.annotation.Resource;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-/**
- * The ApiGateway aggregates calls to microservices based on the needs of the individual clients.
- */
-@RestController
-public class ApiGateway {
-
- @Resource
- private ImageClient imageClient;
-
- @Resource
- private PriceClient priceClient;
-
- /**
- * Retrieves product information that desktop clients need.
- *
- * @return Product information for clients on a desktop
- */
- @GetMapping("/desktop")
- public DesktopProduct getProductDesktop() {
- var desktopProduct = new DesktopProduct();
- desktopProduct.setImagePath(imageClient.getImagePath());
- desktopProduct.setPrice(priceClient.getPrice());
- return desktopProduct;
- }
-
- /**
- * Retrieves product information that mobile clients need.
- *
- * @return Product information for clients on a mobile device
- */
- @GetMapping("/mobile")
- public MobileProduct getProductMobile() {
- var mobileProduct = new MobileProduct();
- mobileProduct.setPrice(priceClient.getPrice());
- return mobileProduct;
- }
-}
diff --git a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java b/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java
deleted file mode 100644
index fbffbf9f6e32..000000000000
--- a/api-gateway/api-gateway-service/src/main/java/com/iluwatar/api/gateway/App.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.api.gateway;
-
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * With the Microservices pattern, a client may need data from multiple different microservices. If
- * the client called each microservice directly, that could contribute to longer load times, since
- * the client would have to make a network request for each microservice called. Moreover, having
- * the client call each microservice directly ties the client to that microservice - if the internal
- * implementations of the microservices change (for example, if two microservices are combined
- * sometime in the future) or if the location (host and port) of a microservice changes, then every
- * client that makes use of those microservices must be updated.
- *
- * The intent of the API Gateway pattern is to alleviate some of these issues. In the API
- * Gateway pattern, an additional entity (the API Gateway) is placed between the client and the
- * microservices. The job of the API Gateway is to aggregate the calls to the microservices. Rather
- * than the client calling each microservice individually, the client calls the API Gateway a single
- * time. The API Gateway then calls each of the microservices that the client needs.
- *
- *
This implementation shows what the API Gateway pattern could look like for an e-commerce
- * site. The {@link ApiGateway} makes calls to the Image and Price microservices using the {@link
- * ImageClientImpl} and {@link PriceClientImpl} respectively. Customers viewing the site on a
- * desktop device can see both price information and an image of a product, so the {@link
- * ApiGateway} calls both of the microservices and aggregates the data in the {@link DesktopProduct}
- * model. However, mobile users only see price information; they do not see a product image. For
- * mobile users, the {@link ApiGateway} only retrieves price information, which it uses to populate
- * the {@link MobileProduct}.
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- SpringApplication.run(App.class, args);
- }
-}
diff --git a/api-gateway/image-microservice/pom.xml b/api-gateway/image-microservice/pom.xml
deleted file mode 100644
index 97b63b37fb55..000000000000
--- a/api-gateway/image-microservice/pom.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- image-microservice
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.image.microservice.ImageApplication
-
-
-
-
-
-
-
-
-
diff --git a/api-gateway/pom.xml b/api-gateway/pom.xml
deleted file mode 100644
index 5033e17cd99f..000000000000
--- a/api-gateway/pom.xml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
-
- java-design-patterns
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- api-gateway
- pom
-
- image-microservice
- price-microservice
- api-gateway-service
-
-
diff --git a/api-gateway/price-microservice/pom.xml b/api-gateway/price-microservice/pom.xml
deleted file mode 100644
index 59c0706688cd..000000000000
--- a/api-gateway/price-microservice/pom.xml
+++ /dev/null
@@ -1,80 +0,0 @@
-
-
-
-
- api-gateway
- com.iluwatar
- 1.26.0-SNAPSHOT
-
- 4.0.0
- price-microservice
- jar
-
-
- org.springframework
- spring-webmvc
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.springframework.boot
- spring-boot-maven-plugin
-
-
-
- repackage
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.price.microservices.PriceApplication
-
-
-
-
-
-
-
-
-
diff --git a/arrange-act-assert/README.md b/arrange-act-assert/README.md
index 6afbe3df1241..584d8601a23e 100644
--- a/arrange-act-assert/README.md
+++ b/arrange-act-assert/README.md
@@ -1,146 +1,167 @@
---
-layout: pattern
-title: Arrange/Act/Assert
-folder: arrange-act-assert
-permalink: /patterns/arrange-act-assert/
-categories: Idiom
+title: "Arrange/Act/Assert Pattern in Java: Enhance Testing Clarity and Simplicity"
+shortTitle: Arrange/Act/Assert
+description: "Learn how to use the Arrange/Act/Assert pattern to structure your unit tests in Java. Improve readability and maintainability of your code with clear testing phases."
+category: Testing
language: en
-tags:
- - Testing
+tag:
+ - Code simplification
+ - Isolation
+ - Testing
---
## Also known as
-Given/When/Then
+* Given/When/Then
-## Intent
+## Intent of Arrange/Act/Assert Design Pattern
-Arrange/Act/Assert (AAA) is a pattern for organizing unit tests.
-It breaks tests down into three clear and distinct steps:
+The Arrange/Act/Assert pattern is essential in unit testing in Java. This testing method structures unit tests clearly by dividing them into three distinct sections: setup (Arrange), execution (Act), and verification (Assert).
-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.
+## Detailed Explanation of Arrange/Act/Assert Pattern with Real-World Examples
-## Explanation
+Real-world example
-This pattern has several significant benefits. It creates a clear separation between a test's
-setup, operations, and results. This structure makes the code easier to read and understand. If
-you place the steps in order and format your code to separate them, you can scan a test and
-quickly comprehend what it does.
+> Imagine you are organizing a small event. To ensure everything runs smoothly, you follow a pattern similar to Arrange/Act/Assert:
+>
+> 1. **Arrange**: You set up the venue, prepare the guest list, arrange seating, and organize the catering.
+> 2. **Act**: You conduct the event according to the plan, welcoming guests, serving food, and following the schedule.
+> 3. **Assert**: After the event, you evaluate its success by checking guest feedback, ensuring all tasks were completed, and reviewing if everything went as planned.
+>
+> This clear separation of preparation, execution, and evaluation helps ensure the event is well-organized and successful, mirroring the structured approach of the Arrange/Act/Assert pattern in software testing.
-It also enforces a certain degree of discipline when you write your tests. You have to think
-clearly about the three steps your test will perform. It makes tests more natural to write at
-the same time since you already have an outline.
+In plain words
-Real world example
+> Arrange/Act/Assert is a testing pattern that organizes tests into three clear steps for easy maintenance.
-> We need to write comprehensive and clear unit test suite for a class.
+WikiWikiWeb says
-In plain words
+> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
-> Arrange/Act/Assert is a testing pattern that organizes tests into three clear steps for easy
-> maintenance.
+Flowchart
-WikiWikiWeb says
+
-> Arrange/Act/Assert is a pattern for arranging and formatting code in UnitTest methods.
+## Programmatic Example of Arrange/Act/Assert Pattern in Java
-**Programmatic Example**
+We need to write comprehensive and clear unit test suite for a class. Using the Arrange/Act/Assert pattern in Java testing ensures clarity.
Let's first introduce our `Cash` class to be unit tested.
```java
public class Cash {
- private int amount;
+ private int amount;
- Cash(int amount) {
- this.amount = amount;
- }
+ Cash(int amount) {
+ this.amount = amount;
+ }
- void plus(int addend) {
- amount += addend;
- }
+ void plus(int addend) {
+ amount += addend;
+ }
- boolean minus(int subtrahend) {
- if (amount >= subtrahend) {
- amount -= subtrahend;
- return true;
- } else {
- return false;
+ boolean minus(int subtrahend) {
+ if (amount >= subtrahend) {
+ amount -= subtrahend;
+ return true;
+ } else {
+ return false;
+ }
}
- }
- int count() {
- return amount;
- }
+ int count() {
+ return amount;
+ }
}
```
-Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the clearly
-separated steps for each unit test.
+Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the clearly separated steps for each unit test.
```java
class CashAAATest {
- @Test
- void testPlus() {
- //Arrange
- var cash = new Cash(3);
- //Act
- cash.plus(4);
- //Assert
- assertEquals(7, cash.count());
- }
-
- @Test
- void testMinus() {
- //Arrange
- var cash = new Cash(8);
- //Act
- var result = cash.minus(5);
- //Assert
- assertTrue(result);
- assertEquals(3, cash.count());
- }
-
- @Test
- void testInsufficientMinus() {
- //Arrange
- var cash = new Cash(1);
- //Act
- var result = cash.minus(6);
- //Assert
- assertFalse(result);
- assertEquals(1, cash.count());
- }
-
- @Test
- void testUpdate() {
- //Arrange
- var cash = new Cash(5);
- //Act
- cash.plus(6);
- var result = cash.minus(3);
- //Assert
- assertTrue(result);
- assertEquals(8, cash.count());
- }
+ @Test
+ void testPlus() {
+ //Arrange
+ var cash = new Cash(3);
+ //Act
+ cash.plus(4);
+ //Assert
+ assertEquals(7, cash.count());
+ }
+
+ @Test
+ void testMinus() {
+ //Arrange
+ var cash = new Cash(8);
+ //Act
+ var result = cash.minus(5);
+ //Assert
+ assertTrue(result);
+ assertEquals(3, cash.count());
+ }
+
+ @Test
+ void testInsufficientMinus() {
+ //Arrange
+ var cash = new Cash(1);
+ //Act
+ var result = cash.minus(6);
+ //Assert
+ assertFalse(result);
+ assertEquals(1, cash.count());
+ }
+
+ @Test
+ void testUpdate() {
+ //Arrange
+ var cash = new Cash(5);
+ //Act
+ cash.plus(6);
+ var result = cash.minus(3);
+ //Assert
+ assertTrue(result);
+ assertEquals(8, cash.count());
+ }
}
```
-## Applicability
+## When to Use the Arrange/Act/Assert Pattern in Java
Use Arrange/Act/Assert pattern when
-* You need to structure your unit tests so that they're easier to read, maintain, and enhance.
+* Unit testing, especially within the context of TDD and BDD
+* Anywhere clarity and structure are needed in test cases
+
+## Real-World Applications of Arrange/Act/Assert Pattern in Java
+
+* This pattern is particularly useful when practicing TDD and/or BDD methodologies in Java.
+* Utilized in various programming languages and testing frameworks, such as JUnit (Java), NUnit (.NET), and xUnit frameworks.
+
+## Benefits and Trade-offs of Arrange/Act/Assert Pattern
+
+Benefits:
+
+* Improved readability of tests by clearly separating the setup, action, and verification steps.
+* Easier maintenance and understanding of tests, as each test is structured in a predictable way.
+* Facilitates debugging by isolating test failures to specific phases within the test.
+
+Trade-offs:
+
+* May introduce redundancy in tests, as similar arrangements may be repeated across tests.
+* Some complex tests might not fit neatly into this structure, requiring additional context or setup outside these three phases.
+
+## Related Java Design Patterns
+
+* [Page Object](https://java-design-patterns.com/patterns/page-object/): A pattern for organizing UI tests that can be used in conjunction with Arrange/Act/Assert.
-## Credits
+## References and Credits
+* [The Art of Unit Testing: with examples in C#](https://amzn.to/49IbdwO)
+* [Test Driven Development: By Example](https://amzn.to/3wEwKbF)
+* [Unit Testing Principles, Practices, and Patterns: Effective testing styles, patterns, and reliable automation for unit testing, mocking, and integration testing with examples in C#](https://amzn.to/4ayjpiM)
+* [xUnit Test Patterns: Refactoring Test Code](https://amzn.to/4dHGDpm)
* [Arrange, Act, Assert: What is AAA Testing?](https://blog.ncrunch.net/post/arrange-act-assert-aaa-testing.aspx)
-* [Bill Wake: 3A – Arrange, Act, Assert](https://xp123.com/articles/3a-arrange-act-assert/)
-* [Martin Fowler: GivenWhenThen](https://martinfowler.com/bliki/GivenWhenThen.html)
-* [xUnit Test Patterns: Refactoring Test Code](https://www.amazon.com/gp/product/0131495054/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0131495054&linkId=99701e8f4af2f7e8dd50d720c9b63dbf)
-* [Unit Testing Principles, Practices, and Patterns](https://www.amazon.com/gp/product/1617296279/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1617296279&linkId=74c75cf22a63c3e4758ae08aa0a0cc35)
-* [Test Driven Development: By Example](https://www.amazon.com/gp/product/0321146530/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0321146530&linkId=5c63a93d8c1175b84ca5087472ef0e05)
+* [Bill Wake: 3A – Arrange, Act, Assert (NCrunch)](https://xp123.com/articles/3a-arrange-act-assert/)
+* [GivenWhenThen (Martin Fowler)](https://martinfowler.com/bliki/GivenWhenThen.html)
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/pom.xml b/arrange-act-assert/pom.xml
index 897895096c4f..6b8ba1b2d490 100644
--- a/arrange-act-assert/pom.xml
+++ b/arrange-act-assert/pom.xml
@@ -1,8 +1,10 @@
+
+ 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 508d9724d619..cdd09cf5b7d1 100644
--- a/balking/README.md
+++ b/balking/README.md
@@ -1,26 +1,25 @@
---
-layout: pattern
-title: Balking
-folder: balking
-permalink: /patterns/balking/
-categories: Concurrency
+title: "Balking Pattern in Java: Smart Control Over Java Execution"
+shortTitle: Balking
+description: "Learn the Balking design pattern in Java, a concurrency pattern that prevents code execution in inappropriate states. Discover examples, use cases, and benefits."
+category: Concurrency
language: en
-tags:
- - Decoupling
+tag:
+ - Concurrency
+ - Decoupling
+ - Fault tolerance
+ - Synchronization
---
-## Intent
+## Intent of Balking Design Pattern
-Balking Pattern is used to prevent an object from executing a certain code if it is in an incomplete
-or inappropriate state.
+The Balking Pattern in Java is a concurrency design pattern that prevents an object from executing certain code if it is in an incomplete or inappropriate state. This pattern is crucial for managing state and concurrency in multithreaded Java applications.
-## Explanation
+## Detailed Explanation of Balking Pattern with Real-World Examples
-Real world example
+Real-world example
-> There's a start-button in a washing machine to initiate the laundry washing. When the washing
-> machine is inactive the button works as expected, but if it's already washing the button does
-> nothing.
+> A real-world analogy of the Balking design pattern can be seen in a laundry service. Imagine a washing machine at a laundromat that only starts washing clothes if the door is properly closed and locked. If a user tries to start the machine while the door is open, the machine balks and does nothing. This ensures that the washing process only begins when it is safe to do so, preventing water spillage and potential damage to the machine. Similarly, the Balking pattern in software design ensures that operations are only executed when the object is in an appropriate state, preventing erroneous actions and maintaining system stability.
In plain words
@@ -28,54 +27,57 @@ In plain words
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.
+> 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.
-**Programmatic Example**
+Flowchart
-In this example implementation, `WashingMachine` is an object that has two states in which it can
-be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe
-method. On the other hand, if it already has been washing and any other thread executes `wash()`
-it won't do that and returns without doing anything.
+
+
+## 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.
+
+There's a start-button in a washing machine to initiate the laundry washing. When the washing machine is inactive the button works as expected, but if it's already washing the button does nothing.
+
+In this example implementation, `WashingMachine` is an object that has two states in which it can be: ENABLED and WASHING. If the machine is ENABLED, the state changes to WASHING using a thread-safe method. On the other hand, if it already has been washing and any other thread executes `wash`it won't do that and returns without doing anything.
Here are the relevant parts of the `WashingMachine` class.
```java
+
@Slf4j
public class WashingMachine {
- private final DelayProvider delayProvider;
- private WashingMachineState washingMachineState;
-
- public WashingMachine(DelayProvider delayProvider) {
- this.delayProvider = delayProvider;
- this.washingMachineState = WashingMachineState.ENABLED;
- }
-
- public WashingMachineState getWashingMachineState() {
- return washingMachineState;
- }
-
- public void wash() {
- synchronized (this) {
- var machineState = getWashingMachineState();
- LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
- if (this.washingMachineState == WashingMachineState.WASHING) {
- LOGGER.error("Cannot wash if the machine has been already washing!");
- return;
- }
- this.washingMachineState = WashingMachineState.WASHING;
+ private final DelayProvider delayProvider;
+ private WashingMachineState washingMachineState;
+
+ public WashingMachine(DelayProvider delayProvider) {
+ this.delayProvider = delayProvider;
+ this.washingMachineState = WashingMachineState.ENABLED;
+ }
+
+ public WashingMachineState getWashingMachineState() {
+ return washingMachineState;
+ }
+
+ public void wash() {
+ synchronized (this) {
+ var machineState = getWashingMachineState();
+ LOGGER.info("{}: Actual machine state: {}", Thread.currentThread().getName(), machineState);
+ if (this.washingMachineState == WashingMachineState.WASHING) {
+ LOGGER.error("Cannot wash if the machine has been already washing!");
+ return;
+ }
+ this.washingMachineState = WashingMachineState.WASHING;
+ }
+ LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
+ this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
+ }
+
+ public synchronized void endOfWashing() {
+ washingMachineState = WashingMachineState.ENABLED;
+ LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
}
- LOGGER.info("{}: Doing the washing", Thread.currentThread().getName());
- this.delayProvider.executeAfterDelay(50, TimeUnit.MILLISECONDS, this::endOfWashing);
- }
-
- public synchronized void endOfWashing() {
- washingMachineState = WashingMachineState.ENABLED;
- LOGGER.info("{}: Washing completed.", Thread.currentThread().getId());
- }
}
```
@@ -83,27 +85,29 @@ Here's the simple `DelayProvider` interface used by the `WashingMachine`.
```java
public interface DelayProvider {
- void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
+ void executeAfterDelay(long interval, TimeUnit timeUnit, Runnable task);
}
```
-Now we introduce the application using the `WashingMachine`.
+Now, we introduce the application using the `WashingMachine`.
```java
- public static void main(String... args) {
+public static void main(String... args) {
final var washingMachine = new WashingMachine();
var executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 3; i++) {
- executorService.execute(washingMachine::wash);
+ executorService.execute(washingMachine::wash);
}
executorService.shutdown();
try {
- executorService.awaitTermination(10, TimeUnit.SECONDS);
+ if (!executorService.awaitTermination(10, TimeUnit.SECONDS)) {
+ executorService.shutdownNow();
+ }
} catch (InterruptedException ie) {
- LOGGER.error("ERROR: Waiting on executor service shutdown!");
- Thread.currentThread().interrupt();
+ LOGGER.error("ERROR: Waiting on executor service shutdown!");
+ Thread.currentThread().interrupt();
}
- }
+}
```
Here is the console output of the program.
@@ -118,23 +122,40 @@ Here is the console output of the program.
14:02:52.324 [pool-1-thread-2] INFO com.iluwatar.balking.WashingMachine - 14: Washing completed.
```
-## Class diagram
-
-
-
-## Applicability
+## When to Use the Balking Pattern in Java
Use the Balking pattern when
* You want to invoke an action on an object only when it is in a particular state
-* Objects are generally only in a state that is prone to balking temporarily but for an unknown
- amount of time
+* Objects are generally only in a state that is prone to balking temporarily but for an unknown amount of time
+* In multithreaded applications where certain actions should only proceed when specific conditions are met, and those conditions are expected to change over time due to external factors or concurrent operations.
+
+## Real-World Applications of Balking Pattern in Java
+
+* Resource pooling, where resources are only allocated if they are in a valid state for allocation.
+* Thread management, where threads only proceed with tasks if certain conditions (like task availability or resource locks) are met.
+
+## Benefits and Trade-offs of Balking Pattern
+
+Benefits:
+
+* Reduces unnecessary lock acquisitions in situations where actions cannot proceed, enhancing performance in concurrent applications.
+* Encourages clear separation of state management and behavior, leading to cleaner code.
+* Simplifies the handling of operations that should only be performed under certain conditions without cluttering the caller code with state checks.
+
+Trade-offs:
+
+* Can introduce complexity by obscuring the conditions under which actions are taken or ignored, potentially making the system harder to debug and understand.
+* May lead to missed opportunities or actions if the state changes are not properly monitored or if the balking condition is too restrictive.
-## Related patterns
+## Related Java Design Patterns
-* [Guarded Suspension Pattern](https://java-design-patterns.com/patterns/guarded-suspension/)
-* [Double Checked Locking Pattern](https://java-design-patterns.com/patterns/double-checked-locking/)
+* [Double-Checked Locking](https://java-design-patterns.com/patterns/double-checked-locking/): Ensures that initialization occurs only when necessary and avoids unnecessary locking, which is related to Balking in terms of conditionally executing logic based on the object's state.
+* [Guarded Suspension](https://java-design-patterns.com/patterns/guarded-suspension/): Similar in ensuring actions are only performed when an object is in a certain state, but typically involves waiting until the state is valid.
+* [State](https://java-design-patterns.com/patterns/state/): The State pattern can be used in conjunction with Balking to manage the states and transitions of the object.
-## Credits
+## References and Credits
-* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML, 2nd Edition, Volume 1](https://www.amazon.com/gp/product/0471227293/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=0471227293&linkId=0e39a59ffaab93fb476036fecb637b99)
+* [Concurrent Programming in Java : Design Principles and Patterns](https://amzn.to/4dIBqxL)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
+* [Patterns in Java: A Catalog of Reusable Design Patterns Illustrated with UML](https://amzn.to/4bOtzwF)
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 70ec1ca57202..818f7549c330 100644
--- a/balking/pom.xml
+++ b/balking/pom.xml
@@ -1,8 +1,10 @@
+
+ 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 1c5ccb8ae064..26567b292c81 100644
--- a/bridge/README.md
+++ b/bridge/README.md
@@ -1,177 +1,191 @@
---
-layout: pattern
-title: Bridge
-folder: bridge
-permalink: /patterns/bridge/
-categories: Structural
+title: "Bridge Pattern in Java: Decouple Abstraction from Implementation"
+shortTitle: Bridge
+description: "Learn about the Bridge design pattern in Java. Decouple abstraction from implementation to enhance flexibility and extensibility. Explore real-world examples, class diagrams, and use cases."
+category: Structural
language: en
-tags:
- - Gang of Four
+tag:
+ - Abstraction
+ - Decoupling
+ - Extensibility
+ - Gang of Four
+ - Object composition
---
## Also known as
-Handle/Body
+* Handle/Body
-## Intent
+## Intent of Bridge Design Pattern
-Decouple an abstraction from its implementation so that the two can vary independently.
+The Bridge design pattern is a structural pattern in Java that decouples an abstraction from its implementation, allowing both to vary independently. This pattern is essential for developing flexible and extensible software systems.
-## Explanation
+## Detailed Explanation of Bridge Pattern with Real-World Examples
Real-world example
-> Consider you have a weapon with different enchantments, and you are supposed to allow mixing
-> different weapons with different enchantments. What would you do? Create multiple copies of each
-> of the weapons for each of the enchantments or would you just create separate enchantment and set
-> it for the weapon as needed? Bridge pattern allows you to do the second.
+> In Java, the Bridge pattern is commonly used in GUI frameworks, database drivers, and device drivers. For instance, a universal remote control (abstraction) can operate various TV brands (implementations) through a consistent interface.
+>
+> Imagine a universal remote control (abstraction) that can operate different brands and types of televisions (implementations). The remote control provides a consistent interface for operations like turning on/off, changing channels, and adjusting the volume. Each television brand or type has its own specific implementation of these operations. By using the Bridge pattern, the remote control interface is decoupled from the television implementations, allowing the remote control to work with any television regardless of its brand or internal workings. This separation allows new television models to be added without changing the remote control's code, and different remote controls can be developed to work with the same set of televisions.
In Plain Words
-> Bridge pattern is about preferring composition over inheritance. Implementation details are pushed
-> from a hierarchy to another object with a separate hierarchy.
+> Bridge pattern is about preferring composition to inheritance. Implementation details are pushed from a hierarchy to another object with a separate hierarchy.
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"
-**Programmatic Example**
+Sequence diagram
-Translating our weapon example from above. Here we have the `Weapon` hierarchy:
+
+
+## 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.
+
+Here we have the `Weapon` hierarchy:
```java
public interface Weapon {
- void wield();
- void swing();
- void unwield();
- Enchantment getEnchantment();
+ void wield();
+
+ void swing();
+
+ void unwield();
+
+ Enchantment getEnchantment();
}
public class Sword implements Weapon {
- private final Enchantment enchantment;
-
- public Sword(Enchantment enchantment) {
- this.enchantment = enchantment;
- }
-
- @Override
- public void wield() {
- LOGGER.info("The sword is wielded.");
- enchantment.onActivate();
- }
-
- @Override
- public void swing() {
- LOGGER.info("The sword is swinged.");
- enchantment.apply();
- }
-
- @Override
- public void unwield() {
- LOGGER.info("The sword is unwielded.");
- enchantment.onDeactivate();
- }
-
- @Override
- public Enchantment getEnchantment() {
- return enchantment;
- }
+ private final Enchantment enchantment;
+
+ public Sword(Enchantment enchantment) {
+ this.enchantment = enchantment;
+ }
+
+ @Override
+ public void wield() {
+ LOGGER.info("The sword is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The sword is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The sword is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
}
public class Hammer implements Weapon {
- private final Enchantment enchantment;
-
- public Hammer(Enchantment enchantment) {
- this.enchantment = enchantment;
- }
-
- @Override
- public void wield() {
- LOGGER.info("The hammer is wielded.");
- enchantment.onActivate();
- }
-
- @Override
- public void swing() {
- LOGGER.info("The hammer is swinged.");
- enchantment.apply();
- }
-
- @Override
- public void unwield() {
- LOGGER.info("The hammer is unwielded.");
- enchantment.onDeactivate();
- }
-
- @Override
- public Enchantment getEnchantment() {
- return enchantment;
- }
+ private final Enchantment enchantment;
+
+ public Hammer(Enchantment enchantment) {
+ this.enchantment = enchantment;
+ }
+
+ @Override
+ public void wield() {
+ LOGGER.info("The hammer is wielded.");
+ enchantment.onActivate();
+ }
+
+ @Override
+ public void swing() {
+ LOGGER.info("The hammer is swung.");
+ enchantment.apply();
+ }
+
+ @Override
+ public void unwield() {
+ LOGGER.info("The hammer is unwielded.");
+ enchantment.onDeactivate();
+ }
+
+ @Override
+ public Enchantment getEnchantment() {
+ return enchantment;
+ }
}
```
-Here's the separate enchantment hierarchy:
+Here's the separate `Enchantment` hierarchy:
```java
public interface Enchantment {
- void onActivate();
- void apply();
- void onDeactivate();
+ void onActivate();
+
+ void apply();
+
+ void onDeactivate();
}
public class FlyingEnchantment implements Enchantment {
- @Override
- public void onActivate() {
- LOGGER.info("The item begins to glow faintly.");
- }
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item begins to glow faintly.");
+ }
- @Override
- public void apply() {
- LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
- }
+ @Override
+ public void apply() {
+ LOGGER.info("The item flies and strikes the enemies finally returning to owner's hand.");
+ }
- @Override
- public void onDeactivate() {
- LOGGER.info("The item's glow fades.");
- }
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("The item's glow fades.");
+ }
}
public class SoulEatingEnchantment implements Enchantment {
- @Override
- public void onActivate() {
- LOGGER.info("The item spreads bloodlust.");
- }
+ @Override
+ public void onActivate() {
+ LOGGER.info("The item spreads bloodlust.");
+ }
- @Override
- public void apply() {
- LOGGER.info("The item eats the soul of enemies.");
- }
+ @Override
+ public void apply() {
+ LOGGER.info("The item eats the soul of enemies.");
+ }
- @Override
- public void onDeactivate() {
- LOGGER.info("Bloodlust slowly disappears.");
- }
+ @Override
+ public void onDeactivate() {
+ LOGGER.info("Bloodlust slowly disappears.");
+ }
}
```
Here are both hierarchies in action:
```java
-LOGGER.info("The knight receives an enchanted sword.");
-var enchantedSword = new Sword(new SoulEatingEnchantment());
-enchantedSword.wield();
-enchantedSword.swing();
-enchantedSword.unwield();
-
-LOGGER.info("The valkyrie receives an enchanted hammer.");
-var hammer = new Hammer(new FlyingEnchantment());
-hammer.wield();
-hammer.swing();
-hammer.unwield();
+public static void main(String[] args) {
+ LOGGER.info("The knight receives an enchanted sword.");
+ var enchantedSword = new Sword(new SoulEatingEnchantment());
+ enchantedSword.wield();
+ enchantedSword.swing();
+ enchantedSword.unwield();
+
+ LOGGER.info("The valkyrie receives an enchanted hammer.");
+ var hammer = new Hammer(new FlyingEnchantment());
+ hammer.wield();
+ hammer.swing();
+ hammer.unwield();
+}
```
Here's the console output.
@@ -193,25 +207,50 @@ The hammer is unwielded.
The item's glow fades.
```
-## Class diagram
+## When to Use the Bridge Pattern in Java
+
+Consider using the Bridge pattern when:
+
+* You need to avoid a permanent binding between an abstraction and its implementation, such as when the implementation must be chosen or switched at runtime.
+* Both the abstractions and their implementations should be extendable via subclassing, allowing independent extension of each component.
+* Changes to the implementation of an abstraction should not affect clients, meaning their code should not require recompilation.
+* You encounter a large number of classes in your hierarchy, indicating the need to split an object into two parts, a concept referred to as "nested generalizations" by Rumbaugh.
+* You want to share an implementation among multiple objects, potentially using reference counting, while keeping this detail hidden from the client, as exemplified by Coplien's String class, where multiple objects can share the same string representation.
+
+## Bridge Pattern Java Tutorials
+
+* [Bridge Pattern Tutorial (DigitalOcean)](https://www.digitalocean.com/community/tutorials/bridge-design-pattern-java)
+
+## Real-World Applications of Bridge Pattern in Java
+
+* GUI Frameworks where the abstraction is the window, and the implementation could be the underlying OS windowing system.
+* Database Drivers where the abstraction is a generic database interface, and the implementations are database-specific drivers.
+* Device Drivers where the abstraction is the device-independent code, and the implementation is the device-dependent code.
+
+## Benefits and Trade-offs of Bridge Pattern
-
+Benefits:
-## Applicability
+* Decoupling Interface and Implementation: The Bridge pattern enhances modularity by separating the interface (the high-level operations) from the implementation (the low-level operations).
+* Improved Extensibility: You can extend the abstraction and implementation hierarchies independently.
+* Hiding Implementation Details: Clients only see the abstraction's interface, not its implementation.
-Use the Bridge pattern when
+Trade-offs:
-* You want to avoid a permanent binding between an abstraction and its implementation. This might be the case, for example, when the implementation must be selected or switched at run-time.
-* Both the abstractions and their implementations should be extensible by subclassing. In this case, the Bridge pattern lets you combine the different abstractions and implementations and extend them independently.
-* Changes in the implementation of an abstraction should have no impact on clients; that is, their code should not have to be recompiled.
-* You have a proliferation of classes. Such a class hierarchy indicates the need for splitting an object into two parts. Rumbaugh uses the term "nested generalizations" to refer to such class hierarchies.
-* You want to share an implementation among multiple objects (perhaps using reference counting), and this fact should be hidden from the client. A simple example is Coplien's String class, in which multiple objects can share the same string representation.
+* Increased Complexity: The pattern can complicate the system architecture and code, especially for clients unfamiliar with the pattern.
+* Runtime Overhead: The extra layer of abstraction can introduce a performance penalty, although it is often negligible in practice.
-## Tutorial
+## Related Java Design Patterns
-* [Bridge Pattern Tutorial](https://www.journaldev.com/1491/bridge-design-pattern-java)
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): The Abstract Factory pattern can be used along with the Bridge pattern to create platforms that are independent of the concrete classes used to create their objects.
+* [Adapter](https://java-design-patterns.com/patterns/adapter/): The Adapter pattern is used to provide a different interface to an object, while the Bridge pattern is used to separate an object's interface from its implementation.
+* [Composite](https://java-design-patterns.com/patterns/composite/): The Bridge pattern is often used with the Composite pattern to model the implementation details of a component.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): The Strategy pattern is like the Bridge pattern, but with a different intent. Both patterns are based on composition: Strategy uses composition to change the behavior of a class, while Bridge uses composition to separate an abstraction from its implementation.
-## Credits
+## References and Credits
-* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3yhh525)
+* [Pattern-Oriented Software Architecture Volume 1: A System of Patterns](https://amzn.to/3TEnhtl)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR)
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 bd2d3ef9f8b9..3cfd33997c05 100644
--- a/bridge/pom.xml
+++ b/bridge/pom.xml
@@ -1,8 +1,10 @@
"-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 a912ac370dcc..3ffce74af493 100644
--- a/caching/pom.xml
+++ b/caching/pom.xml
@@ -1,8 +1,10 @@
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
@@ -65,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;
/**
@@ -109,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);
}
@@ -128,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);
@@ -143,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);
@@ -165,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);
@@ -188,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 53489c83bd68..c1d21fea33fe 100644
--- a/caching/src/main/java/com/iluwatar/caching/AppManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,36 +22,28 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager;
-
import java.util.Optional;
-
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;
/**
@@ -63,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();
@@ -92,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);
@@ -148,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 3d59d73ded5a..b26c52b22159 100644
--- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java
+++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,37 +22,29 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager;
import java.util.List;
import java.util.Optional;
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 d24ad01736da..0ec07ced7b8f 100644
--- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
+++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,37 +22,24 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
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 70450d9292fb..9c9107de6f88 100644
--- a/caching/src/main/java/com/iluwatar/caching/LruCache.java
+++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
import java.util.ArrayList;
@@ -29,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) {
@@ -73,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;
/**
@@ -154,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)) {
@@ -194,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() {
@@ -217,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 1ec3af228035..561c942ac781 100644
--- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java
+++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.caching;
import lombok.AllArgsConstructor;
@@ -28,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 69cd63fef1d5..5e8fc415df4c 100644
--- a/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
+++ b/caching/src/main/java/com/iluwatar/caching/constants/CachingConstants.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,33 +22,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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 8ae963658ca0..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
@@ -1,4 +1,26 @@
-/**
- * Constants.
+/*
+ * 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.
*/
+/** 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 14d7247f8bc6..14b98a66b52b 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java
@@ -1,21 +1,41 @@
+/*
+ * 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.caching.database;
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 90ef432cce26..92031b7c95b0 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java
@@ -1,14 +1,33 @@
+/*
+ * 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.caching.database;
-/**
- * Creates the database connection accroding 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 a9dd006f8ab7..e47eef55cd8c 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
@@ -1,3 +1,27 @@
+/*
+ * 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.caching.database;
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
@@ -13,14 +37,10 @@
import com.mongodb.ServerAddress;
import com.mongodb.client.MongoDatabase;
import com.mongodb.client.model.UpdateOptions;
-import java.util.List;
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";
@@ -29,16 +49,17 @@ public class MongoDb implements DbManager {
private MongoClient client;
private MongoDatabase db;
- /**
- * Connect to Db. Check th connection
- */
+ void setDb(MongoDatabase db) {
+ this.db = db;
+ }
+
+ /** 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(), List.of(mongoCredential), options);
+ client = new MongoClient(new ServerAddress(), mongoCredential, options);
db = client.getDatabase(DATABASE_NAME);
}
@@ -55,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;
}
@@ -79,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;
}
@@ -96,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;
}
@@ -114,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 0c9a14ec964a..6040ca174a21 100644
--- a/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
+++ b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java
@@ -1,23 +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.caching.database;
import com.iluwatar.caching.UserAccount;
-
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 56deee71d7bf..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
@@ -1,4 +1,26 @@
-/**
- * Database classes.
+/*
+ * 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.
*/
+/** Database classes. */
package com.iluwatar.caching.database;
diff --git a/caching/src/main/java/com/iluwatar/caching/package-info.java b/caching/src/main/java/com/iluwatar/caching/package-info.java
index 00687084ff9d..e7a60b3e95b5 100644
--- a/caching/src/main/java/com/iluwatar/caching/package-info.java
+++ b/caching/src/main/java/com/iluwatar/caching/package-info.java
@@ -1,14 +1,19 @@
-/**
+/*
+ * 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-2021 Ilkka Seppälä
+ * 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
diff --git a/caching/src/test/java/com/iluwatar/caching/AppTest.java b/caching/src/test/java/com/iluwatar/caching/AppTest.java
index a50b687c200c..35e01edbc37e 100644
--- a/caching/src/test/java/com/iluwatar/caching/AppTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,26 +22,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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 0c70e24de501..d17cff5bd2ef 100644
--- a/caching/src/test/java/com/iluwatar/caching/CachingTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,25 +22,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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
- public void setUp() {
+ void setUp() {
// VirtualDB (instead of MongoDB) was used in running the JUnit tests
// to avoid Maven compilation errors. Set flag to true to run the
// tests with MongoDB (provided that MongoDB is installed and socket
@@ -67,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 a82876469e8d..87cc1ed6f587 100644
--- a/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/database/MongoDbTest.java
@@ -1,5 +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.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;
@@ -9,22 +40,13 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
-import org.mockito.internal.util.reflection.Whitebox;
-
-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;
@@ -32,10 +54,8 @@ class MongoDbTest {
@BeforeEach
void init() {
db = mock(MongoDatabase.class);
- Whitebox.setInternalState(mongoDb, "db", db);
+ mongoDb.setDb(db);
userAccount = new UserAccount(ID, NAME, ADDITIONAL_INFO);
-
-
}
@Test
@@ -45,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);
@@ -56,28 +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);
- doNothing().when(mongoCollection).insertOne(any(Document.class));
- 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 37761a7b7df3..923132ac5b6a 100644
--- a/callback/README.md
+++ b/callback/README.md
@@ -1,87 +1,135 @@
---
-layout: pattern
-title: Callback
-folder: callback
-permalink: /patterns/callback/
-categories: Idiom
+title: "Callback Pattern in Java: Mastering Asynchronous Communication"
+shortTitle: Callback
+description: "Learn about the Java Callback Design Pattern, including its intent, usage scenarios, benefits, trade-offs, and real-world examples. Understand how to implement and effectively use callbacks in your Java applications."
+category: Functional
language: en
-tags:
- - Reactive
+tag:
+ - Asynchronous
+ - Decoupling
+ - Idiom
+ - Reactive
---
-## Intent
+## Also known as
-Callback is a piece of executable code that is passed as an argument to other code, which is
-expected to call back (execute) the argument at some convenient time.
+* Call-After
+* Event-Subscription
+* Listener
-## Explanation
+## Intent of Callback Design Pattern
-Real world example
+The Java Callback Design Pattern is a piece of executable code passed as an argument to other code, which is expected to call back (execute) the argument at a convenient time.
-> We need to be notified after executing task has finished. We pass a callback method for
-> the executor and wait for it to call back on us.
+## Detailed Explanation of Callback Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy for the Callback design pattern can be found in the restaurant industry. Imagine a situation where you place an order at a busy restaurant. Instead of waiting at the counter for your food to be ready, you provide the cashier with your phone number. Once your order is prepared, the kitchen staff calls or sends a text message to notify you that your meal is ready for pickup.
+>
+> In this analogy, placing your order is analogous to initiating an asynchronous task. Providing your phone number is akin to passing a callback function. The kitchen preparing your order represents the asynchronous processing, and the notification you receive is the callback being executed, allowing you to retrieve your meal without having to wait idly. This separation of task initiation and task completion is the essence of the Callback design pattern.
In plain words
-> Callback is a method passed to the executor which will be called at defined moment.
+> Callback is a method passed to an executor which will be called at a defined moment.
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.
+> 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
-**Programmatic Example**
+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.
-Callback is a simple interface with single method.
+`Callback` is a simple interface with single method.
```java
public interface Callback {
- void call();
+ void call();
}
```
-Next we define a task that will execute the callback after the task execution has finished.
+Next we define `Task` that will execute the callback after the task execution has finished.
```java
public abstract class Task {
- final void executeWith(Callback callback) {
- execute();
- Optional.ofNullable(callback).ifPresent(Callback::call);
- }
+ final void executeWith(Callback callback) {
+ execute();
+ Optional.ofNullable(callback).ifPresent(Callback::call);
+ }
- public abstract void execute();
+ public abstract void execute();
}
@Slf4j
public final class SimpleTask extends Task {
- @Override
- public void execute() {
- LOGGER.info("Perform some important activity and after call the callback method.");
- }
+ @Override
+ public void execute() {
+ LOGGER.info("Perform some important activity and after call the callback method.");
+ }
}
```
Finally, here's how we execute a task and receive a callback when it's finished.
```java
+public static void main(final String[] args) {
var task = new SimpleTask();
task.executeWith(() -> LOGGER.info("I'm done now."));
+}
```
-## Class diagram
+Program output:
-
+```
+17:12:11.680 [main] INFO com.iluwatar.callback.SimpleTask -- Perform some important activity and after call the callback method.
+17:12:11.682 [main] INFO com.iluwatar.callback.App -- I'm done now.
+```
-## Applicability
+## When to Use the Callback Pattern in Java
Use the Callback pattern when
-* when some arbitrary synchronous or asynchronous action must be performed after execution of some defined activity.
+* Asynchronous event handling in GUI applications or event-driven systems
+* Implementing notification mechanisms where certain events need to trigger actions in other components.
+* Decoupling modules or components that need to interact without having a direct dependency on each other
-## Real world examples
+## Real-World Applications of Callback Pattern in Java
+* GUI frameworks often use callbacks for event handling, such as user interactions (clicks, key presses)
+* Node.js heavily relies on callbacks for non-blocking I/O operations
+* Frameworks that deal with asynchronous operations, like Promises in JavaScript, use callbacks to handle the resolution or rejection of asynchronous tasks
* [CyclicBarrier](http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html#CyclicBarrier%28int,%20java.lang.Runnable%29) constructor can accept a callback that will be triggered every time a barrier is tripped.
+
+## Benefits and Trade-offs of Callback Pattern
+
+Benefits:
+
+* Decouples the execution logic of an operation from the signaling or notification logic, enhancing modularity and reusability
+* Facilitates asynchronous processing, improving the responsiveness and scalability of applications
+* Enables a reactive programming model where components can react to events as they occur
+
+Trade-offs:
+
+* Callback hell or pyramid of doom: Deeply nested callbacks can lead to code that is hard to read and maintain
+* Inversion of control can lead to harder-to-follow code flow, making debugging more challenging
+* Potential issues with error handling, especially in languages or environments where exceptions are used, as errors might need to be propagated through callbacks
+
+## Related Java Design Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Callbacks can be implemented as Command objects in scenarios where more flexibility or statefulness is required in the callback operation
+* [Observer](https://java-design-patterns.com/patterns/observer/): Callbacks can be seen as a more dynamic and lightweight form of the Observer pattern, with the ability to subscribe and unsubscribe callback functions dynamically
+* [Promise](https://java-design-patterns.com/patterns/promise/): In some languages or frameworks, Promises or Futures can be used to handle asynchronous operations more cleanly, often using callbacks for success or failure cases
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
+* [Java Concurrency in Practice](https://amzn.to/4aRMruW)
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 e0081992601d..772615f457f9 100644
--- a/callback/pom.xml
+++ b/callback/pom.xml
@@ -1,8 +1,10 @@
"-chain" RequestHandler
-RequestHandler --> "-next" RequestHandler
+OrcKing --> "-handlers" RequestHandler
Request --> "-requestType" RequestType
-OrcCommander --|> RequestHandler
-OrcOfficer --|> RequestHandler
-OrcSoldier --|> RequestHandler
+OrcCommander ..|> RequestHandler
+OrcOfficer ..|> RequestHandler
+OrcSoldier ..|> RequestHandler
@enduml
\ No newline at end of file
diff --git a/chain-of-responsibility/pom.xml b/chain-of-responsibility/pom.xml
index dd1d867127e2..e6a7fb974be7 100644
--- a/chain-of-responsibility/pom.xml
+++ b/chain-of-responsibility/pom.xml
@@ -1,8 +1,10 @@
+
+
+
diff --git a/circuit-breaker/README.md b/circuit-breaker/README.md
index 0f4ab47a5445..99c1b7b4d398 100644
--- a/circuit-breaker/README.md
+++ b/circuit-breaker/README.md
@@ -1,71 +1,112 @@
---
-layout: pattern
-title: Circuit Breaker
-folder: circuit-breaker
-permalink: /patterns/circuit-breaker/
-categories: Behavioral
+title: "Circuit Breaker Pattern in Java: Enhancing System Resilience"
+shortTitle: Circuit Breaker
+description: "Learn about the Circuit Breaker pattern in Java design, which ensures fault tolerance and prevents cascading failures in distributed systems and microservices architectures."
+category: Resilience
language: en
-tags:
- - Performance
- - Decoupling
+tag:
- Cloud distributed
+ - Fault tolerance
+ - Microservices
+ - Retry
---
-## Intent
+## Also known as
-Handle costly remote service calls in such a way that the failure of a single service/component
-cannot bring the whole application down, and we can reconnect to the service as soon as possible.
+* Fault Tolerance Switch
-## Explanation
+## Intent of Circuit Breaker Design Pattern
-Real world example
+The Circuit Breaker pattern is a critical Java design pattern that helps ensure fault tolerance and resilience in microservices and distributed systems. Using Circuit Breaker, it is possible to prevent a system from repeatedly trying to execute an operation likely to fail, allowing it to recover from faults and prevent cascading failures.
-> Imagine a web application that has both local files/images and remote services that are used for
-> fetching data. These remote services may be either healthy and responsive at times, or may become
-> slow and unresponsive at some point of time due to variety of reasons. So if one of the remote
-> services is slow or not responding successfully, our application will try to fetch response from
-> the remote service using multiple threads/processes, soon all of them will hang (also called
-> [thread starvation](https://en.wikipedia.org/wiki/Starvation_(computer_science))) causing our entire web application to crash. We should be able to detect
-> this situation and show the user an appropriate message so that he/she can explore other parts of
-> the app unaffected by the remote service failure. Meanwhile, the other services that are working
-> normally, should keep functioning unaffected by this failure.
+## Detailed Explanation of Circuit Breaker Pattern with Real-World Examples
+
+Real-world example
+
+> Consider a real-world example of an e-commerce website that depends on multiple external payment gateways to process transactions. If one of the payment gateways becomes unresponsive or slow, the Circuit Breaker pattern can be used to detect the failure and prevent the system from repeatedly attempting to use the problematic gateway. Instead, it can quickly switch to alternative payment gateways or display an error message to the user, ensuring that the rest of the website remains functional and responsive. This avoids resource exhaustion and provides a better user experience by allowing transactions to be processed through other available services. This way, the Circuit Breaker pattern handles external API failures, ensuring the system remains functional.
In plain words
-> Circuit Breaker allows graceful handling of failed remote services. It's especially useful when
-> all parts of our application are highly decoupled from each other, and failure of one component
-> doesn't mean the other parts will stop working.
+> Circuit Breaker allows graceful handling of failed remote services. It's especially useful when all parts of our application are highly decoupled from each other, and failure of one component doesn't mean the other parts will stop working.
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.
+> 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
+## Programmatic Example of Circuit Breaker Pattern in Java
-So, how does this all come together? With the above example in mind we will imitate the
-functionality in a simple example. A monitoring service mimics the web app and makes both local and
-remote calls.
+This Java example demonstrates how the Circuit Breaker pattern can manage remote service failures and maintain system stability.
-The service architecture is as follows:
+Imagine a web application that uses both local files/images and remote services to fetch data. Remote services can become slow or unresponsive, which may cause the application to hang due to thread starvation. The Circuit Breaker pattern can help detect such failures and allow the application to degrade gracefully.
-
+1. **Simulating a Delayed Remote Service**
+
+```java
+// The DelayedRemoteService simulates a remote service that responds after a certain delay.
+var delayedService = new DelayedRemoteService(serverStartTime, 5);
+```
-In terms of code, the end user application is:
+2. **Setting Up the Circuit Breaker**
```java
-@Slf4j
-public class App {
+// The DefaultCircuitBreaker wraps the remote service and monitors for failures.
+var delayedServiceCircuitBreaker = new DefaultCircuitBreaker(delayedService, 3000, 2, 2000 * 1000 * 1000);
+```
+
+3. **Monitoring Service to Handle Requests**
+
+```java
+// The MonitoringService is responsible for calling the remote services.
+var monitoringService = new MonitoringService(delayedServiceCircuitBreaker, quickServiceCircuitBreaker);
+
+// Fetch response from local resource
+LOGGER.info(monitoringService.localResourceResponse());
+
+// Fetch response from delayed service 2 times to meet the failure threshold
+LOGGER.info(monitoringService.delayedServiceResponse());
+LOGGER.info(monitoringService.delayedServiceResponse());
+```
+
+4. **Handling Circuit Breaker States**
+
+```java
+// Fetch current state of delayed service circuit breaker after crossing failure threshold limit
+LOGGER.info(delayedServiceCircuitBreaker.getState()); // Should be OPEN
+
+// Meanwhile, the delayed service is down, fetch response from the healthy quick service
+LOGGER.info(monitoringService.quickServiceResponse());
+LOGGER.info(quickServiceCircuitBreaker.getState());
+```
+
+5. **Recovering from Failure**
+
+```java
+// 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
+LOGGER.info(delayedServiceCircuitBreaker.getState());
+
+// Fetch response from delayed service, which should be healthy by now
+LOGGER.info(monitoringService.delayedServiceResponse());
- private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
+// As successful response is fetched, it should be CLOSED again.
+LOGGER.info(delayedServiceCircuitBreaker.getState());
+```
+
+6. **Full example**
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
+```java
+public static void main(String[] args) {
var serverStartTime = System.nanoTime();
@@ -101,7 +142,7 @@ public class App {
LOGGER.info("Waiting for delayed service to become responsive");
Thread.sleep(5000);
} catch (InterruptedException e) {
- e.printStackTrace();
+ LOGGER.error("An error occurred: ", e);
}
//Check the state of delayed circuit breaker, should be HALF_OPEN
LOGGER.info(delayedServiceCircuitBreaker.getState());
@@ -110,221 +151,76 @@ public class App {
LOGGER.info(monitoringService.delayedServiceResponse());
//As successful response is fetched, it should be CLOSED again.
LOGGER.info(delayedServiceCircuitBreaker.getState());
- }
}
```
-The monitoring service:
-
-```java
-public class MonitoringService {
-
- private final CircuitBreaker delayedService;
-
- private final CircuitBreaker quickService;
+Summary of the example
- public MonitoringService(CircuitBreaker delayedService, CircuitBreaker quickService) {
- this.delayedService = delayedService;
- this.quickService = quickService;
- }
+- Initialize the Circuit Breaker with parameters: `timeout`, `failureThreshold`, and `retryTimePeriod`.
+- Start in the `closed` state.
+- On successful calls, reset the state.
+- On failures exceeding the threshold, transition to the `open` state to prevent further calls.
+- After the retry timeout, transition to the `half-open` state to test the service.
+- On success in `half-open` state, transition back to `closed`. On failure, return to `open`.
- //Assumption: Local service won't fail, no need to wrap it in a circuit breaker logic
- public String localResourceResponse() {
- return "Local Service is working";
- }
+Program output:
- /**
- * Fetch response from the delayed service (with some simulated startup time).
- *
- * @return response string
- */
- public String delayedServiceResponse() {
- try {
- return this.delayedService.attemptRequest();
- } catch (RemoteServiceException e) {
- return e.getMessage();
- }
- }
-
- /**
- * Fetches response from a healthy service without any failure.
- *
- * @return response string
- */
- public String quickServiceResponse() {
- try {
- return this.quickService.attemptRequest();
- } catch (RemoteServiceException e) {
- return e.getMessage();
- }
- }
-}
```
-As it can be seen, it does the call to get local resources directly, but it wraps the call to
-remote (costly) service in a circuit breaker object, which prevents faults as follows:
-
-```java
-public class DefaultCircuitBreaker implements CircuitBreaker {
-
- private final long timeout;
- private final long retryTimePeriod;
- private final RemoteService service;
- long lastFailureTime;
- private String lastFailureResponse;
- int failureCount;
- private final int failureThreshold;
- private State state;
- private final long futureTime = 1000 * 1000 * 1000 * 1000;
-
- /**
- * Constructor to create an instance of Circuit Breaker.
- *
- * @param timeout Timeout for the API request. Not necessary for this simple example
- * @param failureThreshold Number of failures we receive from the depended service before changing
- * state to 'OPEN'
- * @param retryTimePeriod Time period after which a new request is made to remote service for
- * status check.
- */
- 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;
- this.failureThreshold = failureThreshold;
- // Timeout for the API request.
- // 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
- this.lastFailureTime = System.nanoTime() + futureTime;
- this.failureCount = 0;
- }
-
- // Reset everything to defaults
- @Override
- public void recordSuccess() {
- this.failureCount = 0;
- this.lastFailureTime = System.nanoTime() + futureTime;
- this.state = State.CLOSED;
- }
-
- @Override
- public void recordFailure(String response) {
- failureCount = failureCount + 1;
- this.lastFailureTime = System.nanoTime();
- // Cache the failure response for returning on open state
- this.lastFailureResponse = 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 ((System.nanoTime() - lastFailureTime) > retryTimePeriod) {
- //We have waited long enough and should try checking if service is up
- state = State.HALF_OPEN;
- } else {
- //Service would still probably be down
- state = State.OPEN;
- }
- } else {
- //Everything is working fine
- state = State.CLOSED;
- }
- }
-
- @Override
- public String getState() {
- evaluateState();
- return state.name();
- }
-
- /**
- * Break the circuit beforehand if it is known service is down Or connect the circuit manually if
- * service comes online before expected.
- *
- * @param state State at which circuit is in
- */
- @Override
- public void setState(State state) {
- this.state = state;
- switch (state) {
- case OPEN:
- this.failureCount = failureThreshold;
- this.lastFailureTime = System.nanoTime();
- break;
- case HALF_OPEN:
- this.failureCount = failureThreshold;
- this.lastFailureTime = System.nanoTime() - retryTimePeriod;
- break;
- default:
- this.failureCount = 0;
- }
- }
-
- /**
- * Executes service call.
- *
- * @return Value from the remote resource, stale response or a custom exception
- */
- @Override
- public String attemptRequest() throws RemoteServiceException {
- evaluateState();
- if (state == State.OPEN) {
- // return cached response if the circuit is in OPEN state
- return this.lastFailureResponse;
- } 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
- var response = service.call();
- // Yay!! the API responded fine. Let's reset everything.
- recordSuccess();
- return response;
- } catch (RemoteServiceException ex) {
- recordFailure(ex.getMessage());
- throw ex;
- }
- }
- }
-}
+16:59:19.767 [main] INFO com.iluwatar.circuitbreaker.App -- Local Service is working
+16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Delayed service is down
+16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Delayed service is down
+16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- OPEN
+16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Quick Service is working
+16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- CLOSED
+16:59:19.769 [main] INFO com.iluwatar.circuitbreaker.App -- Waiting for delayed service to become responsive
+16:59:24.779 [main] INFO com.iluwatar.circuitbreaker.App -- HALF_OPEN
+16:59:24.780 [main] INFO com.iluwatar.circuitbreaker.App -- Delayed service is working
+16:59:24.780 [main] INFO com.iluwatar.circuitbreaker.App -- CLOSED
```
-How does the above pattern prevent failures? Let's understand via this finite state machine
-implemented by it.
+This example demonstrates how the Circuit Breaker pattern can help maintain application stability and resilience by managing remote service failures.
+
+## When to Use the Circuit Breaker Pattern in Java
-
+The Circuit Breaker pattern is applicable:
-- We initialize the Circuit Breaker object with certain parameters: `timeout`, `failureThreshold` and `retryTimePeriod` which help determine how resilient the API is.
-- Initially, we are in the `closed` state and nos remote calls to the API have occurred.
-- Every time the call succeeds, we reset the state to as it was in the beginning.
-- If the number of failures cross a certain threshold, we move to the `open` state, which acts just like an open circuit and prevents remote service calls from being made, thus saving resources. (Here, we return the response called ```stale response from API```)
-- Once we exceed the retry timeout period, we move to the `half-open` state and make another call to the remote service again to check if the service is working so that we can serve fresh content. A failure sets it back to `open` state and another attempt is made after retry timeout period, while a success sets it to `closed` state so that everything starts working normally again.
+* In distributed systems where individual service failures can lead to cascading system-wide failures
+* For applications that interact with third-party services or databases that might become unresponsive or slow
+* In microservices architectures where the failure of one service can affect the availability of others
-## Class diagram
+## Real-World Applications of Circuit Breaker Pattern in Java
-
+* Cloud-based services to gracefully handle the failure of external services
+* E-commerce platforms to manage high volumes of transactions and dependency on external APIs
+* Microservices architectures for maintaining system stability and responsiveness
+* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker)
+* [Netflix Hystrix API](https://github.com/Netflix/Hystrix)
-## Applicability
+## Benefits and Trade-offs of Circuit Breaker Pattern
-Use the Circuit Breaker pattern when
+Benefits:
-- Building a fault-tolerant application where failure of some services shouldn't bring the entire application down.
-- Building a continuously running (always-on) application, so that its components can be upgraded without shutting it down entirely.
+* Prevents the system from performing futile operations that are likely to fail, thus saving resources
+* Helps in maintaining the system stability and performance of the application during partial system failures
+* Facilitates faster system recovery by avoiding the overwhelming of failing services with repeated requests
-## Related Patterns
+Trade-Offs:
-- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry)
+* The complexity of the system increases as the pattern requires additional logic to detect failures and manage the state of the circuit breaker
+* May lead to system degradation if not properly configured, as legitimate requests might be blocked if the circuit is open
+* Requires careful tuning of thresholds and timeout periods to balance between responsiveness and protection
-## Real world examples
+## Related Patterns
-* [Spring Circuit Breaker module](https://spring.io/guides/gs/circuit-breaker)
-* [Netflix Hystrix API](https://github.com/Netflix/Hystrix)
+- Bulkhead: Can be used to isolate different parts of the system to prevent failures from spreading across the system
+- [Retry Pattern](https://github.com/iluwatar/java-design-patterns/tree/master/retry): Can be used in conjunction with the Circuit Breaker pattern to retry failed operations before opening the circuit
-## Credits
+## References and Credits
-* [Understanding Circuit Breaker Pattern](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42)
-* [Martin Fowler on Circuit Breaker](https://martinfowler.com/bliki/CircuitBreaker.html)
-* [Fault tolerance in a high volume, distributed system](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a)
-* [Circuit Breaker pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
+* [Building Microservices: Designing Fine-Grained Systems](https://amzn.to/43Dx86g)
+* [Microservices Patterns: With examples in Java](https://amzn.to/3xaZwk0)
+* [Release It! Design and Deploy Production-Ready Software](https://amzn.to/4aqTNEP)
+* [Understand CircuitBreaker Design Pattern with Simple Practical Example (ITNEXT)](https://itnext.io/understand-circuitbreaker-design-pattern-with-simple-practical-example-92a752615b42)
+* [Circuit Breaker (Martin Fowler)](https://martinfowler.com/bliki/CircuitBreaker.html)
+* [Fault tolerance in a high volume, distributed system (Netflix)](https://medium.com/netflix-techblog/fault-tolerance-in-a-high-volume-distributed-system-91ab4faae74a)
+* [Circuit Breaker pattern (Microsoft)](https://docs.microsoft.com/en-us/azure/architecture/patterns/circuit-breaker)
diff --git a/circuit-breaker/etc/ServiceDiagram.png b/circuit-breaker/etc/ServiceDiagram.png
deleted file mode 100644
index 885320a4d901..000000000000
Binary files a/circuit-breaker/etc/ServiceDiagram.png and /dev/null differ
diff --git a/circuit-breaker/etc/StateDiagram.png b/circuit-breaker/etc/StateDiagram.png
deleted file mode 100644
index 38485526d342..000000000000
Binary files a/circuit-breaker/etc/StateDiagram.png and /dev/null differ
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 c8710f13e448..5b2be0c5605a 100644
--- a/circuit-breaker/pom.xml
+++ b/circuit-breaker/pom.xml
@@ -1,8 +1,10 @@
+
+ 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
new file mode 100644
index 000000000000..e693ef7db977
--- /dev/null
+++ b/client-session/README.md
@@ -0,0 +1,147 @@
+---
+title: "Client-Session Pattern in Java: Streamlining Client Data Across Sessions"
+shortTitle: Client Session
+description: "Explore the Client Session design pattern in Java. Learn how to manage user state and data across multiple requests for seamless, personalized web application experiences."
+category: Behavioral
+language: en
+tags:
+ - Client-server
+ - Session management
+ - State tracking
+ - Web development
+---
+
+## Also known as
+
+* User Session
+
+## Intent of Client Session Design Pattern
+
+The Client Session design pattern is essential for web development involving client-server interactions. It aims to maintain a user's state and data across multiple requests within a web application, ensuring a continuous and personalized user experience. This pattern helps in creating a seamless user experience by managing user state and data effectively across different sessions, crucial for modern web applications.
+
+## Detailed Explanation of Client Session Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world example of the Client Session pattern is a library membership system. When a member logs in, the system starts a session to track their borrowing activities. This session holds data such as the member's ID, current borrowed books, due dates, and any fines. As the member browses the catalog, borrows books, or returns them, the session maintains this stateful information, ensuring the member's interactions are consistent and personalized until they log out or the session expires. This approach helps the library system manage user-specific data efficiently across multiple interactions, providing a seamless and personalized experience for the members.
+
+In plain words
+
+> The Client Session pattern manages user-specific data across multiple requests within a web application to maintain continuity and personalization.
+
+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.
+
+In the given code, we have a `Server` class and a `Session` class. The `Server` class represents the server that processes incoming requests and assigns sessions to clients. The `Session` class represents a session that is assigned to a client.
+
+```java
+// The Server class represents the server that processes incoming requests and assigns sessions to clients.
+public class Server {
+ private String host;
+ private int port;
+
+ public Server(String host, int port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ // Other methods...
+
+ // This method returns a new session for a client.
+ public Session getSession(String name) {
+ return new Session(name, "ClientName");
+ }
+
+ // This method processes a request from a client.
+ public void process(Request request) {
+ // Process the request...
+ }
+}
+
+// The Session class represents a session that is assigned to a client.
+public class Session {
+ private String id;
+ private String clientName;
+
+ public Session(String id, String clientName) {
+ this.id = id;
+ this.clientName = clientName;
+ }
+
+ // Other methods...
+}
+```
+
+In the `main` method, we create an instance of `Server`, create two sessions for two different clients, and then pass these sessions 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.
+
+```java
+public class App {
+ public static void main(String[] args) {
+ var server = new Server("localhost", 8080);
+ var session1 = server.getSession("Session1");
+ var session2 = server.getSession("Session2");
+ var request1 = new Request("Data1", session1);
+ var request2 = new Request("Data2", session2);
+ server.process(request1);
+ server.process(request2);
+ }
+}
+```
+
+In this example, the `Server` class is responsible for creating and managing sessions for clients, and the `Session` class represents the client's session. The `Request` class represents a request from a client, which includes the client's session and data. The server processes the request based on the client's session.
+
+Running the program produces the following console output:
+
+```
+19:28:49.152 [main] INFO com.iluwatar.client.session.Server -- Processing Request with client: Session1 data: Data1
+19:28:49.154 [main] INFO com.iluwatar.client.session.Server -- Processing Request with client: Session2 data: Data2
+```
+
+## When to Use the Client Session Pattern in Java
+
+Use the client state pattern when:
+
+* Web applications requiring user authentication and authorization.
+* Applications needing to track user activities and preferences over multiple requests or visits.
+* Systems where server resources need to be optimized by offloading state management to the client side.
+
+## Real-World Applications of Client Session Pattern in Java
+
+* E-commerce websites to track shopping cart contents across sessions.
+* Online platforms that offer personalized content based on user preferences and history.
+* Web applications requiring user login to access personalized or secured content.
+
+## Benefits and Trade-offs of Client Session Pattern
+
+Benefits:
+
+* Improved server performance by reducing the need to store user state on the server.
+* Enhanced user experience through personalized content and seamless navigation across different parts of the application.
+* Flexibility in managing sessions through various client-side storage mechanisms (e.g., cookies, Web Storage API).
+
+Trade-offs:
+
+* Potential security risks if sensitive information is stored in client sessions without proper encryption and validation.
+* Dependence on client-side capabilities and settings, such as cookie policies, which can vary across browsers and user configurations.
+* Increased complexity in session management logic, especially in handling session expiration, renewal, and synchronization across multiple devices or tabs.
+
+## Related Patterns
+
+* Server Session: Often used in conjunction with the Client Session pattern to provide a balance between client-side efficiency and server-side control.
+* [Singleton](https://java-design-patterns.com/patterns/singleton/): Ensuring a single instance of a user's session throughout the application.
+* [State](https://java-design-patterns.com/patterns/state/): Managing state transitions in a session, such as authenticated, guest, or expired states.
+
+## References and Credits
+
+* [Professional Java for Web Applications](https://amzn.to/4aazY59)
+* [Securing Web Applications with Spring Security](https://amzn.to/3PCCEA1)
+* [Client Session State Design Pattern: Explained Simply (Ram N Java)](https://www.youtube.com/watch?v=ycOSj9g41pc)
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/etc/client-session.urm.puml b/client-session/etc/client-session.urm.puml
new file mode 100644
index 000000000000..eea5ce8a9054
--- /dev/null
+++ b/client-session/etc/client-session.urm.puml
@@ -0,0 +1,51 @@
+@startuml
+package com.iluwatar.client.session {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class Request {
+ - data : String
+ - session : Session
+ + Request(data : String, session : Session)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getData() : String
+ + getSession() : Session
+ + hashCode() : int
+ + setData(data : String)
+ + setSession(session : Session)
+ + toString() : String
+ }
+ class Server {
+ - LOGGER : Logger {static}
+ - host : String
+ - port : int
+ + Server(host : String, port : int)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getHost() : String
+ + getPort() : int
+ + getSession(name : String) : Session
+ + hashCode() : int
+ + process(request : Request)
+ + setHost(host : String)
+ + setPort(port : int)
+ + toString() : String
+ }
+ class Session {
+ - clientName : String
+ - id : String
+ + Session(id : String, clientName : String)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getClientName() : String
+ + getId() : String
+ + hashCode() : int
+ + setClientName(clientName : String)
+ + setId(id : String)
+ + toString() : String
+ }
+}
+Request --> "-session" Session
+@enduml
\ No newline at end of file
diff --git a/client-session/pom.xml b/client-session/pom.xml
new file mode 100644
index 000000000000..1b2ea4564ff9
--- /dev/null
+++ b/client-session/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ client-session
+
+
+ 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.client.session.App
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000000..8f744353ed1b
--- /dev/null
+++ b/client-session/src/main/java/com/iluwatar/client/session/App.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.client.session;
+
+/**
+ * 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
+ * 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 {
+
+ /**
+ * Program entry point.
+ *
+ * @param args Command line args
+ */
+ public static void main(String[] args) {
+ var server = new Server("localhost", 8080);
+ var session1 = server.getSession("Session1");
+ var session2 = server.getSession("Session2");
+ var request1 = new Request("Data1", session1);
+ var request2 = new Request("Data2", session2);
+ server.process(request1);
+ server.process(request2);
+ }
+}
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
new file mode 100644
index 000000000000..e47ed2775ac4
--- /dev/null
+++ b/client-session/src/main/java/com/iluwatar/client/session/Request.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.client.session;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/** The Request class which contains the Session details and data. */
+@Data
+@AllArgsConstructor
+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
new file mode 100644
index 000000000000..13a43a2dda81
--- /dev/null
+++ b/client-session/src/main/java/com/iluwatar/client/session/Server.java
@@ -0,0 +1,67 @@
+/*
+ * 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.client.session;
+
+import java.util.UUID;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The Server class. The client communicates with the server and request processing and getting a
+ * new session.
+ */
+@Slf4j
+@Data
+@AllArgsConstructor
+public class Server {
+ private String host;
+
+ private int port;
+
+ /**
+ * Creates a new session.
+ *
+ * @param name name of the client
+ * @return Session Object
+ */
+ public Session getSession(String name) {
+ return new Session(UUID.randomUUID().toString(), name);
+ }
+
+ /**
+ * Processes a request based on the session.
+ *
+ * @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());
+ }
+}
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
new file mode 100644
index 000000000000..a7639485b83c
--- /dev/null
+++ b/client-session/src/main/java/com/iluwatar/client/session/Session.java
@@ -0,0 +1,44 @@
+/*
+ * 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.client.session;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+/**
+ * The Session class. Each client get assigned a Session which is then used for further
+ * communications.
+ */
+@Data
+@AllArgsConstructor
+public class Session {
+
+ /** Session id. */
+ private String id;
+
+ /** 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
new file mode 100644
index 000000000000..63951ae48ae3
--- /dev/null
+++ b/client-session/src/test/java/com/iluwatar/client/session/AppTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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.client.session;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+
+ @Test
+ void appStartsWithoutException() {
+ 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
new file mode 100644
index 000000000000..0037ca6339fb
--- /dev/null
+++ b/client-session/src/test/java/com/iluwatar/client/session/ServerTest.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.client.session;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.junit.jupiter.api.Test;
+
+class ServerTest {
+
+ @Test
+ void checkGetSession() {
+ Server server = new Server("localhost", 8080);
+ Session session = server.getSession("Session");
+ assertEquals("Session", session.getClientName());
+ }
+}
diff --git a/cloud-static-content-hosting/README.md b/cloud-static-content-hosting/README.md
deleted file mode 100644
index 12b3fd7cd91b..000000000000
--- a/cloud-static-content-hosting/README.md
+++ /dev/null
@@ -1,148 +0,0 @@
----
-layout: pattern
-title: Static Content Hosting
-folder: cloud-static-content-hosting
-permalink: /patterns/cloud-static-content-hosting/
-categories: Cloud
-language: en
-tags:
-- Cloud distributed
----
-
-## Intent
-
-Deploy static content to a cloud-based storage service that can deliver them directly to the client.
-This can reduce the need for potentially expensive compute instances.
-
-## Explanation
-
-Real world example
-
-> A global marketing web site with static content needs to be quickly deployed to start attracting
-> potential customers. To keep the hosting expenses and maintenance minimum, a cloud hosted storage
-> service along with content delivery network is used.
-
-In plain words
-
-> Static Content Hosting pattern utilizes cloud native storage service to store the content and
-> global content delivery network to cache it in multiple data centers around the world.
->
-> On a static website, individual webpages include static content. They might also contain
-> client-side scripts such as Javascript. By contrast, a dynamic website relies on server-side
-> processing, including server-side scripts such as PHP, JSP, or ASP.NET.
-
-Wikipedia says
-
-> A static web page (sometimes called a flat page or a stationary page) is a web page that is
-> delivered to the user's web browser exactly as stored, in contrast to dynamic web pages which are
-> generated by a web application.
->
-> Static web pages are suitable for content that never or rarely needs to be updated, though modern
-> web template systems are changing this. Maintaining large numbers of static pages as files can be
-> impractical without automated tools, such as static site generators.
-
-**Example**
-
-
-
-In this example we create a static web site using AWS S3 and utilize AWS Cloudfront to distribute
-the content globally.
-
-1. First you will need an AWS account. You can create a free one here: [AWS Free Tier](https://aws.amazon.com/free/free-tier/)
-
-2. Login to the [AWS Console](https://console.aws.amazon.com/console/home?nc2=h_ct&src=header-signin)
-
-3. Go to Identity and Access Management (IAM) service.
-
-4. Create IAM user that has only the necessary rights for this application.
-
- * Click `Users`
- * Click `Add user`. Choose `User name` as you wish and `Access type` should be `Programmatic access`. Click `Next: Permissions`.
- * Choose `Attach existing policies directly`. Select `AmazonS3FullAccess` and `CloudFrontFullAccess`. Click `Next: Tags`.
- * No tags are necessarily needed, so just click `Next: Review`.
- * Review the presented information and if all seems good click `Create user`.
- * You are presented with `Access key ID` and `Secret access key` which you will need to complete this example, so store them safely.
- * Click `Close`.
-
-5. [Install AWS Command Line Interface (AWS CLI)](https://docs.aws.amazon.com/cli/latest/userguide/install-cliv1.html) to gain programmic access to AWS cloud.
-
-6. Configure AWS CLI with command `aws configure` as desribed in the [instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html#cli-configure-quickstart-config)
-
-7. Create AWS S3 bucket for the web site content. Note that the S3 bucket names must be globally unique.
-
- * The syntax is `aws s3 mb ` as described in the [instructions](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-buckets-creating)
- * For example `aws s3 mb s3://my-static-website-jh34jsjmg`
- * Verify that the bucket was successfully created with command `aws s3 ls` which list the existing buckets
-
-8. Configure the bucket as a web site with command `aws s3 website` as described in the [instructions](https://docs.aws.amazon.com/cli/latest/reference/s3/website.html).
-
- * E.g. `aws s3 website s3://my-static-website-jh34jsjmg --index-document index.html --error-document error.html`
-
-9. Upload content to the bucket.
-
- * First create the content, at least `index.html` and `error.html` documents.
- * Upload the content to your bucket as described [here](https://docs.aws.amazon.com/cli/latest/userguide/cli-services-s3-commands.html#using-s3-commands-managing-objects-copy)
- * E.g. `aws s3 cp index.html s3://my-static-website-jh34jsjmg` and `aws s3 cp error.html s3://my-static-website-jh34jsjmg`
-
-10. Next we need to set the bucket policy to allow read access.
-
- * Create `policy.json` with the following contents (note that you need to replace the bucket name with your own).
-
- ```json
- {
- "Version": "2012-10-17",
- "Statement": [
- {
- "Sid": "PublicReadGetObject",
- "Effect": "Allow",
- "Principal": "*",
- "Action": "s3:GetObject",
- "Resource": "arn:aws:s3:::my-static-website-jh34jsjmg/*"
- }
- ]
- }
- ```
-
- * Set the bucket policy according to these [instructions](https://docs.aws.amazon.com/cli/latest/reference/s3api/put-bucket-policy.html)
- * E.g. `aws s3api put-bucket-policy --bucket my-static-website-jh34jsjmg --policy file://policy.json`
-
-11. Test the web site in your browser.
-
- * The web site URL format is `http://.s3-website-.amazonaws.com`
- * E.g. this web site was created in `eu-west-1` region with name `my-static-website-jh34jsjmg` so it can be accessed via url `http://my-static-website-jh34jsjmg.s3-website-eu-west-1.amazonaws.com`
-
-12. Create CloudFormation distribution for the web site.
-
- * The syntax is described in [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-distribution.html)
- * E.g. the easiest way is to call `aws cloudfront create-distribution --origin-domain-name my-static-website-jh34jsjmg.s3.amazonaws.com --default-root-object index.html`
- * There's also JSON syntax e.g. `--distribution-config file://dist-config.json` to pass distribution configuration arguments in file
- * The output of the call will show you the exact distribution settings including the generated CloudFront domain name you can use for testing e.g. `d2k3xwnaqa8nqx.cloudfront.net`
- * CloudFormation distribution deployment takes some time, but once it's completed your web site is served from data centers all around the globe!
-
-13. That's it! You have implemented a static web site with content distribution network serving it lightning fast all around the world.
-
- * To update the web site you need to update the objects in S3 bucket and invalidate the objects in the CloudFront distribution
- * To do it from AWS CLI see [this reference](https://docs.aws.amazon.com/cli/latest/reference/cloudfront/create-invalidation.html)
- * Some further development you might want to do is serve the content over https and add a domain name for your site
-
-## Applicability
-
-Use the Static Content Hosting pattern when you want to:
-
-* Minimize the hosting cost for websites and applications that contain some static resources.
-* Build a globally available web site with static content
-* Monitor the web site traffic, bandwidth usage, costs etc.
-
-## Typical Use Case
-
-* Web sites with global reach
-* Content produced by static web site generators
-* Web sites with no dynamic content requirements
-
-## Real world examples
-
-* [Java Design Patterns web site](https://java-design-patterns.com)
-
-## Credits
-
-* [Static Content Hosting pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/static-content-hosting)
diff --git a/cloud-static-content-hosting/etc/static-content-hosting.drawio b/cloud-static-content-hosting/etc/static-content-hosting.drawio
deleted file mode 100644
index 50281e5e6d0d..000000000000
--- a/cloud-static-content-hosting/etc/static-content-hosting.drawio
+++ /dev/null
@@ -1 +0,0 @@
-7Vpbc9o6EP41PMZjW77AY0Kh56HttMOcIX3qCCxsNbLlyjKX/PqzMvING9rMQGE4mWQS72olrfbbXe8KBmgcbz8KnEafeUDYwDaD7QB9GNi2hRwL/inObs8ZIc0IBQ20UM2Y0Veimabm5jQgWUtQcs4kTdvMJU8SspQtHhaCb9piK87au6Y4JB3GbIlZlzungYz23KFr1vx/CA2jcmfL1CMxLoU1I4twwDcNFpoM0FhwLvdP8XZMmDJeaZf9vOmR0UoxQRL5JxOePj8vvN38XzEXc/KNzvDj65cH29PKyV15YhKAATTJhYx4yBPMJjX3aZmLNVGrWkDUIp84TzXzJ5Fyp8HEueTAimTM9CgoLHbPQJiGW5Lfm2MflPuYFbUrqS2Vz6UcPH+vlwCqnqSIcs7+eOpMR82mWRnPxZKcspWr/Q+LkMgTgsMKXQgLwmMCh4B5gjAs6bqtCNb+GVZyNYTwoFF8C6L+NRDVyJg3jox/VWS0lmvMcr1VFyrGILMpSDYRlWSW4uLgG0iubYvrpYiQZHvaft3j6gn2SCukU7M30plqUyc6G2le1Exy/uhCJhreloWc0iLaQrZvGe61beTflo089/ZMNLqhl5r5m5faGdNgWR/ddBYsC7vrgGO9Q3NCSes9bm62rrN60vzfBGfYRsewQKHflOuK+koEBQMQ8X+FrfO2DsiaMJ6CRQ7x5LlkNCHjqpVVBlvxRI4546KQQfAzVds/hQIHlNRjCU8U4ivKWJ94JgV/IQfCAc6iwj3URqoKoND4fsILwr7yjErKExhbcCl53BB4ZDRUA1I50RPW1BJ0USg33Ufprn2r8JaC1udVW+Is3R90RbdKj6eUU7XKZA2LZXoR6JtTNSHehuqKwcCbzDHyrNjrDPUL8ts1ntctgv1ht3gpeefvEjoO8zifAWOG4M8iX76Ar/+R41wa3B5ckLFX8MeGyugHX/wEbbKuU05M13bQcR8+A6qW20YVoS6sXk9N6lXV69mBRUeAHTOeB1MBoXHDuC6VkqtCyQ6cU3foIu+icLqeZ7T7DGQ5PX2GX4k1QR2hSyX3LqaWoS4jBcGSHI9XMIQ8MHpfem7aWbM6wB3iG9MgYMc6P8HzJKh85xxhZrfDzOrJnpbVE2flvPND4nQgsRUkOAhgvTwN9sCUuelukaku0Mu+3OxBpq8rvxwy3esv1AqWgIKt6SIvUtjdAuOWSaMMGf/qwHRvlJwqZEyaAJ9C2AAqdxwvnnMQL9bVYRl1YClfJ+aGLOBVr4KGJEFRNA9sjykkFtBWeOEeEwmWelRKqF8Px8pwe6FinYcEx6QaKOYYGXrQaz+0pwgSggP0TcExfoWmcpMZS6g47tZBDvKp0/OmQ30V5cX8o1y44R+NKg3SKY8xhURqasxOuUdgv6DtJsG/8DD5tW1Ue0ai/G16v7A69s3h2r0yUN1uN/e+Xxe86bogO9d9gdVyGN8x+xqRv3llYL3pY7PSQEWQHwmzM3+M1g4xt6cU9Z2eEKua+DdYDMj6KxXFWOOLKWjyHw==
\ No newline at end of file
diff --git a/collecting-parameter/README.md b/collecting-parameter/README.md
new file mode 100644
index 000000000000..89c37e79bfb8
--- /dev/null
+++ b/collecting-parameter/README.md
@@ -0,0 +1,161 @@
+---
+title: "Collecting Parameter Pattern in Java: Mastering Efficient Parameter Handling"
+shortTitle: Collecting Parameter
+description: "Discover how the Collecting Parameter design pattern simplifies Java method calls by aggregating multiple parameters into a single collection object. Enhance code readability and maintainability with practical examples and real-world applications."
+category: Behavioral
+language: en
+tag:
+ - Accumulation
+ - Data processing
+ - Data transfer
+ - Generic
+---
+
+## Also known as
+
+* Collector
+* Accumulator
+
+## Intent of Collecting Parameter Design Pattern
+
+The Collecting Parameter pattern in Java design patterns aims to simplify method calls by aggregating multiple parameters into a single collection object. This pattern is particularly effective for methods that collect information by passing a single collection object through various method calls. Each method can then add results to this collection, instead of creating its own collection. This approach enhances code readability and maintainability, optimizing the process of information collection in Java programming.
+
+## Detailed Explanation of Collecting Parameter Pattern with Real-World Examples
+
+Real-world example
+
+> In software development, the Collecting Parameter pattern provides significant benefits by optimizing method calls and improving code maintainability.
+>
+> Imagine a scenario in a restaurant where a waiter needs to take an order from a customer. Instead of noting down each item separately (e.g., appetizer, main course, dessert, drink), the waiter uses an order form that collects all the items into a single document. This order form simplifies the communication between the waiter and the kitchen staff by aggregating all the details into one place. Similarly, in software, the Collecting Parameter pattern aggregates multiple parameters into a single object, streamlining method calls and improving code readability and maintainability.
+
+In plain words
+
+> The Collecting Parameter pattern simplifies method calls by encapsulating multiple parameters into a single object.
+
+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.
+
+The following business rules are implemented:
+
+* If an A4 paper is coloured, it must also be single-sided. All other non-coloured papers are accepted
+* A3 papers must be non-coloured and single-sided
+* A2 papers must be single-page, single-sided, and non-coloured
+
+Let's see the implementation first and explain afterward.
+
+```java
+public class App {
+ static PrinterQueue printerQueue = PrinterQueue.getInstance();
+
+ public static void main(String[] args) {
+ printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 5, false, false));
+ printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A3, 2, false, false));
+ printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false));
+
+ var result = new LinkedList();
+
+ addValidA4Papers(result);
+ addValidA3Papers(result);
+ addValidA2Papers(result);
+ }
+
+ public static void addValidA4Papers(Queue printerItemsCollection) {
+ for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
+ if (nextItem.paperSize.equals(PaperSizes.A4)) {
+ var isColouredAndSingleSided = nextItem.isColour && !nextItem.isDoubleSided;
+ if (isColouredAndSingleSided || !nextItem.isColour) {
+ printerItemsCollection.add(nextItem);
+ }
+ }
+ }
+ }
+
+ public static void addValidA3Papers(Queue printerItemsCollection) {
+ for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
+ if (nextItem.paperSize.equals(PaperSizes.A3)) {
+ var isNotColouredAndSingleSided = !nextItem.isColour && !nextItem.isDoubleSided;
+ if (isNotColouredAndSingleSided) {
+ printerItemsCollection.add(nextItem);
+ }
+ }
+ }
+ }
+
+ public static void addValidA2Papers(Queue printerItemsCollection) {
+ for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
+ if (nextItem.paperSize.equals(PaperSizes.A2)) {
+ var isNotColouredSingleSidedAndOnePage = nextItem.pageCount == 1 && !nextItem.isDoubleSided
+ && !nextItem.isColour;
+ if (isNotColouredSingleSidedAndOnePage) {
+ printerItemsCollection.add(nextItem);
+ }
+ }
+ }
+ }
+}
+```
+
+This `App` class is the main entry point of the application. It uses the Collecting Parameter design pattern to filter print jobs based on certain policies.
+
+1. **Initialization**: The `printerQueue` is initialized with three print jobs of different paper sizes (A4, A3, A2).
+
+2. **Creating the Collecting Parameter**: A `LinkedList` named `result` is created to store the print jobs that meet the policy requirements.
+
+3. **Adding Valid Jobs to the Collecting Parameter**: The `addValidA4Papers`, `addValidA3Papers`, and `addValidA2Papers` methods are called. These methods iterate over the `printerQueue` and add the print jobs that meet the policy requirements to the `result` list.
+
+The `result` list, which is the collecting parameter, accumulates the valid print jobs as it is passed from method to method. This is the essence of the Collecting Parameter design pattern.
+
+Utilizing the Collecting Parameter pattern in Java design patterns leads to more efficient method calls and improved overall code structure.
+
+## When to Use the Collecting Parameter Pattern in Java
+
+This pattern is useful for managing parameters in Java coding practices, ensuring efficient code refactoring and enhanced readability.
+
+* Use when a method needs to accept a large number of parameters, making the method signature unwieldy.
+* Use when the same group of parameters is passed to multiple methods, reducing redundancy and potential errors.
+* Use to improve the readability and maintainability of the code.
+
+## Collecting Parameter Pattern Java Tutorials
+
+* [Refactoring To Patterns (Joshua Kerivsky)](http://www.tarrani.net/RefactoringToPatterns.pdf)
+* [Smalltalk Best Practice Patterns (Kent Beck)](https://ptgmedia.pearsoncmg.com/images/9780134769042/samplepages/013476904X.pdf)
+
+## Real-World Applications of Collecting Parameter Pattern in Java
+
+* Use when a method needs to accept a large number of parameters, making the method signature unwieldy.
+* Use when the same group of parameters is passed to multiple methods, reducing redundancy and potential errors.
+* Use to improve the readability and maintainability of the code.
+
+## Benefits and Trade-offs of Collecting Parameter Pattern
+
+Benefits:
+
+* Improves code readability by reducing the number of parameters in method signatures.
+* Facilitates the reuse of parameter sets across different methods.
+* Enhances maintainability by centralizing the parameter structure.
+
+Trade-offs:
+
+* Introduces an additional class, which may increase complexity if not managed properly.
+* Can lead to over-generalization if the parameter object becomes too large or unwieldy.
+
+## Related Java Design Patterns
+
+* [Command](https://java-design-patterns.com/patterns/command/): Commands may utilize Collecting Parameter to aggregate results from multiple operations executed by the command objects.
+* [Composite](https://java-design-patterns.com/patterns/composite/): Can be used in tandem with Collecting Parameter when dealing with hierarchical structures, allowing results to be collected across a composite structure.
+* [Visitor](https://java-design-patterns.com/patterns/visitor/): Often used together, where Visitor handles traversal and operations on a structure, and Collecting Parameter accumulates the results.
+
+## References and Credits
+
+* [Clean Code: A Handbook of Agile Software Craftsmanship](https://amzn.to/4aApLP0)
+* [Refactoring: Improving the Design of Existing Code](https://amzn.to/3TVEgaB)
+* [Collecting Parameter (WikiWikiWeb)](https://wiki.c2.com/?CollectingParameter)
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/etc/collecting-parameter.urm.png b/collecting-parameter/etc/collecting-parameter.urm.png
new file mode 100644
index 000000000000..785d6ecc2da1
Binary files /dev/null and b/collecting-parameter/etc/collecting-parameter.urm.png differ
diff --git a/collecting-parameter/etc/collecting-parameter.urm.puml b/collecting-parameter/etc/collecting-parameter.urm.puml
new file mode 100644
index 000000000000..06320fa022ee
--- /dev/null
+++ b/collecting-parameter/etc/collecting-parameter.urm.puml
@@ -0,0 +1,39 @@
+@startuml
+package com.iluwatar.collectingparameter {
+ class App {
+ ~ printerQueue : PrinterQueue {static}
+ + App()
+ + addValidA2Papers(printerItemsCollection : Queue) {static}
+ + addValidA3Papers(printerItemsCollection : Queue) {static}
+ + addValidA4Papers(printerItemsCollection : Queue) {static}
+ + main(args : String[]) {static}
+ }
+ ~enum PaperSizes {
+ + A2 {static}
+ + A3 {static}
+ + A4 {static}
+ + valueOf(name : String) : PaperSizes {static}
+ + values() : PaperSizes[] {static}
+ }
+ class PrinterItem {
+ ~ isColour : boolean
+ ~ isDoubleSided : boolean
+ ~ pageCount : int
+ ~ paperSize : PaperSizes
+ + PrinterItem(paperSize : PaperSizes, pageCount : int, isDoubleSided : boolean, isColour : boolean)
+ }
+ class PrinterQueue {
+ ~ currentInstance : PrinterQueue {static}
+ - printerItemQueue : Queue
+ - PrinterQueue()
+ + addPrinterItem(printerItem : PrinterItem)
+ + emptyQueue()
+ + getInstance() : PrinterQueue {static}
+ + getPrinterQueue() : Queue
+ }
+}
+PrinterQueue --> "-currentInstance" PrinterQueue
+PrinterQueue --> "-printerItemQueue" PrinterItem
+App --> "-printerQueue" PrinterQueue
+PrinterItem --> "-paperSize" PaperSizes
+@enduml
\ No newline at end of file
diff --git a/collecting-parameter/pom.xml b/collecting-parameter/pom.xml
new file mode 100644
index 000000000000..8c5c015468a4
--- /dev/null
+++ b/collecting-parameter/pom.xml
@@ -0,0 +1,62 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ collecting-parameter
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.collectingparameter.App
+
+
+
+
+
+
+
+
+
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java
new file mode 100644
index 000000000000..d505970fb84f
--- /dev/null
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/App.java
@@ -0,0 +1,140 @@
+/*
+ * 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.collectingparameter;
+
+import java.util.LinkedList;
+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.
+ *
+ * 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,
+ */
+public class App {
+ static PrinterQueue printerQueue = PrinterQueue.getInstance();
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ /*
+ Initialising the printer queue with jobs
+ */
+ printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A4, 5, false, false));
+ printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A3, 2, false, false));
+ printerQueue.addPrinterItem(new PrinterItem(PaperSizes.A2, 5, false, false));
+
+ /*
+ This variable is the collecting parameter, and will store the policy abiding print jobs.
+ */
+ var result = new LinkedList();
+
+ /*
+ Adding A4, A3, and A2 papers that obey the policy
+ */
+ addValidA4Papers(result);
+ addValidA3Papers(result);
+ addValidA2Papers(result);
+ }
+
+ /**
+ * 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.
+ */
+ for (PrinterItem nextItem : printerQueue.getPrinterQueue()) {
+ if (nextItem.paperSize.equals(PaperSizes.A4)) {
+ var isColouredAndSingleSided = nextItem.isColour && !nextItem.isDoubleSided;
+ if (isColouredAndSingleSided || !nextItem.isColour) {
+ printerItemsCollection.add(nextItem);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ 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
+ var isNotColouredAndSingleSided = !nextItem.isColour && !nextItem.isDoubleSided;
+ if (isNotColouredAndSingleSided) {
+ printerItemsCollection.add(nextItem);
+ }
+ }
+ }
+ }
+
+ /**
+ * 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
+ */
+ 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;
+ if (isNotColouredSingleSidedAndOnePage) {
+ printerItemsCollection.add(nextItem);
+ }
+ }
+ }
+ }
+}
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PaperSizes.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PaperSizes.java
new file mode 100644
index 000000000000..cf706acd8872
--- /dev/null
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PaperSizes.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.collectingparameter;
+
+enum PaperSizes {
+ A2,
+ A3,
+ A4
+}
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.java
new file mode 100644
index 000000000000..5669f744dd84
--- /dev/null
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterItem.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.collectingparameter;
+
+import java.util.Objects;
+
+/** 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. */
+ public PrinterItem(PaperSizes paperSize, int pageCount, boolean isDoubleSided, boolean isColour) {
+ if (!Objects.isNull(paperSize)) {
+ this.paperSize = paperSize;
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ if (pageCount > 0) {
+ this.pageCount = pageCount;
+ } else {
+ throw new IllegalArgumentException();
+ }
+
+ this.isColour = isColour;
+ this.isDoubleSided = isDoubleSided;
+ }
+}
diff --git a/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java
new file mode 100644
index 000000000000..8cdfc84107a3
--- /dev/null
+++ b/collecting-parameter/src/main/java/com/iluwatar/collectingparameter/PrinterQueue.java
@@ -0,0 +1,73 @@
+/*
+ * 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.collectingparameter;
+
+import java.util.LinkedList;
+import java.util.Objects;
+import java.util.Queue;
+
+/**
+ * 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.
+ */
+ public static PrinterQueue getInstance() {
+ if (Objects.isNull(currentInstance)) {
+ currentInstance = new PrinterQueue();
+ }
+ return currentInstance;
+ }
+
+ /** Empty the printer queue. */
+ public void emptyQueue() {
+ currentInstance.getPrinterQueue().clear();
+ }
+
+ /** Private constructor prevents instantiation, unless using the getInstance() method. */
+ private PrinterQueue() {
+ printerItemQueue = new LinkedList<>();
+ }
+
+ public Queue getPrinterQueue() {
+ return currentInstance.printerItemQueue;
+ }
+
+ /**
+ * Adds a single print job to the queue.
+ *
+ * @param printerItem The printing job to be added to the queue
+ */
+ 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
new file mode 100644
index 000000000000..8597c4d6f2cb
--- /dev/null
+++ b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/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.collectingparameter;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+ /** Checks whether {@link App} executes without throwing exception */
+ @Test
+ void executesWithoutException() {
+ 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
new file mode 100644
index 000000000000..c53c5ac2c0ed
--- /dev/null
+++ b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/CollectingParameterTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.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;
+
+class CollectingParameterTest {
+
+ @Test
+ @Timeout(1000)
+ void testCollectingParameter() {
+ PrinterQueue printerQueue = PrinterQueue.getInstance();
+ printerQueue.emptyQueue();
+
+ PrinterItem item1 = new PrinterItem(PaperSizes.A4, 1, false, true);
+ PrinterItem item2 = new PrinterItem(PaperSizes.A4, 10, true, false);
+ PrinterItem item3 = new PrinterItem(PaperSizes.A4, 4, true, true);
+ PrinterItem item4 = new PrinterItem(PaperSizes.A3, 9, false, false);
+ PrinterItem item5 = new PrinterItem(PaperSizes.A3, 3, true, true);
+ PrinterItem item6 = new PrinterItem(PaperSizes.A3, 3, false, true);
+ PrinterItem item7 = new PrinterItem(PaperSizes.A3, 3, true, false);
+ PrinterItem item8 = new PrinterItem(PaperSizes.A2, 1, false, false);
+ PrinterItem item9 = new PrinterItem(PaperSizes.A2, 2, false, false);
+ PrinterItem item10 = new PrinterItem(PaperSizes.A2, 1, true, false);
+ PrinterItem item11 = new PrinterItem(PaperSizes.A2, 1, false, true);
+
+ printerQueue.addPrinterItem(item1);
+ printerQueue.addPrinterItem(item2);
+ printerQueue.addPrinterItem(item3);
+ printerQueue.addPrinterItem(item4);
+ printerQueue.addPrinterItem(item5);
+ printerQueue.addPrinterItem(item6);
+ printerQueue.addPrinterItem(item7);
+ printerQueue.addPrinterItem(item8);
+ printerQueue.addPrinterItem(item9);
+ printerQueue.addPrinterItem(item10);
+ printerQueue.addPrinterItem(item11);
+
+ Queue result = new LinkedList<>();
+ App.addValidA4Papers(result);
+ App.addValidA3Papers(result);
+ App.addValidA2Papers(result);
+
+ Queue testResult = new LinkedList<>();
+ testResult.add(item1);
+ testResult.add(item2);
+ testResult.add(item4);
+ testResult.add(item8);
+
+ Assertions.assertArrayEquals(testResult.toArray(), result.toArray());
+ }
+}
diff --git a/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.java b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.java
new file mode 100644
index 000000000000..8c03eea90c95
--- /dev/null
+++ b/collecting-parameter/src/test/java/com/iluwatar/collectingparameter/PrinterQueueTest.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.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;
+
+class PrinterQueueTest {
+
+ @Test
+ @Timeout(1000)
+ void singletonTest() {
+ PrinterQueue printerQueue1 = PrinterQueue.getInstance();
+ PrinterQueue printerQueue2 = PrinterQueue.getInstance();
+ assertSame(printerQueue1, printerQueue2);
+ }
+
+ @Test()
+ @Timeout(1000)
+ void negativePageCount() throws IllegalArgumentException {
+ 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));
+ }
+}
diff --git a/collection-pipeline/README.md b/collection-pipeline/README.md
index 1edec0889bfb..33d207922828 100644
--- a/collection-pipeline/README.md
+++ b/collection-pipeline/README.md
@@ -1,30 +1,126 @@
---
-layout: pattern
-title: Collection Pipeline
-folder: collection-pipeline
-permalink: /patterns/collection-pipeline/
-categories: Functional
+title: "Collection Pipeline Pattern in Java: Streamlining Data Manipulation"
+shortTitle: Collection Pipeline
+description: "Learn how the Collection Pipeline design pattern in Java enhances data processing by chaining operations in a sequence. This pattern promotes a declarative approach, improving code readability, maintainability, and performance."
+category: Functional
language: en
-tags:
- - Reactive
+tag:
+ - Functional decomposition
+ - Data processing
+ - Data transformation
+ - Reactive
---
-## Intent
-Collection Pipeline introduces Function Composition and Collection Pipeline, two functional-style patterns that you can combine to iterate collections in your code.
-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.
+## Intent of Collection Pipeline Design Pattern
-## Class diagram
-
+The Collection Pipeline design pattern in Java processes collections of data by chaining operations in a sequence. Utilizing the Java Stream API, it transforms data declaratively, focusing on what should be done rather than how.
-## Applicability
-Use the Collection Pipeline pattern when
+## Detailed Explanation of Collection Pipeline Pattern with Real-World Examples
-* When you want to perform a sequence of operations where one operation's collected output is fed into the next
-* When you use a lot of statements in your code
-* When you use a lot of loops in your code
+Real-world example
-## Credits
+> Imagine a real-world example of a factory assembly line for manufacturing cars. In this assembly line, each station performs a specific task on the car chassis, such as installing the engine, painting the body, attaching the wheels, and inspecting the final product. Each station takes the output from the previous station and adds its own processing step. This sequence of operations is analogous to the Collection Pipeline design pattern, where each step in the pipeline transforms the data and passes it on to the next step, ensuring an efficient and organized workflow.
-* [Function composition and the Collection Pipeline pattern](https://www.ibm.com/developerworks/library/j-java8idioms2/index.html)
-* [Martin Fowler](https://martinfowler.com/articles/collection-pipeline/)
-* [Java8 Streams](https://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html)
+In plain words
+
+> The Collection Pipeline pattern in Java involves processing data through a series of operations using the Stream API. Each operation transforms the data in sequence, akin to an assembly line in a factory, promoting functional programming principles.
+
+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.
+
+Here's a programmatic example of the Collection Pipeline design pattern:
+
+**Step 1: Filtering**
+
+We start with a list of `Car` objects and we want to filter out those that were manufactured after the year 2000. This is done using the `stream()` method to create a stream from the list, the `filter()` method to filter out the cars we want, and the `collect()` method to collect the results into a new list.
+
+```java
+public static List getModelsAfter2000(List cars){
+ return cars.stream()
+ .filter(car -> car.getYear() > 2000) // Filter cars manufactured after 2000
+ .sorted(comparing(Car::getYear)) // Sort the cars by year
+ .map(Car::getModel) // Get the model of each car
+ .collect(toList()); // Collect the results into a new list
+}
+```
+
+**Step 2: Grouping**
+
+Next, we want to group the cars by their category. This is done using the `groupingBy` collector.
+
+```java
+public static Map> getGroupingOfCarsByCategory(List cars){
+ return cars.stream()
+ .collect(groupingBy(Car::getCategory)); // Group cars by category
+}
+```
+
+**Step 3: Filtering, Sorting and Transforming**
+
+Finally, we want to filter the cars owned by a person to only include sedans, sort them by date, and then transform the sorted cars into a list of `Car` objects.
+
+```java
+public static List getSedanCarsOwnedSortedByDate(List persons){
+ return persons.stream()
+ .flatMap(person -> person.getCars().stream()) // Flatten the list of cars owned by each person
+ .filter(car -> Category.SEDAN.equals(car.getCategory())) // Filter to only include sedans
+ .sorted(comparing(Car::getDate)) // Sort the cars by date
+ .collect(toList()); // Collect the results into a new list
+}
+```
+
+In each of these methods, the Collection Pipeline pattern is used to perform a series of operations on the collection of cars in a declarative manner, which improves readability and maintainability.
+
+## When to Use the Collection Pipeline Pattern in Java
+
+The Collection Pipeline pattern is ideal for Java developers handling bulk data operations, including filtering, mapping, sorting, and reducing collections, particularly with Java 8+ Stream API.
+
+Use the Collection Pipeline pattern:
+
+* When you need to perform a series of transformations on a collection of data.
+* When you want to improve readability and maintainability of complex data processing code.
+* When working with large datasets where intermediate results should not be stored in memory.
+
+## Real-World Applications of Collection Pipeline Pattern in Java
+
+* LINQ in .NET
+* Stream API in Java 8+
+* Collections in modern functional languages (e.g., Haskell, Scala)
+* Database query builders and ORM frameworks
+
+## Benefits and Trade-offs of Collection Pipeline Pattern
+
+Benefits:
+
+* Readability: The code is more readable and declarative, making it easier to understand the sequence of operations.
+* Maintainability: Easier to modify or extend the pipeline with additional operations.
+* Reusability: Common operations can be abstracted into reusable functions.
+* Lazy Evaluation: Some implementations allow for operations to be lazily evaluated, improving performance.
+
+Trade-offs:
+
+* Performance Overhead: Chaining multiple operations can introduce overhead compared to traditional loops, especially for short pipelines or very large collections.
+* Debugging Difficulty: Debugging a chain of operations might be more challenging due to the lack of intermediate variables.
+* Limited to Collections: Primarily focused on collections, and its utility might be limited outside of collection processing.
+
+## Related Java Design Patterns
+
+* [Builder](https://java-design-patterns.com/patterns/builder/): Similar fluent interface style but used for object construction.
+* [Chain of Responsibility](https://java-design-patterns.com/patterns/chain-of-responsibility/): Conceptually similar in chaining handlers, but applied to object requests rather than data collection processing.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): Can be used within a pipeline stage to encapsulate different algorithms that can be selected at runtime.
+
+## References and Credits
+
+* [Functional Programming in Scala](https://amzn.to/4cEo6K2)
+* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3THp4wy)
+* [Refactoring: Improving the Design of Existing Code](https://amzn.to/3VDMWDO)
+* [Collection Pipeline (Martin Fowler)](https://martinfowler.com/articles/collection-pipeline/)
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/etc/collection-pipeline.ucls b/collection-pipeline/etc/collection-pipeline.ucls
deleted file mode 100644
index 6373db62f67a..000000000000
--- a/collection-pipeline/etc/collection-pipeline.ucls
+++ /dev/null
@@ -1,98 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/collection-pipeline/etc/collection-pipeline.urm.png b/collection-pipeline/etc/collection-pipeline.urm.png
new file mode 100644
index 000000000000..074dffe7b14e
Binary files /dev/null and b/collection-pipeline/etc/collection-pipeline.urm.png differ
diff --git a/collection-pipeline/pom.xml b/collection-pipeline/pom.xml
index a97b272d9b25..b3c36cf06ef6 100644
--- a/collection-pipeline/pom.xml
+++ b/collection-pipeline/pom.xml
@@ -1,8 +1,10 @@
4.0.0
-
+
com.iluwatar
java-design-patterns
1.26.0-SNAPSHOT
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 53404ecab9d3..bf1ec4a017de 100644
--- a/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java
+++ b/combinator/src/main/java/com/iluwatar/combinator/CombinatorApp.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,40 +22,49 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.combinator;
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 =
+ """
+ It was many and many a year ago,
+ In a kingdom by the sea,
+ That a maiden there lived whom you may know
+ By the name of ANNABEL LEE;
+ And this maiden she lived with no other thought
+ Than to love and be loved by me.
+ I was a child and she was a child,
+ In this kingdom by the sea;
+ But we loved with a love that was more than love-
+ I and my Annabel Lee;
+ With a love that the winged seraphs of heaven
+ Coveted her and me.""";
+
/**
* 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);
@@ -64,23 +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
- "It was many and many a year ago,\n"
- + "In a kingdom by the sea,\n"
- + "That a maiden there lived whom you may know\n"
- + "By the name of ANNABEL LEE;\n"
- + "And this maiden she lived with no other thought\n"
- + "Than to love and be loved by me.\n"
- + "I was a child and she was a child,\n"
- + "In this kingdom by the sea;\n"
- + "But we loved with a love that was more than love-\n"
- + "I and my Annabel Lee;\n"
- + "With a love that the winged seraphs of heaven\n"
- + "Coveted her and me.";
- }
+ 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 1da87a411e61..cc2d5ce84918 100644
--- a/combinator/src/main/java/com/iluwatar/combinator/Finder.java
+++ b/combinator/src/main/java/com/iluwatar/combinator/Finder.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,20 +22,18 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.combinator;
import java.util.List;
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
*/
@@ -41,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
*/
@@ -65,6 +68,7 @@ default Finder not(Finder notFinder) {
/**
* combinator or.
+ *
* @param orFinder finder to combine
* @return new finder including previous finders
*/
@@ -77,17 +81,15 @@ default Finder or(Finder orFinder) {
}
/**
- * combinator or.
+ * 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 a137c6938b8f..a6f8dd7cac79 100644
--- a/combinator/src/main/java/com/iluwatar/combinator/Finders.java
+++ b/combinator/src/main/java/com/iluwatar/combinator/Finders.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,37 +22,31 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.combinator;
import java.util.ArrayList;
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
@@ -62,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
*/
@@ -81,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 705fab9ae061..4a7a6fff7a4e 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/CombinatorAppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.combinator;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
@@ -31,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/FinderTest.java b/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java
index d87c71e2bdbf..c4a505df075d 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/FinderTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -31,10 +33,13 @@ class FinderTest {
@Test
void contains() {
- var example = "the first one \nthe second one \n";
+ var example = """
+ the first one
+ the second one\s
+ """;
var result = Finder.contains("second").find(example);
assertEquals(1, result.size());
- assertEquals( "the second one ", result.get(0));
+ assertEquals("the second one ", result.get(0));
}
}
diff --git a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
index 4c5a6a6145a6..f8a23927c33e 100644
--- a/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
+++ b/combinator/src/test/java/com/iluwatar/combinator/FindersTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -37,47 +39,45 @@ class FindersTest {
void advancedFinderTest() {
var res = advancedFinder("it was", "kingdom", "sea").find(text());
assertEquals(1, res.size());
- assertEquals( "It was many and many a year ago,", res.get(0));
+ assertEquals("It was many and many a year ago,", res.get(0));
}
@Test
void filteredFinderTest() {
var res = filteredFinder(" was ", "many", "child").find(text());
assertEquals(1, res.size());
- assertEquals( "But we loved with a love that was more than love-", res.get(0));
+ assertEquals("But we loved with a love that was more than love-", res.get(0));
}
@Test
void specializedFinderTest() {
var res = specializedFinder("love", "heaven").find(text());
assertEquals(1, res.size());
- assertEquals( "With a love that the winged seraphs of heaven", res.get(0));
+ assertEquals("With a love that the winged seraphs of heaven", res.get(0));
}
@Test
void expandedFinderTest() {
var res = expandedFinder("It was", "kingdom").find(text());
assertEquals(3, res.size());
- assertEquals( "It was many and many a year ago,", res.get(0));
- assertEquals( "In a kingdom by the sea,", res.get(1));
- assertEquals( "In this kingdom by the sea;", res.get(2));
+ assertEquals("It was many and many a year ago,", res.get(0));
+ assertEquals("In a kingdom by the sea,", res.get(1));
+ assertEquals("In this kingdom by the sea;", res.get(2));
}
-
private String text() {
- return
- "It was many and many a year ago,\n"
- + "In a kingdom by the sea,\n"
- + "That a maiden there lived whom you may know\n"
- + "By the name of ANNABEL LEE;\n"
- + "And this maiden she lived with no other thought\n"
- + "Than to love and be loved by me.\n"
- + "I was a child and she was a child,\n"
- + "In this kingdom by the sea;\n"
- + "But we loved with a love that was more than love-\n"
- + "I and my Annabel Lee;\n"
- + "With a love that the winged seraphs of heaven\n"
- + "Coveted her and me.";
+ return """
+ It was many and many a year ago,
+ In a kingdom by the sea,
+ That a maiden there lived whom you may know
+ By the name of ANNABEL LEE;
+ And this maiden she lived with no other thought
+ Than to love and be loved by me.
+ I was a child and she was a child,
+ In this kingdom by the sea;
+ But we loved with a love that was more than love-
+ I and my Annabel Lee;
+ 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
new file mode 100644
index 000000000000..bc1de83fc681
--- /dev/null
+++ b/command-query-responsibility-segregation/README.md
@@ -0,0 +1,143 @@
+---
+title: "Command Query Responsibility Segregation in Java: Optimizing Data Interaction for Scalability"
+shortTitle: Command Query Responsibility Segregation (CQRS)
+description: "Learn about the Command Query Responsibility Segregation (CQRS) pattern in Java. Discover how segregating commands and queries can enhance the scalability, performance, and maintainability of your software systems."
+category: Architectural
+language: en
+tag:
+ - Event-driven
+ - Performance
+ - Scalability
+---
+
+## Also known as
+
+* CQRS
+
+## Intent of Command Query Responsibility Segregation Design Pattern
+
+Command Query Responsibility Segregation (CQRS) aims to segregate the operations that modify the state of an application (commands) from the operations that read the state (queries). This separation enhances scalability, performance, and maintainability in complex software systems.
+
+## Detailed Explanation of Command Query Responsibility Segregation Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a modern library where the tasks of borrowing and returning books (commands) are handled at the front desk, while the task of searching for books and reading them (queries) happens in the reading area. The front desk optimizes for transaction efficiency and record-keeping, ensuring books are properly checked in and out. Meanwhile, the reading area is optimized for comfort and accessibility, making it easy for readers to find and engage with the books. This separation improves the library's overall efficiency and user experience, much like the CQRS pattern enhances a software system's performance and maintainability.
+
+In plain words
+
+> The CQRS design pattern separates the actions of modifying data (commands) from the actions of retrieving data (queries) to enhance performance, scalability, and maintainability in software systems. By implementing CQRS, you can optimize your system's read and write operations independently, allowing for more efficient data handling and improved overall system performance.
+
+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.
+
+Let's see the code implementation first and explain how it works afterward.
+
+```java
+public static void main(String[] args) {
+
+ // Create Authors and Books using CommandService
+ var commands = new CommandServiceImpl();
+
+ commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
+ commands.authorCreated(AppConstants.J_BLOCH, "Joshua Bloch", "jBloch@email.com");
+ commands.authorCreated(AppConstants.M_FOWLER, "Martin Fowler", "mFowler@email.com");
+
+ commands.bookAddedToAuthor("Domain-Driven Design", 60.08, AppConstants.E_EVANS);
+ 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("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
+ commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
+
+ // Query the database using QueryService
+ var queries = new QueryServiceImpl();
+
+ var nullAuthor = queries.getAuthorByUsername("username");
+ var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
+ var blochBooksCount = queries.getAuthorBooksCount(AppConstants.J_BLOCH);
+ var authorsCount = queries.getAuthorsCount();
+ var dddBook = queries.getBook("Domain-Driven Design");
+ var blochBooks = queries.getAuthorBooks(AppConstants.J_BLOCH);
+
+ LOGGER.info("Author username : {}", nullAuthor);
+ LOGGER.info("Author evans : {}", evans);
+ LOGGER.info("jBloch number of books : {}", blochBooksCount);
+ LOGGER.info("Number of authors : {}", authorsCount);
+ LOGGER.info("DDD book : {}", dddBook);
+ LOGGER.info("jBloch books : {}", blochBooks);
+
+ HibernateUtil.getSessionFactory().close();
+}
+```
+
+1. Command Service: The `CommandServiceImpl` class is used for write operations. It provides methods to create authors and books, and to add books to authors.
+
+2. Query Service: The `QueryServiceImpl` class is used for read operations. It provides methods to get author and book details.
+
+This separation of concerns allows for flexibility in how the application handles data access and manipulation, and is a key aspect of the CQRS pattern.
+
+Program output:
+
+```
+17:37:56.040 [main] INFO com.iluwatar.cqrs.app.App - Author username : null
+17:37:56.040 [main] INFO com.iluwatar.cqrs.app.App - Author evans : Author(name=Eric J. Evans, email=evans@email.com, username=eEvans)
+17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - jBloch number of books : 3
+17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - Number of authors : 3
+17:37:56.041 [main] INFO com.iluwatar.cqrs.app.App - DDD book : Book(title=Domain-Driven Design, price=60.08)
+17:37:56.042 [main] INFO com.iluwatar.cqrs.app.App - jBloch books : [Book(title=Effective Java, price=40.54), Book(title=Java Puzzlers, price=39.99), Book(title=Java Concurrency in Practice, price=29.4)]
+```
+
+## When to Use the Command Query Responsibility Segregation Pattern in Java
+
+* Systems requiring distinct models for read and write operations for scalability and maintainability, such as e-commerce platforms and high-traffic websites.
+* Complex domain models, like financial services or healthcare applications, where the task of updating objects differs significantly from the task of reading object data.
+* Scenarios where performance optimization for read operations is crucial, and the system can benefit from different data models or databases for reads and writes, enhancing data retrieval speed and accuracy.
+
+## Real-World Applications of CQRS Pattern in Java
+
+* Distributed Systems and Microservices Architecture, where different services manage read and write responsibilities.
+* Event-Sourced Systems, where changes to the application state are stored as a sequence of events.
+* High-Performance Web Applications, segregating read and write databases to optimize load handling.
+
+## Benefits and Trade-offs of Command Query Responsibility Segregation Pattern
+
+Benefits:
+
+* Scalability: By separating read and write models, each can be scaled independently according to their specific demands.
+* Optimization: Allows for the optimization of read models for query efficiency and write models for transactional integrity.
+* Maintainability: Reduces complexity by separating the concerns, leading to cleaner, more maintainable code.
+* Flexibility: Offers the flexibility to choose different technologies for the read and write sides according to their requirements.
+
+Trade-Offs:
+
+* Complexity: Introduces complexity due to synchronization between read and write models, especially in consistency maintenance.
+* Overhead: Might be an overkill for simple systems where the benefits do not outweigh the additional complexity.
+* Learning Curve: Requires a deeper understanding and careful design to implement effectively, increasing the initial learning curve.
+
+## Related Java Design Patterns
+
+* [Event Sourcing](https://java-design-patterns.com/patterns/event-sourcing/): Often used in conjunction with CQRS, where changes to the application state are stored as a sequence of events.
+* Domain-Driven Design (DDD): CQRS fits well within the DDD context, providing clear boundaries and separation of concerns.
+* [Repository](https://java-design-patterns.com/patterns/repository/): Can be used to abstract the data layer, providing a more seamless integration between the command and query sides.
+
+## References and Credits
+
+* [Implementing Domain-Driven Design](https://amzn.to/3TJN2HH)
+* [Microsoft .NET: Architecting Applications for the Enterprise](https://amzn.to/4aktRes)
+* [Patterns, Principles, and Practices of Domain-Driven Design](https://amzn.to/3vNV4Hm)
+* [CQRS, Task Based UIs, Event Sourcing agh! (Greg Young)](http://codebetter.com/gregyoung/2010/02/16/cqrs-task-based-uis-event-sourcing-agh/)
+* [CQRS (Martin Fowler)](https://martinfowler.com/bliki/CQRS.html)
+* [CQRS for Great Good (Oliver Wolf)](https://www.youtube.com/watch?v=Ge53swja9Dw)
+* [CQRS pattern (Microsoft)](https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs)
diff --git a/command-query-responsibility-segregation/etc/command-query-responsibility-segregation.urm.puml b/command-query-responsibility-segregation/etc/command-query-responsibility-segregation.urm.puml
new file mode 100644
index 000000000000..aef8eef5c1d2
--- /dev/null
+++ b/command-query-responsibility-segregation/etc/command-query-responsibility-segregation.urm.puml
@@ -0,0 +1,136 @@
+@startuml
+package com.iluwatar.cqrs.util {
+ class HibernateUtil {
+ - LOGGER : Logger {static}
+ - SESSIONFACTORY : SessionFactory {static}
+ + HibernateUtil()
+ - buildSessionFactory() : SessionFactory {static}
+ + getSessionFactory() : SessionFactory {static}
+ }
+}
+package com.iluwatar.cqrs.app {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+}
+package com.iluwatar.cqrs.dto {
+ class Author {
+ - email : String
+ - name : String
+ - username : String
+ + Author()
+ + Author(name : String, email : String, username : String)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getEmail() : String
+ + getName() : String
+ + getUsername() : String
+ + hashCode() : int
+ + toString() : String
+ }
+ class Book {
+ - price : double
+ - title : String
+ + Book()
+ + Book(title : String, price : double)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getPrice() : double
+ + getTitle() : String
+ + hashCode() : int
+ + toString() : String
+ }
+}
+package com.iluwatar.cqrs.commandes {
+ interface CommandService {
+ + authorCreated(String, String, String) {abstract}
+ + authorEmailUpdated(String, String) {abstract}
+ + authorNameUpdated(String, String) {abstract}
+ + authorUsernameUpdated(String, String) {abstract}
+ + bookAddedToAuthor(String, double, String) {abstract}
+ + bookPriceUpdated(String, double) {abstract}
+ + bookTitleUpdated(String, String) {abstract}
+ }
+ class CommandServiceImpl {
+ - sessionFactory : SessionFactory
+ + CommandServiceImpl()
+ + authorCreated(username : String, name : String, email : String)
+ + authorEmailUpdated(username : String, email : String)
+ + authorNameUpdated(username : String, name : String)
+ + authorUsernameUpdated(oldUsername : String, newUsername : String)
+ + bookAddedToAuthor(title : String, price : double, username : String)
+ + bookPriceUpdated(title : String, price : double)
+ + bookTitleUpdated(oldTitle : String, newTitle : String)
+ - getAuthorByUsername(username : String) : Author
+ - getBookByTitle(title : String) : Book
+ }
+}
+package com.iluwatar.cqrs.queries {
+ interface QueryService {
+ + getAuthorBooks(String) : List {abstract}
+ + getAuthorBooksCount(String) : BigInteger {abstract}
+ + getAuthorByUsername(String) : Author {abstract}
+ + getAuthorsCount() : BigInteger {abstract}
+ + getBook(String) : Book {abstract}
+ }
+ class QueryServiceImpl {
+ - sessionFactory : SessionFactory
+ + QueryServiceImpl()
+ + getAuthorBooks(username : String) : List
+ + getAuthorBooksCount(username : String) : BigInteger
+ + getAuthorByUsername(username : String) : Author
+ + getAuthorsCount() : BigInteger
+ + getBook(title : String) : Book
+ }
+}
+package com.iluwatar.cqrs.constants {
+ class AppConstants {
+ + E_EVANS : String {static}
+ + J_BLOCH : String {static}
+ + M_FOWLER : String {static}
+ + USER_NAME : String {static}
+ + AppConstants()
+ }
+}
+package com.iluwatar.cqrs.domain.model {
+ class Author {
+ - email : String
+ - id : long
+ - name : String
+ - username : String
+ # Author()
+ + Author(username : String, name : String, email : String)
+ + getEmail() : String
+ + getId() : long
+ + getName() : String
+ + getUsername() : String
+ + setEmail(email : String)
+ + setId(id : long)
+ + setName(name : String)
+ + setUsername(username : String)
+ + toString() : String
+ }
+ class Book {
+ - author : Author
+ - id : long
+ - price : double
+ - title : String
+ # Book()
+ + Book(title : String, price : double, author : Author)
+ + getAuthor() : Author
+ + getId() : long
+ + getPrice() : double
+ + getTitle() : String
+ + setAuthor(author : Author)
+ + setId(id : long)
+ + setPrice(price : double)
+ + setTitle(title : String)
+ + toString() : String
+ }
+}
+Book --> "-author" Author
+CommandServiceImpl ..|> CommandService
+QueryServiceImpl ..|> QueryService
+@enduml
\ No newline at end of file
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/cqrs/etc/cqrs.png b/command-query-responsibility-segregation/etc/cqrs.png
similarity index 100%
rename from cqrs/etc/cqrs.png
rename to command-query-responsibility-segregation/etc/cqrs.png
diff --git a/cqrs/etc/cqrs.ucls b/command-query-responsibility-segregation/etc/cqrs.ucls
similarity index 100%
rename from cqrs/etc/cqrs.ucls
rename to command-query-responsibility-segregation/etc/cqrs.ucls
diff --git a/cqrs/etc/cqrs.urm.puml b/command-query-responsibility-segregation/etc/cqrs.urm.puml
similarity index 100%
rename from cqrs/etc/cqrs.urm.puml
rename to command-query-responsibility-segregation/etc/cqrs.urm.puml
diff --git a/command-query-responsibility-segregation/pom.xml b/command-query-responsibility-segregation/pom.xml
new file mode 100644
index 000000000000..c3c277dd0b80
--- /dev/null
+++ b/command-query-responsibility-segregation/pom.xml
@@ -0,0 +1,89 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ command-query-responsibility-segregation
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ com.h2database
+ h2
+
+
+ org.hibernate
+ hibernate-core
+ 5.6.15.Final
+
+
+ org.glassfish.jaxb
+ jaxb-runtime
+ 2.3.3
+
+
+ javax.xml.bind
+ jaxb-api
+ 2.3.1
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.cqrs.app.App
+
+
+
+
+
+
+
+
+
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
new file mode 100644
index 000000000000..62317638955c
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/app/App.java
@@ -0,0 +1,89 @@
+/*
+ * 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.cqrs.app;
+
+import com.iluwatar.cqrs.commandes.CommandServiceImpl;
+import com.iluwatar.cqrs.constants.AppConstants;
+import com.iluwatar.cqrs.queries.QueryServiceImpl;
+import com.iluwatar.cqrs.util.HibernateUtil;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * CQRS : Command Query Responsibility Segregation. A pattern used to separate query services from
+ * commands or writes services. The pattern is very simple, but it has many consequences. For
+ * example, it can be used to tackle down a complex domain, or to use other architectures that were
+ * hard to implement with the classical way.
+ *
+ * This implementation is an example of managing books and authors in a library. The persistence
+ * of books and authors is done according to the CQRS architecture. A command side that deals with a
+ * data model to persist(insert,update,delete) objects to a database. And a query side that uses
+ * native queries to get data from the database and return objects as DTOs (Data transfer Objects).
+ */
+@Slf4j
+public class App {
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+
+ // Create Authors and Books using CommandService
+ var commands = new CommandServiceImpl();
+
+ commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
+ commands.authorCreated(AppConstants.J_BLOCH, "Joshua Bloch", "jBloch@email.com");
+ commands.authorCreated(AppConstants.M_FOWLER, "Martin Fowler", "mFowler@email.com");
+
+ commands.bookAddedToAuthor("Domain-Driven Design", 60.08, AppConstants.E_EVANS);
+ 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("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
+ commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
+
+ // Query the database using QueryService
+ var queries = new QueryServiceImpl();
+
+ var nullAuthor = queries.getAuthorByUsername("username");
+ var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
+ var blochBooksCount = queries.getAuthorBooksCount(AppConstants.J_BLOCH);
+ var authorsCount = queries.getAuthorsCount();
+ var dddBook = queries.getBook("Domain-Driven Design");
+ var blochBooks = queries.getAuthorBooks(AppConstants.J_BLOCH);
+
+ LOGGER.info("Author username : {}", nullAuthor);
+ LOGGER.info("Author evans : {}", evans);
+ LOGGER.info("jBloch number of books : {}", blochBooksCount);
+ LOGGER.info("Number of authors : {}", authorsCount);
+ LOGGER.info("DDD book : {}", dddBook);
+ LOGGER.info("jBloch books : {}", blochBooks);
+
+ 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
new file mode 100644
index 000000000000..9cf8c52cbb09
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandService.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.cqrs.commandes;
+
+/** This interface represents the commands of the CQRS pattern. */
+public interface CommandService {
+
+ void authorCreated(String username, String name, String email);
+
+ void bookAddedToAuthor(String title, double price, String username);
+
+ void authorNameUpdated(String username, String name);
+
+ void authorUsernameUpdated(String oldUsername, String newUsername);
+
+ void authorEmailUpdated(String username, String email);
+
+ void bookTitleUpdated(String oldTitle, String newTitle);
+
+ void bookPriceUpdated(String title, double price);
+}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
similarity index 93%
rename from cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
rename to command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
index a25d015c7bfc..b4a368c98319 100644
--- a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/commandes/CommandServiceImpl.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.cqrs.commandes;
import com.iluwatar.cqrs.domain.model.Author;
@@ -29,10 +30,10 @@
import org.hibernate.SessionFactory;
/**
- * This class is an implementation of {@link ICommandService} interface. It uses Hibernate as an api
+ * This class is an implementation of {@link CommandService} interface. It uses Hibernate as an api
* for persistence.
*/
-public class CommandServiceImpl implements ICommandService {
+public class CommandServiceImpl implements CommandService {
private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
@@ -139,5 +140,4 @@ public void bookPriceUpdated(String title, double price) {
session.getTransaction().commit();
}
}
-
}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
similarity index 85%
rename from cqrs/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
rename to command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
index 0b4aae4d8c4f..71d266f43c35 100644
--- a/cqrs/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/constants/AppConstants.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,17 +22,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
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
new file mode 100644
index 000000000000..03155d67a30b
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Author.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.cqrs.domain.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/** This is an Author entity. It is used by Hibernate for persistence. */
+@ToString
+@Getter
+@Setter
+@Entity
+public class Author {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ private String username;
+ private String name;
+ private String email;
+
+ /**
+ * Constructor.
+ *
+ * @param username username 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;
+ this.name = name;
+ this.email = email;
+ }
+
+ 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
new file mode 100644
index 000000000000..2e2c356528e9
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
@@ -0,0 +1,67 @@
+/*
+ * 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.cqrs.domain.model;
+
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.ManyToOne;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * This is a Book entity. It is used by Hibernate for persistence. Many books can be written by one
+ * {@link Author}
+ */
+@ToString
+@Setter
+@Getter
+@Entity
+public class Book {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ private String title;
+ private double price;
+ @ManyToOne private Author author;
+
+ /**
+ * Constructor.
+ *
+ * @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) {
+ this.title = title;
+ this.price = price;
+ this.author = author;
+ }
+
+ 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
new file mode 100644
index 000000000000..58074e6dafe2
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Author.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cqrs.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+/** This is a DTO (Data Transfer Object) author, contains only useful information to be returned. */
+@ToString
+@EqualsAndHashCode
+@Getter
+@NoArgsConstructor
+@AllArgsConstructor
+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
new file mode 100644
index 000000000000..72ce5b8c249b
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/dto/Book.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.cqrs.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.ToString;
+
+/** This is a DTO (Data Transfer Object) book, contains only useful information to be returned. */
+@ToString
+@EqualsAndHashCode
+@Getter
+@AllArgsConstructor
+@NoArgsConstructor
+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
new file mode 100644
index 000000000000..b37c1dad05a9
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryService.java
@@ -0,0 +1,44 @@
+/*
+ * 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.cqrs.queries;
+
+import com.iluwatar.cqrs.dto.Author;
+import com.iluwatar.cqrs.dto.Book;
+import java.math.BigInteger;
+import java.util.List;
+
+/** This interface represents the query methods of the CQRS pattern. */
+public interface QueryService {
+
+ Author getAuthorByUsername(String username);
+
+ Book getBook(String title);
+
+ List getAuthorBooks(String username);
+
+ 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
new file mode 100644
index 000000000000..60847d1c6db6
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
@@ -0,0 +1,111 @@
+/*
+ * 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.cqrs.queries;
+
+import com.iluwatar.cqrs.constants.AppConstants;
+import com.iluwatar.cqrs.dto.Author;
+import com.iluwatar.cqrs.dto.Book;
+import com.iluwatar.cqrs.util.HibernateUtil;
+import java.math.BigInteger;
+import java.util.List;
+import org.hibernate.SessionFactory;
+import org.hibernate.query.Query;
+
+/**
+ * This class is an implementation of {@link QueryService}. It uses Hibernate native queries to
+ * return DTOs from the database.
+ */
+public class QueryServiceImpl implements QueryService {
+
+ private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
+
+ @Override
+ public Author getAuthorByUsername(String username) {
+ Author authorDto;
+ try (var session = sessionFactory.openSession()) {
+ 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");
+ sqlQuery.setParameter(AppConstants.USER_NAME, username);
+ authorDto = sqlQuery.uniqueResult();
+ }
+ return authorDto;
+ }
+
+ @Override
+ public Book getBook(String title) {
+ Book bookDto;
+ try (var session = sessionFactory.openSession()) {
+ 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");
+ sqlQuery.setParameter("title", title);
+ bookDto = sqlQuery.uniqueResult();
+ }
+ return bookDto;
+ }
+
+ @Override
+ public List getAuthorBooks(String username) {
+ List bookDtos;
+ try (var session = sessionFactory.openSession()) {
+ 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");
+ sqlQuery.setParameter(AppConstants.USER_NAME, username);
+ bookDtos = sqlQuery.list();
+ }
+ return bookDtos;
+ }
+
+ @Override
+ 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");
+ sqlQuery.setParameter(AppConstants.USER_NAME, username);
+ bookcount = (BigInteger) sqlQuery.uniqueResult();
+ }
+ return bookcount;
+ }
+
+ @Override
+ public BigInteger getAuthorsCount() {
+ BigInteger authorcount;
+ try (var session = sessionFactory.openSession()) {
+ var sqlQuery = session.createNativeQuery("SELECT count(id) from Author");
+ authorcount = (BigInteger) sqlQuery.uniqueResult();
+ }
+ 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
new file mode 100644
index 000000000000..0b777fc59b1f
--- /dev/null
+++ b/command-query-responsibility-segregation/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
@@ -0,0 +1,57 @@
+/*
+ * 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.cqrs.util;
+
+import lombok.extern.slf4j.Slf4j;
+import org.hibernate.SessionFactory;
+import org.hibernate.boot.MetadataSources;
+import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
+
+/**
+ * This class simply returns one instance of {@link SessionFactory} initialized when the application
+ * is started.
+ */
+@Slf4j
+public class HibernateUtil {
+
+ private static final SessionFactory SESSIONFACTORY = buildSessionFactory();
+
+ private static SessionFactory buildSessionFactory() {
+
+ // configures settings from hibernate.cfg.xml
+ final var registry = new StandardServiceRegistryBuilder().configure().build();
+ try {
+ return new MetadataSources(registry).buildMetadata().buildSessionFactory();
+ } catch (Exception ex) {
+ StandardServiceRegistryBuilder.destroy(registry);
+ LOGGER.error("Initial SessionFactory creation failed.", ex);
+ throw new ExceptionInInitializerError(ex);
+ }
+ }
+
+ public static SessionFactory getSessionFactory() {
+ return SESSIONFACTORY;
+ }
+}
diff --git a/cqrs/src/main/resources/hibernate.cfg.xml b/command-query-responsibility-segregation/src/main/resources/hibernate.cfg.xml
similarity index 100%
rename from cqrs/src/main/resources/hibernate.cfg.xml
rename to command-query-responsibility-segregation/src/main/resources/hibernate.cfg.xml
diff --git a/cqrs/src/main/resources/logback.xml b/command-query-responsibility-segregation/src/main/resources/logback.xml
similarity index 100%
rename from cqrs/src/main/resources/logback.xml
rename to command-query-responsibility-segregation/src/main/resources/logback.xml
diff --git a/cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java b/command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
similarity index 91%
rename from cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
rename to command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
index 51c182df6d89..a4fb9ed3bb7a 100644
--- a/cqrs/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
+++ b/command-query-responsibility-segregation/src/test/java/com/iluwatar/cqrs/IntegrationTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.cqrs;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -29,18 +30,16 @@
import com.iluwatar.cqrs.commandes.CommandServiceImpl;
import com.iluwatar.cqrs.dto.Author;
import com.iluwatar.cqrs.dto.Book;
-import com.iluwatar.cqrs.queries.IQueryService;
+import com.iluwatar.cqrs.queries.QueryService;
import com.iluwatar.cqrs.queries.QueryServiceImpl;
import java.math.BigInteger;
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 IQueryService queryService;
+ private static QueryService queryService;
@BeforeAll
static void initializeAndPopulateDatabase() {
@@ -63,7 +62,6 @@ static void initializeAndPopulateDatabase() {
commandService.bookAddedToAuthor("title2", 20, "username1");
commandService.bookPriceUpdated("title2", 30);
commandService.bookTitleUpdated("title2", "new_title2");
-
}
@Test
@@ -79,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
@@ -108,5 +105,4 @@ void testGetAuthorsCount() {
var authorCount = queryService.getAuthorsCount();
assertEquals(new BigInteger("2"), authorCount);
}
-
}
diff --git a/cqrs/src/test/resources/hibernate.cfg.xml b/command-query-responsibility-segregation/src/test/resources/hibernate.cfg.xml
similarity index 100%
rename from cqrs/src/test/resources/hibernate.cfg.xml
rename to command-query-responsibility-segregation/src/test/resources/hibernate.cfg.xml
diff --git a/cqrs/src/test/resources/logback.xml b/command-query-responsibility-segregation/src/test/resources/logback.xml
similarity index 100%
rename from cqrs/src/test/resources/logback.xml
rename to command-query-responsibility-segregation/src/test/resources/logback.xml
diff --git a/command/README.md b/command/README.md
index 50e1ea47d1c2..761267c04019 100644
--- a/command/README.md
+++ b/command/README.md
@@ -1,29 +1,30 @@
---
-layout: pattern
-title: Command
-folder: command
-permalink: /patterns/command/
-categories: Behavioral
+title: "Command Pattern in Java: Empowering Flexible Command Execution"
+shortTitle: Command
+description: "Learn about the Command design pattern in Java with real-world examples, detailed explanations, and practical use cases. Understand how this pattern encapsulates requests as objects to support undo operations and more."
+category: Behavioral
language: en
-tags:
- - Gang of Four
+tag:
+ - Decoupling
+ - Extensibility
+ - Gang of Four
+ - Undo
---
## Also known as
-Action, Transaction
+* Action
+* Transaction
-## Intent
+## Intent of Command Design Pattern
-Encapsulate a request as an object, thereby letting you parameterize clients with different
-requests, queue or log requests, and support undoable operations.
+The Command design pattern is a behavioral pattern used in Java programming. It encapsulates a request as an object, allowing for parameterization of clients with queues, requests, and operations. This pattern also supports undoable operations, enhancing flexibility in managing and executing commands.
+
+## Detailed Explanation of Command Pattern with Real-World Examples
-## Explanation
Real-world example
-> There is a wizard casting spells on a goblin. The spells are executed on the goblin one by one.
-> The first spell shrinks the goblin and the second makes him invisible. Then the wizard reverses
-> the spells one by one. Each spell here is a command object that can be undone.
+> Imagine a smart home system where you can control devices such as lights, thermostats, and security cameras through a central application. Each command to operate these devices is encapsulated as an object, enabling the system to queue, execute sequentially, and undo commands if necessary. This approach decouples control logic from device implementation, allowing easy addition of new devices or features without altering the core application. This flexibility and functionality illustrate the practical application of the Command design pattern in Java programming.
In plain words
@@ -31,209 +32,192 @@ In plain words
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.
+> 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.
-**Programmatic Example**
+In our example, a `Wizard` casts spells on a `Goblin`. Each spell is a command object that can be executed and undone, demonstrating the core principles of the Command pattern in Java. The spells are executed on the goblin one by one. The first spell shrinks the goblin and the second makes him invisible. Then the wizard reverses the spells one by one. Each spell here is a command object that can be undone.
-Here's the sample code with wizard and goblin. Let's start from the `Wizard` class.
+Let's start from the `Wizard` class.
```java
+
@Slf4j
public class Wizard {
- private final Deque undoStack = new LinkedList<>();
- private final Deque redoStack = new LinkedList<>();
+ private final Deque undoStack = new LinkedList<>();
+ private final Deque redoStack = new LinkedList<>();
- public Wizard() {}
+ public Wizard() {
+ }
- public void castSpell(Runnable runnable) {
- runnable.run();
- undoStack.offerLast(runnable);
- }
+ public void castSpell(Runnable runnable) {
+ runnable.run();
+ undoStack.offerLast(runnable);
+ }
- public void undoLastSpell() {
- if (!undoStack.isEmpty()) {
- var previousSpell = undoStack.pollLast();
- redoStack.offerLast(previousSpell);
- previousSpell.run();
+ public void undoLastSpell() {
+ if (!undoStack.isEmpty()) {
+ var previousSpell = undoStack.pollLast();
+ redoStack.offerLast(previousSpell);
+ previousSpell.run();
+ }
}
- }
- public void redoLastSpell() {
- if (!redoStack.isEmpty()) {
- var previousSpell = redoStack.pollLast();
- undoStack.offerLast(previousSpell);
- previousSpell.run();
+ public void redoLastSpell() {
+ if (!redoStack.isEmpty()) {
+ var previousSpell = redoStack.pollLast();
+ undoStack.offerLast(previousSpell);
+ previousSpell.run();
+ }
}
- }
- @Override
- public String toString() {
- return "Wizard";
- }
+ @Override
+ public String toString() {
+ return "Wizard";
+ }
}
```
-Next, we have the goblin who's the target of the spells.
+Next, we have the `Goblin` who's the `Target` of the spells.
```java
@Slf4j
+@Getter
+@Setter
public abstract class Target {
- private Size size;
-
- private Visibility visibility;
+ private Size size;
- public Size getSize() {
- return size;
- }
+ private Visibility visibility;
- public void setSize(Size size) {
- this.size = size;
- }
-
- public Visibility getVisibility() {
- return visibility;
- }
-
- public void setVisibility(Visibility visibility) {
- this.visibility = visibility;
- }
+ public void printStatus() {
+ LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
+ }
- @Override
- public abstract String toString();
+ public void changeSize() {
+ var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
+ setSize(oldSize);
+ }
- public void printStatus() {
- LOGGER.info("{}, [size={}] [visibility={}]", this, getSize(), getVisibility());
- }
+ public void changeVisibility() {
+ var visible = getVisibility() == Visibility.INVISIBLE
+ ? Visibility.VISIBLE : Visibility.INVISIBLE;
+ setVisibility(visible);
+ }
}
+```
+```java
public class Goblin extends Target {
- public Goblin() {
- setSize(Size.NORMAL);
- setVisibility(Visibility.VISIBLE);
- }
-
- @Override
- public String toString() {
- return "Goblin";
- }
-
- public void changeSize() {
- var oldSize = getSize() == Size.NORMAL ? Size.SMALL : Size.NORMAL;
- setSize(oldSize);
- }
-
- public void changeVisibility() {
- var visible = getVisibility() == Visibility.INVISIBLE
- ? Visibility.VISIBLE : Visibility.INVISIBLE;
- setVisibility(visible);
- }
+ public Goblin() {
+ setSize(Size.NORMAL);
+ setVisibility(Visibility.VISIBLE);
+ }
+
+ @Override
+ public String toString() {
+ return "Goblin";
+ }
}
```
-Finally, we have the wizard in the main function casting spells.
+Finally, we can show the full example of `Wizard` casting spells.
```java
public static void main(String[] args) {
- var wizard = new Wizard();
- var goblin = new Goblin();
-
- // casts shrink/unshrink spell
- wizard.castSpell(goblin::changeSize);
+ var wizard = new Wizard();
+ var goblin = new Goblin();
- // casts visible/invisible spell
- wizard.castSpell(goblin::changeVisibility);
+ goblin.printStatus();
- // undo and redo casts
- wizard.undoLastSpell();
- wizard.redoLastSpell();
-```
+ wizard.castSpell(goblin::changeSize);
+ goblin.printStatus();
-Here's the whole example in action.
-
-```java
-var wizard = new Wizard();
-var goblin = new Goblin();
+ wizard.castSpell(goblin::changeVisibility);
+ goblin.printStatus();
-goblin.printStatus();
-wizard.castSpell(goblin::changeSize);
-goblin.printStatus();
+ wizard.undoLastSpell();
+ goblin.printStatus();
-wizard.castSpell(goblin::changeVisibility);
-goblin.printStatus();
+ wizard.undoLastSpell();
+ goblin.printStatus();
-wizard.undoLastSpell();
-goblin.printStatus();
+ wizard.redoLastSpell();
+ goblin.printStatus();
-wizard.undoLastSpell();
-goblin.printStatus();
-
-wizard.redoLastSpell();
-goblin.printStatus();
-
-wizard.redoLastSpell();
-goblin.printStatus();
+ wizard.redoLastSpell();
+ goblin.printStatus();
+}
```
Here's the program output:
-```java
-Goblin, [size=normal] [visibility=visible]
-Goblin, [size=small] [visibility=visible]
-Goblin, [size=small] [visibility=invisible]
-Goblin, [size=small] [visibility=visible]
-Goblin, [size=normal] [visibility=visible]
-Goblin, [size=small] [visibility=visible]
-Goblin, [size=small] [visibility=invisible]
+```
+20:13:38.406 [main] INFO com.iluwatar.command.Target -- Goblin, [size=normal] [visibility=visible]
+20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=visible]
+20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=invisible]
+20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=visible]
+20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=normal] [visibility=visible]
+20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=visible]
+20:13:38.409 [main] INFO com.iluwatar.command.Target -- Goblin, [size=small] [visibility=invisible]
```
-## Class diagram
-
-
+## When to Use the Command Pattern in Java
-## Applicability
+The Command design pattern is applicable when you need to parameterize objects with actions, support undo operations, or structure a system around high-level operations built on primitive ones. It is commonly used in GUI buttons, database transactions, and macro recording.
Use the Command pattern when you want to:
-* Parameterize objects by an action to perform. You can express such parameterization in a
-procedural language with a callback function, that is, a function that's registered somewhere to be
-called at a later point. Commands are an object-oriented replacement for callbacks.
-* Specify, queue, and execute requests at different times. A Command object can have a life
-independent of the original request. If the receiver of a request can be represented in an address
-space-independent way, then you can transfer a command object for the request to a different process
-and fulfill the request there.
-* Support undo. The Command's execute operation can store state for reversing its effects in the
-command itself. The Command interface must have an added un-execute operation that reverses the
-effects of a previous call to execute. The executed commands are stored in a history list.
-Unlimited-level undo and redo functionality is achieved by traversing this list backward and forward
- calling un-execute and execute, respectively.
-* Support logging changes so that they can be reapplied in case of a system crash. By augmenting the
-Command interface with load and store operations, you can keep a persistent log of changes.
-Recovering from a crash involves reloading logged commands from the disk and re-executing them with
-the execute operation.
-* Structure a system around high-level operations build on primitive operations. Such a structure is
-common in information systems that support transactions. A transaction encapsulates a set of data
-changes. The Command pattern offers a way to model transactions. Commands have a common interface,
-letting you invoke all transactions the same way. The pattern also makes it easy to extend the
-system with new transactions.
+* Parameterize objects with actions to perform, offering an object-oriented alternative to callbacks found in procedural languages. Commands can be registered and executed later.
+* Specify, queue, and execute requests at different times, allowing commands to exist independently of the original request and even be transferred across processes.
+* Support undo functionality, where the Command’s execute operation stores state and includes an un-execute operation to reverse previous actions. This allows for unlimited undo and redo capabilities by maintaining a history list.
+* Log changes to reapply them after a system crash. By adding load and store operations to the Command interface, you can maintain a persistent log of changes and recover by reloading and re-executing commands from this log.
+* Structure a system around high-level operations built on primitive operations, which is common in transaction-based systems. The Command pattern models transactions by providing a common interface for invoking and extending operations.
* Keep a history of requests.
* Implement callback functionality.
-* Implement the undo functionality.
+* Implement undo functionality.
-## Known uses
+## Real-World Applications of Command Pattern in Java
+* GUI Buttons and menu items in desktop applications.
+* Operations in database systems and transactional systems that support rollback.
+* Macro recording in applications like text editors and spreadsheets.
* [java.lang.Runnable](http://docs.oracle.com/javase/8/docs/api/java/lang/Runnable.html)
* [org.junit.runners.model.Statement](https://github.com/junit-team/junit4/blob/master/src/main/java/org/junit/runners/model/Statement.java)
* [Netflix Hystrix](https://github.com/Netflix/Hystrix/wiki)
* [javax.swing.Action](http://docs.oracle.com/javase/8/docs/api/javax/swing/Action.html)
-## Credits
+## Benefits and Trade-offs of Command Pattern
+
+Benefits:
+
+* Decouples the object that invokes the operation from the one that knows how to perform it.
+* It's easy to add new Commands, because you don't have to change existing classes.
+* You can assemble a set of commands into a composite command.
+
+Trade-offs:
+
+* Increases the number of classes for each individual command.
+* Can complicate the design by adding multiple layers between senders and receivers.
+
+## Related Java Design Patterns
+
+* [Composite](https://java-design-patterns.com/patterns/composite/): Commands can be composed using the Composite pattern to create macro commands.
+* [Memento](https://java-design-patterns.com/patterns/memento/): Can be used for implementing undo mechanisms.
+* [Observer](https://java-design-patterns.com/patterns/observer/): The pattern can be observed for changes that trigger commands.
+
+## References and Credits
-* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
-* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
-* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=f27d2644fbe5026ea448791a8ad09c94)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3PFUqSY)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
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 a8bfd44e2fd0..83fb68cbf8e3 100644
--- a/command/pom.xml
+++ b/command/pom.xml
@@ -1,8 +1,10 @@
"-inputComponent" InputComponent
+GameObject --> "-graphicComponent" GraphicComponent
+GameObject --> "-physicComponent" PhysicComponent
+ObjectGraphicComponent ..|> GraphicComponent
+DemoInputComponent ..|> InputComponent
+PlayerInputComponent ..|> InputComponent
+ObjectPhysicComponent ..|> PhysicComponent
+@enduml
\ No newline at end of file
diff --git a/component/pom.xml b/component/pom.xml
new file mode 100644
index 000000000000..e666e283489b
--- /dev/null
+++ b/component/pom.xml
@@ -0,0 +1,73 @@
+
+
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+
+ component
+
+
+ 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.component.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/component/src/main/java/com/iluwatar/component/App.java b/component/src/main/java/com/iluwatar/component/App.java
new file mode 100644
index 000000000000..9401483e92e2
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/App.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.component;
+
+import java.awt.event.KeyEvent;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * The component design pattern is a common game design structure. This pattern is often used to
+ * reduce duplication of code as well as to improve maintainability. In this implementation,
+ * component design pattern has been used to provide two game objects with varying component
+ * interfaces (features). As opposed to copying and pasting same code for the two game objects, the
+ * component interfaces allow game objects to inherit these components from the component classes.
+ *
+ * The implementation has decoupled graphic, physics and input components from the player and NPC
+ * objects. As a result, it avoids the creation of monolithic java classes.
+ *
+ *
The below example in this App class demonstrates the use of the component interfaces for
+ * separate objects (player & NPC) and updating of these components as per the implementations in
+ * GameObject class and the component classes.
+ */
+@Slf4j
+public final class App {
+ /**
+ * Program entry point.
+ *
+ * @param args args command line args.
+ */
+ public static void main(String[] args) {
+ final var player = GameObject.createPlayer();
+ final var npc = GameObject.createNpc();
+
+ LOGGER.info("Player Update:");
+ player.update(KeyEvent.KEY_LOCATION_LEFT);
+ LOGGER.info("NPC Update:");
+ npc.demoUpdate();
+ }
+}
diff --git a/component/src/main/java/com/iluwatar/component/GameObject.java b/component/src/main/java/com/iluwatar/component/GameObject.java
new file mode 100644
index 000000000000..87c35b24e8c4
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/GameObject.java
@@ -0,0 +1,111 @@
+/*
+ * 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.component;
+
+import com.iluwatar.component.component.graphiccomponent.GraphicComponent;
+import com.iluwatar.component.component.graphiccomponent.ObjectGraphicComponent;
+import com.iluwatar.component.component.inputcomponent.DemoInputComponent;
+import com.iluwatar.component.component.inputcomponent.InputComponent;
+import com.iluwatar.component.component.inputcomponent.PlayerInputComponent;
+import com.iluwatar.component.component.physiccomponent.ObjectPhysicComponent;
+import com.iluwatar.component.component.physiccomponent.PhysicComponent;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+
+/**
+ * The GameObject class has three component class instances that allow the creation of different
+ * game objects based on the game design requirements.
+ */
+@Getter
+@RequiredArgsConstructor
+public class GameObject {
+ private final InputComponent inputComponent;
+ private final PhysicComponent physicComponent;
+ private final GraphicComponent graphicComponent;
+
+ private final String name;
+ private int velocity = 0;
+ private int coordinate = 0;
+
+ /**
+ * Creates a player game object.
+ *
+ * @return player object
+ */
+ public static GameObject createPlayer() {
+ return new GameObject(
+ new PlayerInputComponent(),
+ new ObjectPhysicComponent(),
+ new ObjectGraphicComponent(),
+ "player");
+ }
+
+ /**
+ * Creates a NPC game object.
+ *
+ * @return npc object
+ */
+ public static GameObject createNpc() {
+ return new GameObject(
+ new DemoInputComponent(), new ObjectPhysicComponent(), new ObjectGraphicComponent(), "npc");
+ }
+
+ /**
+ * Updates the three components of the NPC object used in the demo in App.java note that this is
+ * simply a duplicate of update() without the key event for demonstration purposes.
+ *
+ *
This method is usually used in games if the player becomes inactive.
+ */
+ public void demoUpdate() {
+ inputComponent.update(this, 0);
+ physicComponent.update(this);
+ graphicComponent.update(this);
+ }
+
+ /**
+ * Updates the three components for objects based on key events.
+ *
+ * @param e key event from the player.
+ */
+ public void update(int e) {
+ inputComponent.update(this, e);
+ physicComponent.update(this);
+ graphicComponent.update(this);
+ }
+
+ /**
+ * Update the velocity based on the acceleration of the GameObject.
+ *
+ * @param acceleration the acceleration of the GameObject
+ */
+ public void updateVelocity(int acceleration) {
+ this.velocity += acceleration;
+ }
+
+ /** Set the c based on the current velocity. */
+ public void updateCoordinate() {
+ this.coordinate += this.velocity;
+ }
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/graphiccomponent/GraphicComponent.java b/component/src/main/java/com/iluwatar/component/component/graphiccomponent/GraphicComponent.java
new file mode 100644
index 000000000000..600ee8e5275d
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/graphiccomponent/GraphicComponent.java
@@ -0,0 +1,32 @@
+/*
+ * 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.component.component.graphiccomponent;
+
+import com.iluwatar.component.GameObject;
+
+/** Generic GraphicComponent interface. */
+public interface GraphicComponent {
+ void update(GameObject gameObject);
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/graphiccomponent/ObjectGraphicComponent.java b/component/src/main/java/com/iluwatar/component/component/graphiccomponent/ObjectGraphicComponent.java
new file mode 100644
index 000000000000..7f595763f10c
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/graphiccomponent/ObjectGraphicComponent.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.component.component.graphiccomponent;
+
+import com.iluwatar.component.GameObject;
+import lombok.extern.slf4j.Slf4j;
+
+/** ObjectGraphicComponent class mimics the graphic component of the Game Object. */
+@Slf4j
+public class ObjectGraphicComponent implements GraphicComponent {
+
+ /**
+ * The method updates the graphics based on the velocity of gameObject.
+ *
+ * @param gameObject the gameObject instance
+ */
+ @Override
+ public void update(GameObject gameObject) {
+ LOGGER.info(gameObject.getName() + "'s current velocity: " + gameObject.getVelocity());
+ }
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/inputcomponent/DemoInputComponent.java b/component/src/main/java/com/iluwatar/component/component/inputcomponent/DemoInputComponent.java
new file mode 100644
index 000000000000..b7ff51c3f2e7
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/inputcomponent/DemoInputComponent.java
@@ -0,0 +1,52 @@
+/*
+ * 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.component.component.inputcomponent;
+
+import com.iluwatar.component.GameObject;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Take this component class to control player or the NPC for demo mode. and implemented the
+ * InputComponent interface.
+ *
+ *
Essentially, the demo mode is utilised during a game if the user become inactive. Please see:
+ * http://gameprogrammingpatterns.com/component.html
+ */
+@Slf4j
+public class DemoInputComponent implements InputComponent {
+ private static final int WALK_ACCELERATION = 2;
+
+ /**
+ * Redundant method in the demo mode.
+ *
+ * @param gameObject the gameObject instance
+ * @param e key event instance
+ */
+ @Override
+ public void update(GameObject gameObject, int e) {
+ gameObject.updateVelocity(WALK_ACCELERATION);
+ LOGGER.info(gameObject.getName() + " has moved right.");
+ }
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/inputcomponent/InputComponent.java b/component/src/main/java/com/iluwatar/component/component/inputcomponent/InputComponent.java
new file mode 100644
index 000000000000..65bab37fc59d
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/inputcomponent/InputComponent.java
@@ -0,0 +1,32 @@
+/*
+ * 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.component.component.inputcomponent;
+
+import com.iluwatar.component.GameObject;
+
+/** Generic InputComponent interface. */
+public interface InputComponent {
+ void update(GameObject gameObject, int e);
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/inputcomponent/PlayerInputComponent.java b/component/src/main/java/com/iluwatar/component/component/inputcomponent/PlayerInputComponent.java
new file mode 100644
index 000000000000..d38682de49f9
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/inputcomponent/PlayerInputComponent.java
@@ -0,0 +1,62 @@
+/*
+ * 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.component.component.inputcomponent;
+
+import com.iluwatar.component.GameObject;
+import java.awt.event.KeyEvent;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * PlayerInputComponent is used to handle user key event inputs, and thus it implements the
+ * InputComponent interface.
+ */
+@Slf4j
+public class PlayerInputComponent implements InputComponent {
+ private static final int WALK_ACCELERATION = 1;
+
+ /**
+ * The update method to change the velocity based on the input key event.
+ *
+ * @param gameObject the gameObject instance
+ * @param e key event instance
+ */
+ @Override
+ public void update(GameObject gameObject, int e) {
+ switch (e) {
+ case KeyEvent.KEY_LOCATION_LEFT -> {
+ gameObject.updateVelocity(-WALK_ACCELERATION);
+ LOGGER.info(gameObject.getName() + " has moved left.");
+ }
+ case KeyEvent.KEY_LOCATION_RIGHT -> {
+ gameObject.updateVelocity(WALK_ACCELERATION);
+ LOGGER.info(gameObject.getName() + " has moved right.");
+ }
+ default -> {
+ LOGGER.info(gameObject.getName() + "'s velocity is unchanged due to the invalid input");
+ gameObject.updateVelocity(0);
+ } // incorrect input
+ }
+ }
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/physiccomponent/ObjectPhysicComponent.java b/component/src/main/java/com/iluwatar/component/component/physiccomponent/ObjectPhysicComponent.java
new file mode 100644
index 000000000000..a7c189efc463
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/physiccomponent/ObjectPhysicComponent.java
@@ -0,0 +1,44 @@
+/*
+ * 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.component.component.physiccomponent;
+
+import com.iluwatar.component.GameObject;
+import lombok.extern.slf4j.Slf4j;
+
+/** Take this component class to update the x coordinate for the Game Object instance. */
+@Slf4j
+public class ObjectPhysicComponent implements PhysicComponent {
+
+ /**
+ * The method update the horizontal (X-axis) coordinate based on the velocity of gameObject.
+ *
+ * @param gameObject the gameObject instance
+ */
+ @Override
+ public void update(GameObject gameObject) {
+ gameObject.updateCoordinate();
+ LOGGER.info(gameObject.getName() + "'s coordinate has been changed.");
+ }
+}
diff --git a/component/src/main/java/com/iluwatar/component/component/physiccomponent/PhysicComponent.java b/component/src/main/java/com/iluwatar/component/component/physiccomponent/PhysicComponent.java
new file mode 100644
index 000000000000..67c33026024d
--- /dev/null
+++ b/component/src/main/java/com/iluwatar/component/component/physiccomponent/PhysicComponent.java
@@ -0,0 +1,32 @@
+/*
+ * 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.component.component.physiccomponent;
+
+import com.iluwatar.component.GameObject;
+
+/** Generic PhysicComponent interface. */
+public interface PhysicComponent {
+ void update(GameObject gameObject);
+}
diff --git a/component/src/test/java/com/iluwatar/component/AppTest.java b/component/src/test/java/com/iluwatar/component/AppTest.java
new file mode 100644
index 000000000000..7de0745e7685
--- /dev/null
+++ b/component/src/test/java/com/iluwatar/component/AppTest.java
@@ -0,0 +1,41 @@
+/*
+ * 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.component;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/**
+ * Tests App class : src/main/java/com/iluwatar/component/App.java General execution test of the
+ * application.
+ */
+class AppTest {
+
+ @Test
+ void shouldExecuteComponentWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/component/src/test/java/com/iluwatar/component/GameObjectTest.java b/component/src/test/java/com/iluwatar/component/GameObjectTest.java
new file mode 100644
index 000000000000..09b3b3995268
--- /dev/null
+++ b/component/src/test/java/com/iluwatar/component/GameObjectTest.java
@@ -0,0 +1,87 @@
+/*
+ * 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.component;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.awt.event.KeyEvent;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** Tests GameObject class. src/main/java/com/iluwatar/component/GameObject.java */
+@Slf4j
+class GameObjectTest {
+ GameObject playerTest;
+ GameObject npcTest;
+
+ @BeforeEach
+ public void initEach() {
+ // creates player & npc objects for testing
+ // note that velocity and coordinates are initialised to 0 in GameObject.java
+ playerTest = GameObject.createPlayer();
+ npcTest = GameObject.createNpc();
+ }
+
+ /** Tests the create methods - createPlayer() and createNPC(). */
+ @Test
+ void objectTest() {
+ LOGGER.info("objectTest:");
+ assertEquals("player", playerTest.getName());
+ assertEquals("npc", npcTest.getName());
+ }
+
+ /** Tests the input component with varying key event inputs. Targets the player game object. */
+ @Test
+ void eventInputTest() {
+ LOGGER.info("eventInputTest:");
+ playerTest.update(KeyEvent.KEY_LOCATION_LEFT);
+ assertEquals(-1, playerTest.getVelocity());
+ assertEquals(-1, playerTest.getCoordinate());
+
+ playerTest.update(KeyEvent.KEY_LOCATION_RIGHT);
+ playerTest.update(KeyEvent.KEY_LOCATION_RIGHT);
+ assertEquals(1, playerTest.getVelocity());
+ assertEquals(0, playerTest.getCoordinate());
+
+ LOGGER.info(Integer.toString(playerTest.getCoordinate()));
+ LOGGER.info(Integer.toString(playerTest.getVelocity()));
+
+ GameObject p2 = GameObject.createPlayer();
+ p2.update(KeyEvent.KEY_LOCATION_LEFT);
+ // in the case of an unknown, object stats are set to default
+ p2.update(KeyEvent.KEY_LOCATION_UNKNOWN);
+ assertEquals(-1, p2.getVelocity());
+ }
+
+ /** Tests the demo component interface. */
+ @Test
+ void npcDemoTest() {
+ LOGGER.info("npcDemoTest:");
+ npcTest.demoUpdate();
+ assertEquals(2, npcTest.getVelocity());
+ assertEquals(2, npcTest.getCoordinate());
+ }
+}
diff --git a/composite-entity/README.md b/composite-entity/README.md
index 4409b4f6ac40..5946663a85ae 100644
--- a/composite-entity/README.md
+++ b/composite-entity/README.md
@@ -1,58 +1,76 @@
---
-layout: pattern
-title: Composite Entity
-folder: composite-entity
-permalink: /patterns/composite-entity/
-categories: Structural
+title: "Composite Entity Pattern in Java: Streamlining Persistent Object Management"
+shortTitle: Composite Entity
+description: "Learn about the Composite Entity design pattern in Java, a structural pattern used to manage interrelated persistent objects as a single entity. Ideal for enterprise applications and EJB, this pattern simplifies complex data structures and client interactions."
+category: Structural
language: en
-tags:
- - Enterprise Integration Pattern
+tag:
+ - Client-server
+ - Data access
+ - Decoupling
+ - Enterprise patterns
+ - Object composition
+ - Persistence
+ - Resource management
---
-## Intent
+## Also known as
-It is used to model, represent, and manage a set of persistent objects that are interrelated, rather than representing them as individual fine-grained entities.
+* Coarse-Grained Entity
-## Explanation
+## Intent of Composite Entity Design Pattern
-Real world example
+The Composite Entity design pattern in Java is aimed at managing a set of interrelated persistent objects as if they were a single entity. It is commonly used in enterprise applications, particularly within the context of Enterprise JavaBeans (EJB) and similar enterprise frameworks, to represent graph-based data structures within business models. This pattern enables clients to treat these complex structures as a single unit, simplifying operations and interactions.
-> For a console, there may be many interfaces that need to be managed and controlled. Using the composite entity pattern, dependent objects such as messages and signals can be combined together and controlled using a single object.
+## Detailed Explanation of Composite Entity Pattern with Real-World Examples
+
+Real-world example
+
+> Consider a university registration system where a "Student" entity is a composite entity. Each "Student" object includes several dependent objects: personal details, course enrollments, grades, and payment information. Instead of managing each of these aspects separately, the Composite Entity design pattern allows the university system to treat the "Student" as a single entity. This simplifies operations such as enrolling a student in a new course, updating grades, and processing payments, since all related actions can be managed through the composite "Student" object.
In plain words
-> Composite entity pattern allows a set of related objects to be represented and managed by a unified object.
+> The Composite Entity pattern in Java allows a set of related persistent objects to be represented and managed by a unified object, simplifying enterprise application design.
+
+Wikipedia says
+
+> Composite entity is a Java EE Software design pattern and it is used to model, represent, and manage a set of interrelated persistent objects rather than representing them as individual fine-grained entity beans, and also a composite entity bean represents a graph of objects.
+
+Flowchart
-**Programmatic Example**
+
-We need a generic solution for the problem. To achieve this, let's introduce a generic
-Composite Entity Pattern.
+## Programmatic Example of Composite Entity in Java
+
+For a console, there may be many interfaces that need to be managed and controlled. Using the composite entity pattern, dependent objects such as messages and signals can be combined and controlled using a single object.
+
+We need a generic solution for the problem. To achieve this, let's introduce a generic Composite Entity Pattern.
```java
public abstract class DependentObject {
- T data;
+ T data;
- public void setData(T message) {
- this.data = message;
- }
+ public void setData(T message) {
+ this.data = message;
+ }
- public T getData() {
- return data;
- }
+ public T getData() {
+ return data;
+ }
}
public abstract class CoarseGrainedObject {
- DependentObject[] dependentObjects;
+ DependentObject[] dependentObjects;
- public void setData(T... data) {
- IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i]));
- }
+ public void setData(T... data) {
+ IntStream.range(0, data.length).forEach(i -> dependentObjects[i].setData(data[i]));
+ }
- public T[] getData() {
- return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray();
- }
+ public T[] getData() {
+ return (T[]) Arrays.stream(dependentObjects).map(DependentObject::getData).toArray();
+ }
}
```
@@ -70,54 +88,83 @@ public class SignalDependentObject extends DependentObject {
public class ConsoleCoarseGrainedObject extends CoarseGrainedObject {
- @Override
- public String[] getData() {
- super.getData();
- return new String[]{
- dependentObjects[0].getData(), dependentObjects[1].getData()
- };
- }
-
- public void init() {
- dependentObjects = new DependentObject[]{
- new MessageDependentObject(), new SignalDependentObject()};
- }
+ @Override
+ public String[] getData() {
+ super.getData();
+ return new String[] {
+ dependentObjects[0].getData(), dependentObjects[1].getData()
+ };
+ }
+
+ public void init() {
+ dependentObjects = new DependentObject[] {
+ new MessageDependentObject(), new SignalDependentObject()};
+ }
}
public class CompositeEntity {
- private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject();
+ private final ConsoleCoarseGrainedObject console = new ConsoleCoarseGrainedObject();
- public void setData(String message, String signal) {
- console.setData(message, signal);
- }
+ public void setData(String message, String signal) {
+ console.setData(message, signal);
+ }
- public String[] getData() {
- return console.getData();
- }
+ public String[] getData() {
+ return console.getData();
+ }
}
```
Now managing the assignment of message and signal objects with the composite entity `console`.
```java
-var console = new CompositeEntity();
-console.init();
-console.setData("No Danger", "Green Light");
-Arrays.stream(console.getData()).forEach(LOGGER::info);
-console.setData("Danger", "Red Light");
-Arrays.stream(console.getData()).forEach(LOGGER::info);
+public App(String message, String signal) {
+ var console = new CompositeEntity();
+ console.init();
+ console.setData(message, signal);
+ Arrays.stream(console.getData()).forEach(LOGGER::info);
+ console.setData("Danger", "Red Light");
+ Arrays.stream(console.getData()).forEach(LOGGER::info);
+}
```
-## Class diagram
+## When to Use the Composite Entity Pattern in Java
+
+* Useful in Java enterprise applications where business objects are complex and involve various interdependent persistent objects.
+* Ideal for scenarios where clients need to work with a unified interface to a set of objects rather than individual entities.
+* Applicable in systems that require a simplified view of a complex data model for external clients or services.
+
+
+## Real-World Applications of Composite Entity Pattern in Java
+
+* Enterprise applications with complex business models, particularly those using EJB or similar enterprise frameworks.
+* Systems requiring abstraction over complex database schemas to simplify client interactions.
+* Applications that need to enforce consistency or transactions across multiple persistent objects in a business entity.
+
+## Benefits and Trade-offs of Composite Entity Pattern
+
+Benefits:
+
+* Simplifies client interactions with complex entity models by providing a unified interface.
+* Enhances reusability and maintainability of the business layer by decoupling client code from the complex internals of business entities.
+* Facilitates easier transaction management and consistency enforcement across a set of related persistent objects.
+
+Trade-offs:
-
+* May introduce a level of indirection that could impact performance.
+* Can lead to overly coarse-grained interfaces that might not be as flexible for all client needs.
+* Requires careful design to avoid bloated composite entities that are difficult to manage.
-## Applicability
+## Related Java Design Patterns
-Use the Composite Entity Pattern in the following situation:
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): For dynamically adding behavior to individual objects within the composite entity without affecting the structure.
+* [Facade](https://java-design-patterns.com/patterns/facade/): Provides a simplified interface to a complex subsystem, similar to how a composite entity simplifies access to a set of objects.
+* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Useful for managing shared objects within a composite entity to reduce memory footprint.
-* You want to manage multiple dependency objects through one object to adjust the degree of granularity between objects. At the same time, the lifetime of dependency objects depends on a coarse-grained object.
-## Credits
+## References and Credits
-* [Composite Entity Pattern in wikipedia](https://en.wikipedia.org/wiki/Composite_entity_pattern)
+* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/4cAbDap)
+* [Enterprise Patterns and MDA: Building Better Software with Archetype Patterns and UML](https://amzn.to/49mslqS)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3xjKdpe)
+* [Composite Entity Pattern (Wikipedia)](https://en.wikipedia.org/wiki/Composite_entity_pattern)
diff --git a/composite-entity/etc/composite-entity-flowchart.png b/composite-entity/etc/composite-entity-flowchart.png
new file mode 100644
index 000000000000..2954db2db2fe
Binary files /dev/null and b/composite-entity/etc/composite-entity-flowchart.png differ
diff --git a/composite-entity/pom.xml b/composite-entity/pom.xml
index 04151a6973d3..5d11234c3a0a 100644
--- a/composite-entity/pom.xml
+++ b/composite-entity/pom.xml
@@ -1,8 +1,10 @@
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ composite-view
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ jakarta.servlet
+ jakarta.servlet-api
+ 6.1.0
+ compile
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.compositeview.App
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/composite-view/src/main/java/com/iluwatar/compositeview/AppServlet.java b/composite-view/src/main/java/com/iluwatar/compositeview/AppServlet.java
new file mode 100644
index 000000000000..3b803a6152f3
--- /dev/null
+++ b/composite-view/src/main/java/com/iluwatar/compositeview/AppServlet.java
@@ -0,0 +1,94 @@
+/*
+ * 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.compositeview;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import lombok.NoArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/** A servlet object that extends HttpServlet. Runs on Tomcat 10 and handles Http requests */
+@Slf4j
+@NoArgsConstructor
+public final class AppServlet extends HttpServlet {
+ private static final String CONTENT_TYPE = "text/html";
+ private String msgPartOne = "This Server Doesn't Support";
+ private String msgPartTwo =
+ """
+ Requests
+ Use a GET request with boolean values for the following parameters
+ 'name'
+ 'bus'
+ 'sports'
+ 'sci'
+ 'world' """;
+
+ private String destination = "newsDisplay.jsp";
+
+ @Override
+ public void doGet(HttpServletRequest req, HttpServletResponse resp) {
+ try {
+ RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
+ ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
+ req.setAttribute("properties", reqParams);
+ requestDispatcher.forward(req, resp);
+ } catch (Exception e) {
+ LOGGER.error("Exception occurred GET request processing ", e);
+ }
+ }
+
+ @Override
+ public void doPost(HttpServletRequest req, HttpServletResponse resp) {
+ resp.setContentType(CONTENT_TYPE);
+ try (PrintWriter out = resp.getWriter()) {
+ out.println(msgPartOne + " Post " + msgPartTwo);
+ } catch (Exception e) {
+ LOGGER.error("Exception occurred POST request processing ", e);
+ }
+ }
+
+ @Override
+ public void doDelete(HttpServletRequest req, HttpServletResponse resp) {
+ resp.setContentType(CONTENT_TYPE);
+ try (PrintWriter out = resp.getWriter()) {
+ out.println(msgPartOne + " Delete " + msgPartTwo);
+ } catch (Exception e) {
+ LOGGER.error("Exception occurred DELETE request processing ", e);
+ }
+ }
+
+ @Override
+ public void doPut(HttpServletRequest req, HttpServletResponse resp) {
+ resp.setContentType(CONTENT_TYPE);
+ try (PrintWriter out = resp.getWriter()) {
+ out.println(msgPartOne + " Put " + msgPartTwo);
+ } catch (Exception e) {
+ LOGGER.error("Exception occurred PUT request processing ", e);
+ }
+ }
+}
diff --git a/composite-view/src/main/java/com/iluwatar/compositeview/ClientPropertiesBean.java b/composite-view/src/main/java/com/iluwatar/compositeview/ClientPropertiesBean.java
new file mode 100644
index 000000000000..ecda0cb38e6e
--- /dev/null
+++ b/composite-view/src/main/java/com/iluwatar/compositeview/ClientPropertiesBean.java
@@ -0,0 +1,75 @@
+/*
+ * 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.compositeview;
+
+import jakarta.servlet.http.HttpServletRequest;
+import java.io.Serializable;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+/**
+ * A Java beans class that parses a http request and stores parameters. Java beans used in JSP's to
+ * dynamically include elements in view. DEFAULT_NAME = a constant, default name to be used for the
+ * default constructor worldNewsInterest = whether current request has world news interest
+ * sportsInterest = whether current request has a sportsInterest businessInterest = whether current
+ * request has a businessInterest scienceNewsInterest = whether current request has a
+ * scienceNewsInterest
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+public class ClientPropertiesBean implements Serializable {
+
+ private static final String WORLD_PARAM = "world";
+ private static final String SCIENCE_PARAM = "sci";
+ private static final String SPORTS_PARAM = "sport";
+ private static final String BUSINESS_PARAM = "bus";
+ private static final String NAME_PARAM = "name";
+
+ private static final String DEFAULT_NAME = "DEFAULT_NAME";
+ private boolean worldNewsInterest = true;
+ private boolean sportsInterest = true;
+ private boolean businessInterest = true;
+ private boolean scienceNewsInterest = true;
+ private String name = DEFAULT_NAME;
+
+ /**
+ * Constructor that parses an HttpServletRequest and stores all the request parameters.
+ *
+ * @param req the HttpServletRequest object that is passed in
+ */
+ public ClientPropertiesBean(HttpServletRequest req) {
+ worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
+ sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
+ businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
+ scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
+ String tempName = req.getParameter(NAME_PARAM);
+ if (tempName == null || tempName.equals("")) {
+ tempName = DEFAULT_NAME;
+ }
+ name = tempName;
+ }
+}
diff --git a/composite-view/src/test/java/com/iluwatar/compositeview/AppServletTest.java b/composite-view/src/test/java/com/iluwatar/compositeview/AppServletTest.java
new file mode 100644
index 000000000000..8219a56e4f39
--- /dev/null
+++ b/composite-view/src/test/java/com/iluwatar/compositeview/AppServletTest.java
@@ -0,0 +1,116 @@
+/*
+ * 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.compositeview;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.*;
+
+import jakarta.servlet.RequestDispatcher;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import org.junit.jupiter.api.Test;
+
+class AppServletTest {
+
+ private final String msgPartOne = "This Server Doesn't Support";
+ private final String msgPartTwo =
+ """
+ Requests
+ Use a GET request with boolean values for the following parameters
+ 'name'
+ 'bus'
+ 'sports'
+ 'sci'
+ 'world' """;
+ private final String destination = "newsDisplay.jsp";
+
+ @Test
+ void testDoGet() throws Exception {
+ HttpServletRequest mockReq = mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = mock(HttpServletResponse.class);
+ RequestDispatcher mockDispatcher = mock(RequestDispatcher.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ when(mockResp.getWriter()).thenReturn(printWriter);
+ when(mockReq.getRequestDispatcher(destination)).thenReturn(mockDispatcher);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doGet(mockReq, mockResp);
+
+ verify(mockReq, times(1)).getRequestDispatcher(destination);
+ verify(mockDispatcher).forward(mockReq, mockResp);
+ }
+
+ @Test
+ void testDoPost() throws Exception {
+ HttpServletRequest mockReq = mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = mock(HttpServletResponse.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ when(mockResp.getWriter()).thenReturn(printWriter);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doPost(mockReq, mockResp);
+ printWriter.flush();
+
+ assertTrue(stringWriter.toString().contains(msgPartOne + " Post " + msgPartTwo));
+ }
+
+ @Test
+ void testDoPut() throws Exception {
+ HttpServletRequest mockReq = mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = mock(HttpServletResponse.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ when(mockResp.getWriter()).thenReturn(printWriter);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doPut(mockReq, mockResp);
+ printWriter.flush();
+
+ assertTrue(stringWriter.toString().contains(msgPartOne + " Put " + msgPartTwo));
+ }
+
+ @Test
+ void testDoDelete() throws Exception {
+ HttpServletRequest mockReq = mock(HttpServletRequest.class);
+ HttpServletResponse mockResp = mock(HttpServletResponse.class);
+ StringWriter stringWriter = new StringWriter();
+ PrintWriter printWriter = new PrintWriter(stringWriter);
+
+ when(mockResp.getWriter()).thenReturn(printWriter);
+
+ AppServlet curServlet = new AppServlet();
+ curServlet.doDelete(mockReq, mockResp);
+ printWriter.flush();
+
+ assertTrue(stringWriter.toString().contains(msgPartOne + " Delete " + msgPartTwo));
+ }
+}
diff --git a/composite-view/src/test/java/com/iluwatar/compositeview/JavaBeansTest.java b/composite-view/src/test/java/com/iluwatar/compositeview/JavaBeansTest.java
new file mode 100644
index 000000000000..8e27a20e06f9
--- /dev/null
+++ b/composite-view/src/test/java/com/iluwatar/compositeview/JavaBeansTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.compositeview;
+
+import static org.junit.jupiter.api.Assertions.*;
+import static org.mockito.Mockito.*;
+
+import jakarta.servlet.http.HttpServletRequest;
+import org.junit.jupiter.api.Test;
+
+class JavaBeansTest {
+
+ @Test
+ void testDefaultConstructor() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertEquals("DEFAULT_NAME", newBean.getName());
+ assertTrue(newBean.isBusinessInterest());
+ assertTrue(newBean.isScienceNewsInterest());
+ assertTrue(newBean.isSportsInterest());
+ assertTrue(newBean.isWorldNewsInterest());
+ }
+
+ @Test
+ void testNameGetterSetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertEquals("DEFAULT_NAME", newBean.getName());
+
+ newBean.setName("TEST_NAME_ONE");
+ assertEquals("TEST_NAME_ONE", newBean.getName());
+ }
+
+ @Test
+ void testBusinessSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isBusinessInterest());
+
+ newBean.setBusinessInterest(false);
+ assertFalse(newBean.isBusinessInterest());
+ }
+
+ @Test
+ void testScienceSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isScienceNewsInterest());
+
+ newBean.setScienceNewsInterest(false);
+ assertFalse(newBean.isScienceNewsInterest());
+ }
+
+ @Test
+ void testSportsSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isSportsInterest());
+
+ newBean.setSportsInterest(false);
+ assertFalse(newBean.isSportsInterest());
+ }
+
+ @Test
+ void testWorldSetterGetter() {
+ ClientPropertiesBean newBean = new ClientPropertiesBean();
+ assertTrue(newBean.isWorldNewsInterest());
+
+ newBean.setWorldNewsInterest(false);
+ assertFalse(newBean.isWorldNewsInterest());
+ }
+
+ @Test
+ void testRequestConstructor() {
+ HttpServletRequest mockReq = mock(HttpServletRequest.class);
+ ClientPropertiesBean newBean = new ClientPropertiesBean(mockReq);
+
+ assertEquals("DEFAULT_NAME", newBean.getName());
+ assertFalse(newBean.isWorldNewsInterest());
+ assertFalse(newBean.isBusinessInterest());
+ assertFalse(newBean.isScienceNewsInterest());
+ assertFalse(newBean.isSportsInterest());
+ }
+}
diff --git a/composite-view/web/WEB-INF/web.xml b/composite-view/web/WEB-INF/web.xml
new file mode 100644
index 000000000000..01d4196204f4
--- /dev/null
+++ b/composite-view/web/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+
+
+
+
+ appServlet
+ com.iluwatar.compositeview.AppServlet
+
+
+ appServlet
+ /news
+
+
\ No newline at end of file
diff --git a/composite-view/web/businessNews.jsp b/composite-view/web/businessNews.jsp
new file mode 100644
index 000000000000..c2e2543565e9
--- /dev/null
+++ b/composite-view/web/businessNews.jsp
@@ -0,0 +1,59 @@
+<%--
+
+ 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.
+
+--%>
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 2:51 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+
+ Generic Business News
+
+
+
+ Stock prices up across the world
+ New tech companies to invest in
+
+
+ Industry leaders unveil new project
+ Price fluctuations and what they mean
+
+
+
+
diff --git a/composite-view/web/header.jsp b/composite-view/web/header.jsp
new file mode 100644
index 000000000000..3327c7bca654
--- /dev/null
+++ b/composite-view/web/header.jsp
@@ -0,0 +1,68 @@
+<%--
+
+ 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.
+
+--%>
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 1:28 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ page import="java.util.Date"%>
+
+
+
+
+
+
+
+ Home
+
+
+ <% String todayDateStr = (new Date().toString()); %>
+ Today's Personalized Frontpage
+ <%= todayDateStr %>
+
+
diff --git a/composite-view/web/index.jsp b/composite-view/web/index.jsp
new file mode 100644
index 000000000000..47a8d774caad
--- /dev/null
+++ b/composite-view/web/index.jsp
@@ -0,0 +1,78 @@
+<%--
+
+ 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.
+
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+ Composite Patterns Mock News Site
+
+
+
+ Welcome To The Composite Patterns Mock News Site
+
+
Send a GET request to the "/news" path to see the composite view with mock news
+ Use the following parameters:
+ name: string - Your name to be dynamically displayed
+ bus: boolean - Set to true to see mock business news
+ world: boolean - Set to true to see mock world news
+ sci: boolean - Set to true to see mock science news
+ sport: boolean - Set to true to see mock sports news
+ Example Request:
+ /news?name=John&bus=true&world=false&sci=true&sport=false
+ If the request fails, ensure you have the correct parameters and try again.
+
+
+
diff --git a/composite-view/web/localNews.jsp b/composite-view/web/localNews.jsp
new file mode 100644
index 000000000000..e596151e923f
--- /dev/null
+++ b/composite-view/web/localNews.jsp
@@ -0,0 +1,50 @@
+<%--
+
+ 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.
+
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+ Generic Local News
+
+
+
+ Mayoral elections coming up in 2 weeks
+
+
+ New parking meter rates downtown coming tomorrow
+
+
+ Park renovations to finish by the next year
+
+
+ Annual marathon sign ups available online
+
+
+
+
+
diff --git a/composite-view/web/newsDisplay.jsp b/composite-view/web/newsDisplay.jsp
new file mode 100644
index 000000000000..7503b2a46014
--- /dev/null
+++ b/composite-view/web/newsDisplay.jsp
@@ -0,0 +1,83 @@
+<%--
+
+ 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.
+
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+<%@ page import="com.iluwatar.compositeview.ClientPropertiesBean"%>
+
+
+
+
+
+ <%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
+ Welcome <%= propertiesBean.getName()%>
+
+
+
+
+
+ <% if(propertiesBean.isWorldNewsInterest()) { %>
+ <%@include file="worldNews.jsp"%>
+ <% } else { %>
+ <%@include file="localNews.jsp"%>
+ <% } %>
+
+
+
+ <% if(propertiesBean.isBusinessInterest()) { %>
+ <%@include file="businessNews.jsp"%>
+ <% } else { %>
+ <%@include file="localNews.jsp"%>
+ <% } %>
+
+ <% if(propertiesBean.isSportsInterest()) { %>
+ <%@include file="sportsNews.jsp"%>
+ <% } else { %>
+ <%@include file="localNews.jsp"%>
+ <% } %>
+
+
+
+ <% if(propertiesBean.isScienceNewsInterest()) { %>
+ <%@include file="scienceNews.jsp"%>
+ <% } else { %>
+ <%@include file="localNews.jsp"%>
+ <% } %>
+
+
+
+
+
diff --git a/composite-view/web/scienceNews.jsp b/composite-view/web/scienceNews.jsp
new file mode 100644
index 000000000000..643fe94ea84b
--- /dev/null
+++ b/composite-view/web/scienceNews.jsp
@@ -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.
+
+--%>
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 4:18 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+ Generic Science News
+
+
+
+ New model of gravity proposed for dark matter
+
+
+ Genetic modifying technique proved on bacteria
+
+
+ Neurology study maps brain with new precision
+
+
+ Survey of rainforest discovers 15 new species
+
+
+ New signalling pathway for immune system discovered
+
+
+
+
+
diff --git a/composite-view/web/sportsNews.jsp b/composite-view/web/sportsNews.jsp
new file mode 100644
index 000000000000..4647cc826c7f
--- /dev/null
+++ b/composite-view/web/sportsNews.jsp
@@ -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.
+
+--%>
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 3:53 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+
+ Generic Sports News
+
+
+ International football match delayed due to weather, will be held next week
+
+
+ New rising stars in winter sports, ten new athletes that will shake up the scene
+
+
+ Biggest upset in basketball history, upstart team sweeps competition
+
+
+
diff --git a/composite-view/web/worldNews.jsp b/composite-view/web/worldNews.jsp
new file mode 100644
index 000000000000..9057faa9ea0c
--- /dev/null
+++ b/composite-view/web/worldNews.jsp
@@ -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.
+
+--%>
+<%--
+ Created by IntelliJ IDEA.
+ User: Kevin
+ Date: 11/29/2021
+ Time: 2:51 PM
+ To change this template use File | Settings | File Templates.
+--%>
+<%@ page contentType="text/html;charset=UTF-8" language="java" %>
+
+
+
+
+
+
+ Generic World News
+
+
+
+ New trade talks happening at UN on Thursday
+
+
+ European Union to announce new resolution next week
+
+
+ UN delivers report on world economic status
+
+
+
+
diff --git a/composite/README.md b/composite/README.md
index 3ce7bc9514ab..91f5e3230ff6 100644
--- a/composite/README.md
+++ b/composite/README.md
@@ -1,112 +1,117 @@
---
-layout: pattern
-title: Composite
-folder: composite
-permalink: /patterns/composite/
-categories: Structural
+title: "Composite Pattern in Java: Building Flexible Tree Structures"
+shortTitle: Composite
+description: "Explore the Composite Design Pattern in Java. Learn how to compose objects into tree structures to represent part-whole hierarchies, making it easier to treat individual objects and compositions uniformly. Ideal for graphical user interfaces, file systems, and organizational structures."
+category: Structural
language: en
-tags:
- - Gang of Four
+tag:
+ - Decoupling
+ - Gang of Four
+ - Object composition
+ - Recursion
---
-## Intent
+## Also known as
-Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients
-treat individual objects and compositions of objects uniformly.
+* Object Tree
+* Composite Structure
-## Explanation
+## Intent of Composite Design Pattern
+
+Compose objects into tree structures to represent part-whole hierarchies. The Composite Design Pattern lets clients treat individual objects and compositions of objects uniformly.
+
+## Detailed Explanation of Composite Pattern with Real-World Examples
Real-world example
-> Every sentence is composed of words which are in turn composed of characters. Each of these
-> objects are printable and they can have something printed before or after them like sentence
-> always ends with full stop and word always has space before it.
+> In a real-world example, consider a company with a complex organizational structure. The company consists of various departments, each of which can contain sub-departments, and ultimately individual employees. The Composite Design Pattern can be used to represent this structure. Each department and employee are treated as a node in a tree structure, where departments can contain other departments or employees, but employees are leaf nodes with no children. This allows the company to perform operations uniformly, such as calculating total salaries or printing the organizational chart, by treating individual employees and entire departments in the same way.
In plain words
-> Composite pattern lets clients uniformly treat the individual objects.
+> The Composite Design Pattern lets clients uniformly treat individual objects and compositions of objects.
Wikipedia says
-> In software engineering, the composite pattern is a partitioning design pattern. The composite
-> pattern describes that a group of objects is to be treated in the same way as a single instance of
-> an object. The intent of a composite is to "compose" objects into tree structures to represent
-> part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects
-> and compositions uniformly.
+> In software engineering, the composite pattern is a partitioning design pattern. The composite pattern describes that a group of objects is to be treated in the same way as a single instance of an object. The intent of a composite is to "compose" objects into tree structures to represent part-whole hierarchies. Implementing the composite pattern lets clients treat individual objects and compositions uniformly.
+
+Flowchart
+
+
-**Programmatic Example**
+## Programmatic Example of Composite Pattern in Java
-Taking our sentence example from above. Here we have the base class `LetterComposite` and the
-different printable types `Letter`, `Word` and `Sentence`.
+Every sentence is composed of words which are in turn composed of characters. Each of these objects are printable, and they can have something printed before or after them like sentence always ends with full stop and word always has space before it.
+
+Here we have the base class `LetterComposite` and the different printable types `Letter`, `Word` and `Sentence`.
```java
public abstract class LetterComposite {
- private final List children = new ArrayList<>();
+ private final List children = new ArrayList<>();
- public void add(LetterComposite letter) {
- children.add(letter);
- }
+ public void add(LetterComposite letter) {
+ children.add(letter);
+ }
- public int count() {
- return children.size();
- }
+ public int count() {
+ return children.size();
+ }
- protected void printThisBefore() {
- }
+ protected void printThisBefore() {
+ }
- protected void printThisAfter() {
- }
+ protected void printThisAfter() {
+ }
- public void print() {
- printThisBefore();
- children.forEach(LetterComposite::print);
- printThisAfter();
- }
+ public void print() {
+ printThisBefore();
+ children.forEach(LetterComposite::print);
+ printThisAfter();
+ }
}
public class Letter extends LetterComposite {
- private final char character;
+ private final char character;
- public Letter(char c) {
- this.character = c;
- }
+ public Letter(char c) {
+ this.character = c;
+ }
- @Override
- protected void printThisBefore() {
- System.out.print(character);
- }
+ @Override
+ protected void printThisBefore() {
+ System.out.print(character);
+ }
}
public class Word extends LetterComposite {
- public Word(List letters) {
- letters.forEach(this::add);
- }
+ public Word(List letters) {
+ letters.forEach(this::add);
+ }
- public Word(char... letters) {
- for (char letter : letters) {
- this.add(new Letter(letter));
+ public Word(char... letters) {
+ for (char letter : letters) {
+ this.add(new Letter(letter));
+ }
}
- }
- @Override
- protected void printThisBefore() {
- System.out.print(" ");
- }
+ @Override
+ protected void printThisBefore() {
+ System.out.print(" ");
+ }
}
public class Sentence extends LetterComposite {
- public Sentence(List words) {
- words.forEach(this::add);
- }
+ public Sentence(List words) {
+ words.forEach(this::add);
+ }
- @Override
- protected void printThisAfter() {
- System.out.print(".");
- }
+ @Override
+ protected void printThisAfter() {
+ System.out.print(".");
+ }
}
```
@@ -115,38 +120,38 @@ Then we have a messenger to carry messages:
```java
public class Messenger {
- LetterComposite messageFromOrcs() {
+ LetterComposite messageFromOrcs() {
- var words = List.of(
- new Word('W', 'h', 'e', 'r', 'e'),
- new Word('t', 'h', 'e', 'r', 'e'),
- new Word('i', 's'),
- new Word('a'),
- new Word('w', 'h', 'i', 'p'),
- new Word('t', 'h', 'e', 'r', 'e'),
- new Word('i', 's'),
- new Word('a'),
- new Word('w', 'a', 'y')
- );
+ var words = List.of(
+ new Word('W', 'h', 'e', 'r', 'e'),
+ new Word('t', 'h', 'e', 'r', 'e'),
+ new Word('i', 's'),
+ new Word('a'),
+ new Word('w', 'h', 'i', 'p'),
+ new Word('t', 'h', 'e', 'r', 'e'),
+ new Word('i', 's'),
+ new Word('a'),
+ new Word('w', 'a', 'y')
+ );
- return new Sentence(words);
+ return new Sentence(words);
- }
+ }
- LetterComposite messageFromElves() {
+ LetterComposite messageFromElves() {
- var words = List.of(
- new Word('M', 'u', 'c', 'h'),
- new Word('w', 'i', 'n', 'd'),
- new Word('p', 'o', 'u', 'r', 's'),
- new Word('f', 'r', 'o', 'm'),
- new Word('y', 'o', 'u', 'r'),
- new Word('m', 'o', 'u', 't', 'h')
- );
+ var words = List.of(
+ new Word('M', 'u', 'c', 'h'),
+ new Word('w', 'i', 'n', 'd'),
+ new Word('p', 'o', 'u', 'r', 's'),
+ new Word('f', 'r', 'o', 'm'),
+ new Word('y', 'o', 'u', 'r'),
+ new Word('m', 'o', 'u', 't', 'h')
+ );
- return new Sentence(words);
+ return new Sentence(words);
- }
+ }
}
```
@@ -154,43 +159,64 @@ public class Messenger {
And then it can be used as:
```java
-var messenger = new Messenger();
+ public static void main(String[] args) {
+
+ var messenger = new Messenger();
-LOGGER.info("Message from the orcs: ");
-messenger.messageFromOrcs().print();
+ LOGGER.info("Message from the orcs: ");
+ messenger.messageFromOrcs().print();
-LOGGER.info("Message from the elves: ");
-messenger.messageFromElves().print();
+ LOGGER.info("Message from the elves: ");
+ messenger.messageFromElves().print();
+}
```
The console output:
```
-Message from the orcs:
+20:43:54.801 [main] INFO com.iluwatar.composite.App -- Message from the orcs:
Where there is a whip there is a way.
-Message from the elves:
+20:43:54.803 [main] INFO com.iluwatar.composite.App -- Message from the elves:
Much wind pours from your mouth.
```
-## Class diagram
-
-
-
-## Applicability
+## When to Use the Composite Pattern in Java
Use the Composite pattern when
* You want to represent part-whole hierarchies of objects.
-* You want clients to be able to ignore the difference between compositions of objects and
-individual objects. Clients will treat all objects in the composite structure uniformly.
+* You want clients to be able to ignore the difference between compositions of objects and individual objects. Clients will treat all objects in the composite structure uniformly.
-## Known uses
+## Real-World Applications of Composite Pattern in Java
+* Graphical user interfaces where components can contain other components (e.g., panels containing buttons, labels, other panels).
+* File system representations where directories can contain files and other directories.
+* Organizational structures where a department can contain sub-departments and employees.
* [java.awt.Container](http://docs.oracle.com/javase/8/docs/api/java/awt/Container.html) and [java.awt.Component](http://docs.oracle.com/javase/8/docs/api/java/awt/Component.html)
* [Apache Wicket](https://github.com/apache/wicket) component tree, see [Component](https://github.com/apache/wicket/blob/91e154702ab1ff3481ef6cbb04c6044814b7e130/wicket-core/src/main/java/org/apache/wicket/Component.java) and [MarkupContainer](https://github.com/apache/wicket/blob/b60ec64d0b50a611a9549809c9ab216f0ffa3ae3/wicket-core/src/main/java/org/apache/wicket/MarkupContainer.java)
-## Credits
+## Benefits and Trade-offs of Composite Pattern
+
+Benefits:
+
+* Simplifies client code, as it can treat composite structures and individual objects uniformly.
+* Makes it easier to add new kinds of components, as existing code doesn't need to be changed.
+
+Trade-offs:
+
+* Can make the design overly general. It might be difficult to restrict the components of a composite.
+* Can make it harder to restrict the types of components in a composite.
+
+## Related Java Design Patterns
+
+* [Flyweight](https://java-design-patterns.com/patterns/flyweight/): Composite can use Flyweight to share component instances among several composites.
+* [Iterator](https://java-design-patterns.com/patterns/iterator/): Can be used to traverse Composite structures.
+* [Visitor](https://java-design-patterns.com/patterns/visitor/): Can apply an operation over a Composite structure.
+
+## References and Credits
-* [Design Patterns: Elements of Reusable Object-Oriented Software](https://www.amazon.com/gp/product/0201633612/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0201633612&linkCode=as2&tag=javadesignpat-20&linkId=675d49790ce11db99d90bde47f1aeb59)
-* [Head First Design Patterns: A Brain-Friendly Guide](https://www.amazon.com/gp/product/0596007124/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596007124&linkCode=as2&tag=javadesignpat-20&linkId=6b8b6eea86021af6c8e3cd3fc382cb5b)
-* [Refactoring to Patterns](https://www.amazon.com/gp/product/0321213351/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321213351&linkCode=as2&tag=javadesignpat-20&linkId=2a76fcb387234bc71b1c61150b3cc3a7)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software](https://amzn.to/49NGldq)
+* [Refactoring to Patterns](https://amzn.to/3VOO4F5)
+* [Pattern-Oriented Software Architecture, Volume 1: A System of Patterns](https://amzn.to/3xoLAmi)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3vBKXWb)
diff --git a/composite/etc/composite-flowchart.png b/composite/etc/composite-flowchart.png
new file mode 100644
index 000000000000..7938250ec332
Binary files /dev/null and b/composite/etc/composite-flowchart.png differ
diff --git a/composite/pom.xml b/composite/pom.xml
index 6d00af2455fa..7beb910d901e 100644
--- a/composite/pom.xml
+++ b/composite/pom.xml
@@ -1,8 +1,10 @@
LayerC
+ServiceContext --> LayerB
+ServiceContext --> LayerA
+ServiceContextFactory ..|> "<>" ServiceContext
+LayerB ..|> LayerA
+@enduml
\ No newline at end of file
diff --git a/context-object/pom.xml b/context-object/pom.xml
new file mode 100644
index 000000000000..82f8862b6905
--- /dev/null
+++ b/context-object/pom.xml
@@ -0,0 +1,70 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ context-object
+
+
+ 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.compositeview.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/context-object/src/main/java/com/iluwatar/context/object/App.java b/context-object/src/main/java/com/iluwatar/context/object/App.java
new file mode 100644
index 000000000000..f0fc505c31e3
--- /dev/null
+++ b/context-object/src/main/java/com/iluwatar/context/object/App.java
@@ -0,0 +1,74 @@
+/*
+ * 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.context.object;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * In the context object pattern, information and data from underlying protocol-specific
+ * classes/systems is decoupled and stored into a protocol-independent object in an organised
+ * format. The pattern ensures the data contained within the context object can be shared and
+ * further structured between different layers of a software system.
+ *
+ * In this example we show how a context object {@link ServiceContext} can be initiated, edited
+ * and passed/retrieved in different layers of the program ({@link LayerA}, {@link LayerB}, {@link
+ * LayerC}) through use of static methods.
+ */
+@Slf4j
+public class App {
+
+ private static final String SERVICE = "SERVICE";
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ // Initiate first layer and add service information into context
+ var layerA = new LayerA();
+ layerA.addAccountInfo(SERVICE);
+
+ logContext(layerA.getContext());
+
+ // Initiate second layer and preserving information retrieved in first layer through passing
+ // context object
+ var layerB = new LayerB(layerA);
+ layerB.addSessionInfo(SERVICE);
+
+ logContext(layerB.getContext());
+
+ // Initiate third layer and preserving information retrieved in first and second layer through
+ // passing context object
+ var layerC = new LayerC(layerB);
+ layerC.addSearchInfo(SERVICE);
+
+ logContext(layerC.getContext());
+ }
+
+ private static void logContext(ServiceContext context) {
+ LOGGER.info("Context = {}", context);
+ }
+}
diff --git a/context-object/src/main/java/com/iluwatar/context/object/LayerA.java b/context-object/src/main/java/com/iluwatar/context/object/LayerA.java
new file mode 100644
index 000000000000..4d37f079052e
--- /dev/null
+++ b/context-object/src/main/java/com/iluwatar/context/object/LayerA.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.context.object;
+
+import lombok.Getter;
+
+/** Layer A in the context object pattern. */
+@Getter
+public class LayerA {
+
+ private ServiceContext context;
+
+ public LayerA() {
+ context = ServiceContextFactory.createContext();
+ }
+
+ public void addAccountInfo(String accountService) {
+ context.setAccountService(accountService);
+ }
+}
diff --git a/context-object/src/main/java/com/iluwatar/context/object/LayerB.java b/context-object/src/main/java/com/iluwatar/context/object/LayerB.java
new file mode 100644
index 000000000000..a67aba9bd5f7
--- /dev/null
+++ b/context-object/src/main/java/com/iluwatar/context/object/LayerB.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.context.object;
+
+import lombok.Getter;
+
+/** Layer B in the context object pattern. */
+@Getter
+public class LayerB {
+
+ private ServiceContext context;
+
+ public LayerB(LayerA layerA) {
+ this.context = layerA.getContext();
+ }
+
+ public void addSessionInfo(String sessionService) {
+ context.setSessionService(sessionService);
+ }
+}
diff --git a/context-object/src/main/java/com/iluwatar/context/object/LayerC.java b/context-object/src/main/java/com/iluwatar/context/object/LayerC.java
new file mode 100644
index 000000000000..d33a62998030
--- /dev/null
+++ b/context-object/src/main/java/com/iluwatar/context/object/LayerC.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.context.object;
+
+import lombok.Getter;
+
+/** Layer C in the context object pattern. */
+@Getter
+public class LayerC {
+
+ public ServiceContext context;
+
+ public LayerC(LayerB layerB) {
+ this.context = layerB.getContext();
+ }
+
+ public void addSearchInfo(String searchService) {
+ context.setSearchService(searchService);
+ }
+}
diff --git a/context-object/src/main/java/com/iluwatar/context/object/ServiceContext.java b/context-object/src/main/java/com/iluwatar/context/object/ServiceContext.java
new file mode 100644
index 000000000000..3de91a20e672
--- /dev/null
+++ b/context-object/src/main/java/com/iluwatar/context/object/ServiceContext.java
@@ -0,0 +1,38 @@
+/*
+ * 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.context.object;
+
+import lombok.Getter;
+import lombok.Setter;
+
+/** Where context objects are defined. */
+@Getter
+@Setter
+public class ServiceContext {
+
+ String accountService;
+ String sessionService;
+ String searchService;
+}
diff --git a/context-object/src/main/java/com/iluwatar/context/object/ServiceContextFactory.java b/context-object/src/main/java/com/iluwatar/context/object/ServiceContextFactory.java
new file mode 100644
index 000000000000..bee65e828914
--- /dev/null
+++ b/context-object/src/main/java/com/iluwatar/context/object/ServiceContextFactory.java
@@ -0,0 +1,33 @@
+/*
+ * 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.context.object;
+
+/** An interface to create context objects passed through layers. */
+public class ServiceContextFactory {
+
+ public static ServiceContext createContext() {
+ return new ServiceContext();
+ }
+}
diff --git a/context-object/src/test/java/com/iluwatar/contect/object/AppTest.java b/context-object/src/test/java/com/iluwatar/contect/object/AppTest.java
new file mode 100644
index 000000000000..2cb5e901a80e
--- /dev/null
+++ b/context-object/src/test/java/com/iluwatar/contect/object/AppTest.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.contect.object;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import com.iluwatar.context.object.App;
+import org.junit.jupiter.api.Test;
+
+public class AppTest {
+
+ /** Test example app runs without error. */
+ @Test
+ void shouldExecuteWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/context-object/src/test/java/com/iluwatar/contect/object/ServiceContextTest.java b/context-object/src/test/java/com/iluwatar/contect/object/ServiceContextTest.java
new file mode 100644
index 000000000000..fdfd56af7948
--- /dev/null
+++ b/context-object/src/test/java/com/iluwatar/contect/object/ServiceContextTest.java
@@ -0,0 +1,101 @@
+/*
+ * 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.contect.object;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+
+import com.iluwatar.context.object.LayerA;
+import com.iluwatar.context.object.LayerB;
+import com.iluwatar.context.object.LayerC;
+import com.iluwatar.context.object.ServiceContext;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/** ServiceContextTest */
+public class ServiceContextTest {
+
+ private static final String SERVICE = "SERVICE";
+
+ private LayerA layerA;
+
+ @BeforeEach
+ void initiateLayerA() {
+ this.layerA = new LayerA();
+ }
+
+ @Test
+ void testSameContextPassedBetweenLayers() {
+ ServiceContext context1 = layerA.getContext();
+ var layerB = new LayerB(layerA);
+ ServiceContext context2 = layerB.getContext();
+ var layerC = new LayerC(layerB);
+ ServiceContext context3 = layerC.getContext();
+
+ assertSame(context1, context2);
+ assertSame(context2, context3);
+ assertSame(context3, context1);
+ }
+
+ @Test
+ void testScopedDataPassedBetweenLayers() {
+ layerA.addAccountInfo(SERVICE);
+ var layerB = new LayerB(layerA);
+ var layerC = new LayerC(layerB);
+ layerC.addSearchInfo(SERVICE);
+ ServiceContext context = layerC.getContext();
+
+ assertEquals(SERVICE, context.getAccountService());
+ assertNull(context.getSessionService());
+ assertEquals(SERVICE, context.getSearchService());
+ }
+
+ @Test
+ void testLayerContexts() {
+ assertAll(
+ () -> assertNull(layerA.getContext().getAccountService()),
+ () -> assertNull(layerA.getContext().getSearchService()),
+ () -> assertNull(layerA.getContext().getSessionService()));
+ layerA.addAccountInfo(SERVICE);
+ assertAll(
+ () -> assertEquals(SERVICE, layerA.getContext().getAccountService()),
+ () -> assertNull(layerA.getContext().getSearchService()),
+ () -> assertNull(layerA.getContext().getSessionService()));
+ var layerB = new LayerB(layerA);
+ layerB.addSessionInfo(SERVICE);
+ assertAll(
+ () -> assertEquals(SERVICE, layerB.getContext().getAccountService()),
+ () -> assertEquals(SERVICE, layerB.getContext().getSessionService()),
+ () -> assertNull(layerB.getContext().getSearchService()));
+ var layerC = new LayerC(layerB);
+ layerC.addSearchInfo(SERVICE);
+ assertAll(
+ () -> assertEquals(SERVICE, layerC.getContext().getAccountService()),
+ () -> assertEquals(SERVICE, layerC.getContext().getSearchService()),
+ () -> assertEquals(SERVICE, layerC.getContext().getSessionService()));
+ }
+}
diff --git a/converter/README.md b/converter/README.md
index cbba3344dbbf..7fbb1fe9e559 100644
--- a/converter/README.md
+++ b/converter/README.md
@@ -1,109 +1,174 @@
---
-layout: pattern
-title: Converter
-folder: converter
-permalink: /patterns/converter/
-categories: Creational
+title: "Converter Pattern in Java: Streamlining Data Conversion Across Layers"
+shortTitle: Converter
+description: "Discover the benefits and implementation of the Converter Pattern in Java. Learn how to achieve seamless bidirectional conversion between different data formats, promoting clean code and flexibility in your applications."
+category: Structural
language: en
-tags:
- - Decoupling
+tag:
+ - Compatibility
+ - Data transformation
+ - Decoupling
+ - Interface
+ - Object mapping
+ - Wrapping
---
-## Intent
+## Also known as
-The purpose of the Converter pattern is to provide a generic, common way of bidirectional
-conversion between corresponding types, allowing a clean implementation in which the types do not
-need to be aware of each other. Moreover, the Converter pattern introduces bidirectional collection
-mapping, reducing a boilerplate code to minimum.
+* Mapper
+* Translator
-## Explanation
+## Intent of Converter Design Pattern
-Real world example
+The purpose of the Converter Pattern is to provide a generic, systematic way of bidirectional conversion between corresponding data types. This allows for a clean, decoupled implementation where types are unaware of each other. Additionally, the Converter pattern supports bidirectional collection mapping, minimizing boilerplate code.
-> In real world applications it is often the case that database layer consists of entities that need
-> to be mapped into DTOs for use on the business logic layer. Similar mapping is done for
-> potentially huge amount of classes and we need a generic way to achieve this.
+## Detailed Explanation of Converter Pattern with Real-World Examples
+
+Real-world example
+
+> In a real-world scenario, consider a library system that interacts with a third-party book database. The library uses an internal book format, while the third-party database uses a different format. By employing the Converter Pattern, a converter class can transform the third-party book data into the library's format and vice versa. This ensures seamless integration without altering the internal structures of either system.
In plain words
-> Converter pattern makes it easy to map instances of one class into instances of another class.
+> The Converter Pattern simplifies mapping instances of one class to instances of another class, ensuring consistent and clean data transformation.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Converter Pattern in Java
-**Programmatic Example**
+In applications, it's common for the database layer to have entities that need mapping to DTOs (Data Transfer Objects) for business logic. This mapping often involves many classes, necessitating a generic solution.
-We need a generic solution for the mapping problem. To achieve this, let's introduce a generic
-converter.
+We introduce a generic `Converter` class:
```java
public class Converter {
- private final Function fromDto;
- private final Function fromEntity;
+ private final Function fromDto;
+ private final Function fromEntity;
- public Converter(final Function fromDto, final Function fromEntity) {
- this.fromDto = fromDto;
- this.fromEntity = fromEntity;
- }
+ public Converter(final Function fromDto, final Function fromEntity) {
+ this.fromDto = fromDto;
+ this.fromEntity = fromEntity;
+ }
- public final U convertFromDto(final T dto) {
- return fromDto.apply(dto);
- }
+ public final U convertFromDto(final T dto) {
+ return fromDto.apply(dto);
+ }
- public final T convertFromEntity(final U entity) {
- return fromEntity.apply(entity);
- }
+ public final T convertFromEntity(final U entity) {
+ return fromEntity.apply(entity);
+ }
- public final List createFromDtos(final Collection dtos) {
- return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
- }
+ public final List createFromDtos(final Collection dtos) {
+ return dtos.stream().map(this::convertFromDto).collect(Collectors.toList());
+ }
- public final List createFromEntities(final Collection entities) {
- return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
- }
+ public final List createFromEntities(final Collection entities) {
+ return entities.stream().map(this::convertFromEntity).collect(Collectors.toList());
+ }
}
```
-The specialized converters inherit from this base class as follows.
+Specialized converters inherit from this base class:
```java
public class UserConverter extends Converter {
- public UserConverter() {
- super(UserConverter::convertToEntity, UserConverter::convertToDto);
- }
+ public UserConverter() {
+ super(UserConverter::convertToEntity, UserConverter::convertToDto);
+ }
- private static UserDto convertToDto(User user) {
- return new UserDto(user.getFirstName(), user.getLastName(), user.isActive(), user.getUserId());
- }
-
- private static User convertToEntity(UserDto dto) {
- return new User(dto.getFirstName(), dto.getLastName(), dto.isActive(), dto.getEmail());
- }
+ private static UserDto convertToDto(User user) {
+ return new UserDto(user.firstName(), user.lastName(), user.active(), user.userId());
+ }
+ private static User convertToEntity(UserDto dto) {
+ return new User(dto.firstName(), dto.lastName(), dto.active(), dto.email());
+ }
}
```
-Now mapping between `User` and `UserDto` becomes trivial.
+Mapping between `User` and `UserDto` becomes straightforward:
```java
-var userConverter = new UserConverter();
-var dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com");
-var user = userConverter.convertFromDto(dtoUser);
+ public static void main(String[] args) {
+ Converter userConverter = new UserConverter();
+
+ UserDto dtoUser = new UserDto("John", "Doe", true, "whatever[at]wherever.com");
+ User user = userConverter.convertFromDto(dtoUser);
+ LOGGER.info("Entity converted from DTO: {}", user);
+
+ var users = List.of(
+ new User("Camile", "Tough", false, "124sad"),
+ new User("Marti", "Luther", true, "42309fd"),
+ new User("Kate", "Smith", true, "if0243")
+ );
+ LOGGER.info("Domain entities:");
+ users.stream().map(User::toString).forEach(LOGGER::info);
+
+ LOGGER.info("DTO entities converted from domain:");
+ List dtoEntities = userConverter.createFromEntities(users);
+ dtoEntities.stream().map(UserDto::toString).forEach(LOGGER::info);
+}
```
-## Class diagram
+Program output:
-
+```
+08:28:27.019 [main] INFO com.iluwatar.converter.App -- Entity converted from DTO: User[firstName=John, lastName=Doe, active=true, userId=whatever[at]wherever.com]
+08:28:27.035 [main] INFO com.iluwatar.converter.App -- Domain entities:
+08:28:27.035 [main] INFO com.iluwatar.converter.App -- User[firstName=Camile, lastName=Tough, active=false, userId=124sad]
+08:28:27.035 [main] INFO com.iluwatar.converter.App -- User[firstName=Marti, lastName=Luther, active=true, userId=42309fd]
+08:28:27.035 [main] INFO com.iluwatar.converter.App -- User[firstName=Kate, lastName=Smith, active=true, userId=if0243]
+08:28:27.036 [main] INFO com.iluwatar.converter.App -- DTO entities converted from domain:
+08:28:27.037 [main] INFO com.iluwatar.converter.App -- UserDto[firstName=Camile, lastName=Tough, active=false, email=124sad]
+08:28:27.037 [main] INFO com.iluwatar.converter.App -- UserDto[firstName=Marti, lastName=Luther, active=true, email=42309fd]
+08:28:27.037 [main] INFO com.iluwatar.converter.App -- UserDto[firstName=Kate, lastName=Smith, active=true, email=if0243]
+```
-## Applicability
+## When to Use the Converter Pattern in Java
Use the Converter Pattern in the following situations:
-* When you have types that logically correspond with each other and you need to convert entities
-between them.
-* When you want to provide different ways of types conversions depending on the context.
-* Whenever you introduce a DTO (Data transfer object), you will probably need to convert it into the
-domain equivalence.
+* When there are types that logically correspond with each other, and there is a need to convert between them.
+* In applications that interact with external systems or services that require data in a specific format.
+* For legacy systems integration where data models differ significantly from newer systems.
+* When aiming to encapsulate conversion logic to promote single responsibility and cleaner code.
+
+## Converter Pattern Java Tutorials
+
+* [Converter Pattern in Java 8 (Boldare)](http://www.xsolve.pl/blog/converter-pattern-in-java-8/)
+
+## Real-World Applications of Converter Pattern in Java
+
+* Data Transfer Objects (DTOs) conversions in multi-layered applications.
+* Adapting third-party data structures or API responses to internal models.
+* ORM (Object-Relational Mapping) frameworks for mapping between database records and domain objects.
+* Microservices architecture for data exchange between different services.
+
+## Benefits and Trade-offs of Converter Pattern
+
+Benefits:
+
+* Separation of Concerns: Encapsulates conversion logic in a single component, keeping the rest of the application unaware of the conversion details.
+* Reusability: Converter components can be reused across the application or even in different applications.
+* Flexibility: Makes it easy to add new conversions without impacting existing code, adhering to the [Open/Closed Principle](https://java-design-patterns.com/principles/#open-closed-principle).
+* Interoperability: Facilitates communication between different systems or application layers by translating data formats.
+
+Trade-offs:
+
+* Overhead: Introducing converters can add complexity and potential performance overhead, especially in systems with numerous data formats.
+* Duplication: There's a risk of duplicating model definitions if not carefully managed, leading to increased maintenance.
+
+## Related Java Design Patterns
+
+* [Adapter](https://java-design-patterns.com/patterns/adapter/): Similar in intent to adapting interfaces, but Converter focuses on data models.
+* [Facade](https://java-design-patterns.com/patterns/facade/): Provides a simplified interface to a complex system, which might involve data conversion.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): Converters can use different strategies for conversion, especially when multiple formats are involved.
-## Credits
+## References and Credits
-* [Converter Pattern in Java 8](http://www.xsolve.pl/blog/converter-pattern-in-java-8/)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
diff --git a/converter/etc/converter-sequence-diagram.png b/converter/etc/converter-sequence-diagram.png
new file mode 100644
index 000000000000..65192915c2af
Binary files /dev/null and b/converter/etc/converter-sequence-diagram.png differ
diff --git a/converter/pom.xml b/converter/pom.xml
index 5ef5d140df8c..525237ed17f5 100644
--- a/converter/pom.xml
+++ b/converter/pom.xml
@@ -1,8 +1,10 @@
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
- cqrs
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- com.h2database
- h2
-
-
- org.hibernate
- hibernate-core
-
-
- com.sun.xml.bind
- jaxb-impl
- 2.1.17
- test
-
-
- javax.xml.bind
- jaxb-api
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.cqrs.app.App
-
-
-
-
-
-
-
-
-
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/app/App.java b/cqrs/src/main/java/com/iluwatar/cqrs/app/App.java
deleted file mode 100644
index 1d8f6934878b..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/app/App.java
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.app;
-
-import com.iluwatar.cqrs.commandes.CommandServiceImpl;
-import com.iluwatar.cqrs.constants.AppConstants;
-import com.iluwatar.cqrs.queries.QueryServiceImpl;
-import com.iluwatar.cqrs.util.HibernateUtil;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * CQRS : Command Query Responsibility Segregation. A pattern used to separate query services from
- * commands or writes services. The pattern is very simple but it has many consequences. For
- * example, it can be used to tackle down a complex domain, or to use other architectures that were
- * hard to implement with the classical way.
- *
- * This implementation is an example of managing books and authors in a library. The persistence
- * of books and authors is done according to the CQRS architecture. A command side that deals with a
- * data model to persist(insert,update,delete) objects to a database. And a query side that uses
- * native queries to get data from the database and return objects as DTOs (Data transfer Objects).
- */
-@Slf4j
-public class App {
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- var commands = new CommandServiceImpl();
-
- // Create Authors and Books using CommandService
- commands.authorCreated(AppConstants.E_EVANS, "Eric Evans", "evans@email.com");
- commands.authorCreated(AppConstants.J_BLOCH, "Joshua Bloch", "jBloch@email.com");
- commands.authorCreated(AppConstants.M_FOWLER, "Martin Fowler", "mFowler@email.com");
-
- commands.bookAddedToAuthor("Domain-Driven Design", 60.08, AppConstants.E_EVANS);
- 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("Domain Specific Languages", 48.89, AppConstants.M_FOWLER);
- commands.authorNameUpdated(AppConstants.E_EVANS, "Eric J. Evans");
-
- var queries = new QueryServiceImpl();
-
- // Query the database using QueryService
- var nullAuthor = queries.getAuthorByUsername("username");
- var evans = queries.getAuthorByUsername(AppConstants.E_EVANS);
- var blochBooksCount = queries.getAuthorBooksCount(AppConstants.J_BLOCH);
- var authorsCount = queries.getAuthorsCount();
- var dddBook = queries.getBook("Domain-Driven Design");
- var blochBooks = queries.getAuthorBooks(AppConstants.J_BLOCH);
-
- LOGGER.info("Author username : {}", nullAuthor);
- LOGGER.info("Author evans : {}", evans);
- LOGGER.info("jBloch number of books : {}", blochBooksCount);
- LOGGER.info("Number of authors : {}", authorsCount);
- LOGGER.info("DDD book : {}", dddBook);
- LOGGER.info("jBloch books : {}", blochBooks);
-
- HibernateUtil.getSessionFactory().close();
- }
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java b/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java
deleted file mode 100644
index 566bc19f872f..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/commandes/ICommandService.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.commandes;
-
-/**
- * This interface represents the commands of the CQRS pattern.
- */
-public interface ICommandService {
-
- void authorCreated(String username, String name, String email);
-
- void bookAddedToAuthor(String title, double price, String username);
-
- void authorNameUpdated(String username, String name);
-
- void authorUsernameUpdated(String oldUsername, String newUsername);
-
- void authorEmailUpdated(String username, String email);
-
- void bookTitleUpdated(String oldTitle, String newTitle);
-
- void bookPriceUpdated(String title, double price);
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java b/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
deleted file mode 100644
index aafc4efb1b09..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Author.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.domain.model;
-
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-/**
- * This is an Author entity. It is used by Hibernate for persistence.
- */
-@ToString
-@Getter
-@Setter
-@Entity
-public class Author {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private long id;
- private String username;
- private String name;
- private String email;
-
- /**
- * Constructor.
- *
- * @param username username 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;
- this.name = name;
- this.email = email;
- }
-
- protected Author() {
- }
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java b/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
deleted file mode 100644
index c885ffb7a846..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/domain/model/Book.java
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.domain.model;
-
-import javax.persistence.Entity;
-import javax.persistence.GeneratedValue;
-import javax.persistence.GenerationType;
-import javax.persistence.Id;
-import javax.persistence.ManyToOne;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-/**
- * This is a Book entity. It is used by Hibernate for persistence. Many books can be written by one
- * {@link Author}
- */
-@ToString
-@Setter
-@Getter
-@Entity
-public class Book {
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- private long id;
- private String title;
- private double price;
- @ManyToOne
- private Author author;
-
- /**
- * Constructor.
- *
- * @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) {
- this.title = title;
- this.price = price;
- this.author = author;
- }
-
- protected Book() {
- }
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java b/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java
deleted file mode 100644
index 46983b8c9ac3..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/dto/Author.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.dto;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
-
-/**
- * This is a DTO (Data Transfer Object) author, contains only useful information to be returned.
- */
-@ToString
-@EqualsAndHashCode
-@Getter
-@NoArgsConstructor
-@AllArgsConstructor
-public class Author {
-
- private String name;
- private String email;
- private String username;
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java b/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java
deleted file mode 100644
index 5088e98fe8ff..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/dto/Book.java
+++ /dev/null
@@ -1,45 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.dto;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.NoArgsConstructor;
-import lombok.ToString;
-
-/**
- * This is a DTO (Data Transfer Object) book, contains only useful information to be returned.
- */
-@ToString
-@EqualsAndHashCode
-@Getter
-@AllArgsConstructor
-@NoArgsConstructor
-public class Book {
-
- private String title;
- private double price;
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java b/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java
deleted file mode 100644
index 6a358f8e7cbe..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/queries/IQueryService.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.queries;
-
-import com.iluwatar.cqrs.dto.Author;
-import com.iluwatar.cqrs.dto.Book;
-import java.math.BigInteger;
-import java.util.List;
-
-/**
- * This interface represents the query methods of the CQRS pattern.
- */
-public interface IQueryService {
-
- Author getAuthorByUsername(String username);
-
- Book getBook(String title);
-
- List getAuthorBooks(String username);
-
- BigInteger getAuthorBooksCount(String username);
-
- BigInteger getAuthorsCount();
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java b/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
deleted file mode 100644
index 7f14af84bf22..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/queries/QueryServiceImpl.java
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.queries;
-
-import com.iluwatar.cqrs.constants.AppConstants;
-import com.iluwatar.cqrs.dto.Author;
-import com.iluwatar.cqrs.dto.Book;
-import com.iluwatar.cqrs.util.HibernateUtil;
-import java.math.BigInteger;
-import java.util.List;
-import org.hibernate.SessionFactory;
-import org.hibernate.transform.Transformers;
-
-/**
- * This class is an implementation of {@link IQueryService}. It uses Hibernate native queries to
- * return DTOs from the database.
- */
-public class QueryServiceImpl implements IQueryService {
-
- private final SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
-
- @Override
- public Author getAuthorByUsername(String username) {
- Author authorDTo;
- try (var session = sessionFactory.openSession()) {
- var sqlQuery = session.createSQLQuery("SELECT a.username as \"username\","
- + " a.name as \"name\", a.email as \"email\""
- + "FROM Author a where a.username=:username");
- sqlQuery.setParameter(AppConstants.USER_NAME, username);
- authorDTo = (Author) sqlQuery.setResultTransformer(Transformers.aliasToBean(Author.class))
- .uniqueResult();
- }
- return authorDTo;
- }
-
- @Override
- public Book getBook(String title) {
- Book bookDTo;
- try (var session = sessionFactory.openSession()) {
- var sqlQuery = session.createSQLQuery("SELECT b.title as \"title\","
- + " b.price as \"price\"" + " FROM Book b where b.title=:title");
- sqlQuery.setParameter("title", title);
- bookDTo =
- (Book) sqlQuery.setResultTransformer(Transformers.aliasToBean(Book.class)).uniqueResult();
- }
- return bookDTo;
- }
-
- @Override
- public List getAuthorBooks(String username) {
- List bookDTos;
- try (var session = sessionFactory.openSession()) {
- var sqlQuery = session.createSQLQuery("SELECT b.title as \"title\", b.price as \"price\""
- + " FROM Author a , Book b where b.author_id = a.id and a.username=:username");
- sqlQuery.setParameter(AppConstants.USER_NAME, username);
- bookDTos = sqlQuery.setResultTransformer(Transformers.aliasToBean(Book.class)).list();
- }
- return bookDTos;
- }
-
- @Override
- public BigInteger getAuthorBooksCount(String username) {
- BigInteger bookcount;
- try (var session = sessionFactory.openSession()) {
- var sqlQuery = session.createSQLQuery(
- "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();
- }
- return bookcount;
- }
-
- @Override
- public BigInteger getAuthorsCount() {
- BigInteger authorcount;
- try (var session = sessionFactory.openSession()) {
- var sqlQuery = session.createSQLQuery("SELECT count(id) from Author");
- authorcount = (BigInteger) sqlQuery.uniqueResult();
- }
- return authorcount;
- }
-
-}
diff --git a/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java b/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
deleted file mode 100644
index 5ae5358613b8..000000000000
--- a/cqrs/src/main/java/com/iluwatar/cqrs/util/HibernateUtil.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.cqrs.util;
-
-import lombok.extern.slf4j.Slf4j;
-import org.hibernate.SessionFactory;
-import org.hibernate.boot.MetadataSources;
-import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
-
-/**
- * This class simply returns one instance of {@link SessionFactory} initialized when the application
- * is started.
- */
-@Slf4j
-public class HibernateUtil {
-
- private static final SessionFactory SESSIONFACTORY = buildSessionFactory();
-
- private static SessionFactory buildSessionFactory() {
-
- // configures settings from hibernate.cfg.xml
- final var registry = new StandardServiceRegistryBuilder().configure().build();
- try {
- return new MetadataSources(registry).buildMetadata().buildSessionFactory();
- } catch (Exception ex) {
- StandardServiceRegistryBuilder.destroy(registry);
- LOGGER.error("Initial SessionFactory creation failed.", ex);
- throw new ExceptionInInitializerError(ex);
- }
- }
-
- public static SessionFactory getSessionFactory() {
- return SESSIONFACTORY;
- }
-
-}
diff --git a/curiously-recurring-template-pattern/README.md b/curiously-recurring-template-pattern/README.md
new file mode 100644
index 000000000000..e44bd7d53097
--- /dev/null
+++ b/curiously-recurring-template-pattern/README.md
@@ -0,0 +1,162 @@
+---
+title: "Curiously Recurring Template Pattern in Java: Leveraging Polymorphism Uniquely"
+shortTitle: Curiously Recurring Template Pattern (CRTP)
+description: "Discover the Curiously Recurring Template Pattern (CRTP) in Java. Learn how to achieve static polymorphism for efficient method overriding and compile-time polymorphic behavior. Perfect for performance-critical applications."
+language: en
+category: Structural
+tag:
+ - Code simplification
+ - Extensibility
+ - Generic
+ - Idiom
+ - Instantiation
+ - Polymorphism
+ - Recursion
+---
+
+## Also known as
+
+* CRTP
+* Mixin Inheritance
+* Recursive Type Bound
+* Recursive Generic
+* Static Polymorphism
+
+## Intent of Curiously Recurring Template Pattern
+
+The Curiously Recurring Template Pattern (CRTP) is a powerful design pattern in Java used to achieve static polymorphism. By having a class template derive from a template instantiation of its own class, CRTP enables method overriding and compile-time polymorphic behavior, enhancing efficiency and performance in your Java applications.
+
+## Detailed Explanation of Curiously Recurring Template Pattern with Real-World Examples
+
+Real-world example
+
+> Consider a scenario where a library system manages various types of media: books, DVDs, and magazines. Each media type has specific attributes and behaviors, but they all share common functionality like borrowing and returning. By applying the Curiously Recurring Template Pattern (CRTP) in Java, you can create a base template class `MediaItem` encompassing these common methods. Each specific media type (e.g., `Book`, `DVD`, `Magazine`) would inherit from `MediaItem` using itself as a template parameter. This approach allows each media type to customize shared functionality efficiently, avoiding the overhead of virtual methods.
+
+In plain words
+
+> The CRTP in Java ensures that certain methods within a type can accept arguments specific to its subtypes, enabling more efficient and type-safe polymorphic behavior at compile time.
+
+Wikipedia says
+
+> The curiously recurring template pattern (CRTP) is an idiom, originally in C++, in which a class X derives from a class template instantiation using X itself as a template argument.
+
+Flowchart
+
+
+
+## Programmatic example of CRTP in Java
+
+For a mixed martial arts promotion that is planning an event, ensuring that the fights are organized between athletes of the same weight class is crucial. This prevents mismatches between fighters of significantly different sizes, such as a heavyweight facing off against a bantamweight.
+
+Let's define the generic interface `Fighter`.
+
+```java
+public interface Fighter {
+
+ void fight(T t);
+
+}
+```
+
+The `MMAFighter` class is used to instantiate fighters distinguished by their weight class.
+
+```java
+@Slf4j
+@Data
+public class MmaFighter> implements Fighter {
+
+ private final String name;
+ private final String surname;
+ private final String nickName;
+ private final String speciality;
+
+ @Override
+ public void fight(T opponent) {
+ LOGGER.info("{} is going to fight against {}", this, opponent);
+ }
+}
+```
+
+The followings are some subtypes of `MmaFighter`.
+
+```java
+class MmaBantamweightFighter extends MmaFighter {
+
+ public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
+ super(name, surname, nickName, speciality);
+ }
+}
+
+public class MmaHeavyweightFighter extends MmaFighter {
+
+ public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
+ super(name, surname, nickName, speciality);
+ }
+}
+```
+
+A fighter is allowed to fight an opponent of the same weight classes. If the opponent is of a different weight class, an error is raised.
+
+```java
+public static void main(String[] args) {
+
+ MmaBantamweightFighter fighter1 = new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
+ MmaBantamweightFighter fighter2 = new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
+ fighter1.fight(fighter2);
+
+ MmaHeavyweightFighter fighter3 = new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
+ MmaHeavyweightFighter fighter4 = new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
+ fighter3.fight(fighter4);
+}
+```
+
+Program output:
+
+```
+08:42:34.048 [main] INFO crtp.MmaFighter -- MmaFighter(name=Joe, surname=Johnson, nickName=The Geek, speciality=Muay Thai) is going to fight against MmaFighter(name=Ed, surname=Edwards, nickName=The Problem Solver, speciality=Judo)
+08:42:34.054 [main] INFO crtp.MmaFighter -- MmaFighter(name=Dave, surname=Davidson, nickName=The Bug Smasher, speciality=Kickboxing) is going to fight against MmaFighter(name=Jack, surname=Jackson, nickName=The Pragmatic, speciality=Brazilian Jiu-Jitsu)
+```
+
+## When to Use the Curiously Recurring Template Pattern in Java
+
+* When you need to extend the functionality of a class through inheritance but prefer compile-time polymorphism to runtime polymorphism for efficiency reasons.
+* When you want to avoid the overhead of virtual functions but still achieve polymorphic behavior.
+* In template metaprogramming to provide implementations of functions or policies that can be selected at compile time.
+* You have type conflicts when chaining methods in an object hierarchy.
+* You want to use a parameterized class method that can accept subclasses of the class as arguments, allowing it to be applied to objects that inherit from the class.
+* You want certain methods to work only with instances of the same type, such as for achieving mutual comparability.
+
+## Curiously Recurring Template Pattern Java Tutorials
+
+* [Curiously Recurring Template Pattern in Java (The NuaH Blog)](https://nuah.livejournal.com/328187.html)
+
+## Real-World Applications of Curiously Recurring Template Pattern in Java
+
+* Implementing compile-time polymorphic interfaces in template libraries.
+* Enhancing code reuse in libraries where performance is critical, like in mathematical computations, embedded systems, and real-time processing applications.
+* Implementation of the `Cloneable` interface in various Java libraries.
+
+## Benefits and Trade-offs of Curiously Recurring Template Pattern
+
+Benefits:
+
+* Elimination of virtual function call overhead, enhancing performance.
+* Safe reuse of the base class code without the risks associated with multiple inheritances.
+* Greater flexibility and extensibility in compile-time polymorphism scenarios.
+
+Trade-offs:
+
+* Increased complexity in understanding and debugging due to the interplay of templates and inheritance.
+* Can lead to code bloat because each instantiation of a template results in a new class.
+* Less flexibility compared to runtime polymorphism as the behavior must be determined entirely at compile time.
+
+## Related Java Design Patterns
+
+* [Factory Method](https://java-design-patterns.com/patterns/factory-method/): Can be used in conjunction with CRTP to instantiate derived classes without knowing their specific types.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): CRTP can implement compile-time strategy selection.
+* [Template Method](https://java-design-patterns.com/patterns/template-method/): Similar in structure but differs in that CRTP achieves behavior variation through compile-time polymorphism.
+
+## References and Credits
+
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI)
+* [Effective Java](https://amzn.to/4cGk2Jz)
diff --git a/curiously-recurring-template-pattern/etc/crtp-flowchart.png b/curiously-recurring-template-pattern/etc/crtp-flowchart.png
new file mode 100644
index 000000000000..a205e1638ca0
Binary files /dev/null and b/curiously-recurring-template-pattern/etc/crtp-flowchart.png differ
diff --git a/aggregator-microservices/etc/aggregator-microservices.urm.puml b/curiously-recurring-template-pattern/etc/curiously-recurring-template-pattern.urm.puml
similarity index 100%
rename from aggregator-microservices/etc/aggregator-microservices.urm.puml
rename to curiously-recurring-template-pattern/etc/curiously-recurring-template-pattern.urm.puml
diff --git a/curiously-recurring-template-pattern/pom.xml b/curiously-recurring-template-pattern/pom.xml
new file mode 100644
index 000000000000..ab7ed0b19a36
--- /dev/null
+++ b/curiously-recurring-template-pattern/pom.xml
@@ -0,0 +1,72 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ curiously-recurring-template-pattern
+
+
+ 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.crtp.App
+
+
+
+
+
+
+
+
+
diff --git a/curiously-recurring-template-pattern/src/main/java/crtp/App.java b/curiously-recurring-template-pattern/src/main/java/crtp/App.java
new file mode 100644
index 000000000000..d592dd717f89
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/main/java/crtp/App.java
@@ -0,0 +1,55 @@
+/*
+ * 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 crtp;
+
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Shows the {@link Fighter} fight method call on some implementations of {@link MmaFighter}. Note
+ * that fighters can only fight against opponents of their same weight class.
+ */
+@Slf4j
+public class App {
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+
+ MmaBantamweightFighter fighter1 =
+ new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
+ MmaBantamweightFighter fighter2 =
+ new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo");
+ fighter1.fight(fighter2);
+
+ MmaHeavyweightFighter fighter3 =
+ new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing");
+ MmaHeavyweightFighter fighter4 =
+ new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu");
+ fighter3.fight(fighter4);
+ }
+}
diff --git a/curiously-recurring-template-pattern/src/main/java/crtp/Fighter.java b/curiously-recurring-template-pattern/src/main/java/crtp/Fighter.java
new file mode 100644
index 000000000000..da149229c8ba
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/main/java/crtp/Fighter.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 crtp;
+
+/**
+ * Fighter interface.
+ *
+ * @param The type of fighter.
+ */
+public interface Fighter {
+
+ void fight(T t);
+}
diff --git a/curiously-recurring-template-pattern/src/main/java/crtp/MmaBantamweightFighter.java b/curiously-recurring-template-pattern/src/main/java/crtp/MmaBantamweightFighter.java
new file mode 100644
index 000000000000..08886a3b371c
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/main/java/crtp/MmaBantamweightFighter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 crtp;
+
+/** MmaBantamweightFighter class. */
+class MmaBantamweightFighter extends MmaFighter {
+
+ public MmaBantamweightFighter(String name, String surname, String nickName, String speciality) {
+ super(name, surname, nickName, speciality);
+ }
+}
diff --git a/curiously-recurring-template-pattern/src/main/java/crtp/MmaFighter.java b/curiously-recurring-template-pattern/src/main/java/crtp/MmaFighter.java
new file mode 100644
index 000000000000..a62ad2811b38
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/main/java/crtp/MmaFighter.java
@@ -0,0 +1,48 @@
+/*
+ * 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 crtp;
+
+import lombok.Data;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * MmaFighter class.
+ *
+ * @param MmaFighter derived class that uses itself as type parameter.
+ */
+@Slf4j
+@Data
+public class MmaFighter> implements Fighter {
+
+ private final String name;
+ private final String surname;
+ private final String nickName;
+ private final String speciality;
+
+ @Override
+ public void fight(T opponent) {
+ LOGGER.info("{} is going to fight against {}", this, opponent);
+ }
+}
diff --git a/curiously-recurring-template-pattern/src/main/java/crtp/MmaHeavyweightFighter.java b/curiously-recurring-template-pattern/src/main/java/crtp/MmaHeavyweightFighter.java
new file mode 100644
index 000000000000..1ed545ede7ec
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/main/java/crtp/MmaHeavyweightFighter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 crtp;
+
+/** MmaHeavyweightFighter. */
+public class MmaHeavyweightFighter extends MmaFighter {
+
+ public MmaHeavyweightFighter(String name, String surname, String nickName, String speciality) {
+ super(name, surname, nickName, speciality);
+ }
+}
diff --git a/curiously-recurring-template-pattern/src/main/java/crtp/MmaLightweightFighter.java b/curiously-recurring-template-pattern/src/main/java/crtp/MmaLightweightFighter.java
new file mode 100644
index 000000000000..433b1934fa15
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/main/java/crtp/MmaLightweightFighter.java
@@ -0,0 +1,33 @@
+/*
+ * 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 crtp;
+
+/** MmaLightweightFighter class. */
+class MmaLightweightFighter extends MmaFighter {
+
+ public MmaLightweightFighter(String name, String surname, String nickName, String speciality) {
+ super(name, surname, nickName, speciality);
+ }
+}
diff --git a/curiously-recurring-template-pattern/src/test/java/crtp/AppTest.java b/curiously-recurring-template-pattern/src/test/java/crtp/AppTest.java
new file mode 100644
index 000000000000..fb19aa31a8d6
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/test/java/crtp/AppTest.java
@@ -0,0 +1,38 @@
+/*
+ * 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 crtp;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Application test */
+class AppTest {
+
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/curiously-recurring-template-pattern/src/test/java/crtp/FightTest.java b/curiously-recurring-template-pattern/src/test/java/crtp/FightTest.java
new file mode 100644
index 000000000000..a70edd0d674d
--- /dev/null
+++ b/curiously-recurring-template-pattern/src/test/java/crtp/FightTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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 crtp;
+
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.ArrayList;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+
+@Slf4j
+public class FightTest {
+
+ /**
+ * A fighter has signed a contract with a promotion, and he will face some other fighters. A list
+ * of opponents is ready but for some reason not all of them belong to the same weight class.
+ * Let's ensure that the fighter will only face opponents in the same weight class.
+ */
+ @Test
+ void testFighterCanFightOnlyAgainstSameWeightOpponents() {
+ MmaBantamweightFighter fighter =
+ new MmaBantamweightFighter("Joe", "Johnson", "The Geek", "Muay Thai");
+ List> opponents = getOpponents();
+ List> challenged = new ArrayList<>();
+
+ opponents.forEach(
+ challenger -> {
+ try {
+ ((MmaBantamweightFighter) challenger).fight(fighter);
+ challenged.add(challenger);
+ } catch (ClassCastException e) {
+ LOGGER.error(e.getMessage());
+ }
+ });
+
+ assertFalse(challenged.isEmpty());
+ assertTrue(challenged.stream().allMatch(c -> c instanceof MmaBantamweightFighter));
+ }
+
+ private static List> getOpponents() {
+ return List.of(
+ new MmaBantamweightFighter("Ed", "Edwards", "The Problem Solver", "Judo"),
+ new MmaLightweightFighter("Evan", "Evans", "Clean Coder", "Sambo"),
+ new MmaHeavyweightFighter("Dave", "Davidson", "The Bug Smasher", "Kickboxing"),
+ new MmaBantamweightFighter("Ray", "Raymond", "Scrum Master", "Karate"),
+ new MmaHeavyweightFighter("Jack", "Jackson", "The Pragmatic", "Brazilian Jiu-Jitsu"));
+ }
+}
diff --git a/currying/README.md b/currying/README.md
new file mode 100644
index 000000000000..b0e86856df03
--- /dev/null
+++ b/currying/README.md
@@ -0,0 +1,237 @@
+---
+title: "Currying Pattern in Java: Enhancing Function Flexibility and Reusability"
+shortTitle: Currying
+description: "Learn about currying in Java, a technique to simplify functions by breaking them into a sequence of single-argument functions. Discover its applications, benefits, and examples in this comprehensive guide."
+category: Functional
+language: en
+tag:
+ - Code simplification
+ - Functional decomposition
+ - Generic
+ - Immutable
+---
+
+## Also known as
+
+* Partial Function Application
+
+## Intent of Currying Design Pattern
+
+Currying decomposes a function that takes multiple arguments into a sequence of functions that each take a single argument. This technique is integral in functional programming, enabling the creation of higher-order functions through partial application of its arguments. Using currying in Java can lead to more modular, reusable, and maintainable code.
+
+## Detailed Explanation of Currying Pattern with Real-World Examples
+
+Real-world example
+
+> Currying in programming can be compared to an assembly line in a factory. Imagine a car manufacturing process where each station on the assembly line performs a specific task, such as installing the engine, painting the car, and adding the wheels. Each station takes a partially completed car and performs a single operation before passing it to the next station. Similarly, in currying, a function that requires multiple arguments is broken down into a series of functions, each taking a single argument and returning another function until all arguments are provided. This step-by-step processing simplifies complex tasks by dividing them into manageable, sequential operations, which is especially useful in Java functional programming.
+
+In plain words
+
+> Decompose a function that take multiple arguments into multiple functions that take a single argument.
+
+Wikipedia says
+
+> In mathematics and computer science, currying is the technique of translating a function that takes multiple arguments into a sequence of families of functions, each taking a single argument.
+
+Sequence diagram
+
+
+
+## Programmatic example of Currying Pattern in Java
+
+Consider a librarian who wants to populate their library with books. The librarian wants functions which can create books corresponding to specific genres and authors. Currying makes this possible by writing a curried book builder function and utilising partial application.
+
+We have a `Book` class and `Genre` enum.
+
+```java
+public class Book {
+ private final Genre genre;
+ private final String author;
+ private final String title;
+ private final LocalDate publicationDate;
+
+ Book(Genre genre, String author, String title, LocalDate publicationDate) {
+ this.genre = genre;
+ this.author = author;
+ this.title = title;
+ this.publicationDate = publicationDate;
+ }
+}
+```
+
+```java
+public enum Genre {
+ FANTASY,
+ HORROR,
+ SCI_FI
+}
+```
+
+We could easily create a `Book` object with the following method:
+
+```java
+Book createBook(Genre genre, String author, String title, LocalDate publicationDate) {
+ return new Book(genre, author, title, publicationDate);
+}
+```
+
+However, what if we only wanted to create books from the `FANTASY` genre? Passing the `FANTASY` parameter with each method call would be repetitive. Alternatively, we could define a new method specifically for creating `FANTASY` books, but it would be impractical to create a separate method for each genre. The solution is to use a curried function.
+
+```java
+static Function>>> book_creator
+ = bookGenre
+ -> bookAuthor
+ -> bookTitle
+ -> bookPublicationDate
+ -> new Book(bookGenre, bookAuthor, bookTitle, bookPublicationDate);
+```
+
+Note that the order of the parameters is important. `genre` must come before `author`, `author` must come before `title` and so on. We must be considerate of this when writing curried functions to take full advantage of partial application. Using the above function, we can define a new function `fantasyBookFunc`, to generate `FANTASY` books as follows:
+
+```java
+Function>> fantasyBookFunc = Book.book_creator.apply(Genre.FANTASY);
+```
+
+Unfortunately, the type signature of `BOOK_CREATOR` and `fantasyBookFunc` are difficult to read and understand. We can improve this by using the [builder pattern](https://java-design-patterns.com/patterns/builder/) and functional interfaces.
+
+```java
+public static AddGenre builder() {
+ return genre
+ -> author
+ -> title
+ -> publicationDate
+ -> new Book(genre, author, title, publicationDate);
+}
+
+public interface AddGenre {
+ Book.AddAuthor withGenre(Genre genre);
+}
+
+public interface AddAuthor {
+ Book.AddTitle withAuthor(String author);
+}
+
+public interface AddTitle {
+ Book.AddPublicationDate withTitle(String title);
+}
+
+public interface AddPublicationDate {
+ Book withPublicationDate(LocalDate publicationDate);
+}
+```
+
+The semantics of the `builder` function can easily be understood. The `builder` function returns a function `AddGenre`, which adds the genre to the book. Similarity, the `AddGenre` function returns another function `AddTitle`, which adds the title to the book and so on, until the `AddPublicationDate` function returns a `Book`. For example, we could create a `Book` as follows:
+
+```java
+Book book = Book.builder().withGenre(Genre.FANTASY)
+ .withAuthor("Author")
+ .withTitle("Title")
+ .withPublicationDate(LocalDate.of(2000, 7, 2));
+```
+
+The below example demonstrates how partial application can be used with the `builder` function to create specialised book builder functions.
+
+```java
+public static void main(String[] args) {
+ LOGGER.info("Librarian begins their work.");
+
+ // Defining genre book functions
+ Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
+ Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
+ Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCIFI);
+
+ // Defining author book functions
+ Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
+ Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King");
+ Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling");
+
+ // Creates books by Stephen King (horror and fantasy genres)
+ Book shining = kingHorrorBooksFunc.withTitle("The Shining")
+ .withPublicationDate(LocalDate.of(1977, 1, 28));
+ Book darkTower = kingFantasyBooksFunc.withTitle("The Dark Tower: Gunslinger")
+ .withPublicationDate(LocalDate.of(1982, 6, 10));
+
+ // Creates fantasy books by J.K. Rowling
+ Book chamberOfSecrets = rowlingFantasyBooksFunc.withTitle("Harry Potter and the Chamber of Secrets")
+ .withPublicationDate(LocalDate.of(1998, 7, 2));
+
+ // Create sci-fi books
+ Book dune = scifiBookFunc.withAuthor("Frank Herbert")
+ .withTitle("Dune")
+ .withPublicationDate(LocalDate.of(1965, 8, 1));
+ Book foundation = scifiBookFunc.withAuthor("Isaac Asimov")
+ .withTitle("Foundation")
+ .withPublicationDate(LocalDate.of(1942, 5, 1));
+
+ LOGGER.info("Stephen King Books:");
+ LOGGER.info(shining.toString());
+ LOGGER.info(darkTower.toString());
+
+ LOGGER.info("J.K. Rowling Books:");
+ LOGGER.info(chamberOfSecrets.toString());
+
+ LOGGER.info("Sci-fi Books:");
+ LOGGER.info(dune.toString());
+ LOGGER.info(foundation.toString());
+}
+```
+
+Program output:
+
+```
+09:04:52.499 [main] INFO com.iluwatar.currying.App -- Librarian begins their work.
+09:04:52.502 [main] INFO com.iluwatar.currying.App -- Stephen King Books:
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=HORROR, author='Stephen King', title='The Shining', publicationDate=1977-01-28}
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=FANTASY, author='Stephen King', title='The Dark Tower: Gunslinger', publicationDate=1982-06-10}
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- J.K. Rowling Books:
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=FANTASY, author='J.K. Rowling', title='Harry Potter and the Chamber of Secrets', publicationDate=1998-07-02}
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- Sci-fi Books:
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Frank Herbert', title='Dune', publicationDate=1965-08-01}
+09:04:52.506 [main] INFO com.iluwatar.currying.App -- Book{genre=SCIFI, author='Isaac Asimov', title='Foundation', publicationDate=1942-05-01}
+```
+
+## When to Use the Currying Pattern in Java
+
+* When functions need to be called with some arguments preset in Java.
+* In functional programming languages or paradigms to simplify functions that take multiple arguments.
+* To improve code reusability and composability by breaking down functions into simpler, unary functions, enhancing the modularity of Java applications.
+
+## Currying Pattern Java Tutorials
+
+* [Currying in Java (Baeldung)](https://www.baeldung.com/java-currying)
+* [What Is Currying in Programming (Towards Data Science)](https://towardsdatascience.com/what-is-currying-in-programming-56fd57103431#:~:text=Currying%20is%20helpful%20when%20you,concise%2C%20and%20more%20readable%20solution.)
+* [Why the fudge should I use currying? (DailyJS)](https://medium.com/dailyjs/why-the-fudge-should-i-use-currying-84e4000c8743)
+
+## Real-World Applications of Currying Pattern in Java
+
+* Functional programming languages like Haskell, Scala, and JavaScript.
+* Java programming, especially with lambda expressions and streams introduced in Java 8.
+* Event handling in UIs where a function with specific parameters needs to be triggered upon an event.
+* APIs that require configuration with multiple parameters.
+
+## Benefits and Trade-offs of Currying Pattern
+
+Benefits:
+
+* Increases function reusability by allowing the creation of specialized functions from more generic ones.
+* Enhances code readability and maintainability by breaking complex functions into simpler, single-argument functions.
+* Facilitates function composition, leading to more declarative and concise code.
+
+Trade-offs:
+
+* Can lead to performance overhead due to the creation of additional closures.
+* May make debugging more challenging, as it introduces additional layers of function calls.
+* Can be less intuitive for developers unfamiliar with functional programming concepts.
+* As shown in the programmatic example above, curried functions with several parameters have a cumbersome type signature in Java.
+
+## Related Java Design Patterns
+
+* Function Composition: Currying is often used in conjunction with function composition to enable more readable and concise code.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): While not the same, currying shares the decorator pattern's concept of wrapping functionality.
+* [Factory](https://java-design-patterns.com/patterns/factory/): Currying can be used to create factory functions that produce variations of a function with certain arguments preset.
+
+## References and Credits
+
+* [Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions](https://amzn.to/3TKeZPD)
+* [Java 8 in Action: Lambdas, Streams, and functional-style programming](https://amzn.to/3J6vEaW)
+* [Modern Java in Action: Lambdas, streams, functional and reactive programming](https://amzn.to/3J6vJLM)
diff --git a/currying/etc/currying-sequence-diagram.png b/currying/etc/currying-sequence-diagram.png
new file mode 100644
index 000000000000..7c3300263566
Binary files /dev/null and b/currying/etc/currying-sequence-diagram.png differ
diff --git a/currying/etc/currying.urm.png b/currying/etc/currying.urm.png
new file mode 100644
index 000000000000..c672bdde37cf
Binary files /dev/null and b/currying/etc/currying.urm.png differ
diff --git a/currying/etc/currying.urm.puml b/currying/etc/currying.urm.puml
new file mode 100644
index 000000000000..9b336ff90828
--- /dev/null
+++ b/currying/etc/currying.urm.puml
@@ -0,0 +1,39 @@
+@startuml
+package com.iluwatar.currying {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class Book {
+ ~ BOOK_CREATOR : Function>>> {static}
+ - author : String
+ - genre : Genre
+ - publicationDate : LocalDate
+ - title : String
+ ~ Book(genre : Genre, author : String, title : String, publicationDate : LocalDate)
+ + builder() : AddGenre {static}
+ }
+ interface AddAuthor {
+ + withAuthor(String) : AddTitle {abstract}
+ }
+ interface AddGenre {
+ + withGenre(Genre) : AddAuthor {abstract}
+ }
+ interface AddPublicationDate {
+ + withPublicationDate(LocalDate) : Book {abstract}
+ }
+ interface AddTitle {
+ + withTitle(String) : AddPublicationDate {abstract}
+ }
+ enum Genre {
+ + FANTASY {static}
+ + HORROR {static}
+ + SCI_FI {static}
+ }
+}
+Book --> "-genre" Genre
+AddPublicationDate ..+ Book
+AddAuthor ..+ Book
+AddTitle ..+ Book
+AddGenre ..+ Book
+@enduml
\ No newline at end of file
diff --git a/currying/pom.xml b/currying/pom.xml
new file mode 100644
index 000000000000..5244aa2792d8
--- /dev/null
+++ b/currying/pom.xml
@@ -0,0 +1,74 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ currying
+
+
+ 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.currying.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/currying/src/main/java/com/iluwatar/currying/App.java b/currying/src/main/java/com/iluwatar/currying/App.java
new file mode 100644
index 000000000000..d919887530a2
--- /dev/null
+++ b/currying/src/main/java/com/iluwatar/currying/App.java
@@ -0,0 +1,92 @@
+/*
+ * 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.currying;
+
+import java.time.LocalDate;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Currying decomposes a function with multiple arguments in multiple functions that take a single
+ * argument. A curried function which has only been passed some of its arguments is called a partial
+ * application. Partial application is useful since it can be used to create specialised functions
+ * in a concise way.
+ *
+ * In this example, a librarian uses a curried book builder function create books belonging to
+ * desired genres and written by specific authors.
+ */
+@Slf4j
+public class App {
+ /** Main entry point of the program. */
+ public static void main(String[] args) {
+ LOGGER.info("Librarian begins their work.");
+
+ // Defining genre book functions
+ Book.AddAuthor fantasyBookFunc = Book.builder().withGenre(Genre.FANTASY);
+ Book.AddAuthor horrorBookFunc = Book.builder().withGenre(Genre.HORROR);
+ Book.AddAuthor scifiBookFunc = Book.builder().withGenre(Genre.SCIFI);
+
+ // Defining author book functions
+ Book.AddTitle kingFantasyBooksFunc = fantasyBookFunc.withAuthor("Stephen King");
+ Book.AddTitle kingHorrorBooksFunc = horrorBookFunc.withAuthor("Stephen King");
+ Book.AddTitle rowlingFantasyBooksFunc = fantasyBookFunc.withAuthor("J.K. Rowling");
+
+ // Creates books by Stephen King (horror and fantasy genres)
+ Book shining =
+ kingHorrorBooksFunc.withTitle("The Shining").withPublicationDate(LocalDate.of(1977, 1, 28));
+ Book darkTower =
+ kingFantasyBooksFunc
+ .withTitle("The Dark Tower: Gunslinger")
+ .withPublicationDate(LocalDate.of(1982, 6, 10));
+
+ // Creates fantasy books by J.K. Rowling
+ Book chamberOfSecrets =
+ rowlingFantasyBooksFunc
+ .withTitle("Harry Potter and the Chamber of Secrets")
+ .withPublicationDate(LocalDate.of(1998, 7, 2));
+
+ // Create sci-fi books
+ Book dune =
+ scifiBookFunc
+ .withAuthor("Frank Herbert")
+ .withTitle("Dune")
+ .withPublicationDate(LocalDate.of(1965, 8, 1));
+ Book foundation =
+ scifiBookFunc
+ .withAuthor("Isaac Asimov")
+ .withTitle("Foundation")
+ .withPublicationDate(LocalDate.of(1942, 5, 1));
+
+ LOGGER.info("Stephen King Books:");
+ LOGGER.info(shining.toString());
+ LOGGER.info(darkTower.toString());
+
+ LOGGER.info("J.K. Rowling Books:");
+ LOGGER.info(chamberOfSecrets.toString());
+
+ LOGGER.info("Sci-fi Books:");
+ LOGGER.info(dune.toString());
+ LOGGER.info(foundation.toString());
+ }
+}
diff --git a/currying/src/main/java/com/iluwatar/currying/Book.java b/currying/src/main/java/com/iluwatar/currying/Book.java
new file mode 100644
index 000000000000..7ec4ed8cb448
--- /dev/null
+++ b/currying/src/main/java/com/iluwatar/currying/Book.java
@@ -0,0 +1,113 @@
+/*
+ * 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.currying;
+
+import java.time.LocalDate;
+import java.util.Objects;
+import java.util.function.Function;
+import lombok.AllArgsConstructor;
+
+/** Book class. */
+@AllArgsConstructor
+public class Book {
+ private final Genre genre;
+ private final String author;
+ private final String title;
+ private final LocalDate publicationDate;
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ Book book = (Book) o;
+ return Objects.equals(author, book.author)
+ && Objects.equals(genre, book.genre)
+ && Objects.equals(title, book.title)
+ && Objects.equals(publicationDate, book.publicationDate);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(author, genre, title, publicationDate);
+ }
+
+ @Override
+ public String toString() {
+ return "Book{"
+ + "genre="
+ + genre
+ + ", author='"
+ + author
+ + '\''
+ + ", title='"
+ + title
+ + '\''
+ + ", publicationDate="
+ + publicationDate
+ + '}';
+ }
+
+ /** Curried book builder/creator function. */
+ static Function>>>
+ book_creator =
+ bookGenre ->
+ bookAuthor ->
+ bookTitle ->
+ bookPublicationDate ->
+ new Book(bookGenre, bookAuthor, bookTitle, bookPublicationDate);
+
+ /**
+ * Implements the builder pattern using functional interfaces to create a more readable book
+ * creator function. This function is equivalent to the BOOK_CREATOR function.
+ */
+ public static AddGenre builder() {
+ return genre ->
+ author -> title -> publicationDate -> new Book(genre, author, title, publicationDate);
+ }
+
+ /** Functional interface which adds the genre to a book. */
+ public interface AddGenre {
+ Book.AddAuthor withGenre(Genre genre);
+ }
+
+ /** Functional interface which adds the author to a book. */
+ public interface AddAuthor {
+ Book.AddTitle withAuthor(String author);
+ }
+
+ /** Functional interface which adds the title to a book. */
+ public interface AddTitle {
+ Book.AddPublicationDate withTitle(String title);
+ }
+
+ /** Functional interface which adds the publication date to a book. */
+ public interface AddPublicationDate {
+ Book withPublicationDate(LocalDate publicationDate);
+ }
+}
diff --git a/currying/src/main/java/com/iluwatar/currying/Genre.java b/currying/src/main/java/com/iluwatar/currying/Genre.java
new file mode 100644
index 000000000000..ad41f4004190
--- /dev/null
+++ b/currying/src/main/java/com/iluwatar/currying/Genre.java
@@ -0,0 +1,32 @@
+/*
+ * 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.currying;
+
+/** Enum representing different book genres. */
+public enum Genre {
+ FANTASY,
+ HORROR,
+ SCIFI
+}
diff --git a/currying/src/test/java/com/iluwatar/currying/AppTest.java b/currying/src/test/java/com/iluwatar/currying/AppTest.java
new file mode 100644
index 000000000000..3074628f29e6
--- /dev/null
+++ b/currying/src/test/java/com/iluwatar/currying/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.currying;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that the App can be run without throwing any exceptions. */
+class AppTest {
+ @Test
+ void executesWithoutExceptions() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/currying/src/test/java/com/iluwatar/currying/BookCurryingTest.java b/currying/src/test/java/com/iluwatar/currying/BookCurryingTest.java
new file mode 100644
index 000000000000..f22ff62850e5
--- /dev/null
+++ b/currying/src/test/java/com/iluwatar/currying/BookCurryingTest.java
@@ -0,0 +1,75 @@
+/*
+ * 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.currying;
+
+import static org.junit.jupiter.api.Assertions.*;
+
+import java.time.LocalDate;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+/** Unit tests for the Book class */
+class BookCurryingTest {
+ private static Book expectedBook;
+
+ @BeforeAll
+ public static void initialiseBook() {
+ expectedBook = new Book(Genre.FANTASY, "Dave", "Into the Night", LocalDate.of(2002, 4, 7));
+ }
+
+ /** Tests that the expected book can be created via curried functions */
+ @Test
+ void createsExpectedBook() {
+ Book builderCurriedBook =
+ Book.builder()
+ .withGenre(Genre.FANTASY)
+ .withAuthor("Dave")
+ .withTitle("Into the Night")
+ .withPublicationDate(LocalDate.of(2002, 4, 7));
+
+ Book funcCurriedBook =
+ Book.book_creator
+ .apply(Genre.FANTASY)
+ .apply("Dave")
+ .apply("Into the Night")
+ .apply(LocalDate.of(2002, 4, 7));
+
+ assertEquals(expectedBook, builderCurriedBook);
+ assertEquals(expectedBook, funcCurriedBook);
+ }
+
+ /** Tests that an intermediate curried function can be used to create the expected book */
+ @Test
+ void functionCreatesExpectedBook() {
+ Book.AddTitle daveFantasyBookFunc = Book.builder().withGenre(Genre.FANTASY).withAuthor("Dave");
+
+ Book curriedBook =
+ daveFantasyBookFunc
+ .withTitle("Into the Night")
+ .withPublicationDate(LocalDate.of(2002, 4, 7));
+
+ assertEquals(expectedBook, curriedBook);
+ }
+}
diff --git a/dao/README.md b/dao/README.md
deleted file mode 100644
index da8b3105c9c9..000000000000
--- a/dao/README.md
+++ /dev/null
@@ -1,162 +0,0 @@
----
-layout: pattern
-title: Data Access Object
-folder: dao
-permalink: /patterns/dao/
-categories: Architectural
-language: en
-tags:
- - Data access
----
-
-## Intent
-
-Object provides an abstract interface to some type of database or other persistence mechanism.
-
-## Explanation
-
-Real world example
-
-> There's a set of customers that need to be persisted to database. Additionally we need the whole
-> set of CRUD (create/read/update/delete) operations so we can operate on customers easily.
-
-In plain words
-
-> DAO is an interface we provide over the base persistence mechanism.
-
-Wikipedia says
-
-> In computer software, a data access object (DAO) is a pattern that provides an abstract interface
-> to some type of database or other persistence mechanism.
-
-**Programmatic Example**
-
-Walking through our customers example, here's the basic `Customer` entity.
-
-```java
-public class Customer {
-
- private int id;
- private String firstName;
- private String lastName;
-
- public Customer(int id, String firstName, String lastName) {
- this.id = id;
- this.firstName = firstName;
- this.lastName = lastName;
- }
- // getters and setters ->
- ...
-}
-```
-
-Here's the `CustomerDao` interface and two different implementations for it. `InMemoryCustomerDao`
-keeps a simple map of customers in memory while `DBCustomerDao` is the real RDBMS implementation.
-
-```java
-public interface CustomerDao {
-
- Stream getAll() throws Exception;
-
- Optional getById(int id) throws Exception;
-
- boolean add(Customer customer) throws Exception;
-
- boolean update(Customer customer) throws Exception;
-
- boolean delete(Customer customer) throws Exception;
-}
-
-public class InMemoryCustomerDao implements CustomerDao {
-
- private final Map idToCustomer = new HashMap<>();
-
- // implement the interface using the map
- ...
-}
-
-@Slf4j
-public class DbCustomerDao implements CustomerDao {
-
- private final DataSource dataSource;
-
- public DbCustomerDao(DataSource dataSource) {
- this.dataSource = dataSource;
- }
-
- // implement the interface using the data source
- ...
-```
-
-Finally here's how we use our DAO to manage customers.
-
-```java
- final var dataSource = createDataSource();
- createSchema(dataSource);
- final var customerDao = new DbCustomerDao(dataSource);
-
- addCustomers(customerDao);
- log.info(ALL_CUSTOMERS);
- try (var customerStream = customerDao.getAll()) {
- customerStream.forEach((customer) -> log.info(customer.toString()));
- }
- log.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
- final var customer = new Customer(4, "Dan", "Danson");
- customerDao.add(customer);
- log.info(ALL_CUSTOMERS + customerDao.getAll());
- customer.setFirstName("Daniel");
- customer.setLastName("Danielson");
- customerDao.update(customer);
- log.info(ALL_CUSTOMERS);
- try (var customerStream = customerDao.getAll()) {
- customerStream.forEach((cust) -> log.info(cust.toString()));
- }
- customerDao.delete(customer);
- log.info(ALL_CUSTOMERS + customerDao.getAll());
-
- deleteSchema(dataSource);
-```
-
-The program output:
-
-```java
-customerDao.getAllCustomers():
-Customer{id=1, firstName='Adam', lastName='Adamson'}
-Customer{id=2, firstName='Bob', lastName='Bobson'}
-Customer{id=3, firstName='Carl', lastName='Carlson'}
-customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}]
-customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@7cef4e59
-customerDao.getAllCustomers():
-Customer{id=1, firstName='Adam', lastName='Adamson'}
-Customer{id=2, firstName='Bob', lastName='Bobson'}
-Customer{id=3, firstName='Carl', lastName='Carlson'}
-Customer{id=4, firstName='Daniel', lastName='Danielson'}
-customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@2db0f6b2
-customerDao.getAllCustomers():
-Customer{id=1, firstName='Adam', lastName='Adamson'}
-Customer{id=2, firstName='Bob', lastName='Bobson'}
-Customer{id=3, firstName='Carl', lastName='Carlson'}
-customerDao.getCustomerById(2): Optional[Customer{id=2, firstName='Bob', lastName='Bobson'}]
-customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@12c8a2c0
-customerDao.getAllCustomers():
-Customer{id=1, firstName='Adam', lastName='Adamson'}
-Customer{id=2, firstName='Bob', lastName='Bobson'}
-Customer{id=3, firstName='Carl', lastName='Carlson'}
-Customer{id=4, firstName='Daniel', lastName='Danielson'}
-customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@6ec8211c
-```
-
-## Class diagram
-
-
-
-## Applicability
-
-Use the Data Access Object in any of the following situations:
-
-* When you want to consolidate how the data layer is accessed.
-* When you want to avoid writing multiple data retrieval/persistence layers.
-
-## Credits
-
-* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
diff --git a/dao/pom.xml b/dao/pom.xml
deleted file mode 100644
index 0149a91ea707..000000000000
--- a/dao/pom.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
- dao
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- com.h2database
- h2
-
-
- org.mockito
- mockito-core
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.dao.App
-
-
-
-
-
-
-
-
-
diff --git a/dao/src/main/java/com/iluwatar/dao/App.java b/dao/src/main/java/com/iluwatar/dao/App.java
deleted file mode 100644
index 5c1d36923803..000000000000
--- a/dao/src/main/java/com/iluwatar/dao/App.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.dao;
-
-import java.sql.SQLException;
-import java.util.List;
-import javax.sql.DataSource;
-import lombok.extern.slf4j.Slf4j;
-import org.h2.jdbcx.JdbcDataSource;
-
-/**
- * Data Access Object (DAO) is an object that provides an abstract interface to some type of
- * database or other persistence mechanism. By mapping application calls to the persistence layer,
- * DAO provide some specific data operations without exposing details of the database. This
- * isolation supports the Single responsibility principle. It separates what data accesses the
- * application needs, in terms of domain-specific objects and data types (the public interface of
- * the DAO), from how these needs can be satisfied with a specific DBMS.
- *
- * With the DAO pattern, we can use various method calls to retrieve/add/delete/update data
- * without directly interacting with the data source. The below example demonstrates basic CRUD
- * operations: select, add, update, and delete.
- */
-@Slf4j
-public class App {
- private static final String DB_URL = "jdbc:h2:~/dao";
- private static final String ALL_CUSTOMERS = "customerDao.getAllCustomers(): ";
-
- /**
- * Program entry point.
- *
- * @param args command line args.
- * @throws Exception if any error occurs.
- */
- public static void main(final String[] args) throws Exception {
- final var inMemoryDao = new InMemoryCustomerDao();
- performOperationsUsing(inMemoryDao);
-
- final var dataSource = createDataSource();
- createSchema(dataSource);
- final var dbDao = new DbCustomerDao(dataSource);
- performOperationsUsing(dbDao);
- deleteSchema(dataSource);
- }
-
- private static void deleteSchema(DataSource dataSource) throws SQLException {
- try (var connection = dataSource.getConnection();
- var statement = connection.createStatement()) {
- statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
- }
- }
-
- private static void createSchema(DataSource dataSource) throws SQLException {
- try (var connection = dataSource.getConnection();
- var statement = connection.createStatement()) {
- statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
- }
- }
-
- private static DataSource createDataSource() {
- var dataSource = new JdbcDataSource();
- dataSource.setURL(DB_URL);
- return dataSource;
- }
-
- private static void performOperationsUsing(final CustomerDao customerDao) throws Exception {
- addCustomers(customerDao);
- LOGGER.info(ALL_CUSTOMERS);
- try (var customerStream = customerDao.getAll()) {
- customerStream.forEach(customer -> LOGGER.info(customer.toString()));
- }
- LOGGER.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
- final var customer = new Customer(4, "Dan", "Danson");
- customerDao.add(customer);
- LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
- customer.setFirstName("Daniel");
- customer.setLastName("Danielson");
- customerDao.update(customer);
- LOGGER.info(ALL_CUSTOMERS);
- try (var customerStream = customerDao.getAll()) {
- customerStream.forEach(cust -> LOGGER.info(cust.toString()));
- }
- customerDao.delete(customer);
- LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
- }
-
- private static void addCustomers(CustomerDao customerDao) throws Exception {
- for (var customer : generateSampleCustomers()) {
- customerDao.add(customer);
- }
- }
-
- /**
- * Generate customers.
- *
- * @return list of customers.
- */
- public static List generateSampleCustomers() {
- final var customer1 = new Customer(1, "Adam", "Adamson");
- final var customer2 = new Customer(2, "Bob", "Bobson");
- final var customer3 = new Customer(3, "Carl", "Carlson");
- return List.of(customer1, customer2, customer3);
- }
-}
diff --git a/dao/src/main/java/com/iluwatar/dao/Customer.java b/dao/src/main/java/com/iluwatar/dao/Customer.java
deleted file mode 100644
index d5c0c583efe3..000000000000
--- a/dao/src/main/java/com/iluwatar/dao/Customer.java
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.dao;
-
-import lombok.AllArgsConstructor;
-import lombok.EqualsAndHashCode;
-import lombok.Getter;
-import lombok.Setter;
-import lombok.ToString;
-
-/**
- * A customer POJO that represents the data that will be read from the data source.
- */
-@Setter
-@Getter
-@ToString
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
-@AllArgsConstructor
-public class Customer {
-
- @EqualsAndHashCode.Include
- private int id;
- private String firstName;
- private String lastName;
-
-}
diff --git a/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java b/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java
deleted file mode 100644
index 6065b32bba46..000000000000
--- a/dao/src/main/java/com/iluwatar/dao/DbCustomerDao.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.dao;
-
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.util.Optional;
-import java.util.Spliterator;
-import java.util.Spliterators;
-import java.util.function.Consumer;
-import java.util.stream.Stream;
-import java.util.stream.StreamSupport;
-import javax.sql.DataSource;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * An implementation of {@link CustomerDao} that persists customers in RDBMS.
- */
-@Slf4j
-@RequiredArgsConstructor
-public class DbCustomerDao implements CustomerDao {
-
- private final DataSource dataSource;
-
- /**
- * Get all customers as Java Stream.
- *
- * @return a lazily populated stream of customers. Note the stream returned must be closed to free
- * all the acquired resources. The stream keeps an open connection to the database till it is
- * complete or is closed manually.
- */
- @Override
- public Stream getAll() throws Exception {
- try {
- var connection = getConnection();
- var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); // NOSONAR
- var resultSet = statement.executeQuery(); // NOSONAR
- return StreamSupport.stream(new Spliterators.AbstractSpliterator(Long.MAX_VALUE,
- Spliterator.ORDERED) {
-
- @Override
- public boolean tryAdvance(Consumer super Customer> action) {
- try {
- if (!resultSet.next()) {
- return false;
- }
- action.accept(createCustomer(resultSet));
- return true;
- } catch (SQLException e) {
- throw new RuntimeException(e); // NOSONAR
- }
- }
- }, false).onClose(() -> mutedClose(connection, statement, resultSet));
- } catch (SQLException e) {
- throw new CustomException(e.getMessage(), e);
- }
- }
-
- private Connection getConnection() throws SQLException {
- return dataSource.getConnection();
- }
-
- private void mutedClose(Connection connection, PreparedStatement statement, ResultSet resultSet) {
- try {
- resultSet.close();
- statement.close();
- connection.close();
- } catch (SQLException e) {
- LOGGER.info("Exception thrown " + e.getMessage());
- }
- }
-
- private Customer createCustomer(ResultSet resultSet) throws SQLException {
- return new Customer(resultSet.getInt("ID"),
- resultSet.getString("FNAME"),
- resultSet.getString("LNAME"));
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Optional getById(int id) throws Exception {
-
- ResultSet resultSet = null;
-
- try (var connection = getConnection();
- var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
-
- statement.setInt(1, id);
- resultSet = statement.executeQuery();
- if (resultSet.next()) {
- return Optional.of(createCustomer(resultSet));
- } else {
- return Optional.empty();
- }
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- } finally {
- if (resultSet != null) {
- resultSet.close();
- }
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean add(Customer customer) throws Exception {
- if (getById(customer.getId()).isPresent()) {
- return false;
- }
-
- try (var connection = getConnection();
- var statement = connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) {
- statement.setInt(1, customer.getId());
- statement.setString(2, customer.getFirstName());
- statement.setString(3, customer.getLastName());
- statement.execute();
- return true;
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean update(Customer customer) throws Exception {
- try (var connection = getConnection();
- var statement =
- connection
- .prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) {
- statement.setString(1, customer.getFirstName());
- statement.setString(2, customer.getLastName());
- statement.setInt(3, customer.getId());
- return statement.executeUpdate() > 0;
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean delete(Customer customer) throws Exception {
- try (var connection = getConnection();
- var statement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
- statement.setInt(1, customer.getId());
- return statement.executeUpdate() > 0;
- } catch (SQLException ex) {
- throw new CustomException(ex.getMessage(), ex);
- }
- }
-}
diff --git a/dao/src/test/java/com/iluwatar/dao/AppTest.java b/dao/src/test/java/com/iluwatar/dao/AppTest.java
deleted file mode 100644
index ccd123374225..000000000000
--- a/dao/src/test/java/com/iluwatar/dao/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.dao;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * Tests that DAO 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.
- */
-
- @Test
- void shouldExecuteDaoWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/data-access-object/README.md b/data-access-object/README.md
new file mode 100644
index 000000000000..7e84299e23e1
--- /dev/null
+++ b/data-access-object/README.md
@@ -0,0 +1,249 @@
+---
+title: "Data Access Object Pattern in Java: Streamlining Database Interaction"
+shortTitle: Data Access Object (DAO)
+description: "Explore the Java Data Access Object (DAO) pattern to effectively separate business logic from database operations. Learn implementation strategies, real-world examples, and best practices."
+category: Structural
+language: en
+tag:
+ - Abstraction
+ - Data access
+ - Data processing
+ - Decoupling
+ - Layered architecture
+ - Persistence
+---
+
+## Also known as
+
+* Data Access Layer
+* DAO
+
+## Intent of Data Access Object Design Pattern
+
+The Data Access Object (DAO) design pattern aims to separate the application's business logic from the persistence layer, typically a database or any other storage mechanism. By using DAOs, the application can access and manipulate data without being dependent on the specific database implementation details.
+
+## Detailed Explanation of Data Access Object Pattern with Real-World Examples
+
+Real-world example
+
+> Imagine a library system where the main application manages book loans, user accounts, and inventory. The Data Access Object (DAO) pattern in this context would be used to separate the database operations (such as fetching book details, updating user records, and checking inventory) from the business logic of managing loans and accounts. For instance, there would be a `BookDAO` class responsible for all database interactions related to books, such as retrieving a book by its ISBN or updating its availability status. This abstraction allows the library system's main application code to focus on business rules and workflows, while the `BookDAO` handles the complex SQL queries and data management. This separation makes the system easier to maintain and test, as changes to the data source or business logic can be managed independently.
+
+In plain words
+
+> DAO is an interface we provide over the base persistence mechanism.
+
+Wikipedia says
+
+> In computer software, a data access object (DAO) is a pattern that provides an abstract interface to some type of database or other persistence mechanism.
+
+Sequence diagram
+
+
+
+## Programmatic Example of DAO Pattern in Java
+
+There's a set of customers that need to be persisted to database. Additionally, we need the whole set of CRUD (create/read/update/delete) operations, so we can operate on customers easily.
+
+Walking through our customers example, here's the basic `Customer` entity.
+
+```java
+@Setter
+@Getter
+@ToString
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@AllArgsConstructor
+public class Customer {
+
+ @EqualsAndHashCode.Include
+ private int id;
+ private String firstName;
+ private String lastName;
+}
+```
+
+Here's the `CustomerDao` interface and two different implementations for it. `InMemoryCustomerDao` keeps a simple map of customers in memory while `DBCustomerDao` is the real RDBMS implementation.
+
+```java
+public interface CustomerDao {
+
+ Stream getAll() throws Exception;
+
+ Optional getById(int id) throws Exception;
+
+ boolean add(Customer customer) throws Exception;
+
+ boolean update(Customer customer) throws Exception;
+
+ boolean delete(Customer customer) throws Exception;
+}
+
+public class InMemoryCustomerDao implements CustomerDao {
+
+ private final Map idToCustomer = new HashMap<>();
+
+ // implement the interface using the map
+}
+
+@Slf4j
+@RequiredArgsConstructor
+public class DbCustomerDao implements CustomerDao {
+
+ private final DataSource dataSource;
+
+ // implement the interface using the data source
+}
+```
+
+Finally, here's how we use our DAO to manage customers.
+
+```java
+
+@Slf4j
+public class App {
+ private static final String DB_URL = "jdbc:h2:mem:dao;DB_CLOSE_DELAY=-1";
+ private static final String ALL_CUSTOMERS = "customerDao.getAllCustomers(): ";
+
+ public static void main(final String[] args) throws Exception {
+ final var inMemoryDao = new InMemoryCustomerDao();
+ performOperationsUsing(inMemoryDao);
+
+ final var dataSource = createDataSource();
+ createSchema(dataSource);
+ final var dbDao = new DbCustomerDao(dataSource);
+ performOperationsUsing(dbDao);
+ deleteSchema(dataSource);
+ }
+
+ private static void deleteSchema(DataSource dataSource) throws SQLException {
+ try (var connection = dataSource.getConnection();
+ var statement = connection.createStatement()) {
+ statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
+ }
+ }
+
+ private static void createSchema(DataSource dataSource) throws SQLException {
+ try (var connection = dataSource.getConnection();
+ var statement = connection.createStatement()) {
+ statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
+ }
+ }
+
+ private static DataSource createDataSource() {
+ var dataSource = new JdbcDataSource();
+ dataSource.setURL(DB_URL);
+ return dataSource;
+ }
+
+ private static void performOperationsUsing(final CustomerDao customerDao) throws Exception {
+ addCustomers(customerDao);
+ LOGGER.info(ALL_CUSTOMERS);
+ try (var customerStream = customerDao.getAll()) {
+ customerStream.forEach(customer -> LOGGER.info(customer.toString()));
+ }
+ LOGGER.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
+ final var customer = new Customer(4, "Dan", "Danson");
+ customerDao.add(customer);
+ LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
+ customer.setFirstName("Daniel");
+ customer.setLastName("Danielson");
+ customerDao.update(customer);
+ LOGGER.info(ALL_CUSTOMERS);
+ try (var customerStream = customerDao.getAll()) {
+ customerStream.forEach(cust -> LOGGER.info(cust.toString()));
+ }
+ customerDao.delete(customer);
+ LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
+ }
+
+ private static void addCustomers(CustomerDao customerDao) throws Exception {
+ for (var customer : generateSampleCustomers()) {
+ customerDao.add(customer);
+ }
+ }
+
+ public static List generateSampleCustomers() {
+ final var customer1 = new Customer(1, "Adam", "Adamson");
+ final var customer2 = new Customer(2, "Bob", "Bobson");
+ final var customer3 = new Customer(3, "Carl", "Carlson");
+ return List.of(customer1, customer2, customer3);
+ }
+}
+```
+
+The program output:
+
+```
+10:02:09.788 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
+10:02:09.793 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
+10:02:09.793 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
+10:02:09.793 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
+10:02:09.794 [main] INFO com.iluwatar.dao.App -- customerDao.getCustomerById(2): Optional[Customer(id=2, firstName=Bob, lastName=Bobson)]
+10:02:09.794 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@4c3e4790
+10:02:09.794 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
+10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
+10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
+10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
+10:02:09.795 [main] INFO com.iluwatar.dao.App -- Customer(id=4, firstName=Daniel, lastName=Danielson)
+10:02:09.795 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@5679c6c6
+10:02:09.894 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
+10:02:09.895 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
+10:02:09.895 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
+10:02:09.895 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
+10:02:09.895 [main] INFO com.iluwatar.dao.App -- customerDao.getCustomerById(2): Optional[Customer(id=2, firstName=Bob, lastName=Bobson)]
+10:02:09.896 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@23282c25
+10:02:09.897 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers():
+10:02:09.897 [main] INFO com.iluwatar.dao.App -- Customer(id=1, firstName=Adam, lastName=Adamson)
+10:02:09.897 [main] INFO com.iluwatar.dao.App -- Customer(id=2, firstName=Bob, lastName=Bobson)
+10:02:09.898 [main] INFO com.iluwatar.dao.App -- Customer(id=3, firstName=Carl, lastName=Carlson)
+10:02:09.898 [main] INFO com.iluwatar.dao.App -- Customer(id=4, firstName=Daniel, lastName=Danielson)
+10:02:09.898 [main] INFO com.iluwatar.dao.App -- customerDao.getAllCustomers(): java.util.stream.ReferencePipeline$Head@f2f2cc1
+```
+
+## When to Use the Data Access Object Pattern in Java
+
+Use the Data Access Object in any of the following situations:
+
+* There is a need to abstract and encapsulate all access to the data source.
+* The application needs to support multiple types of databases or storage mechanisms without significant code changes.
+* You want to keep the database access clean and simple, and separate from business logic.
+
+## Data Access Object Pattern Java Tutorials
+
+* [The DAO Pattern in Java(Baeldung)](https://www.baeldung.com/java-dao-pattern)
+* [Data Access Object Pattern (TutorialsPoint)](https://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm)
+
+## Real-World Applications of DAO Pattern in Java
+
+* Enterprise applications that require database interaction.
+* Applications requiring data access to be adaptable to multiple storage types (relational databases, XML files, flat files, etc.).
+* Frameworks providing generic data access functionalities.
+
+## Benefits and Trade-offs of Data Access Object Pattern
+
+Benefits:
+
+* Decoupling: Separates the data access logic from the business logic, enhancing modularity and clarity.
+* Reusability: DAOs can be reused across different parts of the application or even in different projects.
+* Testability: Simplifies testing by allowing business logic to be tested separately from the data access logic.
+* Flexibility: Makes it easier to switch underlying storage mechanisms with minimal impact on the application code.
+
+Trade-offs:
+
+* Layer Complexity: Introduces additional layers to the application, which can increase complexity and development time.
+* Overhead: For simple applications, the DAO pattern might introduce more overhead than necessary.
+* Learning Curve: Developers might need time to understand and implement the pattern effectively, especially in complex projects.
+
+## Related Java Design Patterns
+
+* [Abstract Factory](https://java-design-patterns.com/patterns/abstract-factory/): Helps in abstracting the creation of DAOs, especially when supporting multiple databases or storage mechanisms.
+* [Factory](https://java-design-patterns.com/patterns/factory/): Can be used to instantiate DAOs dynamically, providing flexibility in the choice of implementation.
+* [Service Layer](https://java-design-patterns.com/patterns/service-layer/): Often used in conjunction with the DAO pattern to define application's boundaries and its set of available operations.
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): Might be employed to change the data access strategy at runtime, depending on the context.
+
+## References and Credits
+
+* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/49u3r91)
+* [Expert One-on-One J2EE Design and Development](https://amzn.to/3vK3pfq)
+* [J2EE Design Patterns](https://amzn.to/4dpzgmx)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3U5cxEI)
+* [Professional Java Development with the Spring Framework](https://amzn.to/49tANF0)
diff --git a/data-access-object/etc/dao-sequence-diagram.png b/data-access-object/etc/dao-sequence-diagram.png
new file mode 100644
index 000000000000..74488bd78107
Binary files /dev/null and b/data-access-object/etc/dao-sequence-diagram.png differ
diff --git a/dao/etc/dao.png b/data-access-object/etc/dao.png
similarity index 100%
rename from dao/etc/dao.png
rename to data-access-object/etc/dao.png
diff --git a/dao/etc/dao.ucls b/data-access-object/etc/dao.ucls
similarity index 100%
rename from dao/etc/dao.ucls
rename to data-access-object/etc/dao.ucls
diff --git a/dao/etc/dao.urm.puml b/data-access-object/etc/dao.urm.puml
similarity index 100%
rename from dao/etc/dao.urm.puml
rename to data-access-object/etc/dao.urm.puml
diff --git a/data-access-object/etc/data-access-object.urm.puml b/data-access-object/etc/data-access-object.urm.puml
new file mode 100644
index 000000000000..5c6e5f703053
--- /dev/null
+++ b/data-access-object/etc/data-access-object.urm.puml
@@ -0,0 +1,69 @@
+@startuml
+package com.iluwatar.dao {
+ class App {
+ - ALL_CUSTOMERS : String {static}
+ - DB_URL : String {static}
+ - LOGGER : Logger {static}
+ + App()
+ - addCustomers(customerDao : CustomerDao) {static}
+ - createDataSource() : DataSource {static}
+ - createSchema(dataSource : DataSource) {static}
+ - deleteSchema(dataSource : DataSource) {static}
+ + generateSampleCustomers() : List {static}
+ + main(args : String[]) {static}
+ - performOperationsUsing(customerDao : CustomerDao) {static}
+ }
+ class Customer {
+ - firstName : String
+ - id : int
+ - lastName : String
+ + Customer(id : int, firstName : String, lastName : String)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getFirstName() : String
+ + getId() : int
+ + getLastName() : String
+ + hashCode() : int
+ + setFirstName(firstName : String)
+ + setId(id : int)
+ + setLastName(lastName : String)
+ + toString() : String
+ }
+ interface CustomerDao {
+ + add(Customer) : boolean {abstract}
+ + delete(Customer) : boolean {abstract}
+ + getAll() : Stream {abstract}
+ + getById(int) : Optional {abstract}
+ + update(Customer) : boolean {abstract}
+ }
+ class CustomerSchemaSql {
+ + CREATE_SCHEMA_SQL : String {static}
+ + DELETE_SCHEMA_SQL : String {static}
+ - CustomerSchemaSql()
+ }
+ class DbCustomerDao {
+ - LOGGER : Logger {static}
+ - dataSource : DataSource
+ + DbCustomerDao(dataSource : DataSource)
+ + add(customer : Customer) : boolean
+ - createCustomer(resultSet : ResultSet) : Customer
+ + delete(customer : Customer) : boolean
+ + getAll() : Stream
+ + getById(id : int) : Optional
+ - getConnection() : Connection
+ - mutedClose(connection : Connection, statement : PreparedStatement, resultSet : ResultSet)
+ + update(customer : Customer) : boolean
+ }
+ class InMemoryCustomerDao {
+ - idToCustomer : Map
+ + InMemoryCustomerDao()
+ + add(customer : Customer) : boolean
+ + delete(customer : Customer) : boolean
+ + getAll() : Stream
+ + getById(id : int) : Optional
+ + update(customer : Customer) : boolean
+ }
+}
+DbCustomerDao ..|> CustomerDao
+InMemoryCustomerDao ..|> CustomerDao
+@enduml
\ No newline at end of file
diff --git a/data-access-object/pom.xml b/data-access-object/pom.xml
new file mode 100644
index 000000000000..f96bf54cd818
--- /dev/null
+++ b/data-access-object/pom.xml
@@ -0,0 +1,79 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ data-access-object
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ com.h2database
+ h2
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.dao.App
+
+
+
+
+
+
+
+
+
diff --git a/data-access-object/src/main/java/com/iluwatar/dao/App.java b/data-access-object/src/main/java/com/iluwatar/dao/App.java
new file mode 100644
index 000000000000..106b7458c9cf
--- /dev/null
+++ b/data-access-object/src/main/java/com/iluwatar/dao/App.java
@@ -0,0 +1,125 @@
+/*
+ * 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.dao;
+
+import java.sql.SQLException;
+import java.util.List;
+import javax.sql.DataSource;
+import lombok.extern.slf4j.Slf4j;
+import org.h2.jdbcx.JdbcDataSource;
+
+/**
+ * Data Access Object (DAO) is an object that provides an abstract interface to some type of
+ * database or other persistence mechanism. By mapping application calls to the persistence layer,
+ * DAO provide some specific data operations without exposing details of the database. This
+ * isolation supports the Single responsibility principle. It separates what data accesses the
+ * application needs, in terms of domain-specific objects and data types (the public interface of
+ * the DAO), from how these needs can be satisfied with a specific DBMS.
+ *
+ * With the DAO pattern, we can use various method calls to retrieve/add/delete/update data
+ * without directly interacting with the data source. The below example demonstrates basic CRUD
+ * operations: select, add, update, and delete.
+ */
+@Slf4j
+public class App {
+ private static final String DB_URL = "jdbc:h2:mem:dao;DB_CLOSE_DELAY=-1";
+ private static final String ALL_CUSTOMERS = "customerDao.getAllCustomers(): ";
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args.
+ * @throws Exception if any error occurs.
+ */
+ public static void main(final String[] args) throws Exception {
+ final var inMemoryDao = new InMemoryCustomerDao();
+ performOperationsUsing(inMemoryDao);
+
+ final var dataSource = createDataSource();
+ createSchema(dataSource);
+ final var dbDao = new DbCustomerDao(dataSource);
+ performOperationsUsing(dbDao);
+ deleteSchema(dataSource);
+ }
+
+ private static void deleteSchema(DataSource dataSource) throws SQLException {
+ try (var connection = dataSource.getConnection();
+ var statement = connection.createStatement()) {
+ statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
+ }
+ }
+
+ private static void createSchema(DataSource dataSource) throws SQLException {
+ try (var connection = dataSource.getConnection();
+ var statement = connection.createStatement()) {
+ statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
+ }
+ }
+
+ private static DataSource createDataSource() {
+ var dataSource = new JdbcDataSource();
+ dataSource.setURL(DB_URL);
+ return dataSource;
+ }
+
+ private static void performOperationsUsing(final CustomerDao customerDao) throws Exception {
+ addCustomers(customerDao);
+ LOGGER.info(ALL_CUSTOMERS);
+ try (var customerStream = customerDao.getAll()) {
+ customerStream.forEach(customer -> LOGGER.info(customer.toString()));
+ }
+ LOGGER.info("customerDao.getCustomerById(2): " + customerDao.getById(2));
+ final var customer = new Customer(4, "Dan", "Danson");
+ customerDao.add(customer);
+ LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
+ customer.setFirstName("Daniel");
+ customer.setLastName("Danielson");
+ customerDao.update(customer);
+ LOGGER.info(ALL_CUSTOMERS);
+ try (var customerStream = customerDao.getAll()) {
+ customerStream.forEach(cust -> LOGGER.info(cust.toString()));
+ }
+ customerDao.delete(customer);
+ LOGGER.info(ALL_CUSTOMERS + customerDao.getAll());
+ }
+
+ private static void addCustomers(CustomerDao customerDao) throws Exception {
+ for (var customer : generateSampleCustomers()) {
+ customerDao.add(customer);
+ }
+ }
+
+ /**
+ * Generate customers.
+ *
+ * @return list of customers.
+ */
+ public static List generateSampleCustomers() {
+ final var customer1 = new Customer(1, "Adam", "Adamson");
+ final var customer2 = new Customer(2, "Bob", "Bobson");
+ final var customer3 = new Customer(3, "Carl", "Carlson");
+ return List.of(customer1, customer2, customer3);
+ }
+}
diff --git a/dao/src/main/java/com/iluwatar/dao/CustomException.java b/data-access-object/src/main/java/com/iluwatar/dao/CustomException.java
similarity index 81%
rename from dao/src/main/java/com/iluwatar/dao/CustomException.java
rename to data-access-object/src/main/java/com/iluwatar/dao/CustomException.java
index eff7c44cc01c..79470bd810ab 100644
--- a/dao/src/main/java/com/iluwatar/dao/CustomException.java
+++ b/data-access-object/src/main/java/com/iluwatar/dao/CustomException.java
@@ -1,43 +1,37 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.dao;
-
-/**
- * Custom exception.
- */
-public class CustomException extends Exception {
-
- private static final long serialVersionUID = 1L;
-
- public CustomException() {
- }
-
- public CustomException(String message) {
- super(message);
- }
-
- public CustomException(String message, Throwable cause) {
- super(message, cause);
- }
-}
+/*
+ * 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.dao;
+
+import java.io.Serial;
+
+/** Custom exception. */
+public class CustomException extends Exception {
+
+ @Serial private static final long serialVersionUID = 1L;
+
+ public CustomException(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/data-access-object/src/main/java/com/iluwatar/dao/Customer.java b/data-access-object/src/main/java/com/iluwatar/dao/Customer.java
new file mode 100644
index 000000000000..17a15fc1b92f
--- /dev/null
+++ b/data-access-object/src/main/java/com/iluwatar/dao/Customer.java
@@ -0,0 +1,44 @@
+/*
+ * 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.dao;
+
+import lombok.AllArgsConstructor;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/** A customer POJO that represents the data that will be read from the data source. */
+@Setter
+@Getter
+@ToString
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@AllArgsConstructor
+public class Customer {
+
+ @EqualsAndHashCode.Include private int id;
+ private String firstName;
+ private String lastName;
+}
diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerDao.java b/data-access-object/src/main/java/com/iluwatar/dao/CustomerDao.java
similarity index 94%
rename from dao/src/main/java/com/iluwatar/dao/CustomerDao.java
rename to data-access-object/src/main/java/com/iluwatar/dao/CustomerDao.java
index 3bd4da85f0a6..5bb9ca2af97b 100644
--- a/dao/src/main/java/com/iluwatar/dao/CustomerDao.java
+++ b/data-access-object/src/main/java/com/iluwatar/dao/CustomerDao.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.dao;
import java.util.Optional;
diff --git a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java b/data-access-object/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java
similarity index 79%
rename from dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java
rename to data-access-object/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java
index c0f1e2c7b9c5..aab41423a748 100644
--- a/dao/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java
+++ b/data-access-object/src/main/java/com/iluwatar/dao/CustomerSchemaSql.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,21 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.dao;
-/**
- * Customer Schema SQL Class.
- */
+/** Customer Schema SQL Class. */
public final class CustomerSchemaSql {
- private CustomerSchemaSql() {
- }
+ private CustomerSchemaSql() {}
public static final String CREATE_SCHEMA_SQL =
- "CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), "
- + "LNAME VARCHAR(100))";
+ "CREATE TABLE CUSTOMERS (ID NUMBER, FNAME VARCHAR(100), " + "LNAME VARCHAR(100))";
public static final String DELETE_SCHEMA_SQL = "DROP TABLE CUSTOMERS";
-
}
diff --git a/data-access-object/src/main/java/com/iluwatar/dao/DbCustomerDao.java b/data-access-object/src/main/java/com/iluwatar/dao/DbCustomerDao.java
new file mode 100644
index 000000000000..cb75b195ddc4
--- /dev/null
+++ b/data-access-object/src/main/java/com/iluwatar/dao/DbCustomerDao.java
@@ -0,0 +1,173 @@
+/*
+ * 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.dao;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Optional;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.function.Consumer;
+import java.util.stream.Stream;
+import java.util.stream.StreamSupport;
+import javax.sql.DataSource;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/** An implementation of {@link CustomerDao} that persists customers in RDBMS. */
+@Slf4j
+@RequiredArgsConstructor
+public class DbCustomerDao implements CustomerDao {
+
+ private final DataSource dataSource;
+
+ /**
+ * Get all customers as Java Stream.
+ *
+ * @return a lazily populated stream of customers. Note the stream returned must be closed to free
+ * all the acquired resources. The stream keeps an open connection to the database till it is
+ * complete or is closed manually.
+ */
+ @Override
+ public Stream getAll() throws Exception {
+ try {
+ var connection = getConnection();
+ var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS"); // NOSONAR
+ var resultSet = statement.executeQuery(); // NOSONAR
+ return StreamSupport.stream(
+ new Spliterators.AbstractSpliterator(Long.MAX_VALUE, Spliterator.ORDERED) {
+
+ @Override
+ public boolean tryAdvance(Consumer super Customer> action) {
+ try {
+ if (!resultSet.next()) {
+ return false;
+ }
+ action.accept(createCustomer(resultSet));
+ return true;
+ } catch (SQLException e) {
+ throw new RuntimeException(e); // NOSONAR
+ }
+ }
+ },
+ false)
+ .onClose(() -> mutedClose(connection, statement, resultSet));
+ } catch (SQLException e) {
+ throw new CustomException(e.getMessage(), e);
+ }
+ }
+
+ private Connection getConnection() throws SQLException {
+ return dataSource.getConnection();
+ }
+
+ private void mutedClose(Connection connection, PreparedStatement statement, ResultSet resultSet) {
+ try {
+ resultSet.close();
+ statement.close();
+ connection.close();
+ } catch (SQLException e) {
+ LOGGER.info("Exception thrown " + e.getMessage());
+ }
+ }
+
+ private Customer createCustomer(ResultSet resultSet) throws SQLException {
+ return new Customer(
+ resultSet.getInt("ID"), resultSet.getString("FNAME"), resultSet.getString("LNAME"));
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public Optional getById(int id) throws Exception {
+
+ ResultSet resultSet = null;
+
+ try (var connection = getConnection();
+ var statement = connection.prepareStatement("SELECT * FROM CUSTOMERS WHERE ID = ?")) {
+
+ statement.setInt(1, id);
+ resultSet = statement.executeQuery();
+ if (resultSet.next()) {
+ return Optional.of(createCustomer(resultSet));
+ } else {
+ return Optional.empty();
+ }
+ } catch (SQLException ex) {
+ throw new CustomException(ex.getMessage(), ex);
+ } finally {
+ if (resultSet != null) {
+ resultSet.close();
+ }
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean add(Customer customer) throws Exception {
+ if (getById(customer.getId()).isPresent()) {
+ return false;
+ }
+
+ try (var connection = getConnection();
+ var statement = connection.prepareStatement("INSERT INTO CUSTOMERS VALUES (?,?,?)")) {
+ statement.setInt(1, customer.getId());
+ statement.setString(2, customer.getFirstName());
+ statement.setString(3, customer.getLastName());
+ statement.execute();
+ return true;
+ } catch (SQLException ex) {
+ throw new CustomException(ex.getMessage(), ex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean update(Customer customer) throws Exception {
+ try (var connection = getConnection();
+ var statement =
+ connection.prepareStatement("UPDATE CUSTOMERS SET FNAME = ?, LNAME = ? WHERE ID = ?")) {
+ statement.setString(1, customer.getFirstName());
+ statement.setString(2, customer.getLastName());
+ statement.setInt(3, customer.getId());
+ return statement.executeUpdate() > 0;
+ } catch (SQLException ex) {
+ throw new CustomException(ex.getMessage(), ex);
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public boolean delete(Customer customer) throws Exception {
+ try (var connection = getConnection();
+ var statement = connection.prepareStatement("DELETE FROM CUSTOMERS WHERE ID = ?")) {
+ statement.setInt(1, customer.getId());
+ return statement.executeUpdate() > 0;
+ } catch (SQLException ex) {
+ throw new CustomException(ex.getMessage(), ex);
+ }
+ }
+}
diff --git a/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java b/data-access-object/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
similarity index 88%
rename from dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
rename to data-access-object/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
index c896a4bf9058..b5b7eb0c4ff7 100644
--- a/dao/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
+++ b/data-access-object/src/main/java/com/iluwatar/dao/InMemoryCustomerDao.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.dao;
import java.util.HashMap;
@@ -30,17 +31,14 @@
/**
* An in memory implementation of {@link CustomerDao}, which stores the customers in JVM memory and
- * data is lost when the application exits.
- *
+ * data is lost when the application exits.
* This implementation is useful as temporary database or for testing.
*/
public class InMemoryCustomerDao implements CustomerDao {
private final Map idToCustomer = new HashMap<>();
- /**
- * An eagerly evaluated stream of customers stored in memory.
- */
+ /** An eagerly evaluated stream of customers stored in memory. */
@Override
public Stream getAll() {
return idToCustomer.values().stream();
diff --git a/data-access-object/src/test/java/com/iluwatar/dao/AppTest.java b/data-access-object/src/test/java/com/iluwatar/dao/AppTest.java
new file mode 100644
index 000000000000..a43f37353341
--- /dev/null
+++ b/data-access-object/src/test/java/com/iluwatar/dao/AppTest.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.dao;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that DAO 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.
+ */
+ @Test
+ void shouldExecuteDaoWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/dao/src/test/java/com/iluwatar/dao/CustomerTest.java b/data-access-object/src/test/java/com/iluwatar/dao/CustomerTest.java
similarity index 86%
rename from dao/src/test/java/com/iluwatar/dao/CustomerTest.java
rename to data-access-object/src/test/java/com/iluwatar/dao/CustomerTest.java
index 492325153d37..f53c28935fea 100644
--- a/dao/src/test/java/com/iluwatar/dao/CustomerTest.java
+++ b/data-access-object/src/test/java/com/iluwatar/dao/CustomerTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.dao;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -29,9 +30,7 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-/**
- * Tests {@link Customer}.
- */
+/** Tests {@link Customer}. */
class CustomerTest {
private Customer customer;
@@ -88,7 +87,10 @@ void equalsWithSameObjects() {
@Test
void testToString() {
- assertEquals(String.format("Customer(id=%s, firstName=%s, lastName=%s)",
- customer.getId(), customer.getFirstName(), customer.getLastName()), customer.toString());
+ assertEquals(
+ String.format(
+ "Customer(id=%s, firstName=%s, lastName=%s)",
+ customer.getId(), customer.getFirstName(), customer.getLastName()),
+ customer.toString());
}
}
diff --git a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java b/data-access-object/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
similarity index 87%
rename from dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
rename to data-access-object/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
index 1c78fbba0f9a..2f116d108519 100644
--- a/dao/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
+++ b/data-access-object/src/test/java/com/iluwatar/dao/DbCustomerDaoTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.dao;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -43,12 +44,10 @@
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
-/**
- * Tests {@link DbCustomerDao}.
- */
+/** Tests {@link DbCustomerDao}. */
class DbCustomerDaoTest {
- private static final String DB_URL = "jdbc:h2:~/dao";
+ private static final String DB_URL = "jdbc:h2:mem:dao;DB_CLOSE_DELAY=-1";
private DbCustomerDao dao;
private final Customer existingCustomer = new Customer(1, "Freddy", "Krueger");
@@ -60,16 +59,14 @@ class DbCustomerDaoTest {
@BeforeEach
void createSchema() throws SQLException {
try (var connection = DriverManager.getConnection(DB_URL);
- var statement = connection.createStatement()) {
+ var statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.CREATE_SCHEMA_SQL);
}
}
- /**
- * Represents the scenario where DB connectivity is present.
- */
+ /** Represents the scenario where DB connectivity is present. */
@Nested
- public class ConnectionSuccess {
+ class ConnectionSuccess {
/**
* Setup for connection success scenario.
@@ -77,7 +74,7 @@ public class ConnectionSuccess {
* @throws Exception if any error occurs.
*/
@BeforeEach
- public void setUp() throws Exception {
+ void setUp() throws Exception {
var dataSource = new JdbcDataSource();
dataSource.setURL(DB_URL);
dao = new DbCustomerDao(dataSource);
@@ -86,7 +83,7 @@ public void setUp() throws Exception {
}
/**
- * Represents the scenario when DAO operations are being performed on a non existing customer.
+ * Represents the scenario when DAO operations are being performed on a non-existing customer.
*/
@Nested
class NonExistingCustomer {
@@ -159,8 +156,8 @@ void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception
}
@Test
- void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws
- Exception {
+ void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation()
+ throws Exception {
final var newFirstname = "Bernard";
final var newLastname = "Montgomery";
final var customer = new Customer(existingCustomer.getId(), newFirstname, newLastname);
@@ -190,7 +187,7 @@ class ConnectivityIssue {
* @throws SQLException if any error occurs.
*/
@BeforeEach
- public void setUp() throws SQLException {
+ void setUp() throws SQLException {
dao = new DbCustomerDao(mockedDatasource());
}
@@ -205,41 +202,32 @@ private DataSource mockedDatasource() throws SQLException {
@Test
void addingACustomerFailsWithExceptionAsFeedbackToClient() {
- assertThrows(Exception.class, () -> {
- dao.add(new Customer(2, "Bernard", "Montgomery"));
- });
+ assertThrows(Exception.class, () -> dao.add(new Customer(2, "Bernard", "Montgomery")));
}
@Test
void deletingACustomerFailsWithExceptionAsFeedbackToTheClient() {
- assertThrows(Exception.class, () -> {
- dao.delete(existingCustomer);
- });
+ assertThrows(Exception.class, () -> dao.delete(existingCustomer));
}
@Test
void updatingACustomerFailsWithFeedbackToTheClient() {
final var newFirstname = "Bernard";
final var newLastname = "Montgomery";
- assertThrows(Exception.class, () -> {
- dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname));
- });
+ assertThrows(
+ Exception.class,
+ () -> dao.update(new Customer(existingCustomer.getId(), newFirstname, newLastname)));
}
@Test
void retrievingACustomerByIdFailsWithExceptionAsFeedbackToClient() {
- assertThrows(Exception.class, () -> {
- dao.getById(existingCustomer.getId());
- });
+ assertThrows(Exception.class, () -> dao.getById(existingCustomer.getId()));
}
@Test
void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() {
- assertThrows(Exception.class, () -> {
- dao.getAll();
- });
+ assertThrows(Exception.class, () -> dao.getAll());
}
-
}
/**
@@ -250,7 +238,7 @@ void retrievingAllCustomersFailsWithExceptionAsFeedbackToClient() {
@AfterEach
void deleteSchema() throws SQLException {
try (var connection = DriverManager.getConnection(DB_URL);
- var statement = connection.createStatement()) {
+ var statement = connection.createStatement()) {
statement.execute(CustomerSchemaSql.DELETE_SCHEMA_SQL);
}
}
diff --git a/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java b/data-access-object/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java
similarity index 92%
rename from dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java
rename to data-access-object/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java
index 9ff9e6a85cdd..93ce2475f2c8 100644
--- a/dao/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java
+++ b/data-access-object/src/test/java/com/iluwatar/dao/InMemoryCustomerDaoTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.dao;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -32,9 +33,7 @@
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
-/**
- * Tests {@link InMemoryCustomerDao}.
- */
+/** Tests {@link InMemoryCustomerDao}. */
class InMemoryCustomerDaoTest {
private InMemoryCustomerDao dao;
@@ -47,8 +46,7 @@ void setUp() {
}
/**
- * Represents the scenario when the DAO operations are being performed on a non existent
- * customer.
+ * Represents the scenario when the DAO operations are being performed on a non-existent customer.
*/
@Nested
class NonExistingCustomer {
@@ -77,7 +75,7 @@ void deletionShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
}
@Test
- void updationShouldBeFailureAndNotAffectExistingCustomers() throws Exception {
+ void updateShouldBeFailureAndNotAffectExistingCustomers() {
final var nonExistingId = getNonExistingCustomerId();
final var newFirstname = "Douglas";
final var newLastname = "MacArthur";
@@ -120,8 +118,8 @@ void deletionShouldBeSuccessAndCustomerShouldBeNonAccessible() throws Exception
}
@Test
- void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation() throws
- Exception {
+ void updationShouldBeSuccessAndAccessingTheSameCustomerShouldReturnUpdatedInformation()
+ throws Exception {
final var newFirstname = "Bernard";
final var newLastname = "Montgomery";
final var customer = new Customer(CUSTOMER.getId(), newFirstname, newLastname);
diff --git a/data-bus/README.md b/data-bus/README.md
index 839af7ad5002..ee687a52d1dd 100644
--- a/data-bus/README.md
+++ b/data-bus/README.md
@@ -1,33 +1,195 @@
---
-layout: pattern
-title: Data Bus
-folder: data-bus
-permalink: /patterns/data-bus/
-categories: Architectural
+title: "Data Bus Pattern in Java: Unifying Component Communication Efficiently"
+shortTitle: Data Bus
+description: "Explore the Data Bus pattern in Java for centralized communication and event handling. Learn how to decouple components, enhance scalability, and maintainability with practical examples and real-world applications."
+category: Messaging
language: en
-tags:
- - Decoupling
+tag:
+ - Decoupling
+ - Event-driven
+ - Messaging
+ - Publish/subscribe
+ - Scalability
---
-## Intent
+## Also known as
-Allows send of messages/events between components of an application
-without them needing to know about each other. They only need to know
-about the type of the message/event being sent.
+* Event Bus
+* Message Bus
-## Class diagram
-
+## Intent of Data Bus Design Pattern
-## Applicability
-Use Data Bus pattern when
+The Data Bus design pattern aims to provide a centralized communication channel through which various components of a system can exchange data without being directly connected, thus promoting loose coupling and enhancing scalability and maintainability.
-* you want your components to decide themselves which messages/events they want to receive
-* you want to have many-to-many communication
-* you want your components to know nothing about each other
+## Detailed Explanation of Data Bus Pattern with Real-World Examples
-## Related Patterns
-Data Bus is similar to
+Real-world example
-* Mediator pattern with Data Bus Members deciding for themselves if they want to accept any given message
-* Observer pattern but supporting many-to-many communication
-* Publish/Subscribe pattern with the Data Bus decoupling the publisher and the subscriber
+> Consider a large airport as an analogous real-world example of the Data Bus pattern. In an airport, various airlines, passengers, baggage handlers, and security personnel all need to communicate and share information. Instead of each entity communicating directly with every other entity, the airport uses a centralized announcement system (the Data Bus). Flight information, security alerts, and other critical updates are broadcast over this system, and each entity listens for the messages relevant to them. This setup allows the airport to decouple the communication process, ensuring that each entity only receives the information they need, while allowing the system to scale and integrate new entities without disrupting the existing ones. This is similar to how the Data Bus pattern in Java promotes centralized communication and event handling, enhancing system scalability and maintainability.
+
+In plain words
+
+> Data Bus is a design pattern that connects components of an application for communication based on the type of message or event being transferred. This pattern promotes decoupling, making it easier to scale and maintain the system by allowing components to communicate without direct dependencies.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Data Bus Pattern in Java
+
+Say you have an app that enables online bookings and participation in events. You want the app to send notifications, such as event advertisements, to all ordinary members of the community or organization holding the events. However, you do not want to send such advertisements to event administrators or organizers. Instead, you want to send them notifications about the timing of new advertisements sent to all members. The Data Bus enables you to selectively notify community members by type (ordinary members or event administrators) by making their classes or components only accept messages of a certain type. Thus, ordinary members and administrators do not need to know about each other or the specific classes or components used to notify the entire community, except for knowing the type of messages being sent.
+
+In the online events app example above, we first define our `Member` interface and its implementations: `MessageCollectorMember` (ordinary community members) and `StatusMember` (event administrators or organizers).
+
+```java
+public interface Member extends Consumer {
+
+ void accept(DataType event);
+}
+```
+
+Next, we implement a data bus to subscribe or unsubscribe members and to publish events to notify all community members.
+
+```java
+public class DataBus {
+
+ private static final DataBus INSTANCE = new DataBus();
+
+ private final Set listeners = new HashSet<>();
+
+ public static DataBus getInstance() {
+ return INSTANCE;
+ }
+
+ public void subscribe(final Member member) {
+ this.listeners.add(member);
+ }
+
+ public void unsubscribe(final Member member) {
+ this.listeners.remove(member);
+ }
+
+ public void publish(final DataType event) {
+ event.setDataBus(this);
+
+ listeners.forEach(
+ listener -> listener.accept(event));
+ }
+}
+```
+
+The `accept` method is applied to each member in the `publish` method.
+
+For ordinary community members (`MessageCollectorMember`), the `accept` method can handle only `MessageData` type messages.
+
+```java
+public class MessageCollectorMember implements Member {
+
+ private final String name;
+
+ private final List messages = new ArrayList<>();
+
+ public MessageCollectorMember(String name) {
+ this.name = name;
+ }
+
+ @Override
+ public void accept(final DataType data) {
+ if (data instanceof MessageData) {
+ handleEvent((MessageData) data);
+ }
+ }
+}
+```
+
+For event administrators or organizers (`StatusMember`), the `accept` method can handle `StartingData` and `StoppingData` type messages.
+
+```java
+public class StatusMember implements Member {
+
+ private final int id;
+
+ private LocalDateTime started;
+
+ private LocalDateTime stopped;
+
+ public StatusMember(int id) {
+ this.id = id;
+ }
+
+ @Override
+ public void accept(final DataType data) {
+ if (data instanceof StartingData) {
+ handleEvent((StartingData) data);
+ } else if (data instanceof StoppingData) {
+ handleEvent((StoppingData) data);
+ }
+ }
+}
+```
+
+Here is the `App` class to demonstrate the Data Bus pattern in action:
+
+```java
+class App {
+
+ public static void main(String[] args) {
+ final var bus = DataBus.getInstance();
+ bus.subscribe(new StatusMember(1));
+ bus.subscribe(new StatusMember(2));
+ final var foo = new MessageCollectorMember("Foo");
+ final var bar = new MessageCollectorMember("Bar");
+ bus.subscribe(foo);
+ bus.publish(StartingData.of(LocalDateTime.now()));
+ }
+}
+```
+
+When the data bus publishes a message, the output is as follows:
+
+```
+02:33:57.627 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 2 sees application started at 2022-10-26T02:33:57.613529100
+02:33:57.633 [main] INFO com.iluwatar.databus.members.StatusMember - Receiver 1 sees application started at 2022-10-26T02:33:57.613529100
+```
+
+As shown, `MessageCollectorMembers` only accept messages of type `MessageData`, so they do not see the `StartingData` or `StoppingData` messages, which are only visible to `StatusMember` (the event administrators or organizers). This selective message handling prevents ordinary community members from receiving administrative notifications.
+
+## When to Use the Data Bus Pattern in Java
+
+* When multiple components need to share data or events but direct coupling is undesirable.
+* In complex, event-driven systems where the flow of information varies dynamically.
+* In distributed systems where components might be deployed across different environments.
+* In microservices architectures for inter-service communication.
+
+## Real-World Applications of Data Bus Pattern in Java
+
+* Event handling systems in large-scale applications.
+* Microservices architectures for inter-service communication.
+* Real-time data processing systems, such as stock trading platforms.
+* In frameworks like Spring, particularly with its application event mechanism.
+
+## Benefits and Trade-offs of Data Bus Pattern
+
+Benefits:
+
+* Loose Coupling: Components can interact without having direct dependencies on each other.
+* Flexibility: New subscribers or publishers can be added without impacting existing components.
+* Scalability: The pattern supports scaling components independently.
+* Reusability: The bus and components can be reused across different systems.
+
+Trade-offs:
+
+* Complexity: Introducing a data bus can add complexity to the system architecture.
+* Performance Overhead: The additional layer of communication may introduce latency.
+* Debugging Difficulty: Tracing data flow through the bus can be challenging, especially in systems with many events.
+
+## Related Java Design Patterns
+
+* [Mediator](https://java-design-patterns.com/patterns/mediator/): Facilitates communication between components, but unlike Data Bus, it centralizes control.
+* [Observer](https://java-design-patterns.com/patterns/observer/): Similar in nature to the publish-subscribe mechanism used in Data Bus for notifying changes to multiple objects.
+* Publish/Subscribe: The Data Bus pattern is often implemented using the publish-subscribe mechanism, where publishers post messages to the bus without knowledge of the subscribers.
+
+## References and Credits
+
+* [Enterprise Integration Patterns](https://amzn.to/3J6WoYS)
+* [Pattern-Oriented Software Architecture, Volume 4: A Pattern Language for Distributed Computing](https://amzn.to/3PTRGBM)
diff --git a/data-bus/etc/data-bus-sequence-diagram.png b/data-bus/etc/data-bus-sequence-diagram.png
new file mode 100644
index 000000000000..8924693923d4
Binary files /dev/null and b/data-bus/etc/data-bus-sequence-diagram.png differ
diff --git a/data-bus/pom.xml b/data-bus/pom.xml
index 59007d7bd0b2..ef7c88acb413 100644
--- a/data-bus/pom.xml
+++ b/data-bus/pom.xml
@@ -1,8 +1,10 @@
"-restClient" TinyRestClient
+App --> "-albumServiceProxy" AlbumService
+@enduml
\ No newline at end of file
diff --git a/dynamic-proxy/pom.xml b/dynamic-proxy/pom.xml
new file mode 100644
index 000000000000..decbb24fd02d
--- /dev/null
+++ b/dynamic-proxy/pom.xml
@@ -0,0 +1,86 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ dynamic-proxy
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.19.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.18.3
+
+
+ org.springframework
+ spring-web
+ 7.0.0-M4
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.dynamicproxy.App
+
+
+
+
+
+
+
+
+
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/Album.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/Album.java
new file mode 100644
index 000000000000..c61223008e19
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/Album.java
@@ -0,0 +1,45 @@
+/*
+ * 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.dynamicproxy;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * This class represents an endpoint resource that we are going to interchange with a Rest API
+ * server.
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor(force = true)
+@Builder
+public class Album {
+
+ private Integer id;
+ private String title;
+ private Integer userId;
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/AlbumInvocationHandler.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/AlbumInvocationHandler.java
new file mode 100644
index 000000000000..84a0b22d041f
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/AlbumInvocationHandler.java
@@ -0,0 +1,62 @@
+/*
+ * 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.dynamicproxy;
+
+import com.iluwatar.dynamicproxy.tinyrestclient.TinyRestClient;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.net.http.HttpClient;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Class whose method 'invoke' will be called every time that an interface's method is called. That
+ * interface is linked to this class by the Proxy class.
+ */
+@Slf4j
+public class AlbumInvocationHandler implements InvocationHandler {
+
+ private TinyRestClient restClient;
+
+ /**
+ * Class constructor. It instantiates a TinyRestClient object.
+ *
+ * @param baseUrl Root url for endpoints.
+ * @param httpClient Handle the http communication.
+ */
+ public AlbumInvocationHandler(String baseUrl, HttpClient httpClient) {
+ this.restClient = new TinyRestClient(baseUrl, httpClient);
+ }
+
+ @Override
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+
+ LOGGER.info(
+ "===== Calling the method {}.{}()",
+ method.getDeclaringClass().getSimpleName(),
+ method.getName());
+
+ return restClient.send(method, args);
+ }
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/AlbumService.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/AlbumService.java
new file mode 100644
index 000000000000..8b4c0d02d692
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/AlbumService.java
@@ -0,0 +1,86 @@
+/*
+ * 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.dynamicproxy;
+
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Body;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Delete;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Get;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Path;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Post;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Put;
+import java.util.List;
+
+/**
+ * Every method in this interface is annotated with the necessary metadata to represents an endpoint
+ * that we can call to communicate with a host server which is serving a resource by Rest API. This
+ * interface is focused in the resource Album.
+ */
+public interface AlbumService {
+
+ /**
+ * Get a list of albums from an endpoint.
+ *
+ * @return List of albums' data.
+ */
+ @Get("/albums")
+ List readAlbums();
+
+ /**
+ * Get a specific album from an endpoint.
+ *
+ * @param albumId Album's id to search for.
+ * @return Album's data.
+ */
+ @Get("/albums/{albumId}")
+ Album readAlbum(@Path("albumId") Integer albumId);
+
+ /**
+ * Creates a new album.
+ *
+ * @param album Album's data to be created.
+ * @return New album's data.
+ */
+ @Post("/albums")
+ Album createAlbum(@Body Album album);
+
+ /**
+ * Updates an existing album.
+ *
+ * @param albumId Album's id to be modified.
+ * @param album New album's data.
+ * @return Updated album's data.
+ */
+ @Put("/albums/{albumId}")
+ Album updateAlbum(@Path("albumId") Integer albumId, @Body Album album);
+
+ /**
+ * Deletes an album.
+ *
+ * @param albumId Album's id to be deleted.
+ * @return Empty album.
+ */
+ @Delete("/albums/{albumId}")
+ Album deleteAlbum(@Path("albumId") Integer albumId);
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/App.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/App.java
new file mode 100644
index 000000000000..d5a19184bf91
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/App.java
@@ -0,0 +1,114 @@
+/*
+ * 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.dynamicproxy;
+
+import java.lang.reflect.Proxy;
+import java.net.http.HttpClient;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * Application to demonstrate the Dynamic Proxy pattern. This application allow us to hit the public
+ * fake API https://jsonplaceholder.typicode.com for the resource Album through an interface. The
+ * call to Proxy.newProxyInstance creates a new dynamic proxy for the AlbumService interface and
+ * sets the AlbumInvocationHandler class as the handler to intercept all the interface's methods.
+ * Everytime that we call an AlbumService's method, the handler's method "invoke" will be call
+ * automatically, and it will pass all the method's metadata and arguments to other specialized
+ * class - TinyRestClient - to prepare the Rest API call accordingly. In this demo, the Dynamic
+ * Proxy pattern help us to run business logic through interfaces without an explicit implementation
+ * of the interfaces and supported on the Java Reflection approach.
+ */
+@Slf4j
+public class App {
+
+ static final String REST_API_URL = "https://jsonplaceholder.typicode.com";
+
+ private String baseUrl;
+ private HttpClient httpClient;
+ private AlbumService albumServiceProxy;
+
+ /**
+ * Class constructor.
+ *
+ * @param baseUrl Root url for endpoints.
+ * @param httpClient Handle the http communication.
+ */
+ public App(String baseUrl, HttpClient httpClient) {
+ this.baseUrl = baseUrl;
+ this.httpClient = httpClient;
+ }
+
+ /**
+ * Application entry point.
+ *
+ * @param args External arguments to be passed. Optional.
+ */
+ public static void main(String[] args) {
+ App app = new App(App.REST_API_URL, HttpClient.newHttpClient());
+ app.createDynamicProxy();
+ app.callMethods();
+ }
+
+ /**
+ * Create the Dynamic Proxy linked to the AlbumService interface and to the
+ * AlbumInvocationHandler.
+ */
+ public void createDynamicProxy() {
+ AlbumInvocationHandler albumInvocationHandler = new AlbumInvocationHandler(baseUrl, httpClient);
+
+ albumServiceProxy =
+ (AlbumService)
+ Proxy.newProxyInstance(
+ App.class.getClassLoader(),
+ new Class>[] {AlbumService.class},
+ albumInvocationHandler);
+ }
+
+ /**
+ * Call the methods of the Dynamic Proxy, in other words, the AlbumService interface's methods and
+ * receive the responses from the Rest API.
+ */
+ public void callMethods() {
+ int albumId = 17;
+ int userId = 3;
+
+ var albums = albumServiceProxy.readAlbums();
+ albums.forEach(album -> LOGGER.info("{}", album));
+
+ var album = albumServiceProxy.readAlbum(albumId);
+ LOGGER.info("{}", album);
+
+ var newAlbum =
+ albumServiceProxy.createAlbum(Album.builder().title("Big World").userId(userId).build());
+ LOGGER.info("{}", newAlbum);
+
+ var editAlbum =
+ albumServiceProxy.updateAlbum(
+ albumId, Album.builder().title("Green Valley").userId(userId).build());
+ LOGGER.info("{}", editAlbum);
+
+ var removedAlbum = albumServiceProxy.deleteAlbum(albumId);
+ LOGGER.info("{}", removedAlbum);
+ }
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/JsonUtil.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/JsonUtil.java
new file mode 100644
index 000000000000..d474c59da20f
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/JsonUtil.java
@@ -0,0 +1,94 @@
+/*
+ * 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.dynamicproxy.tinyrestclient;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.CollectionType;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.extern.slf4j.Slf4j;
+
+/** Utility class to handle Json operations. */
+@Slf4j
+public class JsonUtil {
+
+ private static ObjectMapper objectMapper = new ObjectMapper();
+
+ private JsonUtil() {}
+
+ /**
+ * Convert an object to a Json string representation.
+ *
+ * @param object Object to convert.
+ * @param Object's class.
+ * @return Json string.
+ */
+ public static String objectToJson(T object) {
+ try {
+ return objectMapper.writeValueAsString(object);
+ } catch (JsonProcessingException e) {
+ LOGGER.error("Cannot convert the object " + object + " to Json.", e);
+ return null;
+ }
+ }
+
+ /**
+ * Convert a Json string to an object of a class.
+ *
+ * @param json Json string to convert.
+ * @param clazz Object's class.
+ * @param Object's generic class.
+ * @return Object.
+ */
+ public static T jsonToObject(String json, Class clazz) {
+ try {
+ return objectMapper.readValue(json, clazz);
+ } catch (IOException e) {
+ LOGGER.error("Cannot convert the Json " + json + " to class " + clazz.getName() + ".", e);
+ return null;
+ }
+ }
+
+ /**
+ * Convert a Json string to a List of objects of a class.
+ *
+ * @param json Json string to convert.
+ * @param clazz Object's class.
+ * @param Object's generic class.
+ * @return List of objects.
+ */
+ public static List jsonToList(String json, Class clazz) {
+ try {
+ CollectionType listType =
+ objectMapper.getTypeFactory().constructCollectionType(ArrayList.class, clazz);
+ return objectMapper.reader().forType(listType).readValue(json);
+ } catch (JsonProcessingException e) {
+ LOGGER.error("Cannot convert the Json " + json + " to List of " + clazz.getName() + ".", e);
+ return List.of();
+ }
+ }
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/TinyRestClient.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/TinyRestClient.java
new file mode 100644
index 000000000000..dd952f479fa0
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/TinyRestClient.java
@@ -0,0 +1,194 @@
+/*
+ * 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.dynamicproxy.tinyrestclient;
+
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Body;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Http;
+import com.iluwatar.dynamicproxy.tinyrestclient.annotation.Path;
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.net.HttpURLConnection;
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.web.util.UriUtils;
+
+/**
+ * Class to handle all the http communication with a Rest API. It is supported by the HttpClient
+ * Java library.
+ */
+@Slf4j
+public class TinyRestClient {
+
+ private static Map httpAnnotationByMethod = new HashMap<>();
+
+ private String baseUrl;
+ private HttpClient httpClient;
+
+ /**
+ * Class constructor.
+ *
+ * @param baseUrl Root url for endpoints.
+ * @param httpClient Handle the http communication.
+ */
+ public TinyRestClient(String baseUrl, HttpClient httpClient) {
+ this.baseUrl = baseUrl;
+ this.httpClient = httpClient;
+ }
+
+ /**
+ * Creates a http communication to request and receive data from an endpoint.
+ *
+ * @param method Interface's method which is annotated with a http method.
+ * @param args Method's arguments passed in the call.
+ * @return Response from the endpoint.
+ * @throws IOException Exception thrown when any fail happens in the call.
+ * @throws InterruptedException Exception thrown when call is interrupted.
+ */
+ public Object send(Method method, Object[] args) throws IOException, InterruptedException {
+ var httpAnnotation = getHttpAnnotation(method);
+ if (httpAnnotation == null) {
+ return null;
+ }
+ var httpAnnotationName = httpAnnotation.annotationType().getSimpleName().toUpperCase();
+ var url = baseUrl + buildUrl(method, args, httpAnnotation);
+ var bodyPublisher = buildBodyPublisher(method, args);
+ var httpRequest =
+ HttpRequest.newBuilder()
+ .uri(URI.create(url))
+ .header("Content-Type", "application/json")
+ .method(httpAnnotationName, bodyPublisher)
+ .build();
+ var httpResponse = httpClient.send(httpRequest, HttpResponse.BodyHandlers.ofString());
+ var statusCode = httpResponse.statusCode();
+ if (statusCode >= HttpURLConnection.HTTP_BAD_REQUEST) {
+ var errorDetail = httpResponse.body();
+ LOGGER.error("Error from server: " + errorDetail);
+ return null;
+ }
+ return getResponse(method, httpResponse);
+ }
+
+ private String buildUrl(Method method, Object[] args, Annotation httpMethodAnnotation) {
+ var url = annotationValue(httpMethodAnnotation);
+ if (url == null) {
+ return "";
+ }
+ var index = 0;
+ for (var parameter : method.getParameters()) {
+ var pathAnnotation = getAnnotationOf(parameter.getDeclaredAnnotations(), Path.class);
+ if (pathAnnotation != null) {
+ var pathParam = "{" + annotationValue(pathAnnotation) + "}";
+ var pathValue = UriUtils.encodePath(args[index].toString(), StandardCharsets.UTF_8);
+ url = url.replace(pathParam, pathValue);
+ }
+ index++;
+ }
+ return url;
+ }
+
+ private HttpRequest.BodyPublisher buildBodyPublisher(Method method, Object[] args) {
+ var index = 0;
+ for (var parameter : method.getParameters()) {
+ var bodyAnnotation = getAnnotationOf(parameter.getDeclaredAnnotations(), Body.class);
+ if (bodyAnnotation != null) {
+ var body = JsonUtil.objectToJson(args[index]);
+ return HttpRequest.BodyPublishers.ofString(body);
+ }
+ index++;
+ }
+ return HttpRequest.BodyPublishers.noBody();
+ }
+
+ private Object getResponse(Method method, HttpResponse httpResponse) {
+ var rawData = httpResponse.body();
+ Type returnType;
+ try {
+ returnType = method.getGenericReturnType();
+ } catch (Exception e) {
+ LOGGER.error("Cannot get the generic return type of the method " + method.getName() + "()");
+ return null;
+ }
+ if (returnType instanceof ParameterizedType) {
+ Class> responseClass =
+ (Class>) (((ParameterizedType) returnType).getActualTypeArguments()[0]);
+ return JsonUtil.jsonToList(rawData, responseClass);
+ } else {
+ Class> responseClass = method.getReturnType();
+ return JsonUtil.jsonToObject(rawData, responseClass);
+ }
+ }
+
+ private Annotation getHttpAnnotation(Method method) {
+ return httpAnnotationByMethod.computeIfAbsent(
+ method,
+ m ->
+ Arrays.stream(m.getDeclaredAnnotations())
+ .filter(annot -> annot.annotationType().isAnnotationPresent(Http.class))
+ .findFirst()
+ .orElse(null));
+ }
+
+ private Annotation getAnnotationOf(Annotation[] annotations, Class> clazz) {
+ return Arrays.stream(annotations)
+ .filter(annot -> annot.annotationType().equals(clazz))
+ .findFirst()
+ .orElse(null);
+ }
+
+ private String annotationValue(Annotation annotation) {
+ var valueMethod =
+ Arrays.stream(annotation.annotationType().getDeclaredMethods())
+ .filter(methodAnnot -> methodAnnot.getName().equals("value"))
+ .findFirst()
+ .orElse(null);
+ if (valueMethod == null) {
+ return null;
+ }
+ Object result;
+ try {
+ result = valueMethod.invoke(annotation, (Object[]) null);
+ } catch (Exception e) {
+ LOGGER.error(
+ "Cannot read the value "
+ + annotation.annotationType().getSimpleName()
+ + "."
+ + valueMethod.getName()
+ + "()",
+ e);
+ result = null;
+ }
+ return (result instanceof String strResult ? strResult : null);
+ }
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Body.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Body.java
new file mode 100644
index 000000000000..df89731936ba
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Body.java
@@ -0,0 +1,38 @@
+/*
+ * 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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Annotation to mark a method's parameter as a Body parameter. It is typically used on Post and Put
+ * http methods.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Body {}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Delete.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Delete.java
new file mode 100644
index 000000000000..a43df905798f
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Delete.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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to mark an interface's method as a DELETE http method. */
+@Http
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Delete {
+ /**
+ * Set the url for this http method.
+ *
+ * @return Url address.
+ */
+ String value() default "";
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Get.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Get.java
new file mode 100644
index 000000000000..74f96ac062ec
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Get.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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to mark an interface's method as a GET http method. */
+@Http
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Get {
+ /**
+ * Set the url for this http method.
+ *
+ * @return Url address.
+ */
+ String value() default "";
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Http.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Http.java
new file mode 100644
index 000000000000..f12878de2d90
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Http.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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to mark other annotations to be recognized as http methods. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.ANNOTATION_TYPE)
+public @interface Http {}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Path.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Path.java
new file mode 100644
index 000000000000..4ff1faea4844
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Path.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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to mark a method's parameter as a Path parameter. */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.PARAMETER)
+public @interface Path {
+ /**
+ * Path parameter to be replaced in the url.
+ *
+ * @return Path parameter.
+ */
+ String value();
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Post.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Post.java
new file mode 100644
index 000000000000..f10f38c83984
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Post.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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to mark an interface's method as a POST http method. */
+@Http
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Post {
+ /**
+ * Set the url for this http method.
+ *
+ * @return Url address.
+ */
+ String value() default "";
+}
diff --git a/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Put.java b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Put.java
new file mode 100644
index 000000000000..9af9b27c4dde
--- /dev/null
+++ b/dynamic-proxy/src/main/java/com/iluwatar/dynamicproxy/tinyrestclient/annotation/Put.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.dynamicproxy.tinyrestclient.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/** Annotation to mark an interface's method as a PUT http method. */
+@Http
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface Put {
+ /**
+ * Set the url for this http method.
+ *
+ * @return Url address.
+ */
+ String value() default "";
+}
diff --git a/dynamic-proxy/src/test/java/com/iluwatar/dynamicproxy/AppTest.java b/dynamic-proxy/src/test/java/com/iluwatar/dynamicproxy/AppTest.java
new file mode 100644
index 000000000000..107255695344
--- /dev/null
+++ b/dynamic-proxy/src/test/java/com/iluwatar/dynamicproxy/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.dynamicproxy;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+class AppTest {
+
+ @Test
+ void shouldRunAppWithoutExceptions() {
+ assertDoesNotThrow(() -> App.main(null));
+ }
+}
diff --git a/eip-aggregator/README.md b/eip-aggregator/README.md
deleted file mode 100644
index b2be8db5bbe1..000000000000
--- a/eip-aggregator/README.md
+++ /dev/null
@@ -1,33 +0,0 @@
----
-layout: pattern
-title: EIP Aggregator
-folder: eip-aggregator
-permalink: /patterns/eip-aggregator/
-categories: Integration
-language: en
-tags:
- - Enterprise Integration Pattern
----
-
-## Intent
-Sometimes in enterprise systems there is a need to group incoming data in order to process it as a whole. For example
-you may need to gather offers and after defined number of offers has been received you would like to choose the one with
-the best parameters.
-
-Aggregator allows you to merge messages based on defined criteria and parameters. It gathers original messages,
-applies aggregation strategy and upon fulfilling given criteria, releasing merged messages.
-
-## Diagram
-
-
-## Applicability
-Use the Aggregator pattern when
-
-* You need to combine multiple incoming messages
-* You want to process grouped data
-
-## Credits
-
-* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Aggregator.html)
-* [Apache Camel - Documentation](http://camel.apache.org/aggregator2.html)
-* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
diff --git a/eip-aggregator/etc/aggregator.gif b/eip-aggregator/etc/aggregator.gif
deleted file mode 100644
index b06cdfb0c920..000000000000
Binary files a/eip-aggregator/etc/aggregator.gif and /dev/null differ
diff --git a/eip-aggregator/etc/eip-aggregator.urm.puml b/eip-aggregator/etc/eip-aggregator.urm.puml
deleted file mode 100644
index 4d8661e210c2..000000000000
--- a/eip-aggregator/etc/eip-aggregator.urm.puml
+++ /dev/null
@@ -1,14 +0,0 @@
-@startuml
-package com.iluwatar.eip.aggregator {
- class App {
- + App()
- + main(args : String[]) {static}
- }
-}
-package com.iluwatar.eip.aggregator.routes {
- class MessageAggregationStrategy {
- + MessageAggregationStrategy()
- + aggregate(oldExchange : Exchange, newExchange : Exchange) : Exchange
- }
-}
-@enduml
\ No newline at end of file
diff --git a/eip-aggregator/pom.xml b/eip-aggregator/pom.xml
deleted file mode 100644
index a4aaa751cc58..000000000000
--- a/eip-aggregator/pom.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
- 4.0.0
- eip-aggregator
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.apache.camel
- camel-core
-
-
- org.apache.camel
- camel-spring-boot
- ${camel.version}
-
-
- com.sun.xml.bind
- jaxb-impl
-
-
- javax.xml.bind
- jaxb-api
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.springframework.boot
- spring-boot-starter-test
-
-
- org.apache.camel
- camel-test-spring
- ${camel.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.epi.aggregator.App
-
-
-
-
-
-
-
-
-
diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java
deleted file mode 100644
index d2ac1091246e..000000000000
--- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/App.java
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.aggregator;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * Sometimes in enterprise systems there is a need to group incoming data in order to process it as
- * a whole. For example you may need to gather offers and after defined number of offers has been
- * received you would like to choose the one with the best parameters.
- *
- * Aggregator allows you to merge messages based on defined criteria and parameters. It gathers
- * original messages, applies aggregation strategy and upon fulfilling given criteria, releasing
- * merged messages.
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point. It starts Spring Boot application and using Apache Camel it
- * auto-configures routes.
- *
- * @param args command line args
- */
- public static void main(String[] args) throws Exception {
- // Run Spring Boot application and obtain ApplicationContext
- var context = SpringApplication.run(App.class, args);
-
- // Get CamelContext from ApplicationContext
- var camelContext = (CamelContext) context.getBean("camelContext");
-
- // Add a new routes that will handle endpoints form SplitterRoute class.
- camelContext.addRoutes(new RouteBuilder() {
- @Override
- public void configure() {
- from("{{endpoint}}").log("ENDPOINT: ${body}");
- }
- });
-
- // Add producer that will send test message to an entry point in WireTapRoute
- String[] stringArray = {"Test item #1", "Test item #2", "Test item #3"};
- camelContext.createProducerTemplate().sendBody("{{entry}}", stringArray);
-
- SpringApplication.exit(context);
- }
-}
diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java
deleted file mode 100644
index ae0880c900f5..000000000000
--- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/AggregatorRoute.java
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.aggregator.routes;
-
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-
-/**
- * Sample aggregator route definition.
- *
- *
It consumes messages out of the direct:entry entry point and forwards them to
- * direct:endpoint . Route accepts messages containing String as a body, it aggregates the
- * messages based on the settings and forwards them as CSV to the output chanel.
- *
- *
Settings for the aggregation are: aggregate until 3 messages are bundled or wait 2000ms
- * before sending bundled messages further.
- *
- *
In this example input/output endpoints names are stored in application.properties
- * file.
- */
-@Component
-public class AggregatorRoute extends RouteBuilder {
-
- @Autowired
- private MessageAggregationStrategy aggregator;
-
- /**
- * Configures the route.
- */
- @Override
- public void configure() {
- // Main route
- from("{{entry}}").aggregate(constant(true), aggregator)
- .completionSize(3).completionInterval(2000)
- .to("{{endpoint}}");
- }
-}
diff --git a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java b/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java
deleted file mode 100644
index 365a0b45a2b6..000000000000
--- a/eip-aggregator/src/main/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategy.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.aggregator.routes;
-
-import org.apache.camel.Exchange;
-import org.apache.camel.processor.aggregate.AggregationStrategy;
-import org.springframework.stereotype.Component;
-
-/**
- * Aggregation strategy joining bodies of messages. If message is first one oldMessage is
- * null. All changes are made on IN messages.
- */
-@Component
-public class MessageAggregationStrategy implements AggregationStrategy {
-
- @Override
- public Exchange aggregate(Exchange oldExchange, Exchange newExchange) {
- if (oldExchange == null) {
- return newExchange;
- }
-
- var in1 = (String) oldExchange.getIn().getBody();
- var in2 = (String) newExchange.getIn().getBody();
-
- oldExchange.getIn().setBody(in1 + ";" + in2);
-
- return oldExchange;
- }
-}
diff --git a/eip-aggregator/src/main/resources/application.properties b/eip-aggregator/src/main/resources/application.properties
deleted file mode 100644
index ca07b459e71b..000000000000
--- a/eip-aggregator/src/main/resources/application.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2021 Ilkka Seppälä
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-entry=direct:entry
-endpoint=direct:endpoint
diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java
deleted file mode 100644
index 08e03441c90d..000000000000
--- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.aggregator;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * Test for App class
- */
-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.
- */
-
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java
deleted file mode 100644
index 305201694815..000000000000
--- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/AggregatorRouteTest.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.aggregator.routes;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.apache.camel.EndpointInject;
-import org.apache.camel.ProducerTemplate;
-import org.apache.camel.component.mock.MockEndpoint;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-/**
- * Test class for AggregatorRoute .
- *
- * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need
- * to substitute original endpoint names to mocks.
- *
- */
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(classes = AggregatorRouteTest.class)
-@ActiveProfiles("test")
-@EnableAutoConfiguration
-@ComponentScan
-class AggregatorRouteTest {
-
- @EndpointInject(uri = "{{entry}}")
- private ProducerTemplate entry;
-
- @EndpointInject(uri = "{{endpoint}}")
- private MockEndpoint endpoint;
-
- /**
- * Test if endpoint receives three separate messages.
- *
- * @throws Exception in case of en exception during the test
- */
- @Test
- @DirtiesContext
- void testSplitter() throws Exception {
-
- // Three items in one entry message
- entry.sendBody("TEST1");
- entry.sendBody("TEST2");
- entry.sendBody("TEST3");
- entry.sendBody("TEST4");
- entry.sendBody("TEST5");
-
- // Endpoint should have three different messages in the end order of the messages is not important
- endpoint.expectedMessageCount(2);
- endpoint.assertIsSatisfied();
-
- var body = (String) endpoint.getReceivedExchanges().get(0).getIn().getBody();
- assertEquals(3, body.split(";").length);
-
- var body2 = (String) endpoint.getReceivedExchanges().get(1).getIn().getBody();
- assertEquals(2, body2.split(";").length);
- }
-}
diff --git a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java b/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java
deleted file mode 100644
index 40c15b37084c..000000000000
--- a/eip-aggregator/src/test/java/com/iluwatar/eip/aggregator/routes/MessageAggregationStrategyTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.aggregator.routes;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.impl.DefaultExchange;
-import org.junit.jupiter.api.Test;
-
-/**
- * Tests MessageAggregationStrategy
- */
-class MessageAggregationStrategyTest {
-
- @Test
- void testAggregate() {
- var mas = new MessageAggregationStrategy();
- var oldExchange = new DefaultExchange((CamelContext) null);
- oldExchange.getIn().setBody("TEST1");
-
- var newExchange = new DefaultExchange((CamelContext) null);
- newExchange.getIn().setBody("TEST2");
-
- var output = mas.aggregate(oldExchange, newExchange);
- var outputBody = (String) output.getIn().getBody();
- assertEquals("TEST1;TEST2", outputBody);
- }
-
- @Test
- void testAggregateOldNull() {
- var mas = new MessageAggregationStrategy();
-
- var newExchange = new DefaultExchange((CamelContext) null);
- newExchange.getIn().setBody("TEST2");
-
- var output = mas.aggregate(null, newExchange);
- var outputBody = (String) output.getIn().getBody();
-
- assertEquals(newExchange, output);
- assertEquals("TEST2", outputBody);
- }
-}
diff --git a/eip-aggregator/src/test/resources/application-test.properties b/eip-aggregator/src/test/resources/application-test.properties
deleted file mode 100644
index 843005704fdc..000000000000
--- a/eip-aggregator/src/test/resources/application-test.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2021 Ilkka Seppälä
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-entry=direct:entry
-endpoint=mock:endpoint
diff --git a/eip-message-channel/README.md b/eip-message-channel/README.md
deleted file mode 100644
index 34316a0ed6a5..000000000000
--- a/eip-message-channel/README.md
+++ /dev/null
@@ -1,27 +0,0 @@
----
-layout: pattern
-title: EIP Message Channel
-folder: eip-message-channel
-permalink: /patterns/eip-message-channel/
-categories: Integration
-language: en
-tags:
- - Enterprise Integration Pattern
----
-
-## Intent
-When two applications communicate using a messaging system they do it by using logical addresses
-of the system, so called Message Channels.
-
-## Class diagram
-
-
-## Applicability
-Use the Message Channel pattern when
-
-* two or more applications need to communicate using a messaging system
-
-## Real world examples
-
-* [akka-camel](http://doc.akka.io/docs/akka/snapshot/scala/camel.html)
-* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
diff --git a/eip-message-channel/etc/eip-message-channel.urm.puml b/eip-message-channel/etc/eip-message-channel.urm.puml
deleted file mode 100644
index 38e2369ce562..000000000000
--- a/eip-message-channel/etc/eip-message-channel.urm.puml
+++ /dev/null
@@ -1,9 +0,0 @@
-@startuml
-package com.iluwatar.eip.message.channel {
- class App {
- - LOGGER : Logger {static}
- + App()
- + main(args : String[]) {static}
- }
-}
-@enduml
\ No newline at end of file
diff --git a/eip-message-channel/etc/message-channel.png b/eip-message-channel/etc/message-channel.png
deleted file mode 100644
index 7db473281aff..000000000000
Binary files a/eip-message-channel/etc/message-channel.png and /dev/null differ
diff --git a/eip-message-channel/etc/message-channel.ucls b/eip-message-channel/etc/message-channel.ucls
deleted file mode 100644
index 3ef0ed4bc8fd..000000000000
--- a/eip-message-channel/etc/message-channel.ucls
+++ /dev/null
@@ -1,320 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/eip-message-channel/pom.xml b/eip-message-channel/pom.xml
deleted file mode 100644
index 00cc08d0472e..000000000000
--- a/eip-message-channel/pom.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
- eip-message-channel
-
-
- org.apache.camel
- camel-core
-
-
- org.apache.camel
- camel-stream
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.eip.message.channel.App
-
-
-
-
-
-
-
-
-
diff --git a/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java b/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java
deleted file mode 100644
index 05fe94dba04c..000000000000
--- a/eip-message-channel/src/main/java/com/iluwatar/eip/message/channel/App.java
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.message.channel;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.impl.DefaultCamelContext;
-
-/**
- * When two applications communicate with each other using a messaging system they first need to
- * establish a communication channel that will carry the data. Message Channel decouples Message
- * producers and consumers.
- *
- * The sending application doesn't necessarily know what particular application will end up
- * retrieving it, but it can be assured that the application that retrieves the information is
- * interested in that information. This is because the messaging system has different Message
- * Channels for different types of information the applications want to communicate. When an
- * application sends information, it doesn't randomly add the information to any channel available;
- * it adds it to a channel whose specific purpose is to communicate that sort of information.
- * Likewise, an application that wants to receive particular information doesn't pull info off some
- * random channel; it selects what channel to get information from based on what type of information
- * it wants.
- *
- *
In this example we use Apache Camel to establish two different Message Channels. The first
- * one reads from standard input and delivers messages to Direct endpoint. The second Message
- * Channel is established from the Direct component to console output. No actual messages are sent,
- * only the established routes are printed to standard output.
- */
-@Slf4j
-public class App {
-
- /**
- * Program entry point.
- */
- public static void main(String[] args) throws Exception {
- var context = new DefaultCamelContext();
-
- context.addRoutes(new RouteBuilder() {
-
- @Override
- public void configure() throws Exception {
- from("stream:in").to("direct:greetings");
- from("direct:greetings").to("stream:out");
- }
- });
-
- context.start();
- context.getRoutes().forEach(r -> LOGGER.info(r.toString()));
- context.stop();
- }
-}
diff --git a/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java b/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java
deleted file mode 100644
index ff51f314ccce..000000000000
--- a/eip-message-channel/src/test/java/com/iluwatar/eip/message/channel/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.message.channel;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * 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#main(String[])}
- * throws an exception.
- */
-
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/eip-publish-subscribe/.gitignore b/eip-publish-subscribe/.gitignore
deleted file mode 100644
index b83d22266ac8..000000000000
--- a/eip-publish-subscribe/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/target/
diff --git a/eip-publish-subscribe/README.md b/eip-publish-subscribe/README.md
deleted file mode 100644
index 9d8f10ae1a8d..000000000000
--- a/eip-publish-subscribe/README.md
+++ /dev/null
@@ -1,26 +0,0 @@
----
-layout: pattern
-title: EIP Publish Subscribe
-folder: eip-publish-subscribe
-permalink: /patterns/eip-publish-subscribe/
-categories: Integration
-language: en
-tags:
- - Enterprise Integration Pattern
----
-
-## Intent
-Broadcast messages from sender to all the interested receivers.
-
-## Class diagram
-
-
-## Applicability
-Use the Publish Subscribe Channel pattern when
-
-* two or more applications need to communicate using a messaging system for broadcasts.
-
-## Credits
-
-* [J2EE Design Patterns](https://www.amazon.com/gp/product/0596004273/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0596004273&linkCode=as2&tag=javadesignpat-20&linkId=48d37c67fb3d845b802fa9b619ad8f31)
-* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
diff --git a/eip-publish-subscribe/etc/eip-publish-subscribe.urm.puml b/eip-publish-subscribe/etc/eip-publish-subscribe.urm.puml
deleted file mode 100644
index a201f59bba67..000000000000
--- a/eip-publish-subscribe/etc/eip-publish-subscribe.urm.puml
+++ /dev/null
@@ -1,9 +0,0 @@
-@startuml
-package com.iluwatar.eip.publish.subscribe {
- class App {
- - LOGGER : Logger {static}
- + App()
- + main(args : String[]) {static}
- }
-}
-@enduml
\ No newline at end of file
diff --git a/eip-publish-subscribe/etc/publish-subscribe.png b/eip-publish-subscribe/etc/publish-subscribe.png
deleted file mode 100644
index 99867da66b54..000000000000
Binary files a/eip-publish-subscribe/etc/publish-subscribe.png and /dev/null differ
diff --git a/eip-publish-subscribe/etc/publish-subscribe.ucls b/eip-publish-subscribe/etc/publish-subscribe.ucls
deleted file mode 100644
index 1b121506eed8..000000000000
--- a/eip-publish-subscribe/etc/publish-subscribe.ucls
+++ /dev/null
@@ -1,218 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/eip-publish-subscribe/pom.xml b/eip-publish-subscribe/pom.xml
deleted file mode 100644
index dde4440e5c8a..000000000000
--- a/eip-publish-subscribe/pom.xml
+++ /dev/null
@@ -1,68 +0,0 @@
-
-
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
- eip-publish-subscribe
-
-
- org.apache.camel
- camel-core
-
-
- org.apache.camel
- camel-stream
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.eip.publish.subscribe.App
-
-
-
-
-
-
-
-
-
diff --git a/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java b/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java
deleted file mode 100644
index ef586a56317a..000000000000
--- a/eip-publish-subscribe/src/main/java/com/iluwatar/eip/publish/subscribe/App.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.publish.subscribe;
-
-import lombok.extern.slf4j.Slf4j;
-import org.apache.camel.builder.RouteBuilder;
-import org.apache.camel.impl.DefaultCamelContext;
-
-/**
- * There are well-established patterns for implementing broadcasting. The Observer pattern describes
- * the need to decouple observers from their subject (that is, the originator of the event) so that
- * the subject can easily provide event notification to all interested observers no matter how many
- * observers there are (even none). The Publish-Subscribe pattern expands upon Observer by adding
- * the notion of an event channel for communicating event notifications.
- *
- *
A Publish-Subscribe Channel works like this: It has one input channel that splits into
- * multiple output channels, one for each subscriber. When an event is published into the channel,
- * the Publish-Subscribe Channel delivers a copy of the message to each of the output channels. Each
- * output end of the channel has only one subscriber, which is allowed to consume a message only
- * once. In this way, each subscriber gets the message only once, and consumed copies disappear from
- * their channels.
- *
- *
In this example we use Apache Camel to establish a Publish-Subscribe Channel from
- * "direct-origin" to "mock:foo", "mock:bar" and "stream:out".
- */
-@Slf4j
-public class App {
-
- /**
- * Program entry point.
- */
- public static void main(String[] args) throws Exception {
- var context = new DefaultCamelContext();
- context.addRoutes(new RouteBuilder() {
- @Override
- public void configure() throws Exception {
- from("direct:origin").multicast().to("mock:foo", "mock:bar", "stream:out");
- }
- });
- var template = context.createProducerTemplate();
- context.start();
- context.getRoutes().forEach(r -> LOGGER.info(r.toString()));
- template.sendBody("direct:origin", "Hello from origin");
- context.stop();
- }
-}
diff --git a/eip-publish-subscribe/src/main/resources/logback.xml b/eip-publish-subscribe/src/main/resources/logback.xml
deleted file mode 100644
index 1f177fc247b2..000000000000
--- a/eip-publish-subscribe/src/main/resources/logback.xml
+++ /dev/null
@@ -1,49 +0,0 @@
-
-
-
-
- publish-subscribe.log
-
- publish-subscribe-%d.log
- 5
-
-
- %-5p [%d{ISO8601,UTC}] %c: %m%n
-
-
-
-
- %-5p [%d{ISO8601,UTC}] %c: %m%n
-
-
-
-
-
-
-
-
-
-
diff --git a/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java b/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java
deleted file mode 100644
index 7f683000c7fc..000000000000
--- a/eip-publish-subscribe/src/test/java/com/iluwatar/eip/publish/subscribe/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.publish.subscribe;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * 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#main(String[])}
- * throws an exception.
- */
-
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/eip-splitter/README.md b/eip-splitter/README.md
deleted file mode 100644
index a9c86e309d3b..000000000000
--- a/eip-splitter/README.md
+++ /dev/null
@@ -1,32 +0,0 @@
----
-layout: pattern
-title: EIP Splitter
-folder: eip-splitter
-permalink: /patterns/eip-splitter/
-categories: Integration
-language: en
-tags:
- - Enterprise Integration Pattern
----
-
-## Intent
-It is very common in integration systems that incoming messages consists of many items bundled together. For example
-an invoice document contains multiple invoice lines describing transaction (quantity, name of provided
-service/sold goods, price etc.). Such bundled messages may not be accepted by other systems. This is where splitter
-pattern comes in handy. It will take the whole document, split it based on given criteria and send individual
-items to the endpoint.
-
-## Diagram
-
-
-## Applicability
-Use the Splitter pattern when
-
-* You need to split received data into smaller pieces to process them individually
-* You need to control the size of data batches you are able to process
-
-## Credits
-
-* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/Sequencer.html)
-* [Apache Camel - Documentation](http://camel.apache.org/splitter.html)
-* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
diff --git a/eip-splitter/etc/eip-splitter.urm.puml b/eip-splitter/etc/eip-splitter.urm.puml
deleted file mode 100644
index ad063b709c3d..000000000000
--- a/eip-splitter/etc/eip-splitter.urm.puml
+++ /dev/null
@@ -1,8 +0,0 @@
-@startuml
-package com.iluwatar.eip.splitter {
- class App {
- + App()
- + main(args : String[]) {static}
- }
-}
-@enduml
\ No newline at end of file
diff --git a/eip-splitter/etc/sequencer.gif b/eip-splitter/etc/sequencer.gif
deleted file mode 100644
index a925fa209877..000000000000
Binary files a/eip-splitter/etc/sequencer.gif and /dev/null differ
diff --git a/eip-splitter/pom.xml b/eip-splitter/pom.xml
deleted file mode 100644
index fa2e7d910822..000000000000
--- a/eip-splitter/pom.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
- 4.0.0
- eip-splitter
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.apache.camel
- camel-core
-
-
- org.apache.camel
- camel-spring-boot
- ${camel.version}
-
-
- com.sun.xml.bind
- jaxb-impl
-
-
- javax.xml.bind
- jaxb-api
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.springframework.boot
- spring-boot-starter-test
-
-
- org.apache.camel
- camel-test-spring
- ${camel.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.eip.splitter.App
-
-
-
-
-
-
-
-
-
diff --git a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java
deleted file mode 100644
index 263d9fab59cc..000000000000
--- a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/App.java
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.splitter;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * It is very common in integration systems that incoming messages consists of many items bundled
- * together. For example an invoice document contains multiple invoice lines describing transaction
- * (quantity, name of provided service/sold goods, price etc.). Such bundled messages may not be
- * accepted by other systems. This is where splitter pattern comes in handy. It will take the whole
- * document, split it based on given criteria and send individual items to the endpoint.
- *
- *
- * Splitter allows you to split messages based on defined criteria. It takes original message,
- * process it and send multiple parts to the output channel. It is not defined if it should keep the
- * order of items though.
- *
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point. It starts Spring Boot application and using Apache Camel it
- * auto-configures routes.
- *
- * @param args command line args
- */
- public static void main(String[] args) throws Exception {
- // Run Spring Boot application and obtain ApplicationContext
- var context = SpringApplication.run(App.class, args);
-
- // Get CamelContext from ApplicationContext
- var camelContext = (CamelContext) context.getBean("camelContext");
-
- // Add a new routes that will handle endpoints form SplitterRoute class.
- camelContext.addRoutes(new RouteBuilder() {
-
- @Override
- public void configure() throws Exception {
- from("{{endpoint}}").log("ENDPOINT: ${body}");
- }
-
- });
-
- // Add producer that will send test message to an entry point in WireTapRoute
- String[] stringArray = {"Test item #1", "Test item #2", "Test item #3"};
- camelContext.createProducerTemplate().sendBody("{{entry}}", stringArray);
-
- SpringApplication.exit(context);
- }
-}
diff --git a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java b/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java
deleted file mode 100644
index 4fc4679604a7..000000000000
--- a/eip-splitter/src/main/java/com/iluwatar/eip/splitter/routes/SplitterRoute.java
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.splitter.routes;
-
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.stereotype.Component;
-
-/**
- * Sample splitter route definition.
- *
- * It consumes messages out of the direct:entry entry point and forwards them to
- * direct:endpoint . Route accepts messages having body of array or collection of objects.
- * Splitter component split message body and forwards single objects to the endpoint.
- *
- *
In this example input/output endpoints names are stored in application.properties
- * file.
- */
-@Component
-public class SplitterRoute extends RouteBuilder {
-
- /**
- * Configures the route.
- *
- * @throws Exception in case of exception during configuration
- */
- @Override
- public void configure() throws Exception {
- // Main route
- from("{{entry}}").split().body().to("{{endpoint}}");
- }
-}
diff --git a/eip-splitter/src/main/resources/application.properties b/eip-splitter/src/main/resources/application.properties
deleted file mode 100644
index ca07b459e71b..000000000000
--- a/eip-splitter/src/main/resources/application.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2021 Ilkka Seppälä
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-entry=direct:entry
-endpoint=direct:endpoint
diff --git a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java
deleted file mode 100644
index c9ba7d17bd45..000000000000
--- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.splitter;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * Test for App class
- */
-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.
- */
-
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java b/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java
deleted file mode 100644
index bc48e9ab4a11..000000000000
--- a/eip-splitter/src/test/java/com/iluwatar/eip/splitter/routes/SplitterRouteTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.splitter.routes;
-
-import org.apache.camel.EndpointInject;
-import org.apache.camel.ProducerTemplate;
-import org.apache.camel.component.mock.MockEndpoint;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-/**
- * Test class for SplitterRoute .
- *
- * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need
- * to substitute original endpoint names to mocks.
- *
- */
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(classes = SplitterRouteTest.class)
-@ActiveProfiles("test")
-@EnableAutoConfiguration
-@ComponentScan
-class SplitterRouteTest {
-
- @EndpointInject(uri = "{{entry}}")
- private ProducerTemplate entry;
-
- @EndpointInject(uri = "{{endpoint}}")
- private MockEndpoint endpoint;
-
- /**
- * Test if endpoint receives three separate messages.
- *
- * @throws Exception in case of en exception during the test
- */
- @Test
- @DirtiesContext
- void testSplitter() throws Exception {
-
- // Three items in one entry message
- entry.sendBody(new String[]{"TEST1", "TEST2", "TEST3"});
-
- // Endpoint should have three different messages in the end order of the messages is not important
- endpoint.expectedMessageCount(3);
- endpoint.assertIsSatisfied();
- }
-}
diff --git a/eip-splitter/src/test/resources/application-test.properties b/eip-splitter/src/test/resources/application-test.properties
deleted file mode 100644
index 843005704fdc..000000000000
--- a/eip-splitter/src/test/resources/application-test.properties
+++ /dev/null
@@ -1,25 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2021 Ilkka Seppälä
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-entry=direct:entry
-endpoint=mock:endpoint
diff --git a/eip-wire-tap/README.md b/eip-wire-tap/README.md
deleted file mode 100644
index 808a8b26829a..000000000000
--- a/eip-wire-tap/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
----
-layout: pattern
-title: EIP Wire Tap
-folder: eip-wire-tap
-permalink: /patterns/eip-wire-tap/
-categories: Integration
-language: en
-tags:
- - Enterprise Integration Pattern
----
-
-## Intent
-In most integration cases there is a need to monitor the messages flowing through the system. It is usually achieved
-by intercepting the message and redirecting it to a different location like console, filesystem or the database.
-It is important that such functionality should not modify the original message and influence the processing path.
-
-## Diagram
-
-
-## Applicability
-Use the Wire Tap pattern when
-
-* You need to monitor messages flowing through the system
-* You need to redirect the same, unchanged message to two different endpoints/paths
-
-## Credits
-
-* [Gregor Hohpe, Bobby Woolf - Enterprise Integration Patterns](http://www.enterpriseintegrationpatterns.com/patterns/messaging/WireTap.html)
-* [Apache Camel - Documentation](http://camel.apache.org/wire-tap.html)
-* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://www.amazon.com/gp/product/0321200683/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0321200683&linkCode=as2&tag=javadesignpat-20&linkId=122e0cff74eedd004cc81a3ecfa623cf)
diff --git a/eip-wire-tap/etc/eip-wire-tap.urm.puml b/eip-wire-tap/etc/eip-wire-tap.urm.puml
deleted file mode 100644
index 51ee99723cc8..000000000000
--- a/eip-wire-tap/etc/eip-wire-tap.urm.puml
+++ /dev/null
@@ -1,8 +0,0 @@
-@startuml
-package com.iluwatar.eip.wiretap {
- class App {
- + App()
- + main(args : String[]) {static}
- }
-}
-@enduml
\ No newline at end of file
diff --git a/eip-wire-tap/etc/wiretap.gif b/eip-wire-tap/etc/wiretap.gif
deleted file mode 100644
index 4141737167ca..000000000000
Binary files a/eip-wire-tap/etc/wiretap.gif and /dev/null differ
diff --git a/eip-wire-tap/pom.xml b/eip-wire-tap/pom.xml
deleted file mode 100644
index 853b2a2bd764..000000000000
--- a/eip-wire-tap/pom.xml
+++ /dev/null
@@ -1,91 +0,0 @@
-
-
-
- 4.0.0
- eip-wire-tap
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
-
-
- org.springframework.boot
- spring-boot-starter-web
-
-
- org.apache.camel
- camel-core
-
-
- org.apache.camel
- camel-spring-boot
- ${camel.version}
-
-
- com.sun.xml.bind
- jaxb-impl
-
-
- javax.xml.bind
- jaxb-api
-
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
- org.springframework.boot
- spring-boot-starter-test
-
-
- org.apache.camel
- camel-test-spring
- ${camel.version}
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.eip.wiretap.App
-
-
-
-
-
-
-
-
-
diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java
deleted file mode 100644
index 7940d3e5ee22..000000000000
--- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/App.java
+++ /dev/null
@@ -1,75 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.wiretap;
-
-import org.apache.camel.CamelContext;
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.boot.SpringApplication;
-import org.springframework.boot.autoconfigure.SpringBootApplication;
-
-/**
- * In most integration cases there is a need to monitor the messages flowing through the system. It
- * is usually achieved by intercepting the message and redirecting it to a different location like
- * console, filesystem or the database. It is important that such functionality should not modify
- * the original message and influence the processing path.
- *
- *
- * Wire Tap allows you to route messages to a separate location while they are being forwarded to
- * the ultimate destination. It basically consumes messages of the input channel and publishes the
- * unmodified message to both output channels.
- *
- */
-@SpringBootApplication
-public class App {
-
- /**
- * Program entry point. It starts Spring Boot application and using Apache Camel it
- * auto-configures routes.
- *
- * @param args command line args
- */
- public static void main(String[] args) throws Exception {
- // Run Spring Boot application and obtain ApplicationContext
- var context = SpringApplication.run(App.class, args);
-
- // Get CamelContext from ApplicationContext
- var camelContext = (CamelContext) context.getBean("camelContext");
-
- // Add a new routes that will handle endpoints form WireTapRoute class.
- camelContext.addRoutes(new RouteBuilder() {
-
- @Override
- public void configure() throws Exception {
- from("{{endpoint}}").log("ENDPOINT: ${body}");
- from("{{wireTapEndpoint}}").log("WIRETAPPED ENDPOINT: ${body}");
- }
-
- });
-
- // Add producer that will send test message to an entry point in WireTapRoute
- camelContext.createProducerTemplate().sendBody("{{entry}}", "Test message");
-
- SpringApplication.exit(context);
- }
-}
diff --git a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java b/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java
deleted file mode 100644
index 39d89e7382d9..000000000000
--- a/eip-wire-tap/src/main/java/com/iluwatar/eip/wiretap/routes/WireTapRoute.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.wiretap.routes;
-
-import org.apache.camel.builder.RouteBuilder;
-import org.springframework.stereotype.Component;
-
-/**
- * Sample wire tap route definition.
- *
- * It consumes messages out of the direct:entry entry point and forwards them to
- * direct:endpoint . Wire Tap intercepts the message and sends it to direct:wireTap ,
- * which in turn forwards it to
- * direct:wireTapEndpoint .
- *
- *
In this example input/output endpoints names are stored in application.properties
- * file.
- */
-@Component
-public class WireTapRoute extends RouteBuilder {
-
- /**
- * Configures the route.
- *
- * @throws Exception in case of exception during configuration
- */
- @Override
- public void configure() throws Exception {
- // Main route
- from("{{entry}}").wireTap("direct:wireTap").to("{{endpoint}}");
-
- // Wire tap route
- from("direct:wireTap").log("Message: ${body}").to("{{wireTapEndpoint}}");
- }
-}
diff --git a/eip-wire-tap/src/main/resources/application.properties b/eip-wire-tap/src/main/resources/application.properties
deleted file mode 100644
index cb77786eda26..000000000000
--- a/eip-wire-tap/src/main/resources/application.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2021 Ilkka Seppälä
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-entry=direct:entry
-endpoint=direct:endpoint
-wireTapEndpoint=direct:wireTapEndpoint
\ No newline at end of file
diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java
deleted file mode 100644
index 20fc2a8bce77..000000000000
--- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.wiretap;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * Test for App class
- */
-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.
- */
-
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java b/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java
deleted file mode 100644
index 49bb043c4699..000000000000
--- a/eip-wire-tap/src/test/java/com/iluwatar/eip/wiretap/routes/WireTapRouteTest.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.eip.wiretap.routes;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
-import org.apache.camel.EndpointInject;
-import org.apache.camel.ProducerTemplate;
-import org.apache.camel.component.mock.MockEndpoint;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.ExtendWith;
-import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
-import org.springframework.boot.test.context.SpringBootTest;
-import org.springframework.context.annotation.ComponentScan;
-import org.springframework.test.annotation.DirtiesContext;
-import org.springframework.test.context.ActiveProfiles;
-import org.springframework.test.context.junit.jupiter.SpringExtension;
-
-/**
- * Test class for WireTapRoute .
- *
- * In order for it to work we have to mock endpoints we want to read/write to. To mock those we need
- * to substitute original endpoint names to mocks.
- *
- */
-@ExtendWith(SpringExtension.class)
-@SpringBootTest(classes = WireTapRouteTest.class)
-@ActiveProfiles("test")
-@EnableAutoConfiguration
-@ComponentScan
-class WireTapRouteTest {
-
- @EndpointInject(uri = "{{entry}}")
- private ProducerTemplate entry;
-
- @EndpointInject(uri = "{{endpoint}}")
- private MockEndpoint endpoint;
-
- @EndpointInject(uri = "{{wireTapEndpoint}}")
- private MockEndpoint wireTapEndpoint;
-
- /**
- * Test if both endpoints receive exactly one message containing the same, unchanged body.
- *
- * @throws Exception in case of en exception during the test
- */
- @Test
- @DirtiesContext
- void testWireTap() throws Exception {
- entry.sendBody("TEST");
-
- endpoint.expectedMessageCount(1);
- wireTapEndpoint.expectedMessageCount(1);
-
- endpoint.assertIsSatisfied();
- wireTapEndpoint.assertIsSatisfied();
-
- var endpointIn = endpoint.getExchanges().get(0).getIn();
- var wireTapEndpointIn = wireTapEndpoint.getExchanges().get(0).getIn();
-
- assertEquals("TEST", endpointIn.getBody());
- assertEquals("TEST", wireTapEndpointIn.getBody());
- }
-}
diff --git a/eip-wire-tap/src/test/resources/application-test.properties b/eip-wire-tap/src/test/resources/application-test.properties
deleted file mode 100644
index 801b7c928b09..000000000000
--- a/eip-wire-tap/src/test/resources/application-test.properties
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# The MIT License
-# Copyright © 2014-2021 Ilkka Seppälä
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-
-entry=direct:entry
-endpoint=mock:endpoint
-wireTapEndpoint=mock:wireTapEndpoint
\ No newline at end of file
diff --git a/event-aggregator/README.md b/event-aggregator/README.md
index 2e54ed90420d..063aac502640 100644
--- a/event-aggregator/README.md
+++ b/event-aggregator/README.md
@@ -1,34 +1,206 @@
---
-layout: pattern
-title: Event Aggregator
-folder: event-aggregator
-permalink: /patterns/event-aggregator/
-categories: Structural
+title: "Event Aggregator Pattern in Java: Centralizing Event Management in Large Applications"
+shortTitle: Event Aggregator
+description: "Explore the Event Aggregator design pattern with our in-depth guide. Learn how to implement it effectively with examples and improve your Java applications. Perfect for developers seeking to enhance their design pattern knowledge."
+category: Messaging
language: en
-tags:
- - Reactive
+tag:
+ - Decoupling
+ - Event-driven
+ - Messaging
+ - Publish/subscribe
+ - Reactive
+head:
+ - - meta
+ - name: keywords
+ content:
---
-## Intent
-A system with lots of objects can lead to complexities when a
-client wants to subscribe to events. The client has to find and register for
-each object individually, if each object has multiple events then each event
-requires a separate subscription. An Event Aggregator acts as a single source
-of events for many objects. It registers for all the events of the many objects
-allowing clients to register with just the aggregator.
+## Also known as
-## Class diagram
-
+* Event Channel
+* Event Central
+* Message Hub
+
+## Intent of Event Aggregator Design Pattern
+
+An Event Aggregator is a design pattern used for handling events in a system. It centralizes the event handling logic, making it easier to manage and maintain. The Event Aggregator design pattern aims to decouple event generation from event handling. This design pattern collects events from multiple sources and routes them to the appropriate handlers.
+
+## Detailed Explanation of Event Aggregator Pattern with Real-World Examples
+
+Real-world example
+
+> The Event Aggregator pattern is often compared to a hub in a wheel. In this analogy, the Event Aggregator is the hub, and the spokes are the event sources. The hub collects events from all the spokes and then distributes them to the appropriate handlers.
+
+In Plain Words
+
+> Event Aggregator is a design pattern that allows multiple event sources to communicate with event handlers through a central point, rather than having each event source communicate directly with each handler.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Event Aggregator Pattern in Java
+
+Consider the following example where we use the Event Aggregator to handle multiple events.
+
+King Joffrey sits on the iron throne and rules the seven kingdoms of Westeros. He receives most of his critical information from King's Hand, the second in command. King's hand has many close advisors himself, feeding him with relevant information about events occurring in the kingdom.
+
+In our programmatic example, we demonstrate the implementation of an event aggregator pattern. Some of the objects are event listeners, some are event emitters, and the event aggregator does both.
+
+```java
+public interface EventObserver {
+ void onEvent(Event e);
+}
+```
+
+```java
+public abstract class EventEmitter {
+
+ private final Map> observerLists;
+
+ public EventEmitter() {
+ observerLists = new HashMap<>();
+ }
+
+ public final void registerObserver(EventObserver obs, Event e) {
+ // implementation omitted
+ }
+
+ protected void notifyObservers(Event e) {
+ // implementation omitted
+ }
+}
+```
+
+`KingJoffrey` is listening to events from `KingsHand`.
+
+```java
+
+@Slf4j
+public class KingJoffrey implements EventObserver {
+ @Override
+ public void onEvent(Event e) {
+ LOGGER.info("Received event from the King's Hand: {}", e.toString());
+ }
+}
+```
+
+`KingsHand` is listening to events from his subordinates `LordBaelish`, `LordVarys`, and `Scout`. Whatever he hears from them, he delivers to `KingJoffrey`.
+
+```java
+public class KingsHand extends EventEmitter implements EventObserver {
+
+ public KingsHand() {
+ }
+
+ public KingsHand(EventObserver obs, Event e) {
+ super(obs, e);
+ }
+
+ @Override
+ public void onEvent(Event e) {
+ notifyObservers(e);
+ }
+}
+```
+
+For example, `LordVarys` finds a traitor every Sunday and notifies the `KingsHand`.
+
+```java
+
+@Slf4j
+public class LordVarys extends EventEmitter implements EventObserver {
+ @Override
+ public void timePasses(Weekday day) {
+ if (day == Weekday.SATURDAY) {
+ notifyObservers(Event.TRAITOR_DETECTED);
+ }
+ }
+}
+```
+
+The following snippet demonstrates how the objects are constructed and wired together.
+
+```java
+public static void main(String[] args) {
+
+ var kingJoffrey = new KingJoffrey();
+
+ var kingsHand = new KingsHand();
+ kingsHand.registerObserver(kingJoffrey, Event.TRAITOR_DETECTED);
+ kingsHand.registerObserver(kingJoffrey, Event.STARK_SIGHTED);
+ kingsHand.registerObserver(kingJoffrey, Event.WARSHIPS_APPROACHING);
+ kingsHand.registerObserver(kingJoffrey, Event.WHITE_WALKERS_SIGHTED);
+
+ var varys = new LordVarys();
+ varys.registerObserver(kingsHand, Event.TRAITOR_DETECTED);
+ varys.registerObserver(kingsHand, Event.WHITE_WALKERS_SIGHTED);
+
+ var scout = new Scout();
+ scout.registerObserver(kingsHand, Event.WARSHIPS_APPROACHING);
+ scout.registerObserver(varys, Event.WHITE_WALKERS_SIGHTED);
+
+ var baelish = new LordBaelish(kingsHand, Event.STARK_SIGHTED);
+
+ var emitters = List.of(
+ kingsHand,
+ baelish,
+ varys,
+ scout
+ );
+
+ Arrays.stream(Weekday.values())
+ .>map(day -> emitter -> emitter.timePasses(day))
+ .forEachOrdered(emitters::forEach);
+}
+```
+
+The console output after running the example.
+
+```
+21:37:38.737 [main] INFO com.iluwatar.event.aggregator.KingJoffrey -- Received event from the King's Hand: Warships approaching
+21:37:38.739 [main] INFO com.iluwatar.event.aggregator.KingJoffrey -- Received event from the King's Hand: White walkers sighted
+21:37:38.739 [main] INFO com.iluwatar.event.aggregator.KingJoffrey -- Received event from the King's Hand: Stark sighted
+21:37:38.739 [main] INFO com.iluwatar.event.aggregator.KingJoffrey -- Received event from the King's Hand: Traitor detected
+```
+
+## When to Use the Event Aggregator Pattern in Java
-## Applicability
Use the Event Aggregator pattern when
-* Event Aggregator is a good choice when you have lots of objects that are
- potential event sources. Rather than have the observer deal with registering
- with them all, you can centralize the registration logic to the Event
- Aggregator. As well as simplifying registration, a Event Aggregator also
- simplifies the memory management issues in using observers.
+* You have multiple event sources and handlers.
+* You want to decouple the event generation and handling logic.
+* You need a centralized event management system.
+
+## Real-World Applications of Event Aggregator Pattern in Java
+
+* Enterprise application integrations where systems need a central point to handle events generated by various subsystems.
+* Complex GUI applications where user actions in one part of the interface need to affect other parts without tight coupling between the components.
+
+## Benefits and Trade-offs of Event Aggregator Pattern
+
+Benefits:
+
+* Decoupling: By centralizing event handling, the Event Aggregator minimizes direct interaction between components, leading to a more modular and easier-to-manage system.
+* Improves Flexibility and Scalability: Adding new publishers or subscribers involves less effort since the central aggregator handles all routing.
+* Simplifies Component Interface: Components need to know only about the Event Aggregator, not about other components.
+* Centralizes event management: Makes the system easier to maintain.
+
+Trade-offs:
+
+* Complexity of the Aggregator: The Event Aggregator itself can become a complex and high-maintenance component if not properly designed.
+* Potential Performance Bottleneck: If not scaled properly, the central event handling mechanism can become a bottleneck in the system.
+
+## Related Java Design Patterns
+
+* [Mediator](https://java-design-patterns.com/patterns/mediator/): Similar to Mediator in that it abstracts direct communications between components, but focused specifically on event messages.
+* [Observer](https://java-design-patterns.com/patterns/observer/): The Event Aggregator pattern is often implemented using the Observer pattern, where the aggregator observes events and notifies subscribers.
+* Publish-Subscribe: The Event Aggregator can be seen as a special case of the Publish-Subscribe pattern, with the aggregator acting as the broker.
-## Credits
+## References and Credits
-* [Martin Fowler - Event Aggregator](http://martinfowler.com/eaaDev/EventAggregator.html)
+* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/44eWKXv)
+* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://amzn.to/440b0CZ)
+* [Java Design Pattern Essentials](https://amzn.to/43XHCgM)
+* [Event Aggregator (Martin Fowler)](http://martinfowler.com/eaaDev/EventAggregator.html)
diff --git a/event-aggregator/etc/event-aggregator-sequence-diagram.png b/event-aggregator/etc/event-aggregator-sequence-diagram.png
new file mode 100644
index 000000000000..f96fbc427a86
Binary files /dev/null and b/event-aggregator/etc/event-aggregator-sequence-diagram.png differ
diff --git a/event-aggregator/pom.xml b/event-aggregator/pom.xml
index 656fa23c1f2c..3e7e58120155 100644
--- a/event-aggregator/pom.xml
+++ b/event-aggregator/pom.xml
@@ -1,8 +1,10 @@
-
- 4.0.0
-
- com.iluwatar
- java-design-patterns
- 1.26.0-SNAPSHOT
-
- event-asynchronous
-
-
- org.junit.jupiter
- junit-jupiter-engine
- test
-
-
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
-
-
-
-
-
- com.iluwatar.event.asynchronous.App
-
-
-
-
-
-
-
-
-
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java
deleted file mode 100644
index fd7282fc2481..000000000000
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java
+++ /dev/null
@@ -1,228 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.event.asynchronous;
-
-import java.io.IOException;
-import java.util.Properties;
-import java.util.Scanner;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * This application demonstrates the Event-based Asynchronous pattern. Essentially, users (of
- * the pattern) may choose to run events in an Asynchronous or Synchronous mode. There can be
- * multiple Asynchronous events running at once but only one Synchronous event can run at a time.
- * Asynchronous events are synonymous to multi-threads. The key point here is that the threads run
- * in the background and the user is free to carry on with other processes. Once an event is
- * complete, the appropriate listener/callback method will be called. The listener then proceeds to
- * carry out further processing depending on the needs of the user.
- *
- * The {@link EventManager} manages the events/threads that the user creates. Currently, the
- * supported event operations are: start, stop, getStatus.
- * For Synchronous events, the user is unable to start another (Synchronous) event if one is already
- * running at the time. The running event would have to either be stopped or completed before a new
- * event can be started.
- *
- *
The Event-based Asynchronous Pattern makes available the advantages of multithreaded
- * applications while hiding many of the complex issues inherent in multithreaded design. Using a
- * class that supports this pattern can allow you to:- (1) Perform time-consuming tasks, such as
- * downloads and database operations, "in the background," without interrupting your application.
- * (2) Execute multiple operations simultaneously, receiving notifications when each completes. (3)
- * Wait for resources to become available without stopping ("hanging") your application. (4)
- * Communicate with pending asynchronous operations using the familiar events-and-delegates model.
- *
- * @see EventManager
- * @see Event
- */
-@Slf4j
-public class App {
-
- public static final String PROP_FILE_NAME = "config.properties";
-
- boolean interactiveMode = false;
-
- /**
- * Program entry point.
- *
- * @param args command line args
- */
- public static void main(String[] args) {
- var app = new App();
- app.setUp();
- app.run();
- }
-
- /**
- * App can run in interactive mode or not. Interactive mode == Allow user interaction with command
- * line. Non-interactive is a quick sequential run through the available {@link EventManager}
- * operations.
- */
- public void setUp() {
- var prop = new Properties();
-
- var inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME);
-
- if (inputStream != null) {
- try {
- prop.load(inputStream);
- } catch (IOException e) {
- LOGGER.error("{} was not found. Defaulting to non-interactive mode.", PROP_FILE_NAME, e);
- }
- var property = prop.getProperty("INTERACTIVE_MODE");
- if (property.equalsIgnoreCase("YES")) {
- interactiveMode = true;
- }
- }
- }
-
- /**
- * Run program in either interactive mode or not.
- */
- public void run() {
- if (interactiveMode) {
- runInteractiveMode();
- } else {
- quickRun();
- }
- }
-
- /**
- * Run program in non-interactive mode.
- */
- public void quickRun() {
- var eventManager = new EventManager();
-
- try {
- // Create an Asynchronous event.
- var asyncEventId = eventManager.createAsync(60);
- LOGGER.info("Async Event [{}] has been created.", asyncEventId);
- eventManager.start(asyncEventId);
- LOGGER.info("Async Event [{}] has been started.", asyncEventId);
-
- // Create a Synchronous event.
- var syncEventId = eventManager.create(60);
- LOGGER.info("Sync Event [{}] has been created.", syncEventId);
- eventManager.start(syncEventId);
- LOGGER.info("Sync Event [{}] has been started.", syncEventId);
-
- eventManager.status(asyncEventId);
- eventManager.status(syncEventId);
-
- eventManager.cancel(asyncEventId);
- LOGGER.info("Async Event [{}] has been stopped.", asyncEventId);
- eventManager.cancel(syncEventId);
- LOGGER.info("Sync Event [{}] has been stopped.", syncEventId);
-
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
- | InvalidOperationException e) {
- LOGGER.error(e.getMessage());
- }
- }
-
- /**
- * Run program in interactive mode.
- */
- public void runInteractiveMode() {
- var eventManager = new EventManager();
-
- var s = new Scanner(System.in);
- var option = -1;
- while (option != 4) {
- LOGGER.info("Hello. Would you like to boil some eggs?");
- LOGGER.info("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT");
- LOGGER.info("Choose [1,2,3,4]: ");
- option = s.nextInt();
-
- if (option == 1) {
- processOption1(eventManager, s);
- } else if (option == 2) {
- processOption2(eventManager, s);
- } else if (option == 3) {
- processOption3(eventManager, s);
- } else if (option == 4) {
- eventManager.shutdown();
- }
- }
-
- s.close();
- }
-
- private void processOption3(EventManager eventManager, Scanner s) {
- s.nextLine();
- LOGGER.info("Just one egg (O) OR all of them (A) ?: ");
- var eggChoice = s.nextLine();
-
- if (eggChoice.equalsIgnoreCase("O")) {
- LOGGER.info("Which egg?: ");
- int eventId = s.nextInt();
- try {
- eventManager.status(eventId);
- } catch (EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- } else if (eggChoice.equalsIgnoreCase("A")) {
- eventManager.statusOfAllEvents();
- }
- }
-
- private void processOption2(EventManager eventManager, Scanner s) {
- LOGGER.info("Which egg?: ");
- var eventId = s.nextInt();
- try {
- eventManager.cancel(eventId);
- LOGGER.info("Egg [{}] is removed from boiler.", eventId);
- } catch (EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- }
-
- private void processOption1(EventManager eventManager, Scanner s) {
- s.nextLine();
- LOGGER.info("Boil multiple eggs at once (A) or boil them one-by-one (S)?: ");
- var eventType = s.nextLine();
- LOGGER.info("How long should this egg be boiled for (in seconds)?: ");
- var eventTime = s.nextInt();
- if (eventType.equalsIgnoreCase("A")) {
- try {
- var eventId = eventManager.createAsync(eventTime);
- eventManager.start(eventId);
- LOGGER.info("Egg [{}] is being boiled.", eventId);
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException
- | EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- } else if (eventType.equalsIgnoreCase("S")) {
- try {
- var eventId = eventManager.create(eventTime);
- eventManager.start(eventId);
- LOGGER.info("Egg [{}] is being boiled.", eventId);
- } catch (MaxNumOfEventsAllowedException | InvalidOperationException
- | LongRunningEventException | EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- } else {
- LOGGER.info("Unknown event type.");
- }
- }
-
-}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
deleted file mode 100644
index 77d6ab8c4ef8..000000000000
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.event.asynchronous;
-
-import lombok.Getter;
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-
-/**
- * Each Event runs as a separate/individual thread.
- */
-@Slf4j
-@RequiredArgsConstructor
-public class Event implements IEvent, Runnable {
-
- private final int eventId;
- private final int eventTime;
- @Getter
- private final boolean synchronous;
- private Thread thread;
- private boolean isComplete = false;
- private ThreadCompleteListener eventListener;
-
- @Override
- public void start() {
- thread = new Thread(this);
- thread.start();
- }
-
- @Override
- public void stop() {
- if (null == thread) {
- return;
- }
- thread.interrupt();
- }
-
- @Override
- public void status() {
- if (!isComplete) {
- LOGGER.info("[{}] is not done.", eventId);
- } else {
- LOGGER.info("[{}] is done.", eventId);
- }
- }
-
- @Override
- public void run() {
- var currentTime = System.currentTimeMillis();
- var endTime = currentTime + (eventTime * 1000);
- while (System.currentTimeMillis() < endTime) {
- try {
- Thread.sleep(1000); // Sleep for 1 second.
- } catch (InterruptedException e) {
- Thread.currentThread().interrupt();
- return;
- }
- }
- isComplete = true;
- completed();
- }
-
- public final void addListener(final ThreadCompleteListener listener) {
- this.eventListener = listener;
- }
-
- public final void removeListener(final ThreadCompleteListener listener) {
- this.eventListener = null;
- }
-
- private void completed() {
- if (eventListener != null) {
- eventListener.completedEventHandler(eventId);
- }
- }
-
-}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
deleted file mode 100644
index dd83e23a99fc..000000000000
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
+++ /dev/null
@@ -1,221 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.event.asynchronous;
-
-import java.security.SecureRandom;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * EventManager handles and maintains a pool of event threads. {@link Event} threads are created
- * upon user request. Thre are two types of events; Asynchronous and Synchronous. There can be
- * multiple Asynchronous events running at once but only one Synchronous event running at a time.
- * Currently supported event operations are: start, stop, and getStatus. Once an event is complete,
- * it then notifies EventManager through a listener. The EventManager then takes the event out of
- * the pool.
- */
-public class EventManager implements ThreadCompleteListener {
-
- public static final int MAX_RUNNING_EVENTS = 1000;
- // Just don't wanna have too many running events. :)
- public static final int MIN_ID = 1;
- public static final int MAX_ID = MAX_RUNNING_EVENTS;
- public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes.
- private int currentlyRunningSyncEvent = -1;
- private final SecureRandom rand;
- private final Map eventPool;
-
- private static final String DOES_NOT_EXIST = " does not exist.";
-
- /**
- * EventManager constructor.
- */
- public EventManager() {
- rand = new SecureRandom();
- eventPool = new ConcurrentHashMap<>(MAX_RUNNING_EVENTS);
-
- }
-
- /**
- * Create a Synchronous event.
- *
- * @param eventTime Time an event should run for.
- * @return eventId
- * @throws MaxNumOfEventsAllowedException When too many events are running at a time.
- * @throws InvalidOperationException No new synchronous events can be created when one is
- * already running.
- * @throws LongRunningEventException Long running events are not allowed in the app.
- */
- public int create(int eventTime)
- throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException {
- if (currentlyRunningSyncEvent != -1) {
- throw new InvalidOperationException("Event [" + currentlyRunningSyncEvent + "] is still"
- + " running. Please wait until it finishes and try again.");
- }
-
- var eventId = createEvent(eventTime, true);
- currentlyRunningSyncEvent = eventId;
-
- return eventId;
- }
-
- /**
- * Create an Asynchronous event.
- *
- * @param eventTime Time an event should run for.
- * @return eventId
- * @throws MaxNumOfEventsAllowedException When too many events are running at a time.
- * @throws LongRunningEventException Long running events are not allowed in the app.
- */
- public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException,
- LongRunningEventException {
- return createEvent(eventTime, false);
- }
-
- private int createEvent(int eventTime, boolean isSynchronous)
- throws MaxNumOfEventsAllowedException, LongRunningEventException {
- if (eventPool.size() == MAX_RUNNING_EVENTS) {
- throw new MaxNumOfEventsAllowedException("Too many events are running at the moment."
- + " Please try again later.");
- }
-
- if (eventTime >= MAX_EVENT_TIME) {
- throw new LongRunningEventException(
- "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again.");
- }
-
- var newEventId = generateId();
-
- var newEvent = new Event(newEventId, eventTime, isSynchronous);
- newEvent.addListener(this);
- eventPool.put(newEventId, newEvent);
-
- return newEventId;
- }
-
- /**
- * Starts event.
- *
- * @param eventId The event that needs to be started.
- * @throws EventDoesNotExistException If event does not exist in our eventPool.
- */
- public void start(int eventId) throws EventDoesNotExistException {
- if (!eventPool.containsKey(eventId)) {
- throw new EventDoesNotExistException(eventId + DOES_NOT_EXIST);
- }
-
- eventPool.get(eventId).start();
- }
-
- /**
- * Stops event.
- *
- * @param eventId The event that needs to be stopped.
- * @throws EventDoesNotExistException If event does not exist in our eventPool.
- */
- public void cancel(int eventId) throws EventDoesNotExistException {
- if (!eventPool.containsKey(eventId)) {
- throw new EventDoesNotExistException(eventId + DOES_NOT_EXIST);
- }
-
- if (eventId == currentlyRunningSyncEvent) {
- currentlyRunningSyncEvent = -1;
- }
-
- eventPool.get(eventId).stop();
- eventPool.remove(eventId);
- }
-
- /**
- * Get status of a running event.
- *
- * @param eventId The event to inquire status of.
- * @throws EventDoesNotExistException If event does not exist in our eventPool.
- */
- public void status(int eventId) throws EventDoesNotExistException {
- if (!eventPool.containsKey(eventId)) {
- throw new EventDoesNotExistException(eventId + DOES_NOT_EXIST);
- }
-
- eventPool.get(eventId).status();
- }
-
- /**
- * Gets status of all running events.
- */
- @SuppressWarnings("rawtypes")
- public void statusOfAllEvents() {
- eventPool.entrySet().forEach(entry -> ((Event) ((Map.Entry) entry).getValue()).status());
- }
-
- /**
- * Stop all running events.
- */
- @SuppressWarnings("rawtypes")
- public void shutdown() {
- eventPool.entrySet().forEach(entry -> ((Event) ((Map.Entry) entry).getValue()).stop());
- }
-
- /**
- * Returns a pseudo-random number between min and max, inclusive. The difference between min and
- * max can be at most
- * Integer.MAX_VALUE - 1.
- */
- private int generateId() {
- // nextInt is normally exclusive of the top value,
- // so add 1 to make it inclusive
- var randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
- while (eventPool.containsKey(randomNum)) {
- randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
- }
-
- return randomNum;
- }
-
- /**
- * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool.
- */
- @Override
- public void completedEventHandler(int eventId) {
- eventPool.get(eventId).status();
- if (eventPool.get(eventId).isSynchronous()) {
- currentlyRunningSyncEvent = -1;
- }
- eventPool.remove(eventId);
- }
-
- /**
- * Getter method for event pool.
- */
- public Map getEventPool() {
- return eventPool;
- }
-
- /**
- * Get number of currently running Synchronous events.
- */
- public int numOfCurrentlyRunningSyncEvent() {
- return currentlyRunningSyncEvent;
- }
-}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java
deleted file mode 100644
index 28cdb34f0537..000000000000
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.event.asynchronous;
-
-/**
- * Events that fulfill the start stop and list out current status behaviour follow this interface.
- */
-public interface IEvent {
-
- void start();
-
- void stop();
-
- void status();
-
-}
diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
deleted file mode 100644
index a1145c5efccc..000000000000
--- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.event.asynchronous;
-
-import org.junit.jupiter.api.Test;
-
-import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
-
-/**
- * Tests that EventAsynchronous 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.
- */
-
- @Test
- void shouldExecuteApplicationWithoutException() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
-}
diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java
deleted file mode 100644
index 9ece86a7ac98..000000000000
--- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java
+++ /dev/null
@@ -1,138 +0,0 @@
-/*
- * The MIT License
- * Copyright © 2014-2021 Ilkka Seppälä
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-package com.iluwatar.event.asynchronous;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import org.junit.jupiter.api.Test;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Application test
- */
-class EventAsynchronousTest {
- private static final Logger LOGGER = LoggerFactory.getLogger(EventAsynchronousTest.class);
-
- @Test
- void testAsynchronousEvent() {
- var eventManager = new EventManager();
- try {
- var aEventId = eventManager.createAsync(60);
- eventManager.start(aEventId);
- assertEquals(1, eventManager.getEventPool().size());
- assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
- assertEquals(-1, eventManager.numOfCurrentlyRunningSyncEvent());
- eventManager.cancel(aEventId);
- assertTrue(eventManager.getEventPool().isEmpty());
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- }
-
- @Test
- void testSynchronousEvent() {
- var eventManager = new EventManager();
- try {
- var sEventId = eventManager.create(60);
- eventManager.start(sEventId);
- assertEquals(1, eventManager.getEventPool().size());
- assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
- assertNotEquals(-1, eventManager.numOfCurrentlyRunningSyncEvent());
- eventManager.cancel(sEventId);
- assertTrue(eventManager.getEventPool().isEmpty());
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
- | InvalidOperationException e) {
- LOGGER.error(e.getMessage());
- }
- }
-
- @Test
- void testUnsuccessfulSynchronousEvent() {
- assertThrows(InvalidOperationException.class, () -> {
- var eventManager = new EventManager();
- try {
- var sEventId = eventManager.create(60);
- eventManager.start(sEventId);
- sEventId = eventManager.create(60);
- eventManager.start(sEventId);
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- });
- }
-
- @Test
- void testFullSynchronousEvent() {
- var eventManager = new EventManager();
- try {
- var eventTime = 1;
-
- var sEventId = eventManager.create(eventTime);
- assertEquals(1, eventManager.getEventPool().size());
- eventManager.start(sEventId);
-
- var currentTime = System.currentTimeMillis();
- // +2 to give a bit of buffer time for event to complete properly.
- var endTime = currentTime + (eventTime + 2 * 1000);
- while (System.currentTimeMillis() < endTime) ;
-
- assertTrue(eventManager.getEventPool().isEmpty());
-
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
- | InvalidOperationException e) {
- LOGGER.error(e.getMessage());
- }
- }
-
- @Test
- void testFullAsynchronousEvent() {
- var eventManager = new EventManager();
- try {
- var eventTime = 1;
-
- var aEventId1 = eventManager.createAsync(eventTime);
- var aEventId2 = eventManager.createAsync(eventTime);
- var aEventId3 = eventManager.createAsync(eventTime);
- assertEquals(3, eventManager.getEventPool().size());
-
- eventManager.start(aEventId1);
- eventManager.start(aEventId2);
- eventManager.start(aEventId3);
-
- var currentTime = System.currentTimeMillis();
- // +2 to give a bit of buffer time for event to complete properly.
- var endTime = currentTime + (eventTime + 2 * 1000);
- while (System.currentTimeMillis() < endTime) ;
-
- assertTrue(eventManager.getEventPool().isEmpty());
-
- } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
- LOGGER.error(e.getMessage());
- }
- }
-}
diff --git a/event-based-asynchronous/README.md b/event-based-asynchronous/README.md
new file mode 100644
index 000000000000..6eb885c715e9
--- /dev/null
+++ b/event-based-asynchronous/README.md
@@ -0,0 +1,187 @@
+---
+title: "Event-Based Asynchronous Pattern in Java: Mastering Non-Blocking System Design"
+shortTitle: Event-Based Asynchronous
+description: "Explore the best practices and implementations of event-based asynchronous patterns in Java. Enhance your programming skills with our comprehensive guide and real-world examples."
+category: Concurrency
+language: en
+tag:
+ - Asynchronous
+ - Decoupling
+ - Event-driven
+ - Fault tolerance
+ - Messaging
+ - Reactive
+ - Scalability
+---
+
+## Also known as
+
+* Asynchronous Event Handling
+
+## Intent of Event-Based Asynchronous Design Pattern
+
+The Event-Based Asynchronous pattern allows a system to handle tasks that might take some time to complete without blocking the execution of the program. It enables better resource utilization by freeing up a thread that would otherwise be blocked waiting for the task to complete.
+
+## Detailed Explanation of Event-Based Asynchronous Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy of the Event-Based Asynchronous design pattern is how a restaurant operates. When a customer places an order, the waiter records the order and passes it to the kitchen. Instead of waiting at the kitchen for the food to be prepared, the waiter continues to serve other tables. Once the kitchen completes the order, they signal (event) the waiter, who then delivers the food to the customer. This allows the waiter to handle multiple tasks efficiently without idle waiting, similar to how asynchronous programming handles tasks in parallel, enhancing overall efficiency and responsiveness.
+
+In Plain Words
+
+> The Event-Based Asynchronous design pattern allows tasks to be executed in the background, notifying the main program via events when completed, thereby enhancing system efficiency and responsiveness without blocking ongoing operations.
+
+Sequence diagram
+
+
+
+## Programmatic Example of Event-Based Asynchronous Pattern in Java
+
+Event-Based Asynchronous design pattern allows tasks to be executed in the background, notifying the main program via events when completed, thereby enhancing system efficiency and responsiveness without blocking ongoing operations.
+
+In the provided code, we have several key classes implementing this pattern:
+
+- `App`: The main class that runs the application. It interacts with the `EventManager` to create, start, stop, and check the status of events.
+- `EventManager`: Manages the lifecycle of events, including creating, starting, stopping, and checking the status of events. It maintains a map of event IDs to `Event` objects.
+- `Event`: An abstract class that represents an event. It has two concrete subclasses: `AsyncEvent` and `SyncEvent`.
+- `AsyncEvent` and `SyncEvent`: Represent asynchronous and synchronous events respectively.
+- Custom exceptions: Thrown by the `EventManager` when certain conditions are not met.
+
+Here's a simplified code example of how these classes interact:
+
+```java
+// Create an EventManager
+EventManager eventManager = new EventManager();
+
+// Create an asynchronous event that runs for 60 seconds
+int asyncEventId = eventManager.createAsync(Duration.ofSeconds(60));
+
+// Start the asynchronous event
+eventManager.start(asyncEventId);
+
+// Check the status of the asynchronous event
+eventManager.status(asyncEventId);
+
+// Stop the asynchronous event
+eventManager.cancel(asyncEventId);
+```
+
+In this example, the `App` class creates an `EventManager`, then uses it to create, start, check the status of, and stop an asynchronous event. The `EventManager` creates an `AsyncEvent` object, starts it in a separate thread, checks its status, and stops it when requested.
+
+The `EventManager` class is the core of the Event-Based Asynchronous pattern implementation. It manages the lifecycle of events, including creating, starting, stopping, and checking the status of events. It maintains a map of event IDs to `Event` objects. Here's a snippet of how it creates an asynchronous event:
+
+```java
+public int createAsync(Duration runtime) throws MaxNumOfEventsAllowedException, LongRunningEventException {
+ int id = counter.incrementAndGet();
+ events.put(id, new AsyncEvent(id, runtime));
+ return id;
+}
+```
+
+The `Event` class is an abstract class that represents an event. It has two concrete subclasses: `AsyncEvent` and `SyncEvent`. An `Event` has an ID, a runtime (how long it should run), and a status (whether it's running, completed, or ready to start). It also has methods to start and stop the event. Here's a snippet of how an `AsyncEvent` starts:
+
+```java
+@Override
+public void start() {
+ Thread thread = new Thread(() -> {
+ try {
+ handleRunStart();
+ Thread.sleep(getRuntime().toMillis());
+ handleRunComplete();
+ } catch (InterruptedException e) {
+ handleRunFailure(e.getMessage());
+ }
+ });
+ thread.start();
+}
+```
+
+In this snippet, when an `AsyncEvent` is started, it runs in a separate thread without blocking the main thread.
+
+A synchronous event is created and managed similarly to an asynchronous event. Here's a snippet of how it creates and manages a synchronous event:
+
+```java
+// Create an EventManager
+EventManager eventManager = new EventManager();
+
+// Create a synchronous event that runs for 60 seconds
+int syncEventId = eventManager.create(Duration.ofSeconds(60));
+
+// Start the synchronous event
+eventManager.start(syncEventId);
+
+// Check the status of the synchronous event
+eventManager.status(syncEventId);
+
+// Stop the synchronous event
+eventManager.cancel(syncEventId);
+```
+
+In the `EventManager` class, a synchronous event is created using the `create` method:
+
+```java
+public int create(Duration runtime) throws MaxNumOfEventsAllowedException, LongRunningEventException {
+ int id = counter.incrementAndGet();
+ events.put(id, new SyncEvent(id, runtime));
+ return id;
+}
+```
+
+The `SyncEvent` class is a subclass of `Event` that represents a synchronous event. When a `SyncEvent` is started, it runs on the main thread and blocks it until the event is completed. Here's a snippet of how a `SyncEvent` starts:
+
+```java
+@Override
+public void start() {
+ try {
+ handleRunStart();
+ Thread.sleep(getRuntime().toMillis());
+ handleRunComplete();
+ } catch (InterruptedException e) {
+ handleRunFailure(e.getMessage());
+ }
+}
+```
+
+In this snippet, when a `SyncEvent` is started, it runs on the main thread, blocking it until the event is completed. This is in contrast to an `AsyncEvent`, which runs in a separate thread without blocking the main thread.
+
+These are the key parts of the Event-Based Asynchronous design pattern as implemented in this code. The pattern allows tasks to be executed in the background, notifying the main program via events when completed, thereby enhancing system efficiency and responsiveness without blocking ongoing operations.
+
+## When to Use the Event-Based Asynchronous Pattern in Java
+
+* When multiple tasks can be processed in parallel and independently.
+* Systems that require responsiveness and cannot afford to have threads blocked waiting for an operation to complete.
+* In GUI applications where user interface responsiveness is critical.
+* Distributed systems where long network operations are involved.
+
+## Real-World Applications of Event-Based Asynchronous Pattern in Java
+
+* GUI libraries in Java (e.g., JavaFX, Swing with SwingWorker).
+* Java Message Service (JMS) for handling asynchronous messaging.
+* Java’s CompletableFuture and various Event-Driven Frameworks.
+
+## Benefits and Trade-offs of Event-Based Asynchronous Pattern
+
+Benefits:
+
+* Improves application scalability and responsiveness.
+* Reduces the resources wasted on threads that would simply wait for I/O operations.
+* Enhances fault tolerance through isolation of process execution.
+
+Trade-offs:
+
+* Increases complexity of error handling as errors may occur in different threads or at different times.
+* Can lead to harder-to-follow code and debugging challenges due to the non-linear nature of asynchronous code execution.
+
+## Related Java Design Patterns
+
+* [Observer](https://java-design-patterns.com/patterns/observer/): Often used in conjunction where the observer reacts to events as they occur.
+* Publish/Subscribe: Related in terms of event handling mechanisms, particularly for messaging and event distribution across components.
+* [Command](https://java-design-patterns.com/patterns/command/): Useful for encapsulating all information needed to perform an action or trigger an event.
+
+## References and Credits
+
+* [Java Concurrency in Practice](https://amzn.to/4cYY4kU)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3Uh7rW1)
+* [Pro JavaFX 8: A Definitive Guide to Building Desktop, Mobile, and Embedded Java Clients](https://amzn.to/3vHUqLL)
+* [Event-based Asynchronous Pattern Overview (Microsoft)](https://msdn.microsoft.com/en-us/library/wewwczdw%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396)
diff --git a/event-asynchronous/etc/event-asynchronous.png b/event-based-asynchronous/etc/event-asynchronous.png
similarity index 100%
rename from event-asynchronous/etc/event-asynchronous.png
rename to event-based-asynchronous/etc/event-asynchronous.png
diff --git a/event-asynchronous/etc/event-asynchronous.ucls b/event-based-asynchronous/etc/event-asynchronous.ucls
similarity index 100%
rename from event-asynchronous/etc/event-asynchronous.ucls
rename to event-based-asynchronous/etc/event-asynchronous.ucls
diff --git a/event-asynchronous/etc/event-asynchronous.urm.puml b/event-based-asynchronous/etc/event-asynchronous.urm.puml
similarity index 100%
rename from event-asynchronous/etc/event-asynchronous.urm.puml
rename to event-based-asynchronous/etc/event-asynchronous.urm.puml
diff --git a/event-based-asynchronous/etc/event-based-asynchronous-sequence-diagram.png b/event-based-asynchronous/etc/event-based-asynchronous-sequence-diagram.png
new file mode 100644
index 000000000000..882975df5c0a
Binary files /dev/null and b/event-based-asynchronous/etc/event-based-asynchronous-sequence-diagram.png differ
diff --git a/event-based-asynchronous/etc/event-based-asynchronous.urm.puml b/event-based-asynchronous/etc/event-based-asynchronous.urm.puml
new file mode 100644
index 000000000000..518155ab44f9
--- /dev/null
+++ b/event-based-asynchronous/etc/event-based-asynchronous.urm.puml
@@ -0,0 +1,70 @@
+@startuml
+package com.iluwatar.event.asynchronous {
+ class App {
+ - LOGGER : Logger {static}
+ + PROP_FILE_NAME : String {static}
+ ~ interactiveMode : boolean
+ + App()
+ + main(args : String[]) {static}
+ - processOption1(eventManager : EventManager, s : Scanner)
+ - processOption2(eventManager : EventManager, s : Scanner)
+ - processOption3(eventManager : EventManager, s : Scanner)
+ + quickRun()
+ + run()
+ + runInteractiveMode()
+ + setUp()
+ }
+ class AsyncEvent {
+ - LOGGER : Logger {static}
+ - eventId : int
+ - eventListener : ThreadCompleteListener
+ - eventTime : int
+ - isComplete : AtomicBoolean
+ - synchronous : boolean
+ - thread : Thread
+ + AsyncEvent(eventId : int, eventTime : int, synchronous : boolean)
+ + addListener(listener : ThreadCompleteListener)
+ - completed()
+ + isSynchronous() : boolean
+ + removeListener()
+ + run()
+ + start()
+ + status()
+ + stop()
+ }
+ interface Event {
+ + start() {abstract}
+ + status() {abstract}
+ + stop() {abstract}
+ }
+ class EventManager {
+ - DOES_NOT_EXIST : String {static}
+ + MAX_EVENT_TIME : int {static}
+ + MAX_ID : int {static}
+ + MAX_RUNNING_EVENTS : int {static}
+ + MIN_ID : int {static}
+ - currentlyRunningSyncEvent : int
+ - eventPool : Map
+ - rand : SecureRandom
+ + EventManager()
+ + cancel(eventId : int)
+ + completedEventHandler(eventId : int)
+ + create(eventTime : int) : int
+ + createAsync(eventTime : int) : int
+ - createEvent(eventTime : int, isSynchronous : boolean) : int
+ - generateId() : int
+ + getEventPool() : Map
+ + numOfCurrentlyRunningSyncEvent() : int
+ + shutdown()
+ + start(eventId : int)
+ + status(eventId : int)
+ + statusOfAllEvents()
+ }
+ interface ThreadCompleteListener {
+ + completedEventHandler(int) {abstract}
+ }
+}
+AsyncEvent --> "-eventListener" ThreadCompleteListener
+AsyncEvent ..|> Event
+EventManager ..|> ThreadCompleteListener
+@enduml
\ No newline at end of file
diff --git a/event-based-asynchronous/pom.xml b/event-based-asynchronous/pom.xml
new file mode 100644
index 000000000000..3a66d321c02f
--- /dev/null
+++ b/event-based-asynchronous/pom.xml
@@ -0,0 +1,76 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+ event-based-asynchronous
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.awaitility
+ awaitility
+ 4.3.0
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+
+
+
+ com.iluwatar.event.asynchronous.App
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java
new file mode 100644
index 000000000000..731cd169157e
--- /dev/null
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java
@@ -0,0 +1,234 @@
+/*
+ * 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.event.asynchronous;
+
+import java.io.IOException;
+import java.time.Duration;
+import java.util.Properties;
+import java.util.Scanner;
+import lombok.extern.slf4j.Slf4j;
+
+/**
+ * This application demonstrates the Event-based Asynchronous pattern. Essentially, users (of
+ * the pattern) may choose to run events in an Asynchronous or Synchronous mode. There can be
+ * multiple Asynchronous events running at once but only one Synchronous event can run at a time.
+ * Asynchronous events are synonymous to multi-threads. The key point here is that the threads run
+ * in the background and the user is free to carry on with other processes. Once an event is
+ * complete, the appropriate listener/callback method will be called. The listener then proceeds to
+ * carry out further processing depending on the needs of the user.
+ *
+ * The {@link EventManager} manages the events/threads that the user creates. Currently, the
+ * supported event operations are: start, stop, getStatus.
+ * For Synchronous events, the user is unable to start another (Synchronous) event if one is already
+ * running at the time. The running event would have to either be stopped or completed before a new
+ * event can be started.
+ *
+ *
The Event-based Asynchronous Pattern makes available the advantages of multithreaded
+ * applications while hiding many of the complex issues inherent in multithreaded design. Using a
+ * class that supports this pattern can allow you to:- (1) Perform time-consuming tasks, such as
+ * downloads and database operations, "in the background," without interrupting your application.
+ * (2) Execute multiple operations simultaneously, receiving notifications when each completes. (3)
+ * Wait for resources to become available without stopping ("hanging") your application. (4)
+ * Communicate with pending asynchronous operations using the familiar events-and-delegates model.
+ *
+ * @see EventManager
+ * @see AsyncEvent
+ */
+@Slf4j
+public class App {
+
+ public static final String PROP_FILE_NAME = "config.properties";
+
+ boolean interactiveMode = false;
+
+ /**
+ * Program entry point.
+ *
+ * @param args command line args
+ */
+ public static void main(String[] args) {
+ var app = new App();
+ app.setUp();
+ app.run();
+ }
+
+ /**
+ * App can run in interactive mode or not. Interactive mode == Allow user interaction with command
+ * line. Non-interactive is a quick sequential run through the available {@link EventManager}
+ * operations.
+ */
+ public void setUp() {
+ var prop = new Properties();
+
+ var inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME);
+
+ if (inputStream != null) {
+ try {
+ prop.load(inputStream);
+ } catch (IOException e) {
+ LOGGER.error("{} was not found. Defaulting to non-interactive mode.", PROP_FILE_NAME, e);
+ }
+ var property = prop.getProperty("INTERACTIVE_MODE");
+ if (property.equalsIgnoreCase("YES")) {
+ interactiveMode = true;
+ }
+ }
+ }
+
+ /** Run program in either interactive mode or not. */
+ public void run() {
+ if (interactiveMode) {
+ runInteractiveMode();
+ } else {
+ quickRun();
+ }
+ }
+
+ /** Run program in non-interactive mode. */
+ public void quickRun() {
+ var eventManager = new EventManager();
+
+ try {
+ // Create an Asynchronous event.
+ var asyncEventId = eventManager.createAsync(Duration.ofSeconds(60));
+ LOGGER.info("Async Event [{}] has been created.", asyncEventId);
+ eventManager.start(asyncEventId);
+ LOGGER.info("Async Event [{}] has been started.", asyncEventId);
+
+ // Create a Synchronous event.
+ var syncEventId = eventManager.create(Duration.ofSeconds(60));
+ LOGGER.info("Sync Event [{}] has been created.", syncEventId);
+ eventManager.start(syncEventId);
+ LOGGER.info("Sync Event [{}] has been started.", syncEventId);
+
+ eventManager.status(asyncEventId);
+ eventManager.status(syncEventId);
+
+ eventManager.cancel(asyncEventId);
+ LOGGER.info("Async Event [{}] has been stopped.", asyncEventId);
+ eventManager.cancel(syncEventId);
+ LOGGER.info("Sync Event [{}] has been stopped.", syncEventId);
+
+ } catch (MaxNumOfEventsAllowedException
+ | LongRunningEventException
+ | EventDoesNotExistException
+ | InvalidOperationException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+
+ /** Run program in interactive mode. */
+ public void runInteractiveMode() {
+ var eventManager = new EventManager();
+
+ var s = new Scanner(System.in);
+ var option = -1;
+ while (option != 4) {
+ LOGGER.info("Hello. Would you like to boil some eggs?");
+ LOGGER.info(
+ """
+ (1) BOIL AN EGG
+ (2) STOP BOILING THIS EGG
+ (3) HOW ARE MY EGGS?
+ (4) EXIT
+ """);
+ LOGGER.info("Choose [1,2,3,4]: ");
+ option = s.nextInt();
+
+ if (option == 1) {
+ processOption1(eventManager, s);
+ } else if (option == 2) {
+ processOption2(eventManager, s);
+ } else if (option == 3) {
+ processOption3(eventManager, s);
+ } else if (option == 4) {
+ eventManager.shutdown();
+ }
+ }
+
+ s.close();
+ }
+
+ private void processOption3(EventManager eventManager, Scanner s) {
+ s.nextLine();
+ LOGGER.info("Just one egg (O) OR all of them (A) ?: ");
+ var eggChoice = s.nextLine();
+
+ if (eggChoice.equalsIgnoreCase("O")) {
+ LOGGER.info("Which egg?: ");
+ int eventId = s.nextInt();
+ try {
+ eventManager.status(eventId);
+ } catch (EventDoesNotExistException e) {
+ LOGGER.error(e.getMessage());
+ }
+ } else if (eggChoice.equalsIgnoreCase("A")) {
+ eventManager.statusOfAllEvents();
+ }
+ }
+
+ private void processOption2(EventManager eventManager, Scanner s) {
+ LOGGER.info("Which egg?: ");
+ var eventId = s.nextInt();
+ try {
+ eventManager.cancel(eventId);
+ LOGGER.info("Egg [{}] is removed from boiler.", eventId);
+ } catch (EventDoesNotExistException e) {
+ LOGGER.error(e.getMessage());
+ }
+ }
+
+ private void processOption1(EventManager eventManager, Scanner s) {
+ s.nextLine();
+ LOGGER.info("Boil multiple eggs at once (A) or boil them one-by-one (S)?: ");
+ var eventType = s.nextLine();
+ LOGGER.info("How long should this egg be boiled for (in seconds)?: ");
+ var eventTime = Duration.ofSeconds(s.nextInt());
+ if (eventType.equalsIgnoreCase("A")) {
+ try {
+ var eventId = eventManager.createAsync(eventTime);
+ eventManager.start(eventId);
+ LOGGER.info("Egg [{}] is being boiled.", eventId);
+ } catch (MaxNumOfEventsAllowedException
+ | LongRunningEventException
+ | EventDoesNotExistException e) {
+ LOGGER.error(e.getMessage());
+ }
+ } else if (eventType.equalsIgnoreCase("S")) {
+ try {
+ var eventId = eventManager.create(eventTime);
+ eventManager.start(eventId);
+ LOGGER.info("Egg [{}] is being boiled.", eventId);
+ } catch (MaxNumOfEventsAllowedException
+ | InvalidOperationException
+ | LongRunningEventException
+ | EventDoesNotExistException e) {
+ LOGGER.error(e.getMessage());
+ }
+ } else {
+ LOGGER.info("Unknown event type.");
+ }
+ }
+}
diff --git a/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/AsyncEvent.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/AsyncEvent.java
new file mode 100644
index 000000000000..b58848f0a702
--- /dev/null
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/AsyncEvent.java
@@ -0,0 +1,97 @@
+/*
+ * 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.event.asynchronous;
+
+import java.time.Duration;
+import java.time.Instant;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicBoolean;
+import lombok.Getter;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+
+/** Each Event runs as a separate/individual thread. */
+@Slf4j
+@RequiredArgsConstructor
+public class AsyncEvent implements Event, Runnable {
+
+ private final int eventId;
+ private final Duration eventTime;
+ @Getter private final boolean synchronous;
+ private Thread thread;
+ private final AtomicBoolean isComplete = new AtomicBoolean(false);
+ private ThreadCompleteListener eventListener;
+
+ @Override
+ public void start() {
+ thread = new Thread(this);
+ thread.start();
+ }
+
+ @Override
+ public void stop() {
+ if (null == thread) {
+ return;
+ }
+ thread.interrupt();
+ }
+
+ @Override
+ public void status() {
+ if (isComplete.get()) {
+ LOGGER.info("[{}] is not done.", eventId);
+ } else {
+ LOGGER.info("[{}] is done.", eventId);
+ }
+ }
+
+ @Override
+ public void run() {
+
+ var currentTime = Instant.now();
+ var endTime = currentTime.plusSeconds(eventTime.getSeconds());
+ while (Instant.now().compareTo(endTime) < 0) {
+ try {
+ TimeUnit.SECONDS.sleep(1);
+ } catch (InterruptedException e) {
+ LOGGER.error("Thread was interrupted: ", e);
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+ isComplete.set(true);
+ completed();
+ }
+
+ public final void addListener(final ThreadCompleteListener listener) {
+ this.eventListener = listener;
+ }
+
+ private void completed() {
+ if (eventListener != null) {
+ eventListener.completedEventHandler(eventId);
+ }
+ }
+}
diff --git a/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
new file mode 100644
index 000000000000..6c51f8584ec6
--- /dev/null
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.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.event.asynchronous;
+
+/**
+ * Events that fulfill the start stop and list out current status behaviour follow this interface.
+ */
+public interface Event {
+
+ void start();
+
+ void stop();
+
+ void status();
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
similarity index 79%
rename from event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
rename to event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
index 1fc40dd9a81c..ad6a649c058f 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,15 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.asynchronous;
-/**
- * Custom Exception Class for Non Existent Event.
- */
+import java.io.Serial;
+
+/** Custom Exception Class for Non-Existent Event. */
public class EventDoesNotExistException extends Exception {
- private static final long serialVersionUID = -3398463738273811509L;
+ @Serial private static final long serialVersionUID = -3398463738273811509L;
public EventDoesNotExistException(String message) {
super(message);
diff --git a/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
new file mode 100644
index 000000000000..cef3697ffb55
--- /dev/null
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
@@ -0,0 +1,216 @@
+/*
+ * 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.event.asynchronous;
+
+import java.security.SecureRandom;
+import java.time.Duration;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import lombok.Getter;
+
+/**
+ * EventManager handles and maintains a pool of event threads. {@link AsyncEvent} threads are
+ * created upon user request. Thre are two types of events; Asynchronous and Synchronous. There can
+ * be multiple Asynchronous events running at once but only one Synchronous event running at a time.
+ * Currently supported event operations are: start, stop, and getStatus. Once an event is complete,
+ * it then notifies EventManager through a listener. The EventManager then takes the event out of
+ * the pool.
+ */
+public class EventManager implements ThreadCompleteListener {
+
+ public static final int MAX_RUNNING_EVENTS = 1000;
+ // Just don't want to have too many running events. :)
+ public static final int MIN_ID = 1;
+ public static final int MAX_ID = MAX_RUNNING_EVENTS;
+ public static final Duration MAX_EVENT_TIME = Duration.ofSeconds(1800); // 30 minutes.
+ private int currentlyRunningSyncEvent = -1;
+ private final SecureRandom rand;
+
+ @Getter private final Map eventPool;
+
+ private static final String DOES_NOT_EXIST = " does not exist.";
+
+ /** EventManager constructor. */
+ public EventManager() {
+ rand = new SecureRandom();
+ eventPool = new ConcurrentHashMap<>(MAX_RUNNING_EVENTS);
+ }
+
+ /**
+ * Create a Synchronous event.
+ *
+ * @param eventTime Time an event should run for.
+ * @return eventId
+ * @throws MaxNumOfEventsAllowedException When too many events are running at a time.
+ * @throws InvalidOperationException No new synchronous events can be created when one is already
+ * running.
+ * @throws LongRunningEventException Long-running events are not allowed in the app.
+ */
+ public int create(Duration eventTime)
+ throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException {
+ if (currentlyRunningSyncEvent != -1) {
+ throw new InvalidOperationException(
+ "Event ["
+ + currentlyRunningSyncEvent
+ + "] is still"
+ + " running. Please wait until it finishes and try again.");
+ }
+
+ var eventId = createEvent(eventTime, true);
+ currentlyRunningSyncEvent = eventId;
+
+ return eventId;
+ }
+
+ /**
+ * Create an Asynchronous event.
+ *
+ * @param eventTime Time an event should run for.
+ * @return eventId
+ * @throws MaxNumOfEventsAllowedException When too many events are running at a time.
+ * @throws LongRunningEventException Long-running events are not allowed in the app.
+ */
+ public int createAsync(Duration eventTime)
+ throws MaxNumOfEventsAllowedException, LongRunningEventException {
+ return createEvent(eventTime, false);
+ }
+
+ private int createEvent(Duration eventTime, boolean isSynchronous)
+ throws MaxNumOfEventsAllowedException, LongRunningEventException {
+ if (eventTime.isNegative()) {
+ throw new IllegalArgumentException("eventTime cannot be negative");
+ }
+
+ if (eventPool.size() == MAX_RUNNING_EVENTS) {
+ throw new MaxNumOfEventsAllowedException(
+ "Too many events are running at the moment." + " Please try again later.");
+ }
+
+ if (eventTime.getSeconds() > MAX_EVENT_TIME.getSeconds()) {
+ throw new LongRunningEventException(
+ "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again.");
+ }
+
+ var newEventId = generateId();
+
+ var newEvent = new AsyncEvent(newEventId, eventTime, isSynchronous);
+ newEvent.addListener(this);
+ eventPool.put(newEventId, newEvent);
+
+ return newEventId;
+ }
+
+ /**
+ * Starts event.
+ *
+ * @param eventId The event that needs to be started.
+ * @throws EventDoesNotExistException If event does not exist in our eventPool.
+ */
+ public void start(int eventId) throws EventDoesNotExistException {
+ if (!eventPool.containsKey(eventId)) {
+ throw new EventDoesNotExistException(eventId + DOES_NOT_EXIST);
+ }
+
+ eventPool.get(eventId).start();
+ }
+
+ /**
+ * Stops event.
+ *
+ * @param eventId The event that needs to be stopped.
+ * @throws EventDoesNotExistException If event does not exist in our eventPool.
+ */
+ public void cancel(int eventId) throws EventDoesNotExistException {
+ if (!eventPool.containsKey(eventId)) {
+ throw new EventDoesNotExistException(eventId + DOES_NOT_EXIST);
+ }
+
+ if (eventId == currentlyRunningSyncEvent) {
+ currentlyRunningSyncEvent = -1;
+ }
+
+ eventPool.get(eventId).stop();
+ eventPool.remove(eventId);
+ }
+
+ /**
+ * Get status of a running event.
+ *
+ * @param eventId The event to inquire status of.
+ * @throws EventDoesNotExistException If event does not exist in our eventPool.
+ */
+ public void status(int eventId) throws EventDoesNotExistException {
+ if (!eventPool.containsKey(eventId)) {
+ throw new EventDoesNotExistException(eventId + DOES_NOT_EXIST);
+ }
+
+ eventPool.get(eventId).status();
+ }
+
+ /** Gets status of all running events. */
+ @SuppressWarnings("rawtypes")
+ public void statusOfAllEvents() {
+ eventPool.entrySet().forEach(entry -> ((AsyncEvent) ((Map.Entry) entry).getValue()).status());
+ }
+
+ /** Stop all running events. */
+ @SuppressWarnings("rawtypes")
+ public void shutdown() {
+ eventPool.entrySet().forEach(entry -> ((AsyncEvent) ((Map.Entry) entry).getValue()).stop());
+ }
+
+ /**
+ * Returns a pseudo-random number between min and max, inclusive. The difference between min and
+ * max can be at most Integer.MAX_VALUE - 1.
+ */
+ private int generateId() {
+ // nextInt is normally exclusive of the top value,
+ // so add 1 to make it inclusive
+ var randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
+ while (eventPool.containsKey(randomNum)) {
+ randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
+ }
+
+ return randomNum;
+ }
+
+ /**
+ * Callback from an {@link AsyncEvent} (once it is complete). The Event is then removed from the
+ * pool.
+ */
+ @Override
+ public void completedEventHandler(int eventId) {
+ eventPool.get(eventId).status();
+ if (eventPool.get(eventId).isSynchronous()) {
+ currentlyRunningSyncEvent = -1;
+ }
+ eventPool.remove(eventId);
+ }
+
+ /** Get number of currently running Synchronous events. */
+ public int numOfCurrentlyRunningSyncEvent() {
+ return currentlyRunningSyncEvent;
+ }
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
similarity index 77%
rename from event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
rename to event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
index 349d31b80076..f8a5914602de 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,18 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.asynchronous;
-/**
- * Type of Exception raised when the Operation being invoked is Invalid.
- */
+import java.io.Serial;
+
+/** Type of Exception raised when the Operation being invoked is Invalid. */
public class InvalidOperationException extends Exception {
- private static final long serialVersionUID = -6191545255213410803L;
+ @Serial private static final long serialVersionUID = -6191545255213410803L;
public InvalidOperationException(String message) {
super(message);
}
-
}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
similarity index 77%
rename from event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
rename to event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
index 3ef450359b1c..9045b6dcf9b1 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,15 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.asynchronous;
-/**
- * Type of Exception raised when the Operation being invoked is Long Running.
- */
+import java.io.Serial;
+
+/** Type of Exception raised when the Operation being invoked is Long Running. */
public class LongRunningEventException extends Exception {
- private static final long serialVersionUID = -483423544320148809L;
+ @Serial private static final long serialVersionUID = -483423544320148809L;
public LongRunningEventException(String message) {
super(message);
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
similarity index 77%
rename from event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
rename to event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
index 49ccf452f3bb..16fa5502d1f9 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,15 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.asynchronous;
-/**
- * Type of Exception raised when the max number of allowed events is exceeded.
- */
+import java.io.Serial;
+
+/** Type of Exception raised when the max number of allowed events is exceeded. */
public class MaxNumOfEventsAllowedException extends Exception {
- private static final long serialVersionUID = -8430876973516292695L;
+ @Serial private static final long serialVersionUID = -8430876973516292695L;
public MaxNumOfEventsAllowedException(String message) {
super(message);
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
similarity index 82%
rename from event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
rename to event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
index a2f7a57338e2..f30eb51351a5 100644
--- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
+++ b/event-based-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,12 +22,9 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.asynchronous;
-/**
- * Interface with listener behaviour related to Thread Completion.
- */
+/** Interface with listener behaviour related to Thread Completion. */
public interface ThreadCompleteListener {
void completedEventHandler(final int eventId);
}
diff --git a/event-asynchronous/src/main/resources/config.properties b/event-based-asynchronous/src/main/resources/config.properties
similarity index 100%
rename from event-asynchronous/src/main/resources/config.properties
rename to event-based-asynchronous/src/main/resources/config.properties
diff --git a/event-based-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-based-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
new file mode 100644
index 000000000000..4b5094033b13
--- /dev/null
+++ b/event-based-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.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.event.asynchronous;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+
+import org.junit.jupiter.api.Test;
+
+/** Tests that EventAsynchronous 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.
+ */
+ @Test
+ void shouldExecuteApplicationWithoutException() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
+}
diff --git a/event-based-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-based-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java
new file mode 100644
index 000000000000..ab9dd70bbf3d
--- /dev/null
+++ b/event-based-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.event.asynchronous;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotEquals;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.time.Duration;
+import lombok.SneakyThrows;
+import org.junit.jupiter.api.Test;
+
+/** Application test */
+class EventAsynchronousTest {
+
+ @Test
+ @SneakyThrows
+ void testAsynchronousEvent() {
+ var eventManager = new EventManager();
+ var aEventId = eventManager.createAsync(Duration.ofSeconds(60));
+
+ assertDoesNotThrow(() -> eventManager.start(aEventId));
+
+ assertEquals(1, eventManager.getEventPool().size());
+ assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
+ assertEquals(-1, eventManager.numOfCurrentlyRunningSyncEvent());
+
+ assertDoesNotThrow(() -> eventManager.cancel(aEventId));
+ assertTrue(eventManager.getEventPool().isEmpty());
+ }
+
+ @Test
+ @SneakyThrows
+ void testSynchronousEvent() {
+ var eventManager = new EventManager();
+ var sEventId = eventManager.create(Duration.ofSeconds(60));
+
+ assertDoesNotThrow(() -> eventManager.start(sEventId));
+ assertEquals(1, eventManager.getEventPool().size());
+ assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
+ assertNotEquals(-1, eventManager.numOfCurrentlyRunningSyncEvent());
+
+ assertDoesNotThrow(() -> eventManager.cancel(sEventId));
+ assertTrue(eventManager.getEventPool().isEmpty());
+ }
+
+ @Test
+ @SneakyThrows
+ void testFullSynchronousEvent() {
+ var eventManager = new EventManager();
+
+ var eventTime = Duration.ofSeconds(1);
+
+ var sEventId = eventManager.create(eventTime);
+ assertEquals(1, eventManager.getEventPool().size());
+
+ eventManager.start(sEventId);
+
+ await().until(() -> eventManager.getEventPool().isEmpty());
+ }
+
+ @Test
+ @SneakyThrows
+ void testUnsuccessfulSynchronousEvent() {
+ assertThrows(
+ InvalidOperationException.class,
+ () -> {
+ var eventManager = new EventManager();
+
+ var sEventId = assertDoesNotThrow(() -> eventManager.create(Duration.ofSeconds(60)));
+ eventManager.start(sEventId);
+ sEventId = eventManager.create(Duration.ofSeconds(60));
+ eventManager.start(sEventId);
+ });
+ }
+
+ @Test
+ @SneakyThrows
+ void testFullAsynchronousEvent() {
+ var eventManager = new EventManager();
+ var eventTime = Duration.ofSeconds(1);
+
+ var aEventId1 = assertDoesNotThrow(() -> eventManager.createAsync(eventTime));
+ var aEventId2 = assertDoesNotThrow(() -> eventManager.createAsync(eventTime));
+ var aEventId3 = assertDoesNotThrow(() -> eventManager.createAsync(eventTime));
+ assertEquals(3, eventManager.getEventPool().size());
+
+ eventManager.start(aEventId1);
+ eventManager.start(aEventId2);
+ eventManager.start(aEventId3);
+
+ await().until(() -> eventManager.getEventPool().isEmpty());
+ }
+
+ @Test
+ void testLongRunningEventException() {
+ assertThrows(
+ LongRunningEventException.class,
+ () -> {
+ var eventManager = new EventManager();
+ eventManager.createAsync(Duration.ofMinutes(31));
+ });
+ }
+
+ @Test
+ void testMaxNumOfEventsAllowedException() {
+ assertThrows(
+ MaxNumOfEventsAllowedException.class,
+ () -> {
+ final var eventManager = new EventManager();
+ for (int i = 0; i < 1100; i++) {
+ eventManager.createAsync(Duration.ofSeconds(i));
+ }
+ });
+ }
+}
diff --git a/event-driven-architecture/README.md b/event-driven-architecture/README.md
index 295d23334f6e..c868c174e078 100644
--- a/event-driven-architecture/README.md
+++ b/event-driven-architecture/README.md
@@ -1,36 +1,202 @@
---
-layout: pattern
-title: Event Driven Architecture
-folder: event-driven-architecture
-permalink: /patterns/event-driven-architecture/
-categories: Architectural
+title: "Event-Driven Architecture Pattern in Java: Building Responsive and Scalable Java Systems"
+shortTitle: Event-Driven Architecture
+description: "Discover comprehensive guides on Event-Driven Architecture patterns with practical Java examples. Learn to implement effective event-driven systems in your projects."
+category: Architectural
language: en
-tags:
- - Reactive
+tag:
+ - Asynchronous
+ - Decoupling
+ - Enterprise patterns
+ - Event-driven
+ - Messaging
+ - Publish/subscribe
+ - Reactive
+ - Scalability
---
-## Intent
-Send and notify state changes of your objects to other applications using an Event-driven Architecture.
+## Also known as
-## Class diagram
-
+* Event-Driven System
+* Event-Based Architecture
+
+## Intent of Event-Driven Architecture Design Pattern
+
+Event-Driven Architecture (EDA) is designed to orchestrate behavior around the production, detection, consumption of, and reaction to events. This architecture enables highly decoupled, scalable, and dynamic interconnections between event producers and consumers.
+
+## Detailed Explanation of Event-Driven Architecture Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world example of the Event-Driven Architecture (EDA) pattern is the operation of an air traffic control system. In this system, events such as aircraft entering airspace, changes in weather conditions, and ground vehicle movements trigger specific responses like altering flight paths, scheduling gate assignments, and updating runway usage. This setup allows for highly efficient, responsive, and safe management of airport operations, reflecting EDA's core principles of asynchronous communication and dynamic event handling.
+
+In plain words
+
+> Event-Driven Architecture is a design pattern where system behavior is dictated by the occurrence of specific events, allowing for dynamic, efficient, and decoupled responses.
+
+Wikipedia says
+
+> Event-driven architecture (EDA) is a software architecture paradigm concerning the production and detection of events.
+
+Architecture diagram
+
+
+
+## Programmatic Example of Event-Driven Architecture in Java
+
+The Event-Driven Architecture (EDA) pattern in this module is implemented using several key classes and concepts:
+
+* Event: This is an abstract class that represents an event. It's the base class for all types of events that can occur in the system.
+* UserCreatedEvent and UserUpdatedEvent: These are concrete classes that extend the Event class. They represent specific types of events that can occur in the system, namely the creation and updating of a user.
+* EventDispatcher: This class is responsible for dispatching events to their respective handlers. It maintains a mapping of event types to handlers.
+* UserCreatedEventHandler and UserUpdatedEventHandler: These are the handler classes for the UserCreatedEvent and UserUpdatedEvent respectively. They contain the logic to execute when these events occur.
+
+First, we'll define the `Event` abstract class and the concrete event classes `UserCreatedEvent` and `UserUpdatedEvent`.
+
+```java
+public abstract class Event {
+ // Event related properties and methods
+}
+```
+
+```java
+public class UserCreatedEvent extends Event {
+ private User user;
+
+ public UserCreatedEvent(User user) {
+ this.user = user;
+ }
+
+ public User getUser() {
+ return user;
+ }
+}
+```
+
+```java
+public class UserUpdatedEvent extends Event {
+ private User user;
+
+ public UserUpdatedEvent(User user) {
+ this.user = user;
+ }
+
+ public User getUser() {
+ return user;
+ }
+}
+```
+
+Next, we'll define the event handlers `UserCreatedEventHandler` and `UserUpdatedEventHandler`.
+
+```java
+public class UserCreatedEventHandler {
+ public void onUserCreated(UserCreatedEvent event) {
+ // Logic to execute when a UserCreatedEvent occurs
+ }
+}
+```
+
+```java
+public class UserUpdatedEventHandler {
+ public void onUserUpdated(UserUpdatedEvent event) {
+ // Logic to execute when a UserUpdatedEvent occurs
+ }
+}
+```
+
+Then, we'll define the `EventDispatcher` class that is responsible for dispatching events to their respective handlers.
+
+```java
+public class EventDispatcher {
+ private Map, List>> handlers = new HashMap<>();
+
+ public void registerHandler(Class eventType, Consumer handler) {
+ handlers.computeIfAbsent(eventType, k -> new ArrayList<>()).add(handler::accept);
+ }
+
+ public void dispatch(Event event) {
+ List> eventHandlers = handlers.get(event.getClass());
+ if (eventHandlers != null) {
+ eventHandlers.forEach(handler -> handler.accept(event));
+ }
+ }
+}
+```
+
+Finally, we'll demonstrate how to use these classes in the main application.
+
+```java
+public class App {
+ public static void main(String[] args) {
+ // Create an EventDispatcher
+ EventDispatcher dispatcher = new EventDispatcher();
+
+ // Register handlers for UserCreatedEvent and UserUpdatedEvent
+ dispatcher.registerHandler(UserCreatedEvent.class, new UserCreatedEventHandler()::onUserCreated);
+ dispatcher.registerHandler(UserUpdatedEvent.class, new UserUpdatedEventHandler()::onUserUpdated);
+
+ // Create a User
+ User user = new User("iluwatar");
+
+ // Dispatch UserCreatedEvent
+ dispatcher.dispatch(new UserCreatedEvent(user));
+
+ // Dispatch UserUpdatedEvent
+ dispatcher.dispatch(new UserUpdatedEvent(user));
+ }
+}
+```
+
+Running the example produces the following console output:
+
+```
+22:15:19.997 [main] INFO com.iluwatar.eda.handler.UserCreatedEventHandler -- User 'iluwatar' has been Created!
+22:15:20.000 [main] INFO com.iluwatar.eda.handler.UserUpdatedEventHandler -- User 'iluwatar' has been Updated!
+```
+
+This example demonstrates the Event-Driven Architecture pattern, where the occurrence of events drives the flow of the program. The system is designed to respond to events as they occur, which allows for a high degree of flexibility and decoupling between components.
+
+## When to Use the Event-Driven Architecture Pattern in Java
-## Applicability
Use an Event-driven architecture when
-* you want to create a loosely coupled system
-* you want to build a more responsive system
-* you want a system that is easier to extend
+* Systems where change detection is crucial.
+* Applications that require real-time features and reactive systems.
+* Systems needing to efficiently handle high throughput and sporadic loads.
+* When integrating with microservices to enhance agility and scalability.
-## Real world examples
+## Real-World Applications of Event-Driven Architecture Pattern in Java
+* Real-time data processing applications.
+* Complex event processing systems in finance, such as stock trading platforms.
+* IoT systems for dynamic device and information management.
* Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events)
* Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda)
* MySQL runs triggers based on events such as inserts and update events happening on database tables.
-## Credits
+## Benefits and Trade-offs of Event-Driven Architecture Pattern
+
+Benefits:
+
+* Scalability: Efficiently processes fluctuating loads with asynchronous processing.
+* Flexibility and Agility: New event types and event consumers can be added with minimal impact on existing components.
+* Responsiveness: Improves responsiveness by decoupling event processing and state management.
+
+Trade-offs:
+
+* Complexity in Tracking: Can be challenging to debug and track due to loose coupling and asynchronous behaviors.
+* Dependency on Messaging Systems: Heavily relies on robust messaging infrastructures.
+* Event Consistency: Requires careful design to handle event ordering and consistency.
+
+## Related Java Design Patterns
+
+* Microservices Architecture: Often used together with EDA to enhance agility and scalability.
+* Publish/Subscribe: A common pattern used within EDA for messaging between event producers and consumers.
+
+## References and Credits
-* [Event-driven architecture - Wikipedia](https://en.wikipedia.org/wiki/Event-driven_architecture)
-* [What is an Event-Driven Architecture](https://aws.amazon.com/event-driven-architecture/)
-* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications)
-* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/3Q3vBki)
+* [Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions](https://amzn.to/49Aljz0)
+* [Reactive Messaging Patterns With the Actor Model: Applications and Integration in Scala and Akka](https://amzn.to/3UeoBUa)
+* [What is an Event-Driven Architecture (Amazon)](https://aws.amazon.com/event-driven-architecture/)
diff --git a/event-driven-architecture/etc/eda-architecture-diagram.png b/event-driven-architecture/etc/eda-architecture-diagram.png
new file mode 100644
index 000000000000..4e74bcf2e89a
Binary files /dev/null and b/event-driven-architecture/etc/eda-architecture-diagram.png differ
diff --git a/event-driven-architecture/pom.xml b/event-driven-architecture/pom.xml
index 412543e9cc29..8a9fc27876eb 100644
--- a/event-driven-architecture/pom.xml
+++ b/event-driven-architecture/pom.xml
@@ -1,8 +1,10 @@
- com.google.code.gson
- gson
- 2.8.6
+ com.fasterxml.jackson.core
+ jackson-core
+ 2.19.0
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+ 2.18.3
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java
index 6635dddf7c27..a625e9ace9d3 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/app/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,20 +22,20 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.app;
import com.iluwatar.event.sourcing.event.AccountCreateEvent;
import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
import com.iluwatar.event.sourcing.event.MoneyTransferEvent;
import com.iluwatar.event.sourcing.processor.DomainEventProcessor;
+import com.iluwatar.event.sourcing.processor.JsonFileJournal;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
import java.util.Date;
import lombok.extern.slf4j.Slf4j;
/**
- * Event Sourcing : Instead of storing just the current state of the data in a domain, use an
+ * Event Sourcing: Instead of storing just the current state of the data in a domain, use an
* append-only store to record the full series of actions taken on that data. The store acts as the
* system of record and can be used to materialize the domain objects. This can simplify tasks in
* complex domains, by avoiding the need to synchronize the data model and the business domain,
@@ -41,23 +43,20 @@
* transactional data, and maintain full audit trails and history that can enable compensating
* actions.
*
- * This App class is an example usage of Event Sourcing pattern. As an example, two bank account
- * is created, then some money deposit and transfer actions are taken so a new state of accounts is
- * created. At that point, state is cleared in order to represent a system shot down. After the shot
- * down, system state is recovered by re-creating the past events from event journal. Then state is
- * printed so a user can view the last state is same with the state before system shot down.
- *
- *
Created by Serdar Hamzaogullari on 06.08.2017.
+ *
This App class is an example usage of an Event Sourcing pattern. As an example, two bank
+ * accounts are created, then some money deposit and transfer actions are taken, so a new state of
+ * accounts is created. At that point, state is cleared in order to represent a system shut-down.
+ * After the shut-down, system state is recovered by re-creating the past events from event
+ * journals. Then state is printed so a user can view the last state is same with the state before a
+ * system shut-down.
*/
@Slf4j
public class App {
- /**
- * The constant ACCOUNT OF DAENERYS.
- */
+
+ /** The constant ACCOUNT OF DAENERYS. */
public static final int ACCOUNT_OF_DAENERYS = 1;
- /**
- * The constant ACCOUNT OF JON.
- */
+
+ /** The constant ACCOUNT OF JON. */
public static final int ACCOUNT_OF_JON = 2;
/**
@@ -67,31 +66,31 @@ public class App {
*/
public static void main(String[] args) {
- var eventProcessor = new DomainEventProcessor();
-
+ var eventProcessor = new DomainEventProcessor(new JsonFileJournal());
LOGGER.info("Running the system first time............");
eventProcessor.reset();
LOGGER.info("Creating the accounts............");
- eventProcessor.process(new AccountCreateEvent(
- 0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
+ eventProcessor.process(
+ new AccountCreateEvent(0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
- eventProcessor.process(new AccountCreateEvent(
- 1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
+ eventProcessor.process(
+ new AccountCreateEvent(1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
LOGGER.info("Do some money operations............");
- eventProcessor.process(new MoneyDepositEvent(
- 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
+ eventProcessor.process(
+ new MoneyDepositEvent(
+ 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
- eventProcessor.process(new MoneyDepositEvent(
- 3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100")));
+ eventProcessor.process(
+ new MoneyDepositEvent(3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100")));
- eventProcessor.process(new MoneyTransferEvent(
- 4, new Date().getTime(), new BigDecimal("10000"), ACCOUNT_OF_DAENERYS,
- ACCOUNT_OF_JON));
+ eventProcessor.process(
+ new MoneyTransferEvent(
+ 4, new Date().getTime(), new BigDecimal("10000"), ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON));
LOGGER.info("...............State:............");
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString());
@@ -102,13 +101,11 @@ public static void main(String[] args) {
LOGGER.info("Recover the system by the events in journal file............");
- eventProcessor = new DomainEventProcessor();
+ eventProcessor = new DomainEventProcessor(new JsonFileJournal());
eventProcessor.recover();
LOGGER.info("...............Recovered State:............");
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS).toString());
LOGGER.info(AccountAggregate.getAccount(ACCOUNT_OF_JON).toString());
}
-
-
}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java
index 3f4945d1b351..89f4219d740b 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/domain/Account.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.domain;
import com.iluwatar.event.sourcing.event.AccountCreateEvent;
@@ -67,9 +68,13 @@ public Account copy() {
@Override
public String toString() {
return "Account{"
- + "accountNo=" + accountNo
- + ", owner='" + owner + '\''
- + ", money=" + money
+ + "accountNo="
+ + accountNo
+ + ", owner='"
+ + owner
+ + '\''
+ + ", money="
+ + money
+ '}';
}
@@ -110,11 +115,10 @@ public void handleEvent(MoneyDepositEvent moneyDepositEvent) {
handleDeposit(moneyDepositEvent.getMoney(), moneyDepositEvent.isRealTime());
}
-
/**
* Handles the AccountCreateEvent.
*
- * @param accountCreateEvent the account create event
+ * @param accountCreateEvent the account created event
*/
public void handleEvent(AccountCreateEvent accountCreateEvent) {
AccountAggregate.putAccount(this);
@@ -140,6 +144,4 @@ public void handleTransferFromEvent(MoneyTransferEvent moneyTransferEvent) {
public void handleTransferToEvent(MoneyTransferEvent moneyTransferEvent) {
handleDeposit(moneyTransferEvent.getMoney(), moneyTransferEvent.isRealTime());
}
-
-
}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java
index 0845590ebb68..087752cbf9c6 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/AccountCreateEvent.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,16 +22,17 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.event;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.iluwatar.event.sourcing.domain.Account;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import lombok.Getter;
/**
- * This is the class that implements account create event. Holds the necessary info for an account
- * create event. Implements the process function that finds the event related domain objects and
+ * This is the class that implements account created event. Holds the necessary info for an account
+ * created event. Implements the process function that finds the event-related domain objects and
* calls the related domain object's handle event functions
*
*
Created by Serdar Hamzaogullari on 06.08.2017.
@@ -41,14 +44,19 @@ public class AccountCreateEvent extends DomainEvent {
private final String owner;
/**
- * Instantiates a new Account create event.
+ * Instantiates a new Account created event.
*
- * @param sequenceId the sequence id
+ * @param sequenceId the sequence id
* @param createdTime the created time
- * @param accountNo the account no
- * @param owner the owner
+ * @param accountNo the account no
+ * @param owner the owner
*/
- public AccountCreateEvent(long sequenceId, long createdTime, int accountNo, String owner) {
+ @JsonCreator
+ public AccountCreateEvent(
+ @JsonProperty("sequenceId") long sequenceId,
+ @JsonProperty("createdTime") long createdTime,
+ @JsonProperty("accountNo") int accountNo,
+ @JsonProperty("owner") String owner) {
super(sequenceId, createdTime, "AccountCreateEvent");
this.accountNo = accountNo;
this.owner = owner;
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java
index 05c0c2173cd3..f39ebda43eb0 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/DomainEvent.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.event;
import java.io.Serializable;
@@ -43,9 +44,6 @@ public abstract class DomainEvent implements Serializable {
private final String eventClassName;
private boolean realTime = true;
- /**
- * Process.
- */
+ /** Process. */
public abstract void process();
-
}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java
index 27bcd15b76d8..4f80ec6b6dfc 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyDepositEvent.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,9 +22,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.event;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
import java.util.Optional;
@@ -30,7 +33,7 @@
/**
* This is the class that implements money deposit event. Holds the necessary info for a money
- * deposit event. Implements the process function that finds the event related domain objects and
+ * deposit event. Implements the process function that finds the event-related domain objects and
* calls the related domain object's handle event functions
*
*
Created by Serdar Hamzaogullari on 06.08.2017.
@@ -44,12 +47,17 @@ public class MoneyDepositEvent extends DomainEvent {
/**
* Instantiates a new Money deposit event.
*
- * @param sequenceId the sequence id
+ * @param sequenceId the sequence id
* @param createdTime the created time
- * @param accountNo the account no
- * @param money the money
+ * @param accountNo the account no
+ * @param money the money
*/
- public MoneyDepositEvent(long sequenceId, long createdTime, int accountNo, BigDecimal money) {
+ @JsonCreator
+ public MoneyDepositEvent(
+ @JsonProperty("sequenceId") long sequenceId,
+ @JsonProperty("createdTime") long createdTime,
+ @JsonProperty("accountNo") int accountNo,
+ @JsonProperty("money") BigDecimal money) {
super(sequenceId, createdTime, "MoneyDepositEvent");
this.money = money;
this.accountNo = accountNo;
@@ -57,8 +65,9 @@ public MoneyDepositEvent(long sequenceId, long createdTime, int accountNo, BigDe
@Override
public void process() {
- var account = Optional.ofNullable(AccountAggregate.getAccount(accountNo))
- .orElseThrow(() -> new RuntimeException("Account not found"));
+ var account =
+ Optional.ofNullable(AccountAggregate.getAccount(accountNo))
+ .orElseThrow(() -> new RuntimeException("Account not found"));
account.handleEvent(this);
}
}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java
index 51f0a23b18a5..e6e257dded04 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/event/MoneyTransferEvent.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,9 +22,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.event;
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
import java.util.Optional;
@@ -30,7 +33,7 @@
/**
* This is the class that implements money transfer event. Holds the necessary info for a money
- * transfer event. Implements the process function that finds the event related domain objects and
+ * transfer event. Implements the process function that finds the event-related domain objects and
* calls the related domain object's handle event functions
*
*
Created by Serdar Hamzaogullari on 06.08.2017.
@@ -45,14 +48,19 @@ public class MoneyTransferEvent extends DomainEvent {
/**
* Instantiates a new Money transfer event.
*
- * @param sequenceId the sequence id
- * @param createdTime the created time
- * @param money the money
+ * @param sequenceId the sequence id
+ * @param createdTime the created time
+ * @param money the money
* @param accountNoFrom the account no from
- * @param accountNoTo the account no to
+ * @param accountNoTo the account no to
*/
- public MoneyTransferEvent(long sequenceId, long createdTime, BigDecimal money, int accountNoFrom,
- int accountNoTo) {
+ @JsonCreator
+ public MoneyTransferEvent(
+ @JsonProperty("sequenceId") long sequenceId,
+ @JsonProperty("createdTime") long createdTime,
+ @JsonProperty("money") BigDecimal money,
+ @JsonProperty("accountNoFrom") int accountNoFrom,
+ @JsonProperty("accountNoTo") int accountNoTo) {
super(sequenceId, createdTime, "MoneyTransferEvent");
this.money = money;
this.accountNoFrom = accountNoFrom;
@@ -61,10 +69,12 @@ public MoneyTransferEvent(long sequenceId, long createdTime, BigDecimal money, i
@Override
public void process() {
- var accountFrom = Optional.ofNullable(AccountAggregate.getAccount(accountNoFrom))
- .orElseThrow(() -> new RuntimeException("Account not found " + accountNoFrom));
- var accountTo = Optional.ofNullable(AccountAggregate.getAccount(accountNoTo))
- .orElseThrow(() -> new RuntimeException("Account not found " + accountNoTo));
+ var accountFrom =
+ Optional.ofNullable(AccountAggregate.getAccount(accountNoFrom))
+ .orElseThrow(() -> new RuntimeException("Account not found " + accountNoFrom));
+ var accountTo =
+ Optional.ofNullable(AccountAggregate.getAccount(accountNoTo))
+ .orElseThrow(() -> new RuntimeException("Account not found " + accountNoTo));
accountFrom.handleTransferFromEvent(this);
accountTo.handleTransferToEvent(this);
}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java
index c4dd47612fe4..6b0658b40a43 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/DomainEventProcessor.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,20 +22,23 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.processor;
import com.iluwatar.event.sourcing.event.DomainEvent;
/**
* This is the implementation of event processor. All events are processed by this class. This
- * processor uses processorJournal to persist and recover events.
+ * processor uses eventJournal to persist and recover events.
*
*
Created by Serdar Hamzaogullari on 06.08.2017.
*/
public class DomainEventProcessor {
- private final JsonFileJournal processorJournal = new JsonFileJournal();
+ private final EventJournal eventJournal;
+
+ public DomainEventProcessor(EventJournal eventJournal) {
+ this.eventJournal = eventJournal;
+ }
/**
* Process.
@@ -42,22 +47,18 @@ public class DomainEventProcessor {
*/
public void process(DomainEvent domainEvent) {
domainEvent.process();
- processorJournal.write(domainEvent);
+ eventJournal.write(domainEvent);
}
- /**
- * Reset.
- */
+ /** Reset. */
public void reset() {
- processorJournal.reset();
+ eventJournal.reset();
}
- /**
- * Recover.
- */
+ /** Recover. */
public void recover() {
DomainEvent domainEvent;
- while ((domainEvent = processorJournal.readNext()) != null) {
+ while ((domainEvent = eventJournal.readNext()) != null) {
domainEvent.process();
}
}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java
new file mode 100644
index 000000000000..3b4cdfd3ec3e
--- /dev/null
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/EventJournal.java
@@ -0,0 +1,57 @@
+/*
+ * 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.event.sourcing.processor;
+
+import com.iluwatar.event.sourcing.event.DomainEvent;
+import java.io.File;
+import lombok.extern.slf4j.Slf4j;
+
+/** Base class for Journaling implementations. */
+@Slf4j
+public abstract class EventJournal {
+
+ File file;
+
+ /**
+ * Write.
+ *
+ * @param domainEvent the domain event.
+ */
+ abstract void write(DomainEvent domainEvent);
+
+ /** Reset. */
+ void reset() {
+ if (file.delete()) {
+ LOGGER.info("File cleared successfully............");
+ }
+ }
+
+ /**
+ * Read domain event.
+ *
+ * @return the domain event.
+ */
+ abstract DomainEvent readNext();
+}
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java
index 925f7ca9438b..106dbf95e93a 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/processor/JsonFileJournal.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,12 +22,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.processor;
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParser;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.iluwatar.event.sourcing.event.AccountCreateEvent;
import com.iluwatar.event.sourcing.event.DomainEvent;
import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
@@ -48,20 +48,18 @@
*
*
Created by Serdar Hamzaogullari on 06.08.2017.
*/
-public class JsonFileJournal {
+public class JsonFileJournal extends EventJournal {
- private final File file;
private final List events = new ArrayList<>();
private int index = 0;
- /**
- * Instantiates a new Json file journal.
- */
+ /** Instantiates a new Json file journal. */
public JsonFileJournal() {
file = new File("Journal.json");
if (file.exists()) {
- try (var input = new BufferedReader(
- new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
+ try (var input =
+ new BufferedReader(
+ new InputStreamReader(new FileInputStream(file), StandardCharsets.UTF_8))) {
String line;
while ((line = input.readLine()) != null) {
events.add(line);
@@ -74,45 +72,26 @@ public JsonFileJournal() {
}
}
-
/**
* Write.
*
* @param domainEvent the domain event
*/
+ @Override
public void write(DomainEvent domainEvent) {
- var gson = new Gson();
- JsonElement jsonElement;
- if (domainEvent instanceof AccountCreateEvent) {
- jsonElement = gson.toJsonTree(domainEvent, AccountCreateEvent.class);
- } else if (domainEvent instanceof MoneyDepositEvent) {
- jsonElement = gson.toJsonTree(domainEvent, MoneyDepositEvent.class);
- } else if (domainEvent instanceof MoneyTransferEvent) {
- jsonElement = gson.toJsonTree(domainEvent, MoneyTransferEvent.class);
- } else {
- throw new RuntimeException("Journal Event not recegnized");
- }
-
- try (var output = new BufferedWriter(
- new OutputStreamWriter(new FileOutputStream(file, true), StandardCharsets.UTF_8))) {
- var eventString = jsonElement.toString();
+ var mapper = new ObjectMapper();
+ try (var output =
+ new BufferedWriter(
+ new OutputStreamWriter(new FileOutputStream(file, true), StandardCharsets.UTF_8))) {
+ var eventString = mapper.writeValueAsString(domainEvent);
output.write(eventString + "\r\n");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
-
/**
- * Reset.
- */
- public void reset() {
- file.delete();
- }
-
-
- /**
- * Read next domain event.
+ * Read the next domain event.
*
* @return the domain event
*/
@@ -123,19 +102,20 @@ public DomainEvent readNext() {
var event = events.get(index);
index++;
- var parser = new JsonParser();
- var jsonElement = parser.parse(event);
- var eventClassName = jsonElement.getAsJsonObject().get("eventClassName").getAsString();
- var gson = new Gson();
+ var mapper = new ObjectMapper();
DomainEvent domainEvent;
- if (eventClassName.equals("AccountCreateEvent")) {
- domainEvent = gson.fromJson(jsonElement, AccountCreateEvent.class);
- } else if (eventClassName.equals("MoneyDepositEvent")) {
- domainEvent = gson.fromJson(jsonElement, MoneyDepositEvent.class);
- } else if (eventClassName.equals("MoneyTransferEvent")) {
- domainEvent = gson.fromJson(jsonElement, MoneyTransferEvent.class);
- } else {
- throw new RuntimeException("Journal Event not recegnized");
+ try {
+ var jsonElement = mapper.readTree(event);
+ var eventClassName = jsonElement.get("eventClassName").asText();
+ domainEvent =
+ switch (eventClassName) {
+ case "AccountCreateEvent" -> mapper.treeToValue(jsonElement, AccountCreateEvent.class);
+ case "MoneyDepositEvent" -> mapper.treeToValue(jsonElement, MoneyDepositEvent.class);
+ case "MoneyTransferEvent" -> mapper.treeToValue(jsonElement, MoneyTransferEvent.class);
+ default -> throw new RuntimeException("Journal Event not recognized");
+ };
+ } catch (JsonProcessingException jsonProcessingException) {
+ throw new RuntimeException("Failed to convert JSON");
}
domainEvent.setRealTime(false);
diff --git a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java
index 4bdbb0da3f97..80253036f223 100644
--- a/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java
+++ b/event-sourcing/src/main/java/com/iluwatar/event/sourcing/state/AccountAggregate.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.event.sourcing.state;
import com.iluwatar.event.sourcing.domain.Account;
@@ -37,8 +38,7 @@ public class AccountAggregate {
private static Map accounts = new HashMap<>();
- private AccountAggregate() {
- }
+ private AccountAggregate() {}
/**
* Put account.
@@ -56,15 +56,10 @@ public static void putAccount(Account account) {
* @return the copy of the account or null if not found
*/
public static Account getAccount(int accountNo) {
- return Optional.of(accountNo)
- .map(accounts::get)
- .map(Account::copy)
- .orElse(null);
+ return Optional.of(accountNo).map(accounts::get).map(Account::copy).orElse(null);
}
- /**
- * Reset state.
- */
+ /** Reset state. */
public static void resetState() {
accounts = new HashMap<>();
}
diff --git a/event-sourcing/src/test/java/IntegrationTest.java b/event-sourcing/src/test/java/IntegrationTest.java
index 1530e6e17015..89c7a77fe91e 100644
--- a/event-sourcing/src/test/java/IntegrationTest.java
+++ b/event-sourcing/src/test/java/IntegrationTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -29,6 +31,7 @@
import com.iluwatar.event.sourcing.event.MoneyDepositEvent;
import com.iluwatar.event.sourcing.event.MoneyTransferEvent;
import com.iluwatar.event.sourcing.processor.DomainEventProcessor;
+import com.iluwatar.event.sourcing.processor.JsonFileJournal;
import com.iluwatar.event.sourcing.state.AccountAggregate;
import java.math.BigDecimal;
import java.util.Date;
@@ -36,62 +39,56 @@
import org.junit.jupiter.api.Test;
/**
- * Intergartion Test for Event Sourcing state recovery
- *
- * Created by Serdar Hamzaogullari on 19.08.2017.
+ * Integration Test for Event-Sourcing state recovery
+ *
+ *
Created by Serdar Hamzaogullari on 19.08.2017.
*/
class IntegrationTest {
- /**
- * The Domain event processor.
- */
+ /** The Domain event processor. */
private DomainEventProcessor eventProcessor;
- /**
- * Initialize.
- */
+ /** Initialize. */
@BeforeEach
void initialize() {
- eventProcessor = new DomainEventProcessor();
+ eventProcessor = new DomainEventProcessor(new JsonFileJournal());
}
- /**
- * Test state recovery.
- */
+ /** Test state recovery. */
@Test
void testStateRecovery() {
eventProcessor.reset();
- eventProcessor.process(new AccountCreateEvent(
- 0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
+ eventProcessor.process(
+ new AccountCreateEvent(0, new Date().getTime(), ACCOUNT_OF_DAENERYS, "Daenerys Targaryen"));
- eventProcessor.process(new AccountCreateEvent(
- 1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
+ eventProcessor.process(
+ new AccountCreateEvent(1, new Date().getTime(), ACCOUNT_OF_JON, "Jon Snow"));
- eventProcessor.process(new MoneyDepositEvent(
- 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
+ eventProcessor.process(
+ new MoneyDepositEvent(
+ 2, new Date().getTime(), ACCOUNT_OF_DAENERYS, new BigDecimal("100000")));
- eventProcessor.process(new MoneyDepositEvent(
- 3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100")));
+ eventProcessor.process(
+ new MoneyDepositEvent(3, new Date().getTime(), ACCOUNT_OF_JON, new BigDecimal("100")));
- eventProcessor.process(new MoneyTransferEvent(
- 4, new Date().getTime(), new BigDecimal("10000"), ACCOUNT_OF_DAENERYS,
- ACCOUNT_OF_JON));
+ eventProcessor.process(
+ new MoneyTransferEvent(
+ 4, new Date().getTime(), new BigDecimal("10000"), ACCOUNT_OF_DAENERYS, ACCOUNT_OF_JON));
var accountOfDaenerysBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS);
var accountOfJonBeforeShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON);
AccountAggregate.resetState();
- eventProcessor = new DomainEventProcessor();
+ eventProcessor = new DomainEventProcessor(new JsonFileJournal());
eventProcessor.recover();
var accountOfDaenerysAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_DAENERYS);
var accountOfJonAfterShotDown = AccountAggregate.getAccount(ACCOUNT_OF_JON);
- assertEquals(accountOfDaenerysBeforeShotDown.getMoney(),
- accountOfDaenerysAfterShotDown.getMoney());
+ assertEquals(
+ accountOfDaenerysBeforeShotDown.getMoney(), accountOfDaenerysAfterShotDown.getMoney());
assertEquals(accountOfJonBeforeShotDown.getMoney(), accountOfJonAfterShotDown.getMoney());
}
-
-}
\ No newline at end of file
+}
diff --git a/execute-around/README.md b/execute-around/README.md
index 16d4803c3fbe..9587674ded1e 100644
--- a/execute-around/README.md
+++ b/execute-around/README.md
@@ -1,82 +1,140 @@
---
-layout: pattern
-title: Execute Around
-folder: execute-around
-permalink: /patterns/execute-around/
-categories: Idiom
+title: "Execute Around Pattern in Java: Encapsulating Pre and Post Execution Steps"
+shortTitle: Execute Around
+description: "Explore the Execute Around Pattern in Java with detailed explanations, real-world examples, and best practices. Learn how to implement this design pattern to streamline resource management."
+category: Behavioral
language: en
-tags:
- - Extensibility
+tag:
+ - Closure
+ - Code simplification
+ - Encapsulation
+ - Functional decomposition
+ - Resource management
+head:
+ - - meta
+ - name: keywords
+ content:
---
-## Intent
+## Also known as
-Execute Around idiom frees the user from certain actions that should always be executed before and
-after the business method. A good example of this is resource allocation and deallocation leaving
-the user to specify only what to do with the resource.
+* Around Method Pattern
+* Resource Block Management
-## Explanation
+## Intent of Execute Around Design Pattern
-Real world example
+Real-world business applications often require executing necessary operations before and after the business method invocation. The Execute Around Pattern in Java provides a way to encapsulate these operations, enhancing code readability and reusability.
-> We need to provide a class that can be used to write text strings to files. To make it easy for
-> the user we let our service class open and close the file automatically, the user only has to
-> specify what is written into which file.
+## Detailed Explanation of Execute Around Pattern with Real-World Examples
+
+Real-world example
+
+> A real-world analogy for the Execute Around pattern can be found in the use of rental cars. When you rent a car, the rental company handles all the setup (cleaning the car, filling it with gas, ensuring it's in good condition) and cleanup (checking the car back in, inspecting it for damage, refueling it if necessary) processes for you. As a customer, you simply use the car for your intended purpose without worrying about the setup and cleanup. This pattern of abstracting away the repetitive tasks around the main operation is similar to the Execute Around pattern in software, where the setup and cleanup of resources are handled by a reusable method, allowing the main logic to be executed seamlessly.
In plain words
-> Execute Around idiom handles boilerplate code before and after business method.
+> Execute Around idiom handles boilerplate code before and after business method.
[Stack Overflow](https://stackoverflow.com/questions/341971/what-is-the-execute-around-idiom) says
-> Basically it's the pattern where you write a method to do things which are always required, e.g.
-> resource allocation and clean-up, and make the caller pass in "what we want to do with the
-> resource".
+> Basically it's the pattern where you write a method to do things which are always required, e.g. resource allocation and clean-up, and make the caller pass in "what we want to do with the resource".
+
+Flowchart
+
+
-**Programmatic Example**
+## Programmatic Example of Execute Around Pattern in Java
-Let's introduce our file writer class.
+The Execute Around Pattern is a design pattern that is widely used in Java programming to manage resource allocation and deallocation. It ensures that important setup and cleanup operations are performed reliably around a core business operation. This pattern is particularly useful for resource management, such as handling files, databases, or network connections in Java applications.
+
+A class needs to be provided for writing text strings to files. To make it easy for the user, the service class opens and closes the file automatically. The user only has to specify what is written into which file.
+
+`SimpleFileWriter` class implements the Execute Around idiom. It takes `FileWriterAction` as a constructor argument allowing the user to specify what gets written into the file.
```java
+
@FunctionalInterface
public interface FileWriterAction {
-
- void writeFile(FileWriter writer) throws IOException;
-
+ void writeFile(FileWriter writer) throws IOException;
}
+@Slf4j
public class SimpleFileWriter {
-
- public SimpleFileWriter(String filename, FileWriterAction action) throws IOException {
- try (var writer = new FileWriter(filename)) {
- action.writeFile(writer);
+ public SimpleFileWriter(String filename, FileWriterAction action) throws IOException {
+ LOGGER.info("Opening the file");
+ try (var writer = new FileWriter(filename)) {
+ LOGGER.info("Executing the action");
+ action.writeFile(writer);
+ LOGGER.info("Closing the file");
+ }
}
- }
}
```
-To utilize the file writer the following code is needed.
+The following code demonstrates how `SimpleFileWriter` is used. `Scanner` is used to print the file contents after the writing finishes.
```java
- FileWriterAction writeHello = writer -> {
- writer.write("Hello");
- writer.append(" ");
- writer.append("there!");
- };
+ public static void main(String[] args) throws IOException {
+
+ // create the file writer and execute the custom action
+ FileWriterAction writeHello = writer -> writer.write("Gandalf was here");
new SimpleFileWriter("testfile.txt", writeHello);
+
+ // print the file contents
+ try (var scanner = new Scanner(new File("testfile.txt"))) {
+ while (scanner.hasNextLine()) {
+ LOGGER.info(scanner.nextLine());
+ }
+ }
+}
```
-## Class diagram
+Here's the console output.
+
+```
+21:18:07.185 [main] INFO com.iluwatar.execute.around.SimpleFileWriter - Opening the file
+21:18:07.188 [main] INFO com.iluwatar.execute.around.SimpleFileWriter - Executing the action
+21:18:07.189 [main] INFO com.iluwatar.execute.around.SimpleFileWriter - Closing the file
+21:18:07.199 [main] INFO com.iluwatar.execute.around.App - Gandalf was here
+```
+
+## When to Use the Execute Around Pattern in Java
+
+When to use the Execute Around Pattern in Java:
+
+* Useful in scenarios requiring repetitive setup and cleanup activities, particularly in resource management (e.g., files, network connections, database sessions).
+* Ideal for ensuring proper resource handling and cleanup in the face of exceptions, ensuring resources do not leak.
+* Suitable in any Java application where the same preparation and finalization steps are executed around varying core functionalities.
+
+## Real-World Applications of Execute Around Pattern in Java
+
+In real-world Java applications, the Execute Around Pattern is applied in these scenarios:
+
+* Java's try-with-resources statement, which ensures that resources are closed after execution regardless of whether an exception was thrown.
+* Frameworks like Spring for managing database transactions, where predefined cleanup or rollback operations are performed depending on the execution outcome.
+
+## Benefits and Trade-offs of Execute Around Pattern
+
+Implementing the Execute Around Pattern in Java offers several benefits and trade-offs.
+
+Benefits:
+
+* Reduces boilerplate code by abstracting routine setup and cleanup tasks.
+* Increases code clarity and maintainability by separating business logic from resource management.
+* Ensures robustness by automatically handling resource cleanup, even in error situations.
-
+Trade-offs:
-## Applicability
+* Introduces additional abstraction layers, which might increase complexity and obscure control flow for some developers.
+* May require more sophisticated understanding of closures and functional interfaces in Java.
-Use the Execute Around idiom when
+## Related Java Design Patterns
-* You use an API that requires methods to be called in pairs such as open/close or
-allocate/deallocate.
+* [Template Method](https://java-design-patterns.com/patterns/template-method/): Similar in concept but differs in that it uses inheritance and abstract classes, while Execute Around typically uses interfaces and lambdas.
+* [Decorator](https://java-design-patterns.com/patterns/decorator/): Shares the concept of adding functionality around a core component; can be extended to wrap additional behaviors dynamically.
-## Credits
+## References and Credits
-* [Functional Programming in Java: Harnessing the Power of Java 8 Lambda Expressions](https://www.amazon.com/gp/product/1937785467/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=1937785467&linkCode=as2&tag=javadesignpat-20&linkId=7e4e2fb7a141631491534255252fd08b)
+* [Effective Java](https://amzn.to/4aDdWbs)
+* [Java Design Patterns: A Hands-On Experience with Real-World Examples](https://amzn.to/3vUGApm)
+* [Functional Programming in Java](https://amzn.to/3JUIc5Q)
diff --git a/execute-around/etc/execute-around-flowchart.png b/execute-around/etc/execute-around-flowchart.png
new file mode 100644
index 000000000000..3a9048585f62
Binary files /dev/null and b/execute-around/etc/execute-around-flowchart.png differ
diff --git a/execute-around/pom.xml b/execute-around/pom.xml
index db8aed60c7d8..83a9372b4fe3 100644
--- a/execute-around/pom.xml
+++ b/execute-around/pom.xml
@@ -1,8 +1,10 @@
{}", numbers);
+
+ final List requests =
+ numbers.stream().map(SquareNumberRequest::new).toList();
+
+ var consumer = new Consumer(0L);
+
+ // Pass the request and the consumer to fanOutFanIn or sometimes referred as Orchestrator
+ // function
+ final Long sumOfSquaredNumbers = FanOutFanIn.fanOutFanIn(requests, consumer);
+
+ LOGGER.info("Sum of all squared numbers --> {}", sumOfSquaredNumbers);
+}
+```
+
+Running the example produces the following console output.
+
+```
+06:52:04.622 [main] INFO com.iluwatar.fanout.fanin.App -- Numbers to be squared and get sum --> [1, 3, 4, 7, 8]
+06:52:11.465 [main] INFO com.iluwatar.fanout.fanin.App -- Sum of all squared numbers --> 139
+```
+
+## When to Use the Fan-Out/Fan-In Pattern in Java
+
+The Fan-Out/Fan-In design pattern in Java is appropriate in scenarios where tasks can be broken down and executed in parallel, especially suitable for data processing, batch processing, and situations requiring aggregation of results from various sources.
+
+## Fan-Out/Fan-In Pattern Java Tutorials
+
+* [Fan-out/fan-in scenario in Durable Functions - Cloud backup example (Microsoft)](https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-cloud-backup)
+* [Understanding Azure Durable Functions - Part 8: The Fan Out/Fan In Pattern (Don't Code Tired)](http://dontcodetired.com/blog/post/Understanding-Azure-Durable-Functions-Part-8-The-Fan-OutFan-In-Pattern)
+* [Understanding the Fan-Out/Fan-In API Integration Pattern (DZone)](https://dzone.com/articles/understanding-the-fan-out-fan-in-api-integration-p)
+
+## Real-World Applications of Fan-Out/Fan-In Pattern in Java
+
+* The Fan-Out/Fan-In pattern in Java is widely used in large-scale data processing applications.
+* Services requiring aggregation from multiple sources before delivering a response, such as in distributed caching or load balancing systems.
+
+## Benefits and Trade-offs of Fan-Out/Fan-In Pattern
+
+Benefits:
+
+* Enhances performance by parallel processing.
+* Increases responsiveness of systems.
+* Efficient utilization of multi-core processor architectures.
-## Applicability
+Trade-offs:
-Use this pattern when you can divide the workload into multiple chunks that can be dealt with separately.
+* Increased complexity in error handling.
+* Potential for increased overhead due to task synchronization and result aggregation.
+* Dependency on the underlying infrastructure's ability to support concurrent execution.
-## Related patterns
+## Related Java Design Patterns
-* [Aggregator Microservices](https://java-design-patterns.com/patterns/aggregator-microservices/)
-* [API Gateway](https://java-design-patterns.com/patterns/api-gateway/)
+* MapReduce: Similar to Fan-Out/Fan-In, MapReduce also involves distributing tasks across a number of workers (map) and aggregating the results (reduce), which is particularly useful for processing large data sets.
+* [Command](https://java-design-patterns.com/patterns/command/): Command Pattern facilitates the decoupling of the sender and the receiver, akin to how Fan-Out/Fan-In decouples task submission from task processing.
+* [Producer-Consumer](https://java-design-patterns.com/patterns/producer-consumer/): Works synergistically with Fan-Out/Fan-In by organizing task execution where producers distribute tasks that are processed by multiple consumers, and results are then combined, enhancing throughput and efficiency in data processing.
-## Credits
+## References and Credits
-* [Understanding Azure Durable Functions - Part 8: The Fan Out/Fan In Pattern](http://dontcodetired.com/blog/post/Understanding-Azure-Durable-Functions-Part-8-The-Fan-OutFan-In-Pattern)
-* [Fan-out/fan-in scenario in Durable Functions - Cloud backup example](https://docs.microsoft.com/en-us/azure/azure-functions/durable/durable-functions-cloud-backup)
-* [Understanding the Fan-Out/Fan-In API Integration Pattern](https://dzone.com/articles/understanding-the-fan-out-fan-in-api-integration-p)
\ No newline at end of file
+* [Java Concurrency in Practice](https://amzn.to/3vXytsb)
+* [Patterns of Enterprise Application Architecture](https://amzn.to/49QQcPD)
diff --git a/fanout-fanin/etc/fan-out-fan-in-flowchart.png b/fanout-fanin/etc/fan-out-fan-in-flowchart.png
new file mode 100644
index 000000000000..cf726696cd76
Binary files /dev/null and b/fanout-fanin/etc/fan-out-fan-in-flowchart.png differ
diff --git a/fanout-fanin/pom.xml b/fanout-fanin/pom.xml
index 11ce561780c2..1052918ee83f 100644
--- a/fanout-fanin/pom.xml
+++ b/fanout-fanin/pom.xml
@@ -1,9 +1,10 @@
@@ -36,6 +34,14 @@
4.0.0
fanout-fanin
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
org.junit.jupiter
junit-jupiter-engine
diff --git a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/App.java b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/App.java
index cc70ff141156..7a34c22149b5 100644
--- a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/App.java
+++ b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/App.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,17 +22,12 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.fanout.fanin;
import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
-
import lombok.extern.slf4j.Slf4j;
-
-
/**
* FanOut/FanIn pattern is a concurrency pattern that refers to executing multiple instances of the
* activity function concurrently. The "fan out" part is essentially splitting the data into
@@ -61,7 +58,7 @@ public static void main(String[] args) {
LOGGER.info("Numbers to be squared and get sum --> {}", numbers);
final List requests =
- numbers.stream().map(SquareNumberRequest::new).collect(Collectors.toList());
+ numbers.stream().map(SquareNumberRequest::new).toList();
var consumer = new Consumer(0L);
diff --git a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/Consumer.java b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/Consumer.java
index 79e3445dc668..95b7c663abe2 100644
--- a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/Consumer.java
+++ b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/Consumer.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,17 +22,13 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.fanout.fanin;
import java.util.concurrent.atomic.AtomicLong;
-
import lombok.Getter;
-
-
/**
- * Consumer or callback class that will be called everytime a request is complete This will
+ * Consumer or callback class that will be called every time a request is complete This will
* aggregate individual result to form a final result.
*/
@Getter
diff --git a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/FanOutFanIn.java b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/FanOutFanIn.java
index 71af5ae9fdb5..9239e03f46ee 100644
--- a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/FanOutFanIn.java
+++ b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/FanOutFanIn.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,17 +22,15 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.fanout.fanin;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
-import java.util.stream.Collectors;
/**
- * FanOutFanIn class processes long running requests, when any of the processes gets over, result is
+ * FanOutFanIn class processes long-running requests, when any of the processes gets over, result is
* passed over to the consumer or the callback function. Consumer will aggregate the results as they
* keep on completing.
*/
@@ -38,6 +38,7 @@ public class FanOutFanIn {
/**
* the main fanOutFanIn function or orchestrator function.
+ *
* @param requests List of numbers that need to be squared and summed up
* @param consumer Takes in the squared number from {@link SquareNumberRequest} and sums it up
* @return Aggregated sum of all squared numbers.
@@ -53,7 +54,7 @@ public static Long fanOutFanIn(
.map(
request ->
CompletableFuture.runAsync(() -> request.delayedSquaring(consumer), service))
- .collect(Collectors.toList());
+ .toList();
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();
diff --git a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/SquareNumberRequest.java b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/SquareNumberRequest.java
index 73db6ca870c8..7f12e842031e 100644
--- a/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/SquareNumberRequest.java
+++ b/fanout-fanin/src/main/java/com/iluwatar/fanout/fanin/SquareNumberRequest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,16 +22,14 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.fanout.fanin;
import java.security.SecureRandom;
-
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
/**
- * Squares the number with a little timeout to give impression of long running process that return
+ * Squares the number with a little timeout to give impression of long-running process that return
* at different times.
*/
@Slf4j
@@ -39,10 +39,11 @@ public class SquareNumberRequest {
private final Long number;
/**
- * Squares the number with a little timeout to give impression of long running process that return
+ * Squares the number with a little timeout to give impression of long-running process that return
* at different times.
+ *
* @param consumer callback class that takes the result after the delay.
- * */
+ */
public void delayedSquaring(final Consumer consumer) {
var minTimeOut = 5000L;
diff --git a/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/AppTest.java b/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/AppTest.java
index d221066e2086..0fe64096fddb 100644
--- a/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/AppTest.java
+++ b/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/AppTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,17 +22,16 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.fanout.fanin;
-import org.junit.jupiter.api.Test;
-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import org.junit.jupiter.api.Test;
+
class AppTest {
- @Test
- void shouldLaunchApp() {
- assertDoesNotThrow(() -> App.main(new String[]{}));
- }
+ @Test
+ void shouldLaunchApp() {
+ assertDoesNotThrow(() -> App.main(new String[] {}));
+ }
}
diff --git a/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/FanOutFanInTest.java b/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/FanOutFanInTest.java
index 4273d756ac86..55260c36aa3c 100644
--- a/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/FanOutFanInTest.java
+++ b/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/FanOutFanInTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -22,12 +24,10 @@
*/
package com.iluwatar.fanout.fanin;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.Test;
-
import java.util.Arrays;
import java.util.List;
-import java.util.stream.Collectors;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
class FanOutFanInTest {
@@ -36,7 +36,7 @@ void fanOutFanInTest() {
final List numbers = Arrays.asList(1L, 3L, 4L, 7L, 8L);
final List requests =
- numbers.stream().map(SquareNumberRequest::new).collect(Collectors.toList());
+ numbers.stream().map(SquareNumberRequest::new).toList();
final Consumer consumer = new Consumer(0L);
diff --git a/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/SquareNumberRequestTest.java b/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/SquareNumberRequestTest.java
index 86560b38bc71..738e2fda761d 100644
--- a/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/SquareNumberRequestTest.java
+++ b/fanout-fanin/src/test/java/com/iluwatar/fanout/fanin/SquareNumberRequestTest.java
@@ -1,6 +1,8 @@
/*
+ * 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-2021 Ilkka Seppälä
+ * 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
@@ -20,7 +22,6 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
-
package com.iluwatar.fanout.fanin;
import org.junit.jupiter.api.Assertions;
diff --git a/feature-toggle/README.md b/feature-toggle/README.md
index 2dd53b225572..dc2d913a86ed 100644
--- a/feature-toggle/README.md
+++ b/feature-toggle/README.md
@@ -1,33 +1,151 @@
---
-layout: pattern
-title: Feature Toggle
-folder: feature-toggle
-permalink: /patterns/feature-toggle/
-categories: Behavioral
+title: "Feature Toggle Pattern in Java: Managing Features in Production Seamlessly"
+shortTitle: Feature Toggle
+description: "Learn how to implement the Feature Toggle design pattern in Java. This guide covers dynamic feature management, benefits, use cases, and practical examples to help you enhance your software development process."
+category: Behavioral
language: en
-tags:
- - Extensibility
+tag:
+ - Decoupling
+ - Extensibility
+ - Feature management
+ - Scalability
---
## Also known as
-Feature Flag
-## Intent
-Used to switch code execution paths based on properties or groupings. Allowing new features to be released, tested
-and rolled out. Allowing switching back to the older feature quickly if needed. It should be noted that this pattern,
-can easily introduce code complexity. There is also cause for concern that the old feature that the toggle is eventually
-going to phase out is never removed, causing redundant code smells and increased maintainability.
+* Feature Flag
+* Feature Switch
-## Class diagram
-
+## Intent of Feature Toggle Design Pattern
-## Applicability
-Use the Feature Toggle pattern when
+To enable or disable features in a software application dynamically without deploying new code.
-* Giving different features to different users.
+## Detailed Explanation of Feature Toggle Pattern with Real-World Examples
+
+Real-world Example
+
+> A real-world example of the Feature Toggle pattern is Netflix's rollout of new user interface features. When Netflix decides to introduce a new feature, such as a redesigned homepage layout or a new recommendation algorithm, they use feature toggles to control the release. Initially, the new feature is toggled off for most users, allowing only a small group of users (e.g., beta testers) to experience and provide feedback on the feature. Based on the feedback and performance metrics, Netflix can quickly toggle the feature on for a broader audience or turn it off if issues are detected, all without redeploying the application. This approach allows Netflix to continuously innovate and improve their platform while minimizing risk and ensuring a stable user experience.
+
+In plain words
+
+> The Feature Toggle design pattern in Java allows developers to introduce new features gradually instead of deploying them all at once, facilitating better dynamic feature management.
+
+Wikipedia says
+
+> A feature toggle in software development provides an alternative to maintaining multiple feature branches in source code. A condition within the code enables or disables a feature during runtime. In agile settings the toggle is used in production, to switch on the feature on demand, for some or all the users.
+
+Flowchart
+
+
+
+## Programmatic Example of Feature Toggle Pattern in Java
+
+This Java code example demonstrates how to display a feature when it is enabled by the developer and the user is a Premium member of the application. This approach is useful for managing subscription-locked features.
+
+The Feature Toggle pattern enables the seamless activation or deactivation of entire code executions. This allows features to be managed dynamically based on user information or configuration properties.
+
+Key Components:
+
+1. `PropertiesFeatureToggleVersion`: This class uses properties to control the feature toggle. The properties determine whether the enhanced version of the welcome message, which is personalized, is turned on or off.
+
+2. `TieredFeatureToggleVersion`: This class uses user information to control the feature toggle. The feature of the personalized welcome message is dependent on the user group the user is in.
+
+3. `User`: This class represents the user of the application.
+
+4. `UserGroup`: This class represents the group the user belongs to.
+
+```java
+public static void main(String[] args) {
+
+ // Demonstrates the PropertiesFeatureToggleVersion running with properties
+ // that set the feature toggle to enabled.
+
+ final var properties = new Properties();
+ properties.put("enhancedWelcome", true);
+ var service = new PropertiesFeatureToggleVersion(properties);
+ final var welcomeMessage = service.getWelcomeMessage(new User("Jamie No Code"));
+ LOGGER.info(welcomeMessage);
+
+ // Demonstrates the PropertiesFeatureToggleVersion running with properties
+ // that set the feature toggle to disabled. Note the difference in the printed welcome message
+ // where the username is not included.
+
+ final var turnedOff = new Properties();
+ turnedOff.put("enhancedWelcome", false);
+ var turnedOffService = new PropertiesFeatureToggleVersion(turnedOff);
+ final var welcomeMessageturnedOff =
+ turnedOffService.getWelcomeMessage(new User("Jamie No Code"));
+ LOGGER.info(welcomeMessageturnedOff);
+
+ // Demonstrates the TieredFeatureToggleVersion setup with
+ // two users: one on the free tier and the other on the paid tier. When the
+ // Service#getWelcomeMessage(User) method is called with the paid user, the welcome
+ // message includes their username. In contrast, calling the same service with the free tier user results
+ // in a more generic welcome message without the username.
+
+ var service2 = new TieredFeatureToggleVersion();
+
+ final var paidUser = new User("Jamie Coder");
+ final var freeUser = new User("Alan Defect");
+
+ UserGroup.addUserToPaidGroup(paidUser);
+ UserGroup.addUserToFreeGroup(freeUser);
+
+ final var welcomeMessagePaidUser = service2.getWelcomeMessage(paidUser);
+ final var welcomeMessageFreeUser = service2.getWelcomeMessage(freeUser);
+ LOGGER.info(welcomeMessageFreeUser);
+ LOGGER.info(welcomeMessagePaidUser);
+}
+```
+
+Running the example produces the following output.
+
+```
+07:31:50.802 [main] INFO com.iluwatar.featuretoggle.App -- Welcome Jamie No Code. You're using the enhanced welcome message.
+07:31:50.804 [main] INFO com.iluwatar.featuretoggle.App -- Welcome to the application.
+07:31:50.804 [main] INFO com.iluwatar.featuretoggle.App -- I suppose you can use this software.
+07:31:50.804 [main] INFO com.iluwatar.featuretoggle.App -- You're amazing Jamie Coder. Thanks for paying for this awesome software.
+```
+
+## When to Use the Feature Toggle Pattern in Java
+
+Use the Feature Toggle Pattern in Java when:
+
+* Dynamic feature management to different users and groups.
* Rolling out a new feature incrementally.
* Switching between development and production environments.
+* Quickly disable problematic features
+* External management of feature deployment
+* Ability to maintain multiple version releases of a feature
+* 'Hidden' deployment, releasing a feature in code for designated testing but not publicly making it available
+
+## Real-World Applications of Feature Toggle Pattern in Java
+
+* Many web development platforms utilize the Feature Toggle design pattern to gradually roll out new features to users, ensuring stability and effective dynamic feature management.
+* Enterprise applications use feature toggles to enable or disable features during runtime to cater to different market needs.
+
+## Benefits and Trade-offs of Feature Toggle Pattern
+
+Benefits:
+
+* Facilitates A/B testing and canary releases.
+* Allows for quicker rollback and minimal risk deployments.
+* Enables conditional feature execution without redeploying the application.
+
+Trade-offs:
+
+* Code complexity is increased.
+* Testing of multiple states is harder and more time-consuming.
+* Potential for technical debt if toggles remain in the code longer than necessary.
+* Risk of toggle misconfiguration leading to unexpected behavior.
+
+## Related Java Design Patterns
+
+* [Strategy](https://java-design-patterns.com/patterns/strategy/): Both patterns allow changing the behavior of software at runtime. The Feature Toggle changes features dynamically, while the Strategy allows switching algorithms or strategies.
+* [Observer](https://java-design-patterns.com/patterns/observer/): Useful for implementing feature toggles by notifying components of feature state changes, which allows dynamic feature modification without restarts.
-## Credits
+## References and Credits
-* [Martin Fowler 29 October 2010 (2010-10-29).](http://martinfowler.com/bliki/FeatureToggle.html)
+* [Continuous Delivery: Reliable Software Releases through Build, Test, and Deployment Automation](https://amzn.to/4488ESM)
+* [Release It! Design and Deploy Production-Ready Software](https://amzn.to/3UoeJY4)
+* [Feature Toggle (Martin Fowler)](http://martinfowler.com/bliki/FeatureToggle.html)
diff --git a/feature-toggle/etc/feature-toggle-flowchart.png b/feature-toggle/etc/feature-toggle-flowchart.png
new file mode 100644
index 000000000000..8a62b1b59309
Binary files /dev/null and b/feature-toggle/etc/feature-toggle-flowchart.png differ
diff --git a/feature-toggle/pom.xml b/feature-toggle/pom.xml
index c728bef86e72..c21080827a57 100644
--- a/feature-toggle/pom.xml
+++ b/feature-toggle/pom.xml
@@ -1,8 +1,10 @@
+
+
+ java-design-patterns
+ com.iluwatar
+ 1.26.0-SNAPSHOT
+
+ 4.0.0
+ fluent-interface
+
+
+ org.slf4j
+ slf4j-api
+
+
+ ch.qos.logback
+ logback-classic
+
+
+ org.junit.jupiter
+ junit-jupiter-engine
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
+
+