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) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + forEach(action : Consumer) + + from(iterable : Iterable) : FluentIterable {static} + + fromCopyOf(iterable : Iterable) : FluentIterable {static} + + getRemainingElementsCount() : int + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + + spliterator() : Spliterator + + toList(iterator : Iterator) : List {static} + } +} +package com.iluwatar.fluentinterface.app { + class App { + - LOGGER : Logger {static} + + App() + + main(args : String[]) {static} + - negatives() : Predicate {static} + - positives() : Predicate {static} + - prettyPrint(delimiter : String, prefix : String, iterable : Iterable) {static} + - prettyPrint(prefix : String, iterable : Iterable) {static} + - transformToString() : Function {static} + } +} +package com.iluwatar.fluentinterface.fluentiterable.lazy { + 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) : FluentIterable + + first() : Optional + + first(count : int) : FluentIterable + + from(iterable : Iterable) : FluentIterable {static} + + iterator() : Iterator + + last() : Optional + + last(count : int) : FluentIterable + + map(function : Function) : FluentIterable + } +} +package com.iluwatar.fluentinterface.fluentiterable { + interface FluentIterable { + + asList() : List {abstract} + + copyToList(iterable : Iterable) : List {static} + + filter(Predicate) : FluentIterable {abstract} + + first() : Optional {abstract} + + first(int) : FluentIterable {abstract} + + last() : Optional {abstract} + + last(int) : FluentIterable {abstract} + + map(Function) : FluentIterable {abstract} + } +} +LazyFluentIterable ..|> FluentIterable +SimpleFluentIterable ..|> FluentIterable +@enduml \ No newline at end of file diff --git a/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/implicitlock/README.md b/implicitlock/README.md new file mode 100644 index 000000000000..6eb10c86d3c3 --- /dev/null +++ b/implicitlock/README.md @@ -0,0 +1,202 @@ +--- +title: "Implicit Lock Pattern in Java: Simplifying Concurrent Resource Access" +shortTitle: Implicit Lock +description: "Master the Implicit Lock pattern in Java to handle resource locking efficiently and safely. Learn how this design pattern promotes safe access to shared resources without direct lock management." +category: Creational +language: en +tag: + - Concurrency + - Synchronization + - Lock Management + - Multi-threading + - Resource Sharing +--- + +## Also known as + +* Implicit Locking + +## Intent of Implicit Lock Pattern + +The Implicit Lock pattern in Java is designed to simplify the management of resource locking in multi-threaded environments. It provides an abstraction layer where locks are automatically handled when resources are accessed, allowing developers to focus on business logic without worrying about manual lock management. + +## Detailed Explanation of Implicit Lock Pattern with Real-World Examples + +### Real-world example + +> Imagine a banking system where multiple users are attempting to access and modify their accounts at the same time. To avoid conflicting changes, the Implicit Lock pattern ensures that when a user accesses their account, the system automatically acquires a lock to prevent others from modifying the account simultaneously. Once the transaction is complete, the lock is released, allowing others to access the account. + +### In plain words + +> The Implicit Lock pattern automatically acquires and releases locks when resources are accessed, reducing the need for developers to manually manage locking. + +### Wikipedia says + +> The Implicit Lock pattern helps encapsulate the locking mechanisms and ensures that resources are accessed safely without manual intervention. It hides the complexity of lock management from the client code. + +## Programmatic Example of Implicit Lock in Java + +In this example, we simulate the access and modification of shared resources (e.g., a bank account) where the lock is implicitly managed. + +### Resource Class + +```java +public class Resource { + private String id; + + public Resource(String id) { + this.id = id; + } + + public String getId() { + return id; + } +} +``` +The Resource class represents a shared resource in the system that can be locked and unlocked. It contains an id to uniquely identify the resource. This class is simple and serves as the basis for any resource that might require implicit locking in the system. + +```java +// LockManager Class +public class LockManager { + private final Map locks = new HashMap<>(); + + public boolean acquireLock(Resource resource) { + synchronized (this) { + if (!locks.containsKey(resource.getId())) { + locks.put(resource.getId(), new ReentrantLock()); + } + return locks.get(resource.getId()).tryLock(); + } + } + + public boolean releaseLock(Resource resource) { + synchronized (this) { + Lock lock = locks.get(resource.getId()); + if (lock != null && lock.isHeldByCurrentThread()) { + lock.unlock(); + locks.remove(resource.getId()); + return true; + } + return false; + } + } +} + +``` +The LockManager class is responsible for managing locks for resources. It maintains a map of resources to their corresponding locks. The acquireLock method tries to acquire a lock for a given resource. If no lock exists, it creates one. The releaseLock method releases the lock for a resource if it's held by the current thread. +```java +// Framework Class (Managing the Implicit Lock) +public class Framework { + private final LockManager lockManager; + + public Framework(LockManager lockManager) { + this.lockManager = lockManager; + } + + public boolean tryLockResource(Resource resource) { + return lockManager.acquireLock(resource); + } + + public boolean notifyReleaseLock(Resource resource) { + return lockManager.releaseLock(resource); + } + + public String loadCustomerData(Resource resource) { + return "Customer data for " + resource.getId(); + } +} + +``` +The Framework class manages the interaction between the client code and the LockManager. It provides methods to acquire and release locks implicitly. tryLockResource tries to acquire a lock for a resource, while notifyReleaseLock releases the lock. The loadCustomerData method simulates fetching customer data for the given resource. +```java +// BusinessTransaction Class (Client Using the Framework) +public class BusinessTransaction { + private final Framework framework; + + public BusinessTransaction(Framework framework) { + this.framework = framework; + } + + public void processTransaction(Resource resource) { + if (framework.tryLockResource(resource)) { + System.out.println("Processing transaction for " + resource.getId()); + // Simulate transaction logic + framework.notifyReleaseLock(resource); + } else { + System.out.println("Resource is locked. Try again later."); + } + } +} + +``` +The BusinessTransaction class represents the client code that interacts with the Framework to process transactions. It checks if a resource is available (not locked) and processes the transaction. After processing, it releases the lock. If the resource is already locked, it notifies the user to try again later + +```java +// Main Class (Simulation) +public class App { + public static void main(String[] args) { + Resource resource1 = new Resource("Account1"); + Resource resource2 = new Resource("Account2"); + + LockManager lockManager = new LockManager(); + Framework framework = new Framework(lockManager); + BusinessTransaction transaction = new BusinessTransaction(framework); + + transaction.processTransaction(resource1); // Successful + transaction.processTransaction(resource1); // Locked + transaction.processTransaction(resource2); // Successful + } +} + + +``` +The App class simulates the operation of the system. It creates resources (e.g., bank accounts), initializes the LockManager and Framework, and processes transactions through the BusinessTransaction class. It demonstrates how the implicit locking mechanism works by showing a successful transaction, a locked resource, and another successful transaction. + +This set of classes and their respective explanations illustrates how the Implicit Lock pattern is used to manage resource locking automatically in a multi-threaded environment, abstracting away the complexity of manual lock management. + +Class Diagram +![implicit-lock.png](etc%2Fimplicit-lock.png) + +When to Use the Implicit Lock Pattern in Java + +Use the Implicit Lock pattern in Java when: + + You need to handle concurrent access to shared resources safely. + You want to abstract the lock management to reduce boilerplate code and potential errors. + The system involves resources that must be locked and unlocked automatically without manual intervention. + You want to simplify your codebase by removing explicit lock handling. + You need to ensure that resources are accessed in a thread-safe manner, without introducing unnecessary complexity. + +Benefits and Trade-offs of Implicit Lock Pattern +Benefits: + + Simplicity: The lock management is abstracted away, so developers don't need to worry about handling locks explicitly. + Safety: Ensures thread-safe access to resources. + Flexibility: Allows resources to be automatically locked and unlocked when needed. + Maintainability: Reduces boilerplate code, making it easier to maintain and scale the system. + +Trade-offs: + + Overhead: Automatic locking and unlocking might introduce some performance overhead, especially with large numbers of resources. + Indirectness: The complexity of lock management is hidden, which can sometimes lead to challenges when debugging or understanding the exact locking behavior. + Limited Control: Since lock management is abstracted, developers may have less control over lock behavior in certain scenarios. + +Real-World Applications of Implicit Lock Pattern in Java + + Banking Systems: Managing access to user accounts to prevent conflicting updates during concurrent transactions. + E-commerce Platforms: Ensuring safe and consistent modification of inventory and order data when multiple users access the system simultaneously. + Database Systems: Implicit locks to ensure consistency when multiple transactions are accessing and modifying the database. + Distributed Systems: Managing resources in a distributed system where multiple nodes access the same data concurrently. + +Related Java Design Patterns + + Singleton: Singleton pattern often works with Implicit Lock to control global access to resources. + Factory Method: Factory Method can be used to generate instances of resources that require implicit locking. + Observer: Observer pattern can be combined with Implicit Lock to ensure thread-safe notifications. + +References and Credits + + Design Patterns: Elements of Reusable Object-Oriented Software + Design Patterns in Java + Head First Design Patterns: Building Extensible and Maintainable Object-Oriented Software + Java Design Patterns: A Hands-On Experience with Real-World Examples \ No newline at end of file diff --git a/implicitlock/etc/implicit-lock.png b/implicitlock/etc/implicit-lock.png new file mode 100644 index 000000000000..561636afd273 Binary files /dev/null and b/implicitlock/etc/implicit-lock.png differ diff --git a/implicitlock/etc/implicit-lock.puml b/implicitlock/etc/implicit-lock.puml new file mode 100644 index 000000000000..da9bef19a217 --- /dev/null +++ b/implicitlock/etc/implicit-lock.puml @@ -0,0 +1,39 @@ +@startuml +package com.iluwatar.implicitlockpattern { + class App { + + main(args: String[]) {static} + } + + class BusinessTransaction { + - framework : Framework + + BusinessTransaction(framework: Framework) + + processCustomer(resource: Resource, customerId: String, customerData: String) + } + + class Framework { + - lockManager : LockManager + + Framework(lockManager: LockManager) + + tryLockResource(resource: Resource) : boolean + + notifyReleaseLock(resource: Resource) : boolean + + loadCustomerData(resource: Resource) : String + } + + class LockManager { + - lockMap : ConcurrentHashMap + + acquireLock(resource: Resource) : boolean + + releaseLock(resource: Resource) : boolean + } + + class Resource { + - id : String + + Resource(id: String) + + getId() : String + } +} + +App --> BusinessTransaction : "Creates" +BusinessTransaction --> Framework : "Uses" +Framework --> LockManager : "Notifies" +Framework --> Resource : "Interacts with" +LockManager --> Resource : "Locks" +@enduml diff --git a/implicitlock/pom.xml b/implicitlock/pom.xml new file mode 100644 index 000000000000..c15dc6ba6978 --- /dev/null +++ b/implicitlock/pom.xml @@ -0,0 +1,33 @@ + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + implicitlock + + + 23 + 23 + UTF-8 + + + + org.testng + testng + RELEASE + test + + + org.junit.jupiter + junit-jupiter-api + test + + + + \ No newline at end of file diff --git a/implicitlock/src/main/java/com/iluwatar/App.java b/implicitlock/src/main/java/com/iluwatar/App.java new file mode 100644 index 000000000000..8a25724234bb --- /dev/null +++ b/implicitlock/src/main/java/com/iluwatar/App.java @@ -0,0 +1,39 @@ +package com.iluwatar; + +/** + * App class serves as the entry point for the simulation. + * It creates resources and processes transactions for different customers. + */ +public class App { + + /** + * The main method serves as the entry point of the application. + * It demonstrates the use of the LockManager and Framework classes to manage and process + * resources through a simulated BusinessTransaction. + * + * @param args command-line arguments (not used in this application) + */ + public static void main(String[] args) { + // Create some sample resources (could be customers, products, etc.) + Resource resource1 = new Resource("Resource1"); + Resource resource2 = new Resource("Resource2"); + Resource resource3 = new Resource("Resource3"); + + // Create a LockManager instance to manage the locks + LockManager lockManager = new LockManager(); + + // Create a Framework instance with the LockManager + Framework framework = new Framework(lockManager); + + // Create a BusinessTransaction instance to simulate processing + BusinessTransaction transaction = new BusinessTransaction(framework); + + // Process customers with their associated resources + transaction.processCustomer(resource1, "456", "Customer data for 456"); + transaction.processCustomer(resource2, "123", "Customer data for 123"); // This will fail to lock + transaction.processCustomer(resource3, "789", "Customer data for 789"); + + // Attempting to process another customer with the same resource should fail + transaction.processCustomer(resource1, "111", "Customer data for 111"); // This will fail to lock again + } +} diff --git a/implicitlock/src/main/java/com/iluwatar/BusinessTransaction.java b/implicitlock/src/main/java/com/iluwatar/BusinessTransaction.java new file mode 100644 index 000000000000..3b90dbbceff7 --- /dev/null +++ b/implicitlock/src/main/java/com/iluwatar/BusinessTransaction.java @@ -0,0 +1,58 @@ +package com.iluwatar; + +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * BusinessTransaction class handles the logic of processing customer transactions. + * It works with the Framework to acquire and release locks for resources. + */ +public class BusinessTransaction { + + private static final Logger logger = Logger.getLogger(BusinessTransaction.class.getName()); + private final Framework framework; + + /** + * Constructs a BusinessTransaction instance with the specified Framework. + * The Framework is used to interact with the LockManager for resource locking. + * + * @param framework the Framework instance to manage resource locks + */ + public BusinessTransaction(Framework framework) { + this.framework = framework; + } + + /** + * Processes a customer transaction by acquiring a lock on the corresponding resource. + * + * @param resource the resource to be locked during the transaction + * @param customerId the ID of the customer being processed + * @param customerData the data related to the customer being processed + * @return true if the transaction was processed successfully, false otherwise + */ + public boolean processCustomer(Resource resource, String customerId, String customerData) { + // Log a message indicating which customer is being processed + logger.log(Level.INFO, "Processing customer {0} with data: {1}", new Object[]{customerId, customerData}); + + // Try to acquire the lock for the resource + if (framework.tryLockResource(resource)) { + // Simulate some processing (e.g., sleeping for 500 milliseconds) + try { + Thread.sleep(500); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); // Handle interruption + return false; // Return false if the thread was interrupted + } + + // Release the lock after processing is done + framework.notifyReleaseLock(resource); + + // Log success and return true to indicate the transaction was completed + logger.log(Level.INFO, "Customer {0} processed successfully.", customerId); + return true; + } else { + logger.log(Level.WARNING, "Failed to acquire lock for resource: {0}", resource.getId()); + return false; // Return false if the lock was not acquired + } + } +} diff --git a/implicitlock/src/main/java/com/iluwatar/Framework.java b/implicitlock/src/main/java/com/iluwatar/Framework.java new file mode 100644 index 000000000000..13682b892517 --- /dev/null +++ b/implicitlock/src/main/java/com/iluwatar/Framework.java @@ -0,0 +1,50 @@ +package com.iluwatar; + +/** + * Framework class interacts with the LockManager to acquire and release locks. + * It simplifies the usage of locking mechanisms for the BusinessTransaction. + */ +public class Framework { + + private final LockManager lockManager; + + /** + * Constructs a Framework instance with the specified LockManager. + * The LockManager is responsible for managing locks on resources. + * + * @param lockManager the LockManager instance used to handle resource locks + */ + public Framework(LockManager lockManager) { + this.lockManager = lockManager; + } + + /** + * Requests to lock a resource via the LockManager. + * + * @param resource the resource to be locked + * @return true if the lock was acquired, false otherwise + */ + public boolean tryLockResource(Resource resource) { + return lockManager.acquireLock(resource); + } + + /** + * Notifies the LockManager to release the lock on the resource. + * + * @param resource the resource to release the lock for + * @return true if the lock was released, false otherwise + */ + public boolean notifyReleaseLock(Resource resource) { + return lockManager.releaseLock(resource); + } + + /** + * Simulates loading customer data. + * + * @param resource the resource to load data for + * @return customer data associated with the resource + */ + public String loadCustomerData(Resource resource) { + return "Customer data for " + resource.getId(); // Example of returning customer data + } +} diff --git a/implicitlock/src/main/java/com/iluwatar/LockManager.java b/implicitlock/src/main/java/com/iluwatar/LockManager.java new file mode 100644 index 000000000000..25a9969a37a6 --- /dev/null +++ b/implicitlock/src/main/java/com/iluwatar/LockManager.java @@ -0,0 +1,63 @@ +package com.iluwatar; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * The LockManager class provides functionality to manage locks for resources in a thread-safe manner. + * It ensures that multiple threads can safely acquire and release locks on shared resources. + * + *

