From 457feca1c1053b0e35a0886a10eb1372ed92b32a Mon Sep 17 00:00:00 2001 From: shahdhoss Date: Fri, 6 Dec 2024 21:00:16 +0200 Subject: [PATCH] implemented the session facade design pattern issue #1278 --- .editorconfig | 82 ++--- .../etc/fluent-interface.urm.puml | 72 +++++ .../etc/function-composition.urm.puml | 12 + .../etc/hexagonal-architecture.urm.puml | 282 ++++++++++++++++++ .../etc/marker-interface.urm.puml | 2 + .../etc/microservices-aggregrator.urm.puml | 2 + .../etc/microservices-api-gateway.urm.puml | 2 + ...microservices-idempotent-consumer.urm.puml | 49 +++ .../iluwatar/idempotentconsumer/AppTest.java | 24 ++ .../RequestStateMachineTests.java | 24 ++ .../microservices-log-aggregation.urm.puml | 68 +++++ pom.xml | 1 + .../etc/queue-based-load-leveling.urm.puml | 44 +++ session-facade/README.md | 146 +++++++++ session-facade/etc/session-facade.urm.png | Bin 0 -> 102155 bytes session-facade/etc/session-facade.urm.puml | 50 ++++ session-facade/pom.xml | 74 +++++ .../java/com/iluwatar/sessionfacade/App.java | 52 ++++ .../iluwatar/sessionfacade/CartService.java | 80 +++++ .../iluwatar/sessionfacade/OrderService.java | 53 ++++ .../sessionfacade/PaymentService.java | 65 ++++ .../com/iluwatar/sessionfacade/Product.java | 41 +++ .../sessionfacade/ProductCatalogService.java | 42 +++ .../sessionfacade/ShoppingFacade.java | 100 +++++++ .../com/iluwatar/sessionfacade/AppTest.java | 42 +++ .../sessionfacade/CartServiceTest.java | 94 ++++++ .../sessionfacade/PaymentServiceTest.java | 62 ++++ .../iluwatar/sessionfacade/ProductTest.java | 77 +++++ .../sessionfacade/ShoppingFacadeTest.java | 74 +++++ update-header.sh | 25 ++ virtual-proxy/etc/virtual-proxy.urm.puml | 26 ++ 31 files changed, 1726 insertions(+), 41 deletions(-) create mode 100644 fluent-interface/etc/fluent-interface.urm.puml create mode 100644 function-composition/etc/function-composition.urm.puml create mode 100644 hexagonal-architecture/etc/hexagonal-architecture.urm.puml create mode 100644 marker-interface/etc/marker-interface.urm.puml create mode 100644 microservices-aggregrator/etc/microservices-aggregrator.urm.puml create mode 100644 microservices-api-gateway/etc/microservices-api-gateway.urm.puml create mode 100644 microservices-idempotent-consumer/etc/microservices-idempotent-consumer.urm.puml create mode 100644 microservices-log-aggregation/etc/microservices-log-aggregation.urm.puml create mode 100644 queue-based-load-leveling/etc/queue-based-load-leveling.urm.puml create mode 100644 session-facade/README.md create mode 100644 session-facade/etc/session-facade.urm.png create mode 100644 session-facade/etc/session-facade.urm.puml create mode 100644 session-facade/pom.xml create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/App.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java create mode 100644 session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java create mode 100644 session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java create mode 100644 virtual-proxy/etc/virtual-proxy.urm.puml diff --git a/.editorconfig b/.editorconfig index 4afde59eb070..6da9472eecab 100644 --- a/.editorconfig +++ b/.editorconfig @@ -38,7 +38,7 @@ ij_formatter_off_tag = @formatter:off ij_formatter_on_tag = @formatter:on ij_formatter_tags_enabled = true ij_smart_tabs = false -ij_visual_guides = +ij_visual_guides = ij_wrap_on_typing = false [*.java] @@ -94,7 +94,7 @@ ij_java_blank_lines_before_package = 1 ij_java_block_brace_style = end_of_line ij_java_block_comment_add_space = false ij_java_block_comment_at_first_column = true -ij_java_builder_methods = +ij_java_builder_methods = ij_java_call_parameters_new_line_after_left_paren = false ij_java_call_parameters_right_paren_on_new_line = false ij_java_call_parameters_wrap = normal @@ -128,31 +128,31 @@ ij_java_doc_param_description_on_new_line = false ij_java_doc_preserve_line_breaks = false ij_java_doc_use_throws_not_exception_tag = true ij_java_else_on_new_line = false -ij_java_entity_dd_prefix = +ij_java_entity_dd_prefix = ij_java_entity_dd_suffix = EJB -ij_java_entity_eb_prefix = +ij_java_entity_eb_prefix = ij_java_entity_eb_suffix = Bean -ij_java_entity_hi_prefix = +ij_java_entity_hi_prefix = ij_java_entity_hi_suffix = Home ij_java_entity_lhi_prefix = Local ij_java_entity_lhi_suffix = Home ij_java_entity_li_prefix = Local -ij_java_entity_li_suffix = +ij_java_entity_li_suffix = ij_java_entity_pk_class = java.lang.String -ij_java_entity_ri_prefix = -ij_java_entity_ri_suffix = -ij_java_entity_vo_prefix = +ij_java_entity_ri_prefix = +ij_java_entity_ri_suffix = +ij_java_entity_vo_prefix = ij_java_entity_vo_suffix = VO ij_java_enum_constants_wrap = normal ij_java_extends_keyword_wrap = normal ij_java_extends_list_wrap = normal ij_java_field_annotation_wrap = split_into_lines -ij_java_field_name_prefix = -ij_java_field_name_suffix = -ij_java_filter_class_prefix = -ij_java_filter_class_suffix = -ij_java_filter_dd_prefix = -ij_java_filter_dd_suffix = +ij_java_field_name_prefix = +ij_java_field_name_suffix = +ij_java_filter_class_prefix = +ij_java_filter_class_suffix = +ij_java_filter_dd_prefix = +ij_java_filter_dd_suffix = ij_java_finally_on_new_line = false ij_java_for_brace_force = always ij_java_for_statement_new_line_after_left_paren = false @@ -161,7 +161,7 @@ ij_java_for_statement_wrap = normal ij_java_generate_final_locals = false ij_java_generate_final_parameters = false ij_java_if_brace_force = always -ij_java_imports_layout = $*,|,* +ij_java_imports_layout = $*, |, * ij_java_indent_case_from_switch = true ij_java_insert_inner_class_imports = false ij_java_insert_override_annotation = true @@ -186,13 +186,13 @@ ij_java_layout_static_imports_separately = true ij_java_line_comment_add_space = false ij_java_line_comment_add_space_on_reformat = false ij_java_line_comment_at_first_column = true -ij_java_listener_class_prefix = -ij_java_listener_class_suffix = -ij_java_local_variable_name_prefix = -ij_java_local_variable_name_suffix = -ij_java_message_dd_prefix = +ij_java_listener_class_prefix = +ij_java_listener_class_suffix = +ij_java_local_variable_name_prefix = +ij_java_local_variable_name_suffix = +ij_java_message_dd_prefix = ij_java_message_dd_suffix = EJB -ij_java_message_eb_prefix = +ij_java_message_eb_prefix = ij_java_message_eb_suffix = Bean ij_java_method_annotation_wrap = split_into_lines ij_java_method_brace_style = end_of_line @@ -206,17 +206,17 @@ ij_java_names_count_to_use_import_on_demand = 999 ij_java_new_line_after_lparen_in_annotation = false ij_java_new_line_after_lparen_in_deconstruction_pattern = true ij_java_new_line_after_lparen_in_record_header = false -ij_java_packages_to_use_import_on_demand = +ij_java_packages_to_use_import_on_demand = ij_java_parameter_annotation_wrap = normal -ij_java_parameter_name_prefix = -ij_java_parameter_name_suffix = +ij_java_parameter_name_prefix = +ij_java_parameter_name_suffix = ij_java_parentheses_expression_new_line_after_left_paren = false ij_java_parentheses_expression_right_paren_on_new_line = false ij_java_place_assignment_sign_on_next_line = false ij_java_prefer_longer_names = true ij_java_prefer_parameters_wrap = false ij_java_record_components_wrap = normal -ij_java_repeat_annotations = +ij_java_repeat_annotations = ij_java_repeat_synchronized = true ij_java_replace_instanceof_and_cast = false ij_java_replace_null_check = true @@ -227,23 +227,23 @@ ij_java_resource_list_wrap = normal ij_java_rparen_on_new_line_in_annotation = false ij_java_rparen_on_new_line_in_deconstruction_pattern = true ij_java_rparen_on_new_line_in_record_header = false -ij_java_servlet_class_prefix = -ij_java_servlet_class_suffix = -ij_java_servlet_dd_prefix = -ij_java_servlet_dd_suffix = -ij_java_session_dd_prefix = +ij_java_servlet_class_prefix = +ij_java_servlet_class_suffix = +ij_java_servlet_dd_prefix = +ij_java_servlet_dd_suffix = +ij_java_session_dd_prefix = ij_java_session_dd_suffix = EJB -ij_java_session_eb_prefix = +ij_java_session_eb_prefix = ij_java_session_eb_suffix = Bean -ij_java_session_hi_prefix = +ij_java_session_hi_prefix = ij_java_session_hi_suffix = Home ij_java_session_lhi_prefix = Local ij_java_session_lhi_suffix = Home ij_java_session_li_prefix = Local -ij_java_session_li_suffix = -ij_java_session_ri_prefix = -ij_java_session_ri_suffix = -ij_java_session_si_prefix = +ij_java_session_li_suffix = +ij_java_session_ri_prefix = +ij_java_session_ri_suffix = +ij_java_session_si_prefix = ij_java_session_si_suffix = Service ij_java_space_after_closing_angle_bracket_in_type_argument = false ij_java_space_after_colon = true @@ -324,13 +324,13 @@ ij_java_spaces_within_synchronized_parentheses = false ij_java_spaces_within_try_parentheses = false ij_java_spaces_within_while_parentheses = false ij_java_special_else_if_treatment = true -ij_java_static_field_name_prefix = -ij_java_static_field_name_suffix = -ij_java_subclass_name_prefix = +ij_java_static_field_name_prefix = +ij_java_static_field_name_suffix = +ij_java_subclass_name_prefix = ij_java_subclass_name_suffix = Impl ij_java_ternary_operation_signs_on_next_line = false ij_java_ternary_operation_wrap = normal -ij_java_test_name_prefix = +ij_java_test_name_prefix = ij_java_test_name_suffix = Test ij_java_throws_keyword_wrap = normal ij_java_throws_list_wrap = normal 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/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..9b5c219ca472 100644 --- a/pom.xml +++ b/pom.xml @@ -218,6 +218,7 @@ function-composition microservices-distributed-tracing microservices-idempotent-consumer + session-facade 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/session-facade/README.md b/session-facade/README.md new file mode 100644 index 000000000000..cb5db50f6f3d --- /dev/null +++ b/session-facade/README.md @@ -0,0 +1,146 @@ +--- +title: "Session Facade Pattern in Java: Simplifying Complex System Interfaces" +shortTitle: Session Facade +description: "Learn how to implement the Session Facade Design Pattern in Java to create a unified interface for complex subsystems. Simplify your code and enhance maintainability with practical examples and use cases." +category: Structural +language: en +tag: + - Abstraction + - API design + - Code simplification + - Decoupling + - Encapsulation + - Gang Of Four + - Interface +--- + +## Also known as + +* Session Facade + +## Intent of Session Facade Design Pattern + +Abstracting the underlying business object interactions by providing a service layer that exposes only the required interfaces + +## Detailed Explanation of Session Facade Pattern with Real-World Examples + +Real-world example + +> In an e-commerce website, users interact with several subsystems like product catalogs, shopping carts, +> payment services, and order management. The Session Facade pattern provides a simplified, centralized interface for these subsystems, +> allowing the client to interact with just a few high-level methods (e.g., addToCart(), placeOrder(), selectPaymentMethod()), instead of directly communicating with each subsystem, using a facade supports low coupling between classes and high cohesion within each service, allowing them to focus on their specific responsibilities. + +In plain words + +> The Session Facade design pattern is an excellent choice for decoupling complex components of the system that need to be interacting frequently. + +## Programmatic Example of Session Facade Pattern in Java + +The Session Facade design pattern is a structural design pattern that provides a simplified interface to a set of complex subsystems, reducing the complexity for the client. This pattern is particularly useful in situations where the client needs to interact with multiple services or systems but doesn’t need to know the internal workings of each service. + +In the context of an e-commerce website, imagine a system where users can browse products, add items to the shopping cart, process payments, and place orders. Instead of the client directly interacting with each individual service (cart, order, payment), the Session Facade provides a single, unified interface for these operations. + +Example Scenario: +In this example, the ShoppingFacade class manages interactions with three subsystems: the `CartService`, `OrderService`, and `PaymentService`. The client interacts with the facade to perform high-level operations like adding items to the cart, placing an order, and selecting a payment method. + +Here’s a simplified programmatic example: +```java +public class App { + public static void main(String[] args) { + ShoppingFacade shoppingFacade = new ShoppingFacade(); + shoppingFacade.addToCart(1); + shoppingFacade.order(); + shoppingFacade.selectPaymentMethod("cash"); + } +} +``` + +The `ShoppingFacade` acts as an intermediary that facilitates interaction between different services promoting low coupling between these services. +```java +public class ShoppingFacade { + List productCatalog; + List cart; + CartService cartService; + OrderService orderService; + PaymentService paymentService; + + public ShoppingFacade() { + productCatalog = new ArrayList<>(); + productCatalog.add(new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); + productCatalog.add(new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); + cart = new ArrayList<>(); + cartService = new CartService(cart, productCatalog); + orderService = new OrderService(cart); + paymentService = new PaymentService(); + } + + public void addToCart(int productId) { + this.cartService.addToCart(productId); + } + + public void removeFromCart(int productId) { + this.cartService.removeFromCart(productId); + } + + public void order() { + this.orderService.order(); + } + + public void selectPaymentMethod(String method) { + this.paymentService.selectPaymentMethod(method); + } +} +``` + +Console output for starting the `App` class's `main` method: + +``` +19:43:17.883 [main] INFO com.iluwatar.sessionfacade.CartService -- ID: 1 +Name: Wireless Mouse +Price: $25.99 +Description: Ergonomic wireless mouse with USB receiver. successfully added to the cart +19:43:17.910 [main] INFO com.iluwatar.sessionfacade.OrderService -- Client has chosen to order [ID: 1 +Name: Wireless Mouse +Price: $25.99 +Description: Ergonomic wireless mouse with USB receiver.] +19:43:17.910 [main] INFO com.iluwatar.sessionfacade.PaymentService -- Client have chosen cash payment option +``` + +This is a basic example of the Session Facade design pattern. The actual implementation would depend on specific requirements of your application. + +## When to Use the Session Facade Pattern in Java + +* Use when building complex applications with multiple interacting services, where you want to simplify the interaction between various subsystems. +* Ideal for decoupling complex systems that need to interact but should not be tightly coupled. +* Suitable for applications where you need a single point of entry to interact with multiple backend services, like ecommerce platforms, booking systems, or order management systems. + +## Real-World Applications of Server Session Pattern in Java + +* Enterprise JavaBeans (EJB) +* Java EE (Jakarta EE) Applications + +## Benefits and Trade-offs of Server Session Pattern + + +* Simplifies client-side logic by providing a single entry point for complex operations across multiple services. +* Decouples components of the application, making them easier to maintain, test, and modify without affecting other parts of the system. +* Improves modularity by isolating the implementation details of subsystems from the client. +* Centralizes business logic in one place, making the code easier to manage and update. + +## Trade-offs: + +* Potential performance bottleneck: Since all requests pass through the facade, it can become a bottleneck if not optimized. +* Increased complexity: If the facade becomes too large or complex, it could counteract the modularity it aims to achieve. +* Single point of failure: If the facade encounters issues, it could affect the entire system's operation, making it crucial to handle errors and exceptions properly. + +## Related Java Design Patterns + +* [Facade](https://java-design-patterns.com/patterns/facade/): The Session Facade pattern is a specific application of the more general Facade pattern, which simplifies access to complex subsystems. +* [Command](https://java-design-patterns.com/patterns/command/): Useful for encapsulating requests and passing them to the session facade, which could then manage the execution order. +* [Singleton](https://java-design-patterns.com/patterns/singleton/): Often used to create a single instance of the session facade for managing the entire workflow of a subsystem. + +## References and Credits + +* [Core J2EE Patterns: Best Practices and Design Strategies](https://amzn.to/4cAbDap) +* [Design Patterns: Elements of Reusable Object-Oriented Software](https://amzn.to/3w0pvKI) +* [Patterns of Enterprise Application Architecture](https://amzn.to/3WfKBPR) diff --git a/session-facade/etc/session-facade.urm.png b/session-facade/etc/session-facade.urm.png new file mode 100644 index 0000000000000000000000000000000000000000..5e62265e874d8fc5c1283c012ec632b119d73557 GIT binary patch literal 102155 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 literal 0 HcmV?d00001 diff --git a/session-facade/etc/session-facade.urm.puml b/session-facade/etc/session-facade.urm.puml new file mode 100644 index 000000000000..bdcd730a72d3 --- /dev/null +++ b/session-facade/etc/session-facade.urm.puml @@ -0,0 +1,50 @@ +@startuml +package com.iluwatar.sessionfacade { + class App { + + App() + + main(args : String[]) {static} + } + class CartService { + - LOGGER : Logger {static} + - cart : List + - productCatalog : List + + CartService(cart : List, productCatalog : List) + + addToCart(productId : int) + + removeFromCart(productId : int) + } + class OrderService { + - LOGGER : Logger {static} + - products : List + + OrderService(products : List) + + order() + } + class PaymentService { + - LOGGER : Logger {static} + + PaymentService() + + cashPayment() + + creditCardPayment() + + selectPaymentMethod(method : String) + } + class ProductCatalogService { + - LOGGER : Logger {static} + - products : List + + ProductCatalogService(products : List) + + getProductDetails(productId : int) + } + class ShoppingFacade { + ~ cart : List + ~ cartService : CartService + ~ orderService : OrderService + ~ paymentService : PaymentService + ~ productCatalog : List + + ShoppingFacade() + + addToCart(productId : int) + + order() + + removeFromCart(productId : int) + + selectPaymentMethod(method : String) + } +} +ShoppingFacade --> "-cartService" CartService +ShoppingFacade --> "-paymentService" PaymentService +ShoppingFacade --> "-orderService" OrderService +@enduml \ No newline at end of file diff --git a/session-facade/pom.xml b/session-facade/pom.xml new file mode 100644 index 000000000000..57c103875168 --- /dev/null +++ b/session-facade/pom.xml @@ -0,0 +1,74 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.26.0-SNAPSHOT + + + session-facade + + + 17 + 17 + UTF-8 + + + + org.testng + testng + RELEASE + test + + + junit + junit + test + + + org.junit.jupiter + junit-jupiter + test + + + org.mockito + mockito-core + test + + + org.mockito + mockito-junit-jupiter + test + + + + \ No newline at end of file diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.java new file mode 100644 index 000000000000..210068df1b34 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/App.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.sessionfacade; + +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; + +/** + * The type App. + */ +public class App { + /** + * The entry point of application. + * + * @param args the input arguments + */ + public static void main(String[] args) { + ShoppingFacade shoppingFacade1 = new ShoppingFacade(); + shoppingFacade1.addToCart(1); + shoppingFacade1.order(); + shoppingFacade1.selectPaymentMethod("cash"); + + ShoppingFacade shoppingFacade2 = new ShoppingFacade(); + shoppingFacade1.addToCart(2); + shoppingFacade2.order(); + shoppingFacade2.selectPaymentMethod("credit"); + + } +} \ No newline at end of file diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java new file mode 100644 index 000000000000..0c2b6191d348 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/CartService.java @@ -0,0 +1,80 @@ +/* + * This project is licensed under 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.sessionfacade; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + +/** + * The type Cart service. + */ +@Slf4j +public class CartService { + + private final List cart; + private final List productCatalog; + + /** + * Instantiates a new Cart service. + * + * @param cart the cart + * @param productCatalog the product catalog + */ + public CartService(List cart, List productCatalog) { + this.cart = cart; + this.productCatalog = productCatalog; + } + + /** + * Add to cart. + * + * @param productId the product id + */ + public void addToCart(int productId) { + for (Product product : productCatalog) { + if (productId == product.id()) { + this.cart.add(product); + LOGGER.info("{} successfully added to the cart", product); + return; + } + } + LOGGER.info("No product is found with id {}", productId); + } + + /** + * Remove from cart. + * + * @param productId the product id + */ + public void removeFromCart(int productId) { + for (Product product : productCatalog) { + if (productId == product.id()) { + this.cart.remove(product); + LOGGER.info("{} successfully removed from the cart", product); + return; + } + } + LOGGER.info("No product is found with the id {}", productId); + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.java new file mode 100644 index 000000000000..82f322bd932d --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/OrderService.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.sessionfacade; +import java.util.List; +import lombok.extern.slf4j.Slf4j; + + +/** + * The type Order service. + */ +@Slf4j +public class OrderService { + private final List cart; + + /** + * Instantiates a new Order service. + * + * @param cart the cart + */ + public OrderService(List cart) { + this.cart = cart; + } + + /** + * Order. + */ + public void order() { + LOGGER.info("Client has chosen to order {}", cart); + cart.clear(); + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.java new file mode 100644 index 000000000000..9071b5ae128c --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/PaymentService.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.sessionfacade; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The type Payment service. + */ +@Slf4j +public class PaymentService { + public static Logger LOGGER = LoggerFactory.getLogger(PaymentService.class); + + /** + * Select payment method. + * + * @param method the method + */ + public void selectPaymentMethod(String method) { + if (method.equals("cash")) { + cashPayment(); + } else if (method.equals("credit")) { + creditCardPayment(); + } else { + LOGGER.info("Unspecified payment method type"); + } + } + + /** + * Cash payment. + */ + public void cashPayment() { + LOGGER.info("Client have chosen cash payment option"); + } + + /** + * Credit card payment. + */ + public void creditCardPayment() { + LOGGER.info("Client have chosen credit card payment option"); + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.java new file mode 100644 index 000000000000..fb234818da2b --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/Product.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.sessionfacade; + +import lombok.Getter; + +/** + * The type Product. + */ +public record Product(int id, String name, double price, String description) { + + @Override + public String toString() { + return "ID: " + id + "\nName: " + name + "\nPrice: $" + price + "\nDescription: " + description; + } + +} + + diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.java new file mode 100644 index 000000000000..eecffe21d9d7 --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/ProductCatalogService.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.sessionfacade; +import java.util.List; + +/** + * The type Product catalog service. + */ +public class ProductCatalogService { + private List products; + + /** + * Instantiates a new Product catalog service. + * + * @param products the products + */ + public ProductCatalogService(List products) { + this.products = products; + } +} diff --git a/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java b/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java new file mode 100644 index 000000000000..7f4ed63c885a --- /dev/null +++ b/session-facade/src/main/java/com/iluwatar/sessionfacade/ShoppingFacade.java @@ -0,0 +1,100 @@ +/* + * This project is licensed under 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.sessionfacade; +import java.util.ArrayList; +import java.util.List; + +/** + * The type Shopping facade. + */ +public class ShoppingFacade { + /** + * The Product catalog. + */ + List productCatalog = new ArrayList<>(); + /** + * The Cart. + */ + List cart = new ArrayList<>(); + /** + * The Cart service. + */ + CartService cartService = new CartService(cart, productCatalog); + /** + * The Order service. + */ + OrderService orderService = new OrderService(cart); + /** + * The Payment service. + */ + PaymentService paymentService = new PaymentService(); + + /** + * Instantiates a new Shopping facade. + */ + public ShoppingFacade() { + productCatalog = new ArrayList<>(); + productCatalog.add(new Product(1, "Wireless Mouse", 25.99, "Ergonomic wireless mouse with USB receiver.")); + productCatalog.add(new Product(2, "Gaming Keyboard", 79.99, "RGB mechanical gaming keyboard with programmable keys.")); + cart = new ArrayList<>(); + cartService = new CartService(cart, productCatalog); + orderService = new OrderService(cart); + paymentService = new PaymentService(); + } + + /** + * Add to cart. + * + * @param productId the product id + */ + public void addToCart(int productId) { + this.cartService.addToCart(productId); + } + + /** + * Remove from cart. + * + * @param productId the product id + */ + public void removeFromCart(int productId) { + this.cartService.removeFromCart(productId); + } + + /** + * Order. + */ + public void order() { + this.orderService.order(); + } + + /** + * Select payment method. + * + * @param method the method + */ + public void selectPaymentMethod(String method) { + this.paymentService.selectPaymentMethod(method); + } +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/AppTest.java new file mode 100644 index 000000000000..012fcf4623ad --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/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.sessionfacade; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; + +/** + * The type App test. + */ +public class AppTest { + + /** + * Should execute application without exception. + */ + @org.junit.jupiter.api.Test + void shouldExecuteApplicationWithoutException() { + assertDoesNotThrow(() -> App.main(new String[]{})); + } + +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.java new file mode 100644 index 000000000000..ca9cf4479bb5 --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/CartServiceTest.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.sessionfacade; +import static org.junit.jupiter.api.Assertions.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import lombok.extern.slf4j.Slf4j; +import org.mockito.MockitoAnnotations; +import java.util.ArrayList; +import java.util.List; + +/** + * The type Cart service test. + */ +@Slf4j +class CartServiceTest { + + private CartService cartService; + private List cart; + + /** + * Sets up. + */ + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + cart = new ArrayList<>(); + List productCatalog = new ArrayList<>(); + productCatalog.add(new Product(1, "Product A", 2.0, "any description")); + productCatalog.add(new Product(2, "Product B", 300.0, "a watch")); + cartService = new CartService(cart, productCatalog); + } + + /** + * Test add to cart. + */ + @Test + void testAddToCart() { + cartService.addToCart(1); + assertEquals(1, cart.size()); + assertEquals("Product A", cart.get(0).name()); + } + + /** + * Test remove from cart. + */ + @Test + void testRemoveFromCart() { + cartService.addToCart(1); + assertEquals(1, cart.size()); + cartService.removeFromCart(1); + assertTrue(cart.isEmpty()); + } + + /** + * Test add to cart with invalid product id. + */ + @Test + void testAddToCartWithInvalidProductId() { + cartService.addToCart(999); + assertTrue(cart.isEmpty()); + } + + /** + * Test remove from cart with invalid product id. + */ + @Test + void testRemoveFromCartWithInvalidProductId() { + cartService.removeFromCart(999); + assertTrue(cart.isEmpty()); + } +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.java new file mode 100644 index 000000000000..35daacced5b3 --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/PaymentServiceTest.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.sessionfacade; +import static org.mockito.Mockito.*; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; + +class PaymentServiceTest { + private PaymentService paymentService; + private Logger mockLogger; + + @BeforeEach + void setUp() { + paymentService = new PaymentService(); + mockLogger = mock(Logger.class); + paymentService.LOGGER = mockLogger; + } + + @Test + void testSelectCashPaymentMethod() { + String method = "cash"; + paymentService.selectPaymentMethod(method); + verify(mockLogger).info("Client have chosen cash payment option"); + } + + @Test + void testSelectCreditCardPaymentMethod() { + String method = "credit"; + paymentService.selectPaymentMethod(method); + verify(mockLogger).info("Client have chosen credit card payment option"); + } + + @Test + void testSelectUnspecifiedPaymentMethod() { + String method = "cheque"; + paymentService.selectPaymentMethod(method); + verify(mockLogger).info("Unspecified payment method type"); + } +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.java new file mode 100644 index 000000000000..2d664698551f --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/ProductTest.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.sessionfacade; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * The type Product test. + */ +public class ProductTest { + /** + * Test product creation. + */ + @Test + public void testProductCreation() { + int id = 1; + String name = "Product A"; + double price = 200.0; + String description = "a description"; + Product product = new Product(id,name,price,description); + assertEquals(id, product.id()); + assertEquals(name, product.name()); + assertEquals(price, product.price()); + assertEquals(description, product.description()); + } + + /** + * Test equals and hash code. + */ + @Test + public void testEqualsAndHashCode() { + Product product1 = new Product(1, "Product A", 99.99, "a description"); + Product product2 = new Product(1, "Product A", 99.99, "a description"); + Product product3 = new Product(2, "Product B", 199.99, "a description"); + + assertEquals(product1, product2); + assertNotEquals(product1, product3); + assertEquals(product1.hashCode(), product2.hashCode()); + assertNotEquals(product1.hashCode(), product3.hashCode()); + } + + /** + * Test to string. + */ + @Test + public void testToString() { + Product product = new Product(1, "Product A", 99.99, "a description"); + String toStringResult = product.toString(); + assertTrue(toStringResult.contains("Product A")); + assertTrue(toStringResult.contains("99.99")); + } + +} diff --git a/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java b/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.java new file mode 100644 index 000000000000..75c84beb12b2 --- /dev/null +++ b/session-facade/src/test/java/com/iluwatar/sessionfacade/ShoppingFacadeTest.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.sessionfacade; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +/** + * Unit tests for ShoppingFacade. + */ +class ShoppingFacadeTest { + + private ShoppingFacade shoppingFacade; + + @BeforeEach + void setUp() { + shoppingFacade = new ShoppingFacade(); + } + + @Test + void testAddToCart() { + shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + List cart = shoppingFacade.cart; + assertEquals(2, cart.size(), "Cart should contain two items."); + assertEquals("Wireless Mouse", cart.get(0).name(), "First item in the cart should be 'Wireless Mouse'."); + assertEquals("Gaming Keyboard", cart.get(1).name(), "Second item in the cart should be 'Gaming Keyboard'."); + } + + @Test + void testRemoveFromCart() { + shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + shoppingFacade.removeFromCart(1); + List cart = shoppingFacade.cart; + assertEquals(1, cart.size(), "Cart should contain one item after removal."); + assertEquals("Gaming Keyboard", cart.get(0).name(), "Remaining item should be 'Gaming Keyboard'."); + } + + @Test + void testOrder() { + shoppingFacade.addToCart(1); + shoppingFacade.addToCart(2); + shoppingFacade.order(); + assertTrue(shoppingFacade.cart.isEmpty(), "Cart should be empty after placing the order."); + } +} 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