diff --git a/FiniteStateMachine/etc/FiniteStateMachine.urm.puml b/FiniteStateMachine/etc/FiniteStateMachine.urm.puml
new file mode 100644
index 000000000000..d665b26a8900
--- /dev/null
+++ b/FiniteStateMachine/etc/FiniteStateMachine.urm.puml
@@ -0,0 +1,34 @@
+@startuml
+package com.iluwatar.trafficlight {
+ class GreenLightState {
+ + GreenLightState()
+ + handleEvent(context : TrafficLightContext)
+ }
+ class RedLightState {
+ + RedLightState()
+ + handleEvent(context : TrafficLightContext)
+ }
+ class TrafficLightContext {
+ - currentState : TrafficLightState
+ + TrafficLightContext(initialState : TrafficLightState)
+ + getCurrentState() : TrafficLightState
+ + handleEvent()
+ + setState(newState : TrafficLightState)
+ }
+ class TrafficLightFsm {
+ + TrafficLightFsm()
+ + main(args : String[]) {static}
+ }
+ interface TrafficLightState {
+ + handleEvent(TrafficLightContext) {abstract}
+ }
+ class YellowLightState {
+ + YellowLightState()
+ + handleEvent(context : TrafficLightContext)
+ }
+}
+TrafficLightContext --> "-currentState" TrafficLightState
+GreenLightState ..|> TrafficLightState
+RedLightState ..|> TrafficLightState
+YellowLightState ..|> TrafficLightState
+@enduml
\ No newline at end of file
diff --git a/FiniteStateMachine/pom.xml b/FiniteStateMachine/pom.xml
new file mode 100644
index 000000000000..59f8ab32f006
--- /dev/null
+++ b/FiniteStateMachine/pom.xml
@@ -0,0 +1,31 @@
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.26.0-SNAPSHOT
+
+
+ FiniteStateMachine
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+ org.junit.jupiter
+ junit-jupiter-api
+ test
+
+
+ org.mockito
+ mockito-core
+ test
+
+
+
+
\ No newline at end of file
diff --git a/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/GreenLightState.java b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/GreenLightState.java
new file mode 100644
index 000000000000..29573fa20253
--- /dev/null
+++ b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/GreenLightState.java
@@ -0,0 +1,23 @@
+package com.iluwatar.trafficlight;
+
+/**
+ * Concrete state representing the Green Light.
+ */
+public class GreenLightState implements TrafficLightState {
+
+ /**
+ * Handles the event for the Green Light.
+ * This method transitions the traffic light to the Yellow state after the Green state.
+ *
+ * @param context The traffic light context to manage the state transitions.
+ */
+ @Override
+ public void handleEvent(TrafficLightContext context) {
+ System.out.println("Green Light: Go!");
+ // Transition to the Yellow light state
+ context.setState(new YellowLightState());
+ }
+}
+
+
+
diff --git a/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/RedLightState.java b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/RedLightState.java
new file mode 100644
index 000000000000..c7514b468555
--- /dev/null
+++ b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/RedLightState.java
@@ -0,0 +1,14 @@
+package com.iluwatar.trafficlight;
+
+/**
+ * Concrete state representing the Red Light.
+ */
+public class RedLightState implements TrafficLightState {
+ @Override
+ public void handleEvent(TrafficLightContext context) {
+ System.out.println("Red Light: Stop!");
+ context.setState(new GreenLightState());
+ }
+}
+
+
diff --git a/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightContext.java b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightContext.java
new file mode 100644
index 000000000000..fe5361ab1357
--- /dev/null
+++ b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightContext.java
@@ -0,0 +1,43 @@
+package com.iluwatar.trafficlight;
+
+/**
+ * Context class for managing the current state and transitions.
+ */
+public class TrafficLightContext {
+ private TrafficLightState currentState;
+
+ /**
+ * Initializes the context with the given initial state.
+ *
+ * @param initialState the initial state of the traffic light
+ */
+ public TrafficLightContext(TrafficLightState initialState) {
+ this.currentState = initialState;
+ }
+
+ /**
+ * Updates the current state of the traffic light.
+ *
+ * @param newState the new state to transition to
+ */
+ public void setState(TrafficLightState newState) {
+ this.currentState = newState;
+ }
+
+ /**
+ * Handles the current state's event and transitions to the next state.
+ */
+ public void handleEvent() {
+ currentState.handleEvent(this);
+ }
+
+ /**
+ * Gets the current state of the traffic light.
+ * This can be useful for testing purposes.
+ */
+ public TrafficLightState getCurrentState() {
+ return currentState;
+ }
+}
+
+
diff --git a/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightFsm.java b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightFsm.java
new file mode 100644
index 000000000000..9c3dd0c6d7d0
--- /dev/null
+++ b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightFsm.java
@@ -0,0 +1,27 @@
+package com.iluwatar.trafficlight;
+
+/**
+ * Simulates a traffic light system using a Finite State Machine (FSM).
+ */
+public class TrafficLightFsm {
+
+ /**
+ * Runs the traffic light simulation.
+ *
+ * @param args command-line arguments (not used here)
+ */
+ public static void main(String[] args) {
+ // Start with the Red light
+ TrafficLightContext trafficLight = new TrafficLightContext(new RedLightState());
+
+ // Cycle through the traffic light states
+ for (int i = 0; i < 6; i++) {
+ trafficLight.handleEvent();
+ }
+ }
+}
+
+
+
+
+
diff --git a/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightState.java b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightState.java
new file mode 100644
index 000000000000..bb06e45a2453
--- /dev/null
+++ b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/TrafficLightState.java
@@ -0,0 +1,19 @@
+package com.iluwatar.trafficlight;
+
+/**
+ * State interface for traffic light states.
+ */
+public interface TrafficLightState {
+
+ /**
+ * Handles the transition to the next state based on the current state.
+ *
+ * @param context the context object that manages the traffic light's state
+ */
+ void handleEvent(TrafficLightContext context);
+}
+
+
+
+
+
diff --git a/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/YellowLightState.java b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/YellowLightState.java
new file mode 100644
index 000000000000..07306f600ee9
--- /dev/null
+++ b/FiniteStateMachine/src/main/java/com/iluwatar/trafficlight/YellowLightState.java
@@ -0,0 +1,13 @@
+package com.iluwatar.trafficlight;
+
+/**
+ * Concrete state representing the Yellow Light.
+ */
+public class YellowLightState implements TrafficLightState {
+ @Override
+ public void handleEvent(TrafficLightContext context) {
+ System.out.println("Yellow Light: Caution!");
+ context.setState(new RedLightState());
+ }
+}
+
diff --git a/FiniteStateMachine/src/test/java/com/iluwater/TrafficLightTest.java b/FiniteStateMachine/src/test/java/com/iluwater/TrafficLightTest.java
new file mode 100644
index 000000000000..4f2d3d3253a5
--- /dev/null
+++ b/FiniteStateMachine/src/test/java/com/iluwater/TrafficLightTest.java
@@ -0,0 +1,176 @@
+package com.iluwater;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import com.iluwatar.trafficlight.GreenLightState;
+import com.iluwatar.trafficlight.RedLightState;
+import com.iluwatar.trafficlight.TrafficLightContext;
+import com.iluwatar.trafficlight.TrafficLightFsm;
+import com.iluwatar.trafficlight.YellowLightState;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * Test class for the Traffic Light FSM.
+ */
+public class TrafficLightTest {
+
+ private TrafficLightContext context;
+
+ @BeforeEach
+ void setUp() {
+ // Start with the Red Light state
+ context = new TrafficLightContext(new RedLightState());
+ }
+
+ @Test
+ void testInitialState() {
+ assertTrue(context.getCurrentState() instanceof RedLightState, "Initial state should be RedLightState.");
+ }
+
+ @Test
+ void testRedToGreenTransition() {
+ context.handleEvent();
+ assertTrue(context.getCurrentState() instanceof GreenLightState, "Red Light should transition to Green Light.");
+ }
+
+ @Test
+ void testGreenToYellowTransition() {
+ context.setState(new GreenLightState());
+ context.handleEvent();
+ assertTrue(context.getCurrentState() instanceof YellowLightState, "Green Light should transition to Yellow Light.");
+ }
+
+ @Test
+ void testYellowToRedTransition() {
+ context.setState(new YellowLightState());
+ context.handleEvent();
+ assertTrue(context.getCurrentState() instanceof RedLightState, "Yellow Light should transition to Red Light.");
+ }
+
+ @Test
+ void testFullCycle() {
+ context.handleEvent(); // Red -> Green
+ assertTrue(context.getCurrentState() instanceof GreenLightState);
+
+ context.handleEvent(); // Green -> Yellow
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+
+ context.handleEvent(); // Yellow -> Red
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+ }
+
+ // Test invalid state transition
+ @Test
+ void testInvalidStateTransition() {
+ context.setState(new RedLightState());
+ try {
+ // This should fail, as it doesn't make sense for Red to handle an event again.
+ context.handleEvent();
+ } catch (IllegalStateException e) {
+ assertTrue(true, "Handled invalid state transition.");
+ }
+ }
+
+ // Test state reset
+ @Test
+ void testStateReset() {
+ context.setState(new YellowLightState());
+ context.handleEvent(); // Yellow -> Red
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+
+ context.setState(new GreenLightState());
+ context.handleEvent(); // Green -> Yellow
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+
+ context.setState(new RedLightState());
+ context.handleEvent(); // Red -> Green
+ assertTrue(context.getCurrentState() instanceof GreenLightState);
+ }
+
+ // Test manually setting the state
+ @Test
+ void testManualStateSet() {
+ context.setState(new GreenLightState());
+ assertTrue(context.getCurrentState() instanceof GreenLightState);
+
+ context.setState(new YellowLightState());
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+
+ context.setState(new RedLightState());
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+ }
+
+ // Additional tests for edge cases
+
+ // Test if state is correctly set in the middle of a cycle
+ @Test
+ void testMidCycleStateSet() {
+ context.handleEvent(); // Red -> Green
+ assertTrue(context.getCurrentState() instanceof GreenLightState);
+
+ // Set state manually in the middle of the cycle
+ context.setState(new YellowLightState());
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+
+ context.handleEvent(); // Yellow -> Red
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+ }
+
+ // Test if context properly resets after complete cycle
+ @Test
+ void testContextResetAfterFullCycle() {
+ context.handleEvent(); // Red -> Green
+ context.handleEvent(); // Green -> Yellow
+ context.handleEvent(); // Yellow -> Red
+
+ // Reset and verify again
+ context.setState(new GreenLightState());
+ assertTrue(context.getCurrentState() instanceof GreenLightState);
+
+ context.handleEvent(); // Green -> Yellow
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+
+ context.handleEvent(); // Yellow -> Red
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+ }
+
+ // Test if the initial state remains unchanged after multiple cycles
+ @Test
+ void testMultipleCycles() {
+ context.handleEvent(); // Red -> Green
+ context.handleEvent(); // Green -> Yellow
+ context.handleEvent(); // Yellow -> Red
+
+ // Perform another full cycle and ensure the state remains correct
+ context.handleEvent(); // Red -> Green
+ assertTrue(context.getCurrentState() instanceof GreenLightState);
+
+ context.handleEvent(); // Green -> Yellow
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+
+ context.handleEvent(); // Yellow -> Red
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+ }
+
+ // Test if state changes properly when reset in the middle of cycles
+ @Test
+ void testResetMidCycle() {
+ context.handleEvent(); // Red -> Green
+ context.setState(new YellowLightState()); // Manually set Yellow
+ assertTrue(context.getCurrentState() instanceof YellowLightState);
+ context.handleEvent(); // Yellow -> Red
+ assertTrue(context.getCurrentState() instanceof RedLightState);
+ }
+
+ // Test for traffic light FSM running through multiple cycles
+ @Test
+ void testTrafficLightFsmCycles() {
+ // Start the FSM and cycle through multiple events
+ TrafficLightFsm.main(new String[0]); // This will run the logic in the main function of TrafficLightFsm
+ }
+}
+
+
+
+
diff --git a/fluent-interface/etc/fluent-interface.urm.puml b/fluent-interface/etc/fluent-interface.urm.puml
new file mode 100644
index 000000000000..d343a478bff0
--- /dev/null
+++ b/fluent-interface/etc/fluent-interface.urm.puml
@@ -0,0 +1,72 @@
+@startuml
+package com.iluwatar.fluentinterface.fluentiterable.simple {
+ class SimpleFluentIterable {
+ - iterable : Iterable
+ + SimpleFluentIterable(iterable : Iterable)
+ + asList() : List
+ + filter(predicate : Predicate super E>) : FluentIterable
+ + first() : Optional
+ + first(count : int) : FluentIterable
+ + forEach(action : Consumer super E>)
+ + from(iterable : Iterable) : FluentIterable {static}
+ + fromCopyOf(iterable : Iterable) : FluentIterable {static}
+ + getRemainingElementsCount() : int
+ + iterator() : Iterator
+ + last() : Optional
+ + last(count : int) : FluentIterable
+ + map(function : Function super E, T>) : FluentIterable
+ + spliterator() : Spliterator
+ + toList(iterator : Iterator) : List {static}
+ }
+}
+package com.iluwatar.fluentinterface.app {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ - negatives() : Predicate super Integer> {static}
+ - positives() : Predicate super Integer> {static}
+ - prettyPrint(delimiter : String, prefix : String, iterable : Iterable) {static}
+ - prettyPrint(prefix : String, iterable : Iterable) {static}
+ - transformToString() : Function {static}
+ }
+}
+package com.iluwatar.fluentinterface.fluentiterable.lazy {
+ abstract class DecoratingIterator {
+ # fromIterator : Iterator
+ - next : E
+ + DecoratingIterator(fromIterator : Iterator)
+ + computeNext() : E {abstract}
+ + hasNext() : boolean
+ + next() : E
+ }
+ class LazyFluentIterable {
+ - iterable : Iterable
+ # LazyFluentIterable()
+ + LazyFluentIterable(iterable : Iterable)
+ + asList() : List
+ + filter(predicate : Predicate super E>) : FluentIterable
+ + first() : Optional
+ + first(count : int) : FluentIterable
+ + from(iterable : Iterable) : FluentIterable {static}
+ + iterator() : Iterator
+ + last() : Optional
+ + last(count : int) : FluentIterable
+ + map(function : Function super E, T>) : FluentIterable
+ }
+}
+package com.iluwatar.fluentinterface.fluentiterable {
+ interface FluentIterable {
+ + asList() : List {abstract}
+ + copyToList(iterable : Iterable) : List {static}
+ + filter(Predicate super E>) : FluentIterable {abstract}
+ + first() : Optional {abstract}
+ + first(int) : FluentIterable {abstract}
+ + last() : Optional {abstract}
+ + last(int) : FluentIterable {abstract}
+ + map(Function super E, T>) : FluentIterable {abstract}
+ }
+}
+LazyFluentIterable ..|> FluentIterable
+SimpleFluentIterable ..|> FluentIterable
+@enduml
\ No newline at end of file
diff --git a/function-composition/etc/function-composition.urm.puml b/function-composition/etc/function-composition.urm.puml
new file mode 100644
index 000000000000..79b2a898fd12
--- /dev/null
+++ b/function-composition/etc/function-composition.urm.puml
@@ -0,0 +1,12 @@
+@startuml
+package com.iluwatar.function.composition {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class FunctionComposer {
+ + FunctionComposer()
+ + composeFunctions(f1 : Function, f2 : Function) : Function {static}
+ }
+}
+@enduml
\ No newline at end of file
diff --git a/hexagonal-architecture/etc/hexagonal-architecture.urm.puml b/hexagonal-architecture/etc/hexagonal-architecture.urm.puml
new file mode 100644
index 000000000000..f86e734747f3
--- /dev/null
+++ b/hexagonal-architecture/etc/hexagonal-architecture.urm.puml
@@ -0,0 +1,282 @@
+@startuml
+package com.iluwatar.hexagonal.sampledata {
+ class SampleData {
+ - PLAYERS : List {static}
+ - RANDOM : SecureRandom {static}
+ + SampleData()
+ - getRandomPlayerDetails() : PlayerDetails {static}
+ + submitTickets(lotteryService : LotteryService, numTickets : int) {static}
+ }
+}
+package com.iluwatar.hexagonal.service {
+ class ConsoleLottery {
+ - LOGGER : Logger {static}
+ + ConsoleLottery()
+ + main(args : String[]) {static}
+ - printMainMenu() {static}
+ - readString(scanner : Scanner) : String {static}
+ }
+ interface LotteryConsoleService {
+ + addFundsToLotteryAccount(WireTransfers, Scanner) {abstract}
+ + checkTicket(LotteryService, Scanner) {abstract}
+ + queryLotteryAccountFunds(WireTransfers, Scanner) {abstract}
+ + submitTicket(LotteryService, Scanner) {abstract}
+ }
+ class LotteryConsoleServiceImpl {
+ - logger : Logger
+ + LotteryConsoleServiceImpl(logger : Logger)
+ + addFundsToLotteryAccount(bank : WireTransfers, scanner : Scanner)
+ + checkTicket(service : LotteryService, scanner : Scanner)
+ + queryLotteryAccountFunds(bank : WireTransfers, scanner : Scanner)
+ - readString(scanner : Scanner) : String
+ + submitTicket(service : LotteryService, scanner : Scanner)
+ }
+}
+package com.iluwatar.hexagonal.mongo {
+ class MongoConnectionPropertiesLoader {
+ - DEFAULT_HOST : String {static}
+ - DEFAULT_PORT : int {static}
+ - LOGGER : Logger {static}
+ + MongoConnectionPropertiesLoader()
+ + load() {static}
+ }
+}
+package com.iluwatar.hexagonal.domain {
+ class LotteryAdministration {
+ - notifications : LotteryEventLog
+ - repository : LotteryTicketRepository
+ - wireTransfers : WireTransfers
+ + LotteryAdministration(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers)
+ + getAllSubmittedTickets() : Map
+ + performLottery() : LotteryNumbers
+ + resetLottery()
+ }
+ class LotteryConstants {
+ + PLAYER_MAX_BALANCE : int {static}
+ + PRIZE_AMOUNT : int {static}
+ + SERVICE_BANK_ACCOUNT : String {static}
+ + SERVICE_BANK_ACCOUNT_BALANCE : int {static}
+ + TICKET_PRIZE : int {static}
+ - LotteryConstants()
+ }
+ class LotteryNumbers {
+ + MAX_NUMBER : int {static}
+ + MIN_NUMBER : int {static}
+ + NUM_NUMBERS : int {static}
+ - numbers : Set
+ - LotteryNumbers()
+ - LotteryNumbers(givenNumbers : Set)
+ # canEqual(other : Object) : boolean
+ + create(givenNumbers : Set) : LotteryNumbers {static}
+ + createRandom() : LotteryNumbers {static}
+ + equals(o : Object) : boolean
+ - generateRandomNumbers()
+ + getNumbers() : Set
+ + getNumbersAsString() : String
+ + hashCode() : int
+ + toString() : String
+ }
+ -class RandomNumberGenerator {
+ - randomIterator : OfInt
+ + RandomNumberGenerator(min : int, max : int)
+ + nextInt() : int
+ }
+ class LotteryService {
+ - notifications : LotteryEventLog
+ - repository : LotteryTicketRepository
+ - wireTransfers : WireTransfers
+ + LotteryService(repository : LotteryTicketRepository, notifications : LotteryEventLog, wireTransfers : WireTransfers)
+ + checkTicketForPrize(id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult
+ + submitTicket(ticket : LotteryTicket) : Optional
+ }
+ class LotteryTicketCheckResult {
+ - prizeAmount : int
+ - result : CheckResult
+ + LotteryTicketCheckResult(result : CheckResult)
+ + LotteryTicketCheckResult(result : CheckResult, prizeAmount : int)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getPrizeAmount() : int
+ + getResult() : CheckResult
+ + hashCode() : int
+ }
+ enum CheckResult {
+ + NO_PRIZE {static}
+ + TICKET_NOT_SUBMITTED {static}
+ + WIN_PRIZE {static}
+ + valueOf(name : String) : CheckResult {static}
+ + values() : CheckResult[] {static}
+ }
+ class LotteryTicketId {
+ - id : int
+ - numAllocated : AtomicInteger {static}
+ + LotteryTicketId()
+ + LotteryTicketId(id : int)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getId() : int
+ + hashCode() : int
+ + toString() : String
+ }
+ class LotteryUtils {
+ - LotteryUtils()
+ + checkTicketForPrize(repository : LotteryTicketRepository, id : LotteryTicketId, winningNumbers : LotteryNumbers) : LotteryTicketCheckResult {static}
+ }
+}
+package com.iluwatar.hexagonal.banking {
+ class InMemoryBank {
+ - accounts : Map {static}
+ + InMemoryBank()
+ + getFunds(bankAccount : String) : int
+ + setFunds(bankAccount : String, amount : int)
+ + transferFunds(amount : int, sourceAccount : String, destinationAccount : String) : boolean
+ }
+ class MongoBank {
+ - DEFAULT_ACCOUNTS_COLLECTION : String {static}
+ - DEFAULT_DB : String {static}
+ - accountsCollection : MongoCollection
+ - database : MongoDatabase
+ - mongoClient : MongoClient
+ + MongoBank()
+ + MongoBank(dbName : String, accountsCollectionName : String)
+ + connect()
+ + connect(dbName : String, accountsCollectionName : String)
+ + getAccountsCollection() : MongoCollection
+ + getDatabase() : MongoDatabase
+ + getFunds(bankAccount : String) : int
+ + getMongoClient() : MongoClient
+ + setFunds(bankAccount : String, amount : int)
+ + transferFunds(amount : int, sourceAccount : String, destinationAccount : String) : boolean
+ }
+ interface WireTransfers {
+ + getFunds(String) : int {abstract}
+ + setFunds(String, int) {abstract}
+ + transferFunds(int, String, String) : boolean {abstract}
+ }
+}
+package com.iluwatar.hexagonal.database {
+ class InMemoryTicketRepository {
+ - tickets : Map {static}
+ + InMemoryTicketRepository()
+ + deleteAll()
+ + findAll() : Map
+ + findById(id : LotteryTicketId) : Optional
+ + save(ticket : LotteryTicket) : Optional
+ }
+ interface LotteryTicketRepository {
+ + deleteAll() {abstract}
+ + findAll() : Map {abstract}
+ + findById(LotteryTicketId) : Optional {abstract}
+ + save(LotteryTicket) : Optional {abstract}
+ }
+ class MongoTicketRepository {
+ - DEFAULT_COUNTERS_COLLECTION : String {static}
+ - DEFAULT_DB : String {static}
+ - DEFAULT_TICKETS_COLLECTION : String {static}
+ - TICKET_ID : String {static}
+ - countersCollection : MongoCollection
+ - database : MongoDatabase
+ - mongoClient : MongoClient
+ - ticketsCollection : MongoCollection
+ + MongoTicketRepository()
+ + MongoTicketRepository(dbName : String, ticketsCollectionName : String, countersCollectionName : String)
+ + connect()
+ + connect(dbName : String, ticketsCollectionName : String, countersCollectionName : String)
+ + deleteAll()
+ - docToTicket(doc : Document) : LotteryTicket
+ + findAll() : Map
+ + findById(id : LotteryTicketId) : Optional
+ + getCountersCollection() : MongoCollection
+ + getNextId() : int
+ + getTicketsCollection() : MongoCollection
+ - initCounters()
+ + save(ticket : LotteryTicket) : Optional
+ }
+}
+package com.iluwatar.hexagonal {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+}
+package com.iluwatar.hexagonal.administration {
+ class ConsoleAdministration {
+ - LOGGER : Logger {static}
+ + ConsoleAdministration()
+ + main(args : String[]) {static}
+ - printMainMenu() {static}
+ - readString(scanner : Scanner) : String {static}
+ }
+ interface ConsoleAdministrationSrv {
+ + getAllSubmittedTickets() {abstract}
+ + performLottery() {abstract}
+ + resetLottery() {abstract}
+ }
+ class ConsoleAdministrationSrvImpl {
+ - administration : LotteryAdministration
+ - logger : Logger
+ + ConsoleAdministrationSrvImpl(administration : LotteryAdministration, logger : Logger)
+ + getAllSubmittedTickets()
+ + performLottery()
+ + resetLottery()
+ }
+}
+package com.iluwatar.hexagonal.eventlog {
+ interface LotteryEventLog {
+ + prizeError(PlayerDetails, int) {abstract}
+ + ticketDidNotWin(PlayerDetails) {abstract}
+ + ticketSubmitError(PlayerDetails) {abstract}
+ + ticketSubmitted(PlayerDetails) {abstract}
+ + ticketWon(PlayerDetails, int) {abstract}
+ }
+ class MongoEventLog {
+ - DEFAULT_DB : String {static}
+ - DEFAULT_EVENTS_COLLECTION : String {static}
+ - EMAIL : String {static}
+ + MESSAGE : String {static}
+ - PHONE : String {static}
+ - database : MongoDatabase
+ - eventsCollection : MongoCollection
+ - mongoClient : MongoClient
+ - stdOutEventLog : StdOutEventLog
+ + MongoEventLog()
+ + MongoEventLog(dbName : String, eventsCollectionName : String)
+ + connect()
+ + connect(dbName : String, eventsCollectionName : String)
+ + getDatabase() : MongoDatabase
+ + getEventsCollection() : MongoCollection
+ + getMongoClient() : MongoClient
+ + prizeError(details : PlayerDetails, prizeAmount : int)
+ + ticketDidNotWin(details : PlayerDetails)
+ + ticketSubmitError(details : PlayerDetails)
+ + ticketSubmitted(details : PlayerDetails)
+ + ticketWon(details : PlayerDetails, prizeAmount : int)
+ }
+ class StdOutEventLog {
+ - LOGGER : Logger {static}
+ + StdOutEventLog()
+ + prizeError(details : PlayerDetails, prizeAmount : int)
+ + ticketDidNotWin(details : PlayerDetails)
+ + ticketSubmitError(details : PlayerDetails)
+ + ticketSubmitted(details : PlayerDetails)
+ + ticketWon(details : PlayerDetails, prizeAmount : int)
+ }
+}
+LotteryAdministration --> "-wireTransfers" WireTransfers
+LotteryService --> "-notifications" LotteryEventLog
+LotteryAdministration --> "-repository" LotteryTicketRepository
+MongoEventLog --> "-stdOutEventLog" StdOutEventLog
+LotteryService --> "-wireTransfers" WireTransfers
+LotteryAdministration --> "-notifications" LotteryEventLog
+ConsoleAdministrationSrvImpl --> "-administration" LotteryAdministration
+LotteryService --> "-repository" LotteryTicketRepository
+LotteryTicketCheckResult --> "-result" CheckResult
+ConsoleAdministrationSrvImpl ..|> ConsoleAdministrationSrv
+InMemoryBank ..|> WireTransfers
+MongoBank ..|> WireTransfers
+InMemoryTicketRepository ..|> LotteryTicketRepository
+MongoTicketRepository ..|> LotteryTicketRepository
+MongoEventLog ..|> LotteryEventLog
+StdOutEventLog ..|> LotteryEventLog
+LotteryConsoleServiceImpl ..|> LotteryConsoleService
+@enduml
\ No newline at end of file
diff --git a/marker-interface/etc/marker-interface.urm.puml b/marker-interface/etc/marker-interface.urm.puml
new file mode 100644
index 000000000000..02af47ddf261
--- /dev/null
+++ b/marker-interface/etc/marker-interface.urm.puml
@@ -0,0 +1,2 @@
+@startuml
+@enduml
\ No newline at end of file
diff --git a/microservices-aggregrator/etc/microservices-aggregrator.urm.puml b/microservices-aggregrator/etc/microservices-aggregrator.urm.puml
new file mode 100644
index 000000000000..02af47ddf261
--- /dev/null
+++ b/microservices-aggregrator/etc/microservices-aggregrator.urm.puml
@@ -0,0 +1,2 @@
+@startuml
+@enduml
\ No newline at end of file
diff --git a/microservices-api-gateway/etc/microservices-api-gateway.urm.puml b/microservices-api-gateway/etc/microservices-api-gateway.urm.puml
new file mode 100644
index 000000000000..02af47ddf261
--- /dev/null
+++ b/microservices-api-gateway/etc/microservices-api-gateway.urm.puml
@@ -0,0 +1,2 @@
+@startuml
+@enduml
\ No newline at end of file
diff --git a/microservices-idempotent-consumer/etc/microservices-idempotent-consumer.urm.puml b/microservices-idempotent-consumer/etc/microservices-idempotent-consumer.urm.puml
new file mode 100644
index 000000000000..43fe77181375
--- /dev/null
+++ b/microservices-idempotent-consumer/etc/microservices-idempotent-consumer.urm.puml
@@ -0,0 +1,49 @@
+@startuml
+package com.iluwatar.idempotentconsumer {
+ class App {
+ - LOGGER : Logger {static}
+ + App()
+ + main(args : String[]) {static}
+ + run(requestService : RequestService, requestRepository : RequestRepository) : CommandLineRunner
+ }
+ class Request {
+ - status : Status
+ - uuid : UUID
+ + Request()
+ + Request(uuid : UUID)
+ + Request(uuid : UUID, status : Status)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getStatus() : Status
+ + getUuid() : UUID
+ + hashCode() : int
+ + setStatus(status : Status)
+ + setUuid(uuid : UUID)
+ + toString() : String
+ }
+ ~enum Status {
+ + COMPLETED {static}
+ + PENDING {static}
+ + STARTED {static}
+ + valueOf(name : String) : Status {static}
+ + values() : Status[] {static}
+ }
+ interface RequestRepository {
+ }
+ class RequestService {
+ ~ requestRepository : RequestRepository
+ ~ requestStateMachine : RequestStateMachine
+ + RequestService(requestRepository : RequestRepository, requestStateMachine : RequestStateMachine)
+ + complete(uuid : UUID) : Request
+ + create(uuid : UUID) : Request
+ + start(uuid : UUID) : Request
+ }
+ class RequestStateMachine {
+ + RequestStateMachine()
+ + next(req : Request, nextStatus : Status) : Request
+ }
+}
+RequestService --> "-requestRepository" RequestRepository
+Request --> "-status" Status
+RequestService --> "-requestStateMachine" RequestStateMachine
+@enduml
\ No newline at end of file
diff --git a/microservices-log-aggregation/etc/microservices-log-aggregation.urm.puml b/microservices-log-aggregation/etc/microservices-log-aggregation.urm.puml
new file mode 100644
index 000000000000..1d4551ed025f
--- /dev/null
+++ b/microservices-log-aggregation/etc/microservices-log-aggregation.urm.puml
@@ -0,0 +1,68 @@
+@startuml
+package com.iluwatar.logaggregation {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ class CentralLogStore {
+ - LOGGER : Logger {static}
+ - logs : ConcurrentLinkedQueue
+ + CentralLogStore()
+ + displayLogs()
+ + storeLog(logEntry : LogEntry)
+ }
+ class LogAggregator {
+ - BUFFER_THRESHOLD : int {static}
+ - LOGGER : Logger {static}
+ - buffer : ConcurrentLinkedQueue
+ - centralLogStore : CentralLogStore
+ - executorService : ExecutorService
+ - logCount : AtomicInteger
+ - minLogLevel : LogLevel
+ + LogAggregator(centralLogStore : CentralLogStore, minLogLevel : LogLevel)
+ + collectLog(logEntry : LogEntry)
+ - flushBuffer()
+ - startBufferFlusher()
+ + stop()
+ }
+ class LogEntry {
+ - level : LogLevel
+ - message : String
+ - serviceName : String
+ - timestamp : LocalDateTime
+ + LogEntry(serviceName : String, level : LogLevel, message : String, timestamp : LocalDateTime)
+ # canEqual(other : Object) : boolean
+ + equals(o : Object) : boolean
+ + getLevel() : LogLevel
+ + getMessage() : String
+ + getServiceName() : String
+ + getTimestamp() : LocalDateTime
+ + hashCode() : int
+ + setLevel(level : LogLevel)
+ + setMessage(message : String)
+ + setServiceName(serviceName : String)
+ + setTimestamp(timestamp : LocalDateTime)
+ + toString() : String
+ }
+ enum LogLevel {
+ + DEBUG {static}
+ + ERROR {static}
+ + INFO {static}
+ + valueOf(name : String) : LogLevel {static}
+ + values() : LogLevel[] {static}
+ }
+ class LogProducer {
+ - LOGGER : Logger {static}
+ - aggregator : LogAggregator
+ - serviceName : String
+ + LogProducer(serviceName : String, aggregator : LogAggregator)
+ + generateLog(level : LogLevel, message : String)
+ }
+}
+LogAggregator --> "-centralLogStore" CentralLogStore
+LogEntry --> "-level" LogLevel
+CentralLogStore --> "-logs" LogEntry
+LogAggregator --> "-buffer" LogEntry
+LogAggregator --> "-minLogLevel" LogLevel
+LogProducer --> "-aggregator" LogAggregator
+@enduml
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index ef3a39265d53..6692720741cc 100644
--- a/pom.xml
+++ b/pom.xml
@@ -218,6 +218,7 @@
function-composition
microservices-distributed-tracing
microservices-idempotent-consumer
+ FiniteStateMachine
diff --git a/queue-based-load-leveling/etc/queue-based-load-leveling.urm.puml b/queue-based-load-leveling/etc/queue-based-load-leveling.urm.puml
new file mode 100644
index 000000000000..ca90842d92dd
--- /dev/null
+++ b/queue-based-load-leveling/etc/queue-based-load-leveling.urm.puml
@@ -0,0 +1,44 @@
+@startuml
+package com.iluwatar.queue.load.leveling {
+ class App {
+ - LOGGER : Logger {static}
+ - SHUTDOWN_TIME : int {static}
+ + App()
+ + main(args : String[]) {static}
+ }
+ class Message {
+ - msg : String
+ + Message(msg : String)
+ + getMsg() : String
+ + toString() : String
+ }
+ class MessageQueue {
+ - LOGGER : Logger {static}
+ - blkQueue : BlockingQueue
+ + MessageQueue()
+ + retrieveMsg() : Message
+ + submitMsg(msg : Message)
+ }
+ class ServiceExecutor {
+ - LOGGER : Logger {static}
+ - msgQueue : MessageQueue
+ + ServiceExecutor(msgQueue : MessageQueue)
+ + run()
+ }
+ interface Task {
+ + submit(Message) {abstract}
+ }
+ class TaskGenerator {
+ - LOGGER : Logger {static}
+ - msgCount : int
+ - msgQueue : MessageQueue
+ + TaskGenerator(msgQueue : MessageQueue, msgCount : int)
+ + run()
+ + submit(msg : Message)
+ }
+}
+MessageQueue --> "-blkQueue" Message
+ServiceExecutor --> "-msgQueue" MessageQueue
+TaskGenerator --> "-msgQueue" MessageQueue
+TaskGenerator ..|> Task
+@enduml
\ No newline at end of file
diff --git a/update-header.sh b/update-header.sh
index 48da4dcd6125..568d00d52a03 100755
--- a/update-header.sh
+++ b/update-header.sh
@@ -1,4 +1,29 @@
#!/bin/bash
+#
+# 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.
+#
+
# Find all README.md files in subdirectories one level deep
# and replace "### " with "## " at the beginning of lines
diff --git a/virtual-proxy/etc/virtual-proxy.urm.puml b/virtual-proxy/etc/virtual-proxy.urm.puml
new file mode 100644
index 000000000000..e30149e3809e
--- /dev/null
+++ b/virtual-proxy/etc/virtual-proxy.urm.puml
@@ -0,0 +1,26 @@
+@startuml
+package com.iluwatar.virtual.proxy {
+ class App {
+ + App()
+ + main(args : String[]) {static}
+ }
+ interface ExpensiveObject {
+ + process() {abstract}
+ }
+ class RealVideoObject {
+ - LOGGER : Logger {static}
+ + RealVideoObject()
+ - heavyInitialConfiguration()
+ + process()
+ }
+ class VideoObjectProxy {
+ - realVideoObject : RealVideoObject
+ + VideoObjectProxy()
+ + getRealVideoObject() : RealVideoObject
+ + process()
+ }
+}
+VideoObjectProxy --> "-realVideoObject" RealVideoObject
+RealVideoObject ..|> ExpensiveObject
+VideoObjectProxy ..|> ExpensiveObject
+@enduml
\ No newline at end of file