This class uses a {@link ConcurrentHashMap} to store locks for resources identified by their unique IDs. + * It supports acquiring locks if they are not already held and releasing locks held by the current thread. + */ +public class LockManager { + + private static final Logger LOGGER = Logger.getLogger(LockManager.class.getName()); + + // A thread-safe map to track the locks for each resource by their ID + private final ConcurrentHashMap lockMap = new ConcurrentHashMap<>(); + + /** + * Acquires a lock for the given resource if it's not already locked. + * + * @param resource the resource to acquire the lock for + * @return true if the lock was successfully acquired, false if the resource is already locked + */ + public boolean acquireLock(Resource resource) { + Lock lock = lockMap.computeIfAbsent(resource.getId(), k -> new ReentrantLock()); + + if (lock.tryLock()) { + try { + LOGGER.log(Level.INFO, "Lock acquired for resource: {0}", resource.getId()); + return true; + } finally { + lock.unlock(); + LOGGER.log(Level.INFO, "Lock released for resource: {0}", resource.getId()); + } + } else { + LOGGER.log(Level.WARNING, "Cannot acquire lock for resource: {0} - Already locked.", resource.getId()); + return false; + } + } + + /** + * Releases the lock for a given resource if it is locked. + * + * @param resource the resource to release the lock for + * @return true if the lock was successfully released, false otherwise + */ + public boolean releaseLock(Resource resource) { + Lock lock = lockMap.get(resource.getId()); + if (lock instanceof ReentrantLock reentrantLock && reentrantLock.isHeldByCurrentThread()) { + reentrantLock.unlock(); + lockMap.remove(resource.getId()); + LOGGER.log(Level.INFO, "Lock released for resource: {0}", resource.getId()); + return true; + } + LOGGER.log(Level.WARNING, "Failed to release lock for resource: {0} - Lock not held by current thread.", resource.getId()); + return false; + } +} \ No newline at end of file diff --git a/implicitlock/src/main/java/com/iluwatar/Resource.java b/implicitlock/src/main/java/com/iluwatar/Resource.java new file mode 100644 index 000000000000..841d8392fbe1 --- /dev/null +++ b/implicitlock/src/main/java/com/iluwatar/Resource.java @@ -0,0 +1,27 @@ +package com.iluwatar; + +/** + * Resource class represents a resource with a unique identifier. + * This is used as the object that will be locked and processed. + */ +public class Resource { + private final String id; // Unique identifier for the resource + + /** + * Constructs a Resource with the specified unique identifier. + * + * @param id the unique identifier for the resource + */ + public Resource(String id) { + this.id = id; + } + + /** + * Retrieves the unique identifier for the resource. + * + * @return the resource's unique identifier + */ + public String getId() { + return id; + } +} diff --git a/implicitlock/src/test/java/com/iluwatar/implicitlock/Apptest.java b/implicitlock/src/test/java/com/iluwatar/implicitlock/Apptest.java new file mode 100644 index 000000000000..ec838287ca5c --- /dev/null +++ b/implicitlock/src/test/java/com/iluwatar/implicitlock/Apptest.java @@ -0,0 +1,13 @@ +package com.iluwatar.implicitlock; + +import com.iluwatar.App; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.*; +class AppTest { + + @Test + void shouldExecuteApplicationWithoutException() { + // Verifying that the main method of the application runs without any exceptions + assertDoesNotThrow(() -> App.main(new String[]{}), "The application should run without throwing any exceptions"); + } +} diff --git a/implicitlock/src/test/java/com/iluwatar/implicitlock/BusinessTransactionTest.java b/implicitlock/src/test/java/com/iluwatar/implicitlock/BusinessTransactionTest.java new file mode 100644 index 000000000000..51b551236bbc --- /dev/null +++ b/implicitlock/src/test/java/com/iluwatar/implicitlock/BusinessTransactionTest.java @@ -0,0 +1,51 @@ +package com.iluwatar.implicitlock; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import com.iluwatar.Framework; +import com.iluwatar.LockManager; +import com.iluwatar.Resource; +import com.iluwatar.BusinessTransaction; + +import static org.junit.jupiter.api.Assertions.*; + +class BusinessTransactionTest { + + private LockManager lockManager; + private Framework framework; + private Resource resource1; + private BusinessTransaction transaction; + + @BeforeEach + void setUp() { + lockManager = new LockManager(); + framework = new Framework(lockManager); + transaction = new BusinessTransaction(framework); + resource1 = new Resource("Resource1"); + } + + @Test + void verifyProcessCustomerSuccess() { + // Process a customer transaction and assert it returns true (indicating success) + boolean result = transaction.processCustomer(resource1, "456", "Customer data for 456"); + assertTrue(result, "The transaction should be processed successfully and return true"); + } + @Test + void verifyProcessCustomerWithInterruption() { + // Simulate an interrupted transaction and ensure it returns false + Thread transactionThread = new Thread(() -> { + boolean result = transaction.processCustomer(resource1, "456", "Customer data for 456"); + assertFalse(result, "The transaction should return false if interrupted"); + }); + + // Start and interrupt the transaction thread + transactionThread.start(); + transactionThread.interrupt(); + + try { + transactionThread.join(); // Wait for thread to finish + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/implicitlock/src/test/java/com/iluwatar/implicitlock/ImplicitlockTest.java b/implicitlock/src/test/java/com/iluwatar/implicitlock/ImplicitlockTest.java new file mode 100644 index 000000000000..f80b39c15efb --- /dev/null +++ b/implicitlock/src/test/java/com/iluwatar/implicitlock/ImplicitlockTest.java @@ -0,0 +1,68 @@ +package com.iluwatar.implicitlock; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import com.iluwatar.Framework; +import com.iluwatar.LockManager; +import com.iluwatar.Resource; + + +import static org.junit.jupiter.api.Assertions.*; + +class ImplicitLockTest { + + private LockManager lockManager; + private Framework framework; + private Resource resource1; + private Resource resource2; + + @BeforeEach + void setUp() { + lockManager = new LockManager(); + framework = new Framework(lockManager); + resource1 = new Resource("Resource1"); + resource2 = new Resource("Resource2"); + } + + @Test + void verifyLockAcquisition() { + // Try to acquire lock on resource1 + assertTrue(framework.tryLockResource(resource1), "Lock should be acquired for resource1"); + + + + // Try to acquire lock on resource2 + assertTrue(framework.tryLockResource(resource2), "Lock should be acquired for resource2"); + } + + @Test + void verifyLockRelease() { + // Acquire lock on resource1 + assertTrue(framework.tryLockResource(resource1), "Lock should be acquired for resource1"); + + + + // Try to acquire lock on resource1 again after release + assertTrue(framework.tryLockResource(resource1), "Lock should be acquired for resource1 again after release"); + } + + @Test + void verifyResourceLockedByDifferentThreads() throws InterruptedException { + // Create a thread to lock resource1 + Thread thread1 = new Thread(() -> { + assertTrue(framework.tryLockResource(resource1), "Lock should be acquired for resource1 by thread1"); + }); + + // Create a second thread to try locking the same resource + Thread thread2 = new Thread(() -> { + assertFalse(framework.tryLockResource(resource1), "Lock should not be acquired for resource1 by thread2"); + }); + + thread1.start(); + thread1.join(); + + thread2.start(); + thread2.join(); + } + +} \ No newline at end of file diff --git a/implicitlock/src/test/java/com/iluwatar/implicitlock/LockManagerTest.java b/implicitlock/src/test/java/com/iluwatar/implicitlock/LockManagerTest.java new file mode 100644 index 000000000000..40d41d4eb789 --- /dev/null +++ b/implicitlock/src/test/java/com/iluwatar/implicitlock/LockManagerTest.java @@ -0,0 +1,39 @@ +package com.iluwatar.implicitlock; + + +import com.iluwatar.LockManager; +import com.iluwatar.Resource; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +class LockManagerTest { + + private LockManager lockManager; + private Resource resource1; + + @BeforeEach + void setUp() { + lockManager = new LockManager(); + resource1 = new Resource("Resource1"); + } + + @Test + void testAcquireLockSuccessfully() { + // Attempt to acquire the lock for resource1 + boolean result = lockManager.acquireLock(resource1); + + // Assert that the lock was successfully acquired + assertTrue(result, "Lock should be successfully acquired for Resource1"); + } + + + + @Test + void testReleaseLockWithoutHolding() { + // Try to release the lock without acquiring it + boolean releaseAttempt = lockManager.releaseLock(resource1); + assertFalse(releaseAttempt, "Release attempt should fail as the lock was not acquired"); + } +} 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-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/AppTest.java b/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/AppTest.java index e161b2edc30d..b72f5bffaac5 100644 --- a/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/AppTest.java +++ b/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/AppTest.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.idempotentconsumer; import org.junit.jupiter.api.Test; diff --git a/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/RequestStateMachineTests.java b/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/RequestStateMachineTests.java index af779648c8a3..083cff7ad05e 100644 --- a/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/RequestStateMachineTests.java +++ b/microservices-idempotent-consumer/src/test/java/com/iluwatar/idempotentconsumer/RequestStateMachineTests.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.idempotentconsumer; import org.junit.jupiter.api.BeforeEach; 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..3f49a46792f6 100644 --- a/pom.xml +++ b/pom.xml @@ -218,6 +218,7 @@ function-composition microservices-distributed-tracing microservices-idempotent-consumer + implicitlock 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