Compare commits

..

70 Commits

Author SHA1 Message Date
allcontributors[bot]
3b87565fb6
docs: add HattoriHenzo as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-04-15 20:16:46 +03:00
HattoriHenzo
da9f26bbc0
fix: Correction of the CheckStyle issue the project acyclic-visitor () 2022-04-15 20:14:12 +03:00
Subhrodip Mohanta
dde31bfe34
enhancement: Update Gitignore for OSX () 2022-02-12 20:14:44 +02:00
allcontributors[bot]
1596e9048d
docs: add ShivanshCharak as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-01-30 21:01:10 +02:00
ShivanshCharak
a4aff05123
refactoring: modified modem.java () 2022-01-30 20:57:46 +02:00
Ilkka Seppälä
c87689b247
task: Update Multiton example ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-24 21:58:44 +05:30
Ilkka Seppälä
5ce0419b44
task: Update Maven version ()
* Update maven and maven wrapper to the latest versions

* Update maven-war-plugin version

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-24 21:57:02 +05:30
allcontributors[bot]
58ce3a2ab2
docs: add Kevinyl3 as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
2022-01-18 21:58:43 +02:00
Kevin
7652b11bca
new pattern: Issue#1264: Implemented Composite-View Pattern ()
* initial commit, created package, README, pom, and directory structure.

* Issue#1264, continue working on JavaBeans, added getters, setters, and private fields. Created test file for JavaBeans.

* set up junit for tests folder.

* Issue#1264, set up local server and added web-application framework to composite-view to allow the JSP to run on a local Tomcat container. Wrote unit tests for Java-bean class, working on JSP pages.

* Issue#1264, Added forwarding functionality to servlet and main composite view page.

* Issue#1264, Finished composite view template in newsDisplay.jsp and created atomic sub-view components in businessNews.jsp, header.jsp, localNews.jsp, scienceNews.jsp, sportsNews.jsp, worldNews.jsp. Composite view page renders correctly, atomic views are inserted in and substituted in the template page depending on request parameters.

* Issue#1264, Added all views, updated README.md with documentation.

* Issue#1264, updated README.md, moved images folder into etc folder.

* Issue#1264, removed build artifacts from tracked files.

* Issue#1264, updated README.md

* Issue#1264, updated README.md

* Issue#1264, removed unused import, made AppServlet class final, changed to .equals() for string comparison.

* Issue#1264, in AppServlet, put the output writing into try blocks to ensure writers are closed.

* Issue#1264, added tests for Servlet, coverage up to 100%, used lombok to reduce boilerplate setters and getter, updated README.md with better grammar, appropriate tags and links to related patterns. Updated pom.xml to get rid of superfluous lines.

* Issue#1264, made changes as requested in README.md.

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
2022-01-18 21:51:53 +02:00
Ilkka Seppälä
07ee94d671
refactoring: execute around idiom ()
* Refactor execute around the idiom

* fix checkstyle errors

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-10 17:04:19 +05:30
Ilkka Seppälä
c5492184b7
enhancement: check spelling and update topic ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-08 18:03:19 +05:30
Ilkka Seppälä
4f8007d674
enhancement: Refactor and add explanation for value object ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-08 18:01:09 +05:30
Ilkka Seppälä
2d2dec98e8
enhancement: Add explanation for factory kit ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-08 17:59:30 +05:30
Ilkka Seppälä
3cc9bc2dea
refactoring: unit of work ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2022-01-08 17:57:11 +05:30
Ilkka Seppälä
11f20593b2
Update throttling pattern ()
* Create component.urm.puml

* Create App.java

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Create AppTest.java

* Add files via upload

* Update README.md

* Update README.md

* Update pom.xml

* Update App.java

* Update BjornGraphicsComponent.java

* Update BjornInputComponent.java

* Update BjornPhysicsComponent.java

* Update Component.java

* Update App.java

* Delete App.java

* Delete BjornGraphicsComponent.java

* Delete BjornInputComponent.java

* Delete BjornPhysicsComponent.java

* Delete Component.java

* Delete GameObject.java

* Delete GraphicsComponent.java

* Delete InputComponent.java

* Delete PhysicsComponent.java

* Create App.java

* Update App.java

* Update App.java

* Create BjornGraphicsComponent.java

* Create BjornInputComponent.java

* Create BjornPhysicsComponent.java

* Create Component.java

* Create GameObject.java

* Create GraphicsComponent.java

* Create InputComponent.java

* Create PhysicsComponent.java

* Delete AppTest.java

* Delete UpdateTest.java

* Create AppTest.java

* Create UpdateTest.java

* Update throttling pattern example

* delete unwanted files

Co-authored-by: YanchaoMiao <11710204@mail.sustech.edu.cn>
2022-01-07 09:46:59 +02:00
Ilkka Seppälä
c66ca67201
add explanation for event aggregator () 2022-01-06 18:43:16 +02:00
allcontributors[bot]
2679f7aa6f
docs: add castleKing1997 as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2022-01-01 20:48:21 +02:00
DragonDreamer
8403fdacdd
feature: Metadata Mapping pattern ()
* metadata-mapping

* Update README.md

* add class diagram

* update README.md

* fix identation

* Update pom.xml

* fix indentation

* fix ci

* remove e.printstack

* fix ci

* update class diagram

* fix ci

* fix ci

* fix sc

* fix smells

* Update DatabaseUtil.java

* fix coverage

* Update DatabaseUtil.java

* Update DatabaseUtil.java

* Update DatabaseUtil.java

* Update metadata-mapping/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* fix review

* fix review

* Update App.java

* Update App.java

* fix review

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
2022-01-01 20:46:20 +02:00
allcontributors[bot]
f670ae547b
docs: add Leisterbecker as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-12-23 20:45:14 +02:00
Leisterbecker
df73d80365
improvement: EventAggregator now considers Event-Type ()
* Implemented more granular registration of observers with HashMap;
Key: Event, Value: List of Observers registered for the event;

* Edited constructors and super calls of all EventEmitter extenders

* Edited constructor calls in App.java

* Edited EventEmitterTest

* Added new white walkers event, scout emits at wednesday, varys observes and emits at saturday

* Added white walkers event to KingsHandTest.java

* Varys now passes events

* Corrected some indentation levels and added curly braces to if statements

* Corrected some styling

* Switched lines in App.java,
added javadoc to registerObserver

* Fixed some indents, added param comments
2021-12-23 20:43:36 +02:00
allcontributors[bot]
4588e09939
docs: add yuhangbin as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-12-23 18:11:48 +02:00
CharlieYu
69883196d2
improvement: Optimized NioReactor stop() (Reactor Pattern) ()
* Optimized NioReactor stop()

* Optimized ThreadPoolDispatcher stop()
2021-12-23 18:10:17 +02:00
allcontributors[bot]
4dcc20b733
docs: add interactwithankush as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-12-16 20:06:21 +05:30
interactwithankush
600227d2e4
fix: Sonar report - fix blocker and critical ones ()
* update SpatialPartitionBubbles - fix Sonar blocker issue

* fix Sonar critical issue - Define constant instead of duplicating the literal

* fix Sonar critical issue - remove unnecessary default constructor

* fix Sonar critical issue - Define constant instead of duplicating the literal

* fix Sonar critical issue - Define constant instead of duplicating the literal

* fix Sonar critical issue - Define constant instead of duplicating the literal

* fix Sonar critical issue - fix checkstyle issue

* fix Sonar critical issue - fix code smells

* fix Sonar critical issue - fix code smells

* fix Sonar critical issue - fix code smells

* fix sonarbugs - adding test cases for Commander class

* sonar fix - add assert commands in CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar fix - add test cases for CommanderTest

* sonar bug fix & test cases

* sonar bug fix & test cases

* sonar bug fix & test cases

* sonar bug fix & test cases

* sonar bug fix & test cases

* Revert "sonar bug fix & test cases"

This reverts commit 640dd55e35a9730e981d14665913f3d9b5b2d3b2.

* sonar bug fix & test cases

* sonar bug fix & test cases

* sonar bug fix & test cases

* sonar bug fix : avoid Thread.sleep

* sonar bug fix : cleanup Thread.sleep

* sonar bug fix: test commit

* sonar bug fix: test commit

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
Co-authored-by: atayal <Ankush_Tayal@intuit.com>
2021-12-16 20:05:13 +05:30
Ilkka Seppälä
fee898cd27
fix: Add language to claim check frontmatter ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2021-12-16 20:00:25 +05:30
Ilkka Seppälä
b1242629c8
fix: Grammatical fixes for Adapter ()
* Grammatical fixes

* Update adapter/README.md

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2021-12-16 19:59:06 +05:30
allcontributors[bot]
5d78a77b97
docs: add Shrirang97 as a contributor for review, code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-12-11 17:08:30 +02:00
Shrirang
9063336687
feature: Claim check pattern azure ()
* Archietecture Diagram added

* Added pom.xml

* Architecture Diagram Updated

* Added Microservices and kafka

* ReadME File Added

* ReadME file Updated

*  ReadMe file updated and working pattern code added

*  readme file updated

*  readme file updated and java documentation added

*  repository merged

* Update claim-check-pattern/ReadME.md

 Real world description updated

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Update claim-check-pattern/ReadME.md

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Applicability section in ReadMe file updated

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Tutorial section in ReadMe file updated

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Storage Data section in ReadMe File updated

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  workflow section Update claim-check-pattern/ReadME.md

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Update claim-check-pattern/pom.xml

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  deleted mvnw.cmd file

*  deleted  drawio file

*  deleted mvnw file

*  deleted mvnw file

*  mvnw.cmd file deleted

*  Update claim-check-pattern/usage-cost-processor/src/main/java/com/callusage/domain/MessageHeader.java

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Update claim-check-pattern/usage-cost-processor/src/main/java/com/callusage/domain/UsageCostDetail.java

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Update claim-check-pattern/usage-cost-processor/src/main/java/com/callusage/domain/UsageDetail.java

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  deleted mvnw file

*  deleted mvnw.cmd file

*  Update claim-check-pattern/usage-detail-sender/src/main/java/com/callusage/domain/Message.java

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  Update claim-check-pattern/ReadME.md

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>

*  pom file dependencies fixed, readmeflie updated, removed unused imports

*  Readfile updated, class javadoc added

*   UML class diagrams added, readme file updated

*   lombok annotations used on model classes, common dependencies moved to parent pom

*   test cases added

* include claim-check-pattern in parent pom

*  code smells fixed

*  code smells removed

*  security issues fixed

*  updated pom files and refactored packages name

*  checkstyle warning fixed

*  code coverage increased

*  minor changed.

* checkpoint created with common utility

* Claim-Check-Pattern | Shrirang97 | Implemented using Java Azure Functions

* Claim-Check-Pattern | Shrirang97 | Updated Functions logic

* Update MessageHandlerUtility.java

* Update UsageCostProcessorFunction.java

* claim-check-pattern | Shrirang97 | Added test cases

* claim-check-pattern | Shrirang97 | Test cases for Azure functions fixed

* Claim-Check-Pattern | Shrirang | Used string as request body

* claim-check-pattern | Shrirang | Working test cases

* claim-check-pattern | Shrirang | Issue fixed while deserializing

* claim-check-pattern | Shrirang | removed unused import

* claim-check-pattern | Shrirang | fixed refactoring

* claim-chek-pattern | Shrirang | added lombok | fixed dependencies & test cases

* Delete .DS_Store

* claim-check-pattern | Shrirang | Fixed unrelated file

* Update BookService.java

* Update BookService.java

* claim-check-pattern | Shrirang | Fixed unrelated files

* claim-check-pattern | Shrirang | Fixed review comments

* Update UsageCostProcessorFunction.java

* Update ReadME.md

* Rename ReadME.md to README.md

* claim-check-pattern | Shrirang | Incorporated review comments.

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update cloud-claim-check-pattern/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Updated readme file

* Read me file updated | Added more description

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
2021-12-11 16:59:36 +02:00
allcontributors[bot]
b22c8bc32f
docs: add Kevinyl3 as a contributor for review ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-12-11 16:34:05 +02:00
allcontributors[bot]
f7fc48d6b7
docs: add vashisthabhinav as a contributor for doc ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-12-06 11:59:55 +02:00
Abhinav Vashisth
414e3263b1
docs: Correcting some grammatical errors of README.md ()
* Update README.md

Corrected some sentences grammatically.

* Update README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
2021-12-06 11:56:46 +02:00
Ilkka Seppälä
2674cb9523
Set version for the next development iteration 2021-11-11 21:49:55 +02:00
Ilkka Seppälä
925755fa35
Reach milestone 1.25.0 2021-11-11 21:47:49 +02:00
Zhang WH
89b2072131
fix: use double check to fix concurrent problem ()
Co-authored-by: zhangwenhe <zhangwh@ehualu.com>
2021-11-11 19:46:24 +02:00
allcontributors[bot]
0a7b524bd1
docs: add harshalkh as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-31 09:12:05 +02:00
Harshal
36c6ce1df7
sonar fix: Update App.java ()
* Update App.java

Sonar issue fix

* Update App.java
2021-10-31 09:10:58 +02:00
Harshal
22ddd57146
sonar fix: Update App.java () 2021-10-31 09:08:49 +02:00
Subhrodip Mohanta
72bb189dc0
CI: Update Workflows ()
* Update Workflows



* Updated requested changes
2021-10-27 21:44:00 +03:00
Fiordy
bee1283371
docs: updated example with lombok as per code () 2021-10-24 17:27:24 +03:00
allcontributors[bot]
785cbf42b7
docs: add Fiordy as a contributor for doc ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-24 09:51:18 +03:00
Fiordy
c51eb66c89
docs: abstract-factory - readme - corrected code description as per real file () 2021-10-24 09:49:52 +03:00
allcontributors[bot]
1eb74203fc
docs: add uh-zz as a contributor for translation ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-24 09:41:49 +03:00
Reo Uehara
c6803f920d
translation: Add Japanese README () 2021-10-24 09:40:05 +03:00
Anum Amin
b80ac39e1f
docs: Add tutorial link for Acyclic Visitor pattern () () 2021-10-24 09:23:12 +03:00
Anum Amin
da6d20e997
docs: Add tutorial links for Builder pattern () () 2021-10-24 09:11:34 +03:00
Anum Amin
5dbb176a33
docs: Add tutorial links for Adapter pattern () () 2021-10-24 09:09:06 +03:00
Anum Amin
e3c3b02efd
docs: Add tutorial links for Active object pattern () () 2021-10-24 09:06:15 +03:00
allcontributors[bot]
988c478ad4
docs: add blueberry404 as a contributor for doc ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-24 09:04:52 +03:00
Anum Amin
221daf5d74
docs: Add tutorial links for Visitor pattern () () 2021-10-24 09:03:29 +03:00
allcontributors[bot]
90b1b922e1
docs: add ManviGoel26 as a contributor for doc ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-21 21:15:45 +03:00
Manvi Goel
bd71bc1311
docs: add explanation for twin pattern ()
* add explanation for twin pattern

* updated explanation of twin pattern
2021-10-21 21:14:10 +03:00
allcontributors[bot]
54d19d4c87
docs: add carldea as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

* Removed Conflicts

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
Co-authored-by: Subhrodip Mohanta <contact@subho.xyz>
2021-10-21 23:39:05 +05:30
allcontributors[bot]
464b166fa3
docs: add Mozartuss as a contributor for translation ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-21 20:40:58 +03:00
Mozartus
78abdb4e13
Fix typo, grammer and links () 2021-10-21 20:39:34 +03:00
Carl Dea
d1c26f9d84
task: update GHA JDK build vendor from adopt build to Zulu ()
* Using the latest LTS and fixed (major) versions of the OpenJDK.
Signed-off-by: Carl Dea <carldea@gmail.com>

* Using the latest LTS and fixed (major) versions of the OpenJDK. Removed 17 & 18-ea releases.
Signed-off-by: Carl Dea <carldea@gmail.com>

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>
2021-10-21 21:00:45 +05:30
Mozartus
ba51a49802
translation: Add a German README ()
* Add German Readme

* Add german readme
2021-10-20 21:29:15 +03:00
Victor
73eca64b9f
refactoring: Issue-1694: Improve caching pattern code quality ()
* Changed database implementation. Removed static objects.

* Fix Logs

* Fix 40 errors from checkstyle plugin run. 139 left))

* Fix CacheStore errors from checkstyle plugin 107 left

* Fix last errors in checkstyle.

* Fix sonar issues

* Fix issues in VALIDATE phase

* Fix Bug with mongo connection. Used "Try with resources"

* Add test

* Added docker-compose for mongo db. MongoDb db work fixed.

* Provided missing tests

* Comments to start Application with mongo.

* Fixes according PR comments. Mainly Readme edits.

* Remove duplicated imports
2021-10-20 20:44:01 +03:00
Subhrodip Mohanta
8aac45ab69
maintenance: Update Spacing of XML ()
PR Set 4 of 4
2021-10-19 20:56:44 +03:00
Subhrodip Mohanta
2c93c81cf9
maintenance: Update Spacing of XML ()
PR Set 3 of 4
2021-10-19 20:54:24 +03:00
Subhrodip Mohanta
656b441e29
maintenance: Update Spacing of XML ()
PR Set 2 of 4
2021-10-19 20:51:43 +03:00
Subhrodip Mohanta
37d9ff3342
maintenance: Update Spacing of XML ()
PR Set 1 of 4
2021-10-19 20:49:46 +03:00
Subhrodip Mohanta
d9dae3ae6a
doc fix: Recalculate already added rows. () 2021-10-19 20:06:38 +03:00
Subhrodip Mohanta
1354bf0f55
docs: Update README.md to accommodate 7 contributors per row ()
* Update per line Contributor to 7 from 4

This will be helpful to reduce page size and shorten the length of the README, which is our landing page in GitHub and help us make it clutter free

* Update the Readme to accommodate upto 7 rows
2021-10-16 20:19:25 +03:00
allcontributors[bot]
dc7c99c32f
docs: add Dev-AliGhasemi as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-16 20:11:23 +03:00
Ali Ghasemi
c2786e5dc4
add monitor design pattern ()
* add monitor design pattern .

* add extra line and change compiler version to 11 in pom.xml.

* encapsulate getBalance method .

* update puml file .

* export uml as png .

* duplicate codes eliminated .

* update tag

* change the format of pom.xml

* using logger to print

* change AtomicRefrence to type inference var

* explanations added !

* Update monitor/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update monitor/README.md

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update monitor/src/main/java/com/iluwatar/monitor/Main.java

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update monitor/src/main/java/com/iluwatar/monitor/Main.java

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update monitor/src/main/java/com/iluwatar/monitor/Main.java

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* Update monitor/src/main/java/com/iluwatar/monitor/Main.java

Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>

* e.printStackTrace have changed to logger to prints standard output (STD OUT) .

* add programmatic example .

* Delete mvnw

* mvnw.cmd deleted .

* added mvnw from master

* AddUnitTest

* Add language to readme.md

Co-authored-by: Subhrodip Mohanta <subhrodipmohanta@gmail.com>
Co-authored-by: Ilkka Seppälä <iluwatar@users.noreply.github.com>
Co-authored-by: Subhrodip Mohanta <subhromo@cisco.com>
Co-authored-by: Subhrodip Mohanta <contact@subho.xyz>
2021-10-16 20:08:53 +03:00
allcontributors[bot]
a1f3c6fe20
docs: add JCarlosR as a contributor for translation ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-15 20:27:23 +03:00
JCarlos
0ad44ced24
docs: Fix typos spanish readme and factory ()
* Fix typos for Spanish README

* Fix typos in the factory example
2021-10-15 20:24:49 +03:00
allcontributors[bot]
4c5c0fd63e
docs: add sims-keshri as a contributor for code ()
* docs: update README.md [skip ci]

* docs: update .all-contributorsrc [skip ci]

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com>
2021-10-13 13:17:48 +05:30
Simran Keshri
5a644f1092
refactoring: Critical Sonar Issues ()
* Resolve Sonar Code Smell: Define a constant instead of duplicating this literal 'Space rocket <' 4 times.

* Resolve Sonar Critical Code Smell: Define a constant instead of duplicating this literal 'Error connecting to MongoDB' 4 times.

* Fix checkstyle violation.

* Resolve Sonar Critical Code Smell: Define a constant instead of duplicating this literal 'LITERAL 0' 4 times.

Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2021-10-13 13:15:22 +05:30
Ilkka Seppälä
cab9048e06
docs: fixes for yaml frontmatter ()
Co-authored-by: Subhrodip Mohanta <hello@subho.xyz>
2021-10-13 12:59:55 +05:30
324 changed files with 11362 additions and 6527 deletions
.all-contributorsrc
.github/workflows
.gitignore
.mvn/wrapper
README.md
abstract-document
abstract-factory
active-object
acyclic-visitor
README.mdpom.xml
src/main/java/com/iluwatar/acyclicvisitor
adapter
README.mdpom.xml
src/test/java/com/iluwatar/adapter
aggregator-microservices
aggregator-service
information-microservice
inventory-microservice
pom.xml
ambassador
api-gateway
api-gateway-service
image-microservice
pom.xml
price-microservice
arrange-act-assert
async-method-invocation
pom.xml
src/main/java/com/iluwatar/async/method/invocation
balking
bridge
builder
business-delegate
bytecode
pom.xml
src/main/java/com/iluwatar/bytecode
caching
etc
pom.xml
src/test/java/com/iluwatar/caching/database
callback
chain-of-responsibility
checkstyle-suppressions.xml
circuit-breaker
cloud-claim-check-pattern
collection-pipeline
combinator
command
pom.xml
src/main/java/com/iluwatar/command
commander
pom.xml
src
main/java/com/iluwatar/commander
test/java/com/iluwatar/commander
composite-entity
composite-view

@ -16,6 +16,17 @@
"content"
]
},
{
"login": "ohbus",
"name": "Subhrodip Mohanta",
"avatar_url": "https://avatars0.githubusercontent.com/u/13291222?v=4",
"profile": "http://subho.xyz",
"contributions": [
"code",
"review",
"maintenance"
]
},
{
"login": "amit1307",
"name": "amit1307",
@ -1125,17 +1136,6 @@
"ideas"
]
},
{
"login": "ohbus",
"name": "Subhrodip Mohanta",
"avatar_url": "https://avatars0.githubusercontent.com/u/13291222?v=4",
"profile": "http://subho.xyz",
"contributions": [
"code",
"review",
"maintenance"
]
},
{
"login": "nahteb",
"name": "Bethan Palmer",
@ -1631,9 +1631,182 @@
"contributions": [
"translation"
]
},
{
"login": "sims-keshri",
"name": "Simran Keshri",
"avatar_url": "https://avatars.githubusercontent.com/u/62168475?v=4",
"profile": "https://github.com/sims-keshri",
"contributions": [
"code"
]
},
{
"login": "JCarlosR",
"name": "JCarlos",
"avatar_url": "https://avatars.githubusercontent.com/u/3101238?v=4",
"profile": "https://programacionymas.com",
"contributions": [
"translation"
]
},
{
"login": "Dev-AliGhasemi",
"name": "Ali Ghasemi",
"avatar_url": "https://avatars.githubusercontent.com/u/60359433?v=4",
"profile": "https://www.mrmoshkel.ir",
"contributions": [
"code"
]
},
{
"login": "carldea",
"name": "Carl Dea",
"avatar_url": "https://avatars.githubusercontent.com/u/1594624?v=4",
"profile": "http://carlfx.wordpress.com",
"contributions": [
"code"
]
},
{
"login": "Mozartuss",
"name": "Mozartus",
"avatar_url": "https://avatars.githubusercontent.com/u/32893711?v=4",
"profile": "https://github.com/Mozartuss",
"contributions": [
"translation"
]
},
{
"login": "ManviGoel26",
"name": "Manvi Goel",
"avatar_url": "https://avatars.githubusercontent.com/u/55682355?v=4",
"profile": "https://github.com/ManviGoel26",
"contributions": [
"doc"
]
},
{
"login": "blueberry404",
"name": "Anum Amin",
"avatar_url": "https://avatars.githubusercontent.com/u/39243539?v=4",
"profile": "https://github.com/blueberry404",
"contributions": [
"doc"
]
},
{
"login": "uh-zz",
"name": "Reo Uehara",
"avatar_url": "https://avatars.githubusercontent.com/u/47747828?v=4",
"profile": "https://uh-zz.github.io/blog/",
"contributions": [
"translation"
]
},
{
"login": "Fiordy",
"name": "Fiordy",
"avatar_url": "https://avatars.githubusercontent.com/u/53420573?v=4",
"profile": "https://github.com/Fiordy",
"contributions": [
"doc"
]
},
{
"login": "harshalkh",
"name": "Harshal",
"avatar_url": "https://avatars.githubusercontent.com/u/37841724?v=4",
"profile": "https://github.com/harshalkh",
"contributions": [
"code"
]
},
{
"login": "vashisthabhinav",
"name": "Abhinav Vashisth",
"avatar_url": "https://avatars.githubusercontent.com/u/89785800?v=4",
"profile": "https://www.linkedin.com/in/abhinav-vashisth-06613b208/",
"contributions": [
"doc"
]
},
{
"login": "Kevinyl3",
"name": "Kevin",
"avatar_url": "https://avatars.githubusercontent.com/u/47126749?v=4",
"profile": "http://no website",
"contributions": [
"review",
"code"
]
},
{
"login": "Shrirang97",
"name": "Shrirang",
"avatar_url": "https://avatars.githubusercontent.com/u/28738668?v=4",
"profile": "https://github.com/Shrirang97",
"contributions": [
"review",
"code"
]
},
{
"login": "interactwithankush",
"name": "interactwithankush",
"avatar_url": "https://avatars.githubusercontent.com/u/18613127?v=4",
"profile": "https://github.com/interactwithankush",
"contributions": [
"code"
]
},
{
"login": "yuhangbin",
"name": "CharlieYu",
"avatar_url": "https://avatars.githubusercontent.com/u/17566866?v=4",
"profile": "https://github.com/yuhangbin",
"contributions": [
"code"
]
},
{
"login": "Leisterbecker",
"name": "Leisterbecker",
"avatar_url": "https://avatars.githubusercontent.com/u/20650323?v=4",
"profile": "https://github.com/Leisterbecker",
"contributions": [
"code"
]
},
{
"login": "castleKing1997",
"name": "DragonDreamer",
"avatar_url": "https://avatars.githubusercontent.com/u/35420129?v=4",
"profile": "http://rosaecrucis.cn",
"contributions": [
"code"
]
},
{
"login": "ShivanshCharak",
"name": "ShivanshCharak",
"avatar_url": "https://avatars.githubusercontent.com/u/96943825?v=4",
"profile": "https://github.com/ShivanshCharak",
"contributions": [
"code"
]
},
{
"login": "HattoriHenzo",
"name": "HattoriHenzo",
"avatar_url": "https://avatars.githubusercontent.com/u/5141285?v=4",
"profile": "https://github.com/HattoriHenzo",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 4,
"contributorsPerLine": 7,
"projectName": "java-design-patterns",
"projectOwner": "iluwatar",
"repoType": "github",

@ -24,6 +24,9 @@
# This workflow will build a Java project with Maven
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
# We are using two jobs here for testing our code on the latest JDK 11 build as well as a more satble build version of 11.0.3
# You can see the full discussion here https://github.com/iluwatar/java-design-patterns/pull/1868#issue-1029459688
name: Java CI
on:
@ -33,10 +36,11 @@ on:
jobs:
build:
# This Workflow Job will build this project and run Sonar analysis using JDK 11.0.3
build-and-analyze:
name: Build and Run Sonar analysis on JDK 11.0.3
runs-on: ubuntu-20.04
steps:
- name: Checkout Code
@ -45,12 +49,14 @@ jobs:
# Disabling shallow clone for improving relevancy of SonarQube reporting
fetch-depth: 0
- name: Set up JDK 11
- name: Set up JDK 11.0.3
uses: actions/setup-java@v2
with:
java-version: 11
distribution: 'adopt'
java-version: 11.0.3
distribution: 'zulu'
cache: 'maven'
# Cache Sonar packages which as used to run anaylysis and collect metrics
- name: Cache SonarCloud packages
uses: actions/cache@v2
with:
@ -58,14 +64,6 @@ jobs:
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Maven dependencies
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
# Some tests need screen access
- name: Install xvfb
run: sudo apt-get install -y xvfb
@ -76,3 +74,28 @@ jobs:
# These two env variables are needed for sonar analysis
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
# This Workflow Job is going to build the project on the latest stable JDK 11
build:
name: Build and Test on JDK 11
runs-on: ubuntu-20.04
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Set up JDK 11 (Latest)
uses: actions/setup-java@v2
with:
java-version: 11
distribution: 'zulu'
cache: 'maven'
# Some tests need screen access
- name: Install xvfb
run: sudo apt-get install -y xvfb
- name: Build with Maven
run: xvfb-run ./mvnw clean verify

@ -34,25 +34,22 @@ on:
jobs:
build:
name: Build JDP
runs-on: ubuntu-20.04
strategy:
matrix:
java-version: [ 11.0.3, 11 ]
steps:
- name: Checkout Code
uses: actions/checkout@v2
- name: Set up JDK 11
- name: Set up JDK ${{ matrix.java-version }}
uses: actions/setup-java@v2
with:
java-version: 11
distribution: 'adopt'
- name: Cache Maven Dependecies
uses: actions/cache@v2
with:
path: ~/.m2/repository
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
restore-keys: |
${{ runner.os }}-maven-
java-version: ${{ matrix.java-version }}
distribution: 'zulu'
cache: 'maven'
# Some tests need screen access
- name: Install xvfb

1
.gitignore vendored

@ -12,6 +12,7 @@ tmp/
local.properties
.loadpath
.recommenders
.DS_Store
####### Java annotation processor (APT) ########
.factorypath

@ -1,25 +1,18 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar

135
README.md

@ -10,18 +10,18 @@
[![Coverage](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=coverage)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
[![All Contributors](https://img.shields.io/badge/all_contributors-179-orange.svg?style=flat-square)](#contributors-)
[![All Contributors](https://img.shields.io/badge/all_contributors-198-orange.svg?style=flat-square)](#contributors-)
<!-- ALL-CONTRIBUTORS-BADGE:END -->
<br/>
Read in different language : [**zh**](localization/zh/README.md), [**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md)
Read in different language : [**zh**](localization/zh/README.md), [**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md), [**de**](localization/de/README.md), [**ja**](localization/ja/README.md)
<br/>
# Introduction
Design patterns are the best formalized practices a programmer can use to
Design patterns are the best, formalized practices a programmer can use to
solve common problems when designing an application or system.
Design patterns can speed up the development process by providing tested, proven
@ -34,11 +34,11 @@ are familiar with the patterns.
# Getting started
This site showcases Java Design Patterns. The solutions have been developed by
experienced programmers and architects from the open source community. The
patterns can be browsed by their high level descriptions or by looking at their
source code. The source code examples are well commented and can be thought as
experienced programmers and architects from the open-source community. The
patterns can be browsed by their high-level descriptions or by looking at their
source code. The source code examples are well commented and can be thought of as
programming tutorials on how to implement a specific pattern. We use the most
popular battle-proven open source Java technologies.
popular battle-proven open-source Java technologies.
Before you dive into the material, you should be familiar with various
[Software Design Principles](https://java-design-patterns.com/principles/).
@ -56,8 +56,8 @@ of the following approaches
- Using tags such as `Performance`, `Gang of Four` or `Data access`.
- Using pattern categories, `Creational`, `Behavioral`, and others.
Hopefully you find the object oriented solutions presented on this site useful
in your architectures and have as much fun learning them as we had developing them.
Hopefully, you find the object-oriented solutions presented on this site useful
in your architectures and have as much fun learning them as we had while developing them.
# How to contribute
@ -77,29 +77,26 @@ This project is licensed under the terms of the MIT license.
<table>
<tr>
<td align="center"><a href="https://github.com/iluwatar"><img src="https://avatars1.githubusercontent.com/u/582346?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ilkka Seppälä</b></sub></a><br /><a href="#projectManagement-iluwatar" title="Project Management">📆</a> <a href="#maintenance-iluwatar" title="Maintenance">🚧</a> <a href="#content-iluwatar" title="Content">🖋</a></td>
<td align="center"><a href="http://subho.xyz"><img src="https://avatars0.githubusercontent.com/u/13291222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Subhrodip Mohanta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ohbus" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aohbus" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-ohbus" title="Maintenance">🚧</a></td>
<td align="center"><a href="https://github.com/amit1307"><img src="https://avatars0.githubusercontent.com/u/23420222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>amit1307</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit1307" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/npathai"><img src="https://avatars2.githubusercontent.com/u/1792515?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Narendra Pathai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npathai" title="Code">💻</a> <a href="#ideas-npathai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Anpathai" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/fluxw42"><img src="https://avatars1.githubusercontent.com/u/1545460?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeroen Meulemeester</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fluxw42" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.joemccarthy.co.uk"><img src="https://avatars0.githubusercontent.com/u/4526195?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joseph McCarthy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mikulucky" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/thomasoss"><img src="https://avatars1.githubusercontent.com/u/22516154?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thomas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=thomasoss" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/anuragagarwal561994"><img src="https://avatars1.githubusercontent.com/u/6075379?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag Agarwal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anuragagarwal561994" title="Code">💻</a></td>
<td align="center"><a href="https://markusmo3.github.io"><img src="https://avatars1.githubusercontent.com/u/3317416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Markus Moser</b></sub></a><br /><a href="#design-markusmo3" title="Design">🎨</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=markusmo3" title="Code">💻</a> <a href="#ideas-markusmo3" title="Ideas, Planning, & Feedback">🤔</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/anuragagarwal561994"><img src="https://avatars1.githubusercontent.com/u/6075379?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag Agarwal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anuragagarwal561994" title="Code">💻</a></td>
<td align="center"><a href="https://markusmo3.github.io"><img src="https://avatars1.githubusercontent.com/u/3317416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Markus Moser</b></sub></a><br /><a href="#design-markusmo3" title="Design">🎨</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=markusmo3" title="Code">💻</a> <a href="#ideas-markusmo3" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="https://twitter.com/i_sabiq"><img src="https://avatars1.githubusercontent.com/u/19510920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sabiq Ihab</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=isabiq" title="Code">💻</a></td>
<td align="center"><a href="http://inbravo.github.io"><img src="https://avatars3.githubusercontent.com/u/5253764?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Dixit</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=inbravo" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/piyushchaudhari04"><img src="https://avatars3.githubusercontent.com/u/10268029?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piyush Kailash Chaudhari</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=piyushchaudhari04" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/joshzambales"><img src="https://avatars1.githubusercontent.com/u/8704552?v=4?s=100" width="100px;" alt=""/><br /><sub><b>joshzambales</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joshzambales" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Crossy147"><img src="https://avatars2.githubusercontent.com/u/7272996?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kamil Pietruszka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Crossy147" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Crossy147"><img src="https://avatars2.githubusercontent.com/u/7272996?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kamil Pietruszka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Crossy147" title="Code">💻</a></td>
<td align="center"><a href="http://cs.joensuu.fi/~zkhayda"><img src="https://avatars2.githubusercontent.com/u/660742?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zafar Khaydarov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Documentation">📖</a></td>
<td align="center"><a href="https://kemitix.github.io/"><img src="https://avatars1.githubusercontent.com/u/1147749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul Campbell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kemitix" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Argyro-Sioziou"><img src="https://avatars0.githubusercontent.com/u/22822639?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Argyro Sioziou</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Argyro-Sioziou" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/TylerMcConville"><img src="https://avatars0.githubusercontent.com/u/4946449?v=4?s=100" width="100px;" alt=""/><br /><sub><b>TylerMcConville</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=TylerMcConville" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/saksham93"><img src="https://avatars1.githubusercontent.com/u/37399540?v=4?s=100" width="100px;" alt=""/><br /><sub><b>saksham93</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=saksham93" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/nikhilbarar"><img src="https://avatars2.githubusercontent.com/u/37332144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nikhilbarar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nikhilbarar" title="Code">💻</a></td>
@ -110,38 +107,32 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/JuhoKang"><img src="https://avatars1.githubusercontent.com/u/4745294?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juho Kang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=JuhoKang" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dheeraj-mummareddy"><img src="https://avatars2.githubusercontent.com/u/7002230?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dheeraj Mummareddy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dheeraj-mummareddy" title="Code">💻</a></td>
<td align="center"><a href="https://www.bernardosulzbach.com"><img src="https://avatars0.githubusercontent.com/u/8271090?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bernardo Sulzbach</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=bernardosulzbach" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/4lexis"><img src="https://avatars0.githubusercontent.com/u/19871727?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandar Dudukovic</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=4lexis" title="Code">💻</a></td>
<td align="center"><a href="https://www.yusufaytas.com"><img src="https://avatars2.githubusercontent.com/u/1049483?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yusuf Aytaş</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yusufaytas" title="Code">💻</a></td>
<td align="center"><a href="http://futurehomes.hu"><img src="https://avatars2.githubusercontent.com/u/1001491?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihály Kuprivecz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qpi" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kapinuss"><img src="https://avatars0.githubusercontent.com/u/17639945?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stanislav Kapinus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kapinuss" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/kapinuss"><img src="https://avatars0.githubusercontent.com/u/17639945?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stanislav Kapinus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kapinuss" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/gvsharma"><img src="https://avatars1.githubusercontent.com/u/6648152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GVSharma</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gvsharma" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/SrdjanPaunovic"><img src="https://avatars1.githubusercontent.com/u/22815104?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Srđan Paunović</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=SrdjanPaunovic" title="Code">💻</a></td>
<td align="center"><a href="https://sideris.xyz/"><img src="https://avatars3.githubusercontent.com/u/5484694?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Petros G. Sideris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sideris" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/pramodgupta3/"><img src="https://avatars1.githubusercontent.com/u/2184241?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pramod Gupta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AIAmPramod" title="Reviewed Pull Requests">👀</a></td>
</tr>
<tr>
<td align="center"><a href="https://amarnath510.github.io/portfolio"><img src="https://avatars0.githubusercontent.com/u/4599623?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amarnath Chandana</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Amarnath510" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Anurag870"><img src="https://avatars1.githubusercontent.com/u/6295975?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag870</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Anurag870" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Anurag870" title="Documentation">📖</a></td>
<td align="center"><a href="http://theerroris.me"><img src="https://avatars0.githubusercontent.com/u/1685953?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wes Gilleland</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Deathnerd" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Harshrajsinh"><img src="https://avatars2.githubusercontent.com/u/22811531?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harshraj Thakor</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Harshrajsinh" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://theerroris.me"><img src="https://avatars0.githubusercontent.com/u/1685953?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wes Gilleland</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Deathnerd" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Harshrajsinh"><img src="https://avatars2.githubusercontent.com/u/22811531?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harshraj Thakor</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Harshrajsinh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MaVdbussche"><img src="https://avatars1.githubusercontent.com/u/26136934?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin Vandenbussche</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MaVdbussche" title="Code">💻</a></td>
<td align="center"><a href="https://alexsomai.com"><img src="https://avatars1.githubusercontent.com/u/5720977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandru Somai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=alexsomai" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/amogozov"><img src="https://avatars3.githubusercontent.com/u/7372215?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artur Mogozov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amogozov" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/anthonycampbell"><img src="https://avatars3.githubusercontent.com/u/10249255?v=4?s=100" width="100px;" alt=""/><br /><sub><b>anthony</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anthonycampbell" title="Code">💻</a></td>
<td align="center"><a href="http://ccygnus.com/"><img src="https://avatars1.githubusercontent.com/u/9342724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Cygnus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christophercolumbusdog" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://ccygnus.com/"><img src="https://avatars1.githubusercontent.com/u/9342724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Cygnus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christophercolumbusdog" title="Code">💻</a></td>
<td align="center"><a href="https://about.me/dzmitryh"><img src="https://avatars2.githubusercontent.com/u/5390492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dima Gubin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dzmitryh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jjjimenez100"><img src="https://avatars3.githubusercontent.com/u/22243493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joshua Jimenez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jjjimenez100" title="Code">💻</a></td>
<td align="center"><a href="http://about.me/kaiwinter"><img src="https://avatars0.githubusercontent.com/u/110982?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Winter</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kaiwinter" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/lbroman"><img src="https://avatars1.githubusercontent.com/u/86007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lbroman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=lbroman" title="Code">💻</a></td>
<td align="center"><a href="https://przemeknowak.com"><img src="https://avatars1.githubusercontent.com/u/3254609?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Przemek</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pnowy" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/prafful1"><img src="https://avatars0.githubusercontent.com/u/14350274?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Prafful Agarwal</b></sub></a><br /><a href="#content-prafful1" title="Content">🖋</a></td>
@ -152,38 +143,32 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/valdar-hu"><img src="https://avatars3.githubusercontent.com/u/17962817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krisztián Nagy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=valdar-hu" title="Code">💻</a></td>
<td align="center"><a href="https://www.vanogrid.com"><img src="https://avatars0.githubusercontent.com/u/4307918?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexander Ivanov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vanogrid" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yosfik"><img src="https://avatars3.githubusercontent.com/u/4850270?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yosfik Alqadri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yosfik" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/7agustibm"><img src="https://avatars0.githubusercontent.com/u/8149332?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Agustí Becerra Milà</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=7agustibm" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Juaanma"><img src="https://avatars3.githubusercontent.com/u/7390500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juan Manuel Suárez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Juaanma" title="Code">💻</a></td>
<td align="center"><a href="http://www.devsedge.net/"><img src="https://avatars0.githubusercontent.com/u/9956006?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luigi Cortese</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=LuigiCortese" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Rzeposlaw"><img src="https://avatars2.githubusercontent.com/u/18425745?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Katarzyna Rzepecka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Rzeposlaw" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Rzeposlaw"><img src="https://avatars2.githubusercontent.com/u/18425745?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Katarzyna Rzepecka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Rzeposlaw" title="Code">💻</a></td>
<td align="center"><a href="http://adamski.pro"><img src="https://avatars1.githubusercontent.com/u/6537430?v=4?s=100" width="100px;" alt=""/><br /><sub><b>adamski.pro</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=akrystian" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/baislsl"><img src="https://avatars0.githubusercontent.com/u/17060584?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shengli Bai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=baislsl" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/besok"><img src="https://avatars2.githubusercontent.com/u/29834592?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=besok" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/dmitraver"><img src="https://avatars3.githubusercontent.com/u/1798156?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dmitry Avershin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dmitraver" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/fanofxiaofeng"><img src="https://avatars0.githubusercontent.com/u/3983683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>靳阳</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fanofxiaofeng" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hoangnam2261"><img src="https://avatars2.githubusercontent.com/u/31692990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hoangnam2261</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoangnam2261" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jarpit96"><img src="https://avatars2.githubusercontent.com/u/10098713?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Jain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jarpit96" title="Code">💻</a></td>
<td align="center"><a href="http://joningi.net"><img src="https://avatars2.githubusercontent.com/u/6115148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jón Ingi Sveinbjörnsson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joningiwork" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/jarpit96"><img src="https://avatars2.githubusercontent.com/u/10098713?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Jain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jarpit96" title="Code">💻</a></td>
<td align="center"><a href="http://joningi.net"><img src="https://avatars2.githubusercontent.com/u/6115148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jón Ingi Sveinbjörnsson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joningiwork" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kirill-vlasov"><img src="https://avatars3.githubusercontent.com/u/16112495?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kirill Vlasov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kirill-vlasov" title="Code">💻</a></td>
<td align="center"><a href="http://mitchell-irvin.com"><img src="https://avatars0.githubusercontent.com/u/16233245?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mitchell Irvin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mitchellirvin" title="Code">💻</a></td>
<td align="center"><a href="https://ranjeet-floyd.github.io"><img src="https://avatars0.githubusercontent.com/u/1992972?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ranjeet</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ranjeet-floyd" title="Code">💻</a></td>
<td align="center"><a href="https://alwayswithme.github.io"><img src="https://avatars3.githubusercontent.com/u/3234786?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PhoenixYip</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Alwayswithme" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/MSaifAsif"><img src="https://avatars1.githubusercontent.com/u/6280554?v=4?s=100" width="100px;" alt=""/><br /><sub><b>M Saif Asif</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MSaifAsif" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/MSaifAsif"><img src="https://avatars1.githubusercontent.com/u/6280554?v=4?s=100" width="100px;" alt=""/><br /><sub><b>M Saif Asif</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MSaifAsif" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/kanwarpreet25"><img src="https://avatars0.githubusercontent.com/u/39183641?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kanwarpreet25</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kanwarpreet25" title="Code">💻</a></td>
<td align="center"><a href="http://leonmak.me"><img src="https://avatars3.githubusercontent.com/u/13071508?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leon Mak</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=leonmak" title="Code">💻</a></td>
<td align="center"><a href="http://www.wramdemark.se"><img src="https://avatars2.githubusercontent.com/u/7052193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Per Wramdemark</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=perwramdemark" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/waisuan"><img src="https://avatars2.githubusercontent.com/u/10975700?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Evan Sia Wai Suan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=waisuan" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/AnaghaSasikumar"><img src="https://avatars2.githubusercontent.com/u/42939261?v=4?s=100" width="100px;" alt=""/><br /><sub><b>AnaghaSasikumar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=AnaghaSasikumar" title="Code">💻</a></td>
<td align="center"><a href="https://christofferh.com"><img src="https://avatars1.githubusercontent.com/u/767643?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christoffer Hamberg</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christofferh" title="Code">💻</a></td>
@ -194,38 +179,32 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/leogtzr"><img src="https://avatars0.githubusercontent.com/u/1211969?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leo Gutiérrez Ramírez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=leogtzr" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/npczwh"><img src="https://avatars0.githubusercontent.com/u/14066422?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zhang WH</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npczwh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/oconnelc"><img src="https://avatars0.githubusercontent.com/u/1112973?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christopher O'Connell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=oconnelc" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/giorgosmav21"><img src="https://avatars2.githubusercontent.com/u/22855493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Mavroeidis</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=giorgosmav21" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hbothra15"><img src="https://avatars1.githubusercontent.com/u/7418012?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hemant Bothra</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hbothra15" title="Code">💻</a> <a href="#design-hbothra15" title="Design">🎨</a></td>
<td align="center"><a href="https://www.kevinpeters.net/about/"><img src="https://avatars1.githubusercontent.com/u/12736734?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Peters</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=igeligel" title="Code">💻</a></td>
<td align="center"><a href="https://llorllale.github.io/"><img src="https://avatars1.githubusercontent.com/u/2019896?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Aristy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=llorllale" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://llorllale.github.io/"><img src="https://avatars1.githubusercontent.com/u/2019896?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Aristy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=llorllale" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mookkiah"><img src="https://avatars1.githubusercontent.com/u/8975264?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mahendran Mookkiah</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mookkiah" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Azureyjt"><img src="https://avatars2.githubusercontent.com/u/18476317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Azureyjt</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Azureyjt" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vehpsr"><img src="https://avatars2.githubusercontent.com/u/3133265?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vehpsr" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ThatGuyWithTheHat"><img src="https://avatars0.githubusercontent.com/u/24470582?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt</b></sub></a><br /><a href="#content-ThatGuyWithTheHat" title="Content">🖋</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.linkedin.com/in/gopinathlangote/"><img src="https://avatars2.githubusercontent.com/u/10210778?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gopinath Langote</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gopinath-langote" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/hoswey"><img src="https://avatars3.githubusercontent.com/u/3689445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hoswey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoswey" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/amit2103"><img src="https://avatars3.githubusercontent.com/u/7566692?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Pandey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit2103" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/gwildor28"><img src="https://avatars0.githubusercontent.com/u/16000365?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gwildor28</b></sub></a><br /><a href="#content-gwildor28" title="Content">🖋</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/amit2103"><img src="https://avatars3.githubusercontent.com/u/7566692?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Pandey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit2103" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/gwildor28"><img src="https://avatars0.githubusercontent.com/u/16000365?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gwildor28</b></sub></a><br /><a href="#content-gwildor28" title="Content">🖋</a></td>
<td align="center"><a href="https://t.me/paul_docker"><img src="https://avatars1.githubusercontent.com/u/2404785?v=4?s=100" width="100px;" alt=""/><br /><sub><b>田浩</b></sub></a><br /><a href="#content-llitfkitfk" title="Content">🖋</a></td>
<td align="center"><a href="https://twitter.com/StPitsios"><img src="https://avatars1.githubusercontent.com/u/6773603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stamatis Pitsios</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pitsios-s" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/qza"><img src="https://avatars3.githubusercontent.com/u/233149?v=4?s=100" width="100px;" alt=""/><br /><sub><b>qza</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qza" title="Code">💻</a></td>
<td align="center"><a href="http://tschis.github.io"><img src="https://avatars1.githubusercontent.com/u/20662669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodolfo Forte</b></sub></a><br /><a href="#content-Tschis" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/ankurkaushal"><img src="https://avatars2.githubusercontent.com/u/2236616?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ankur Kaushal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ankurkaushal" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ankurkaushal"><img src="https://avatars2.githubusercontent.com/u/2236616?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ankur Kaushal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ankurkaushal" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/ovidijus-okinskas/"><img src="https://avatars0.githubusercontent.com/u/20372387?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ovidijus Okinskas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=okinskas" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/robertt240"><img src="https://avatars1.githubusercontent.com/u/9137432?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert Kasperczyk</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=robertt240" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/trautonen"><img src="https://avatars3.githubusercontent.com/u/1641063?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tapio Rautonen</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=trautonen" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://vk.com/yuri.orlov"><img src="https://avatars0.githubusercontent.com/u/1595733?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yuri Orlov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yorlov" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/varunu28/"><img src="https://avatars0.githubusercontent.com/u/7676016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Varun Upadhyay</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=varunu28" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/PalAditya"><img src="https://avatars2.githubusercontent.com/u/25523604?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aditya Pal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=PalAditya" title="Code">💻</a></td>
@ -236,50 +215,41 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/d4gg4d"><img src="https://avatars2.githubusercontent.com/u/99457?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sami Airaksinen</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=d4gg4d" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vertti"><img src="https://avatars0.githubusercontent.com/u/557751?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Janne Sinivirta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vertti" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Bobo1239"><img src="https://avatars1.githubusercontent.com/u/2302947?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris-Chengbiao Zhou</b></sub></a><br /><a href="#content-Bobo1239" title="Content">🖋</a></td>
</tr>
<tr>
<td align="center"><a href="https://jahhein.github.io"><img src="https://avatars2.githubusercontent.com/u/10779515?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jacob Hein</b></sub></a><br /><a href="#content-Jahhein" title="Content">🖋</a></td>
<td align="center"><a href="https://github.com/iamrichardjones"><img src="https://avatars3.githubusercontent.com/u/14842151?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Jones</b></sub></a><br /><a href="#content-iamrichardjones" title="Content">🖋</a></td>
<td align="center"><a href="https://rachelcarmena.github.io"><img src="https://avatars0.githubusercontent.com/u/22792183?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rachel M. Carmena</b></sub></a><br /><a href="#content-rachelcarmena" title="Content">🖋</a></td>
<td align="center"><a href="https://zd-zero.github.io"><img src="https://avatars0.githubusercontent.com/u/21978370?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zaerald Denze Lungos</b></sub></a><br /><a href="#content-zd-zero" title="Content">🖋</a></td>
</tr>
<tr>
<td align="center"><a href="https://zd-zero.github.io"><img src="https://avatars0.githubusercontent.com/u/21978370?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zaerald Denze Lungos</b></sub></a><br /><a href="#content-zd-zero" title="Content">🖋</a></td>
<td align="center"><a href="https://webpro.nl"><img src="https://avatars1.githubusercontent.com/u/456426?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lars Kappert</b></sub></a><br /><a href="#content-webpro" title="Content">🖋</a></td>
<td align="center"><a href="https://xiaod.info"><img src="https://avatars2.githubusercontent.com/u/21277644?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike Liu</b></sub></a><br /><a href="#translation-xiaod-dev" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/charlesfinley"><img src="https://avatars1.githubusercontent.com/u/6307904?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Dolan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=charlesfinley" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Acharlesfinley" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/MananS77"><img src="https://avatars3.githubusercontent.com/u/21033516?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AMananS77" title="Reviewed Pull Requests">👀</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/nishant"><img src="https://avatars2.githubusercontent.com/u/15331971?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nishant Arora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nishant" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/raja-peeyush-kumar-singh"><img src="https://avatars0.githubusercontent.com/u/5496024?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peeyush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=raja-peeyush-kumar-singh" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aravening" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/vINCENT8888801"><img src="https://avatars0.githubusercontent.com/u/8037883?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wei Seng</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vINCENT8888801" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aravening" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://github.com/vINCENT8888801"><img src="https://avatars0.githubusercontent.com/u/8037883?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wei Seng</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vINCENT8888801" title="Code">💻</a></td>
<td align="center"><a href="https://www.linkedin.com/in/ashish-trivedi-218379135/"><img src="https://avatars3.githubusercontent.com/u/23194128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ashish Trivedi</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ashishtrivedi16" title="Code">💻</a></td>
<td align="center"><a href="https://rayyounghong.com"><img src="https://avatars1.githubusercontent.com/u/41055099?v=4?s=100" width="100px;" alt=""/><br /><sub><b>洪月阳</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=RayYH" title="Code">💻</a></td>
<td align="center"><a href="https://xdvrx1.github.io/"><img src="https://avatars0.githubusercontent.com/u/47092464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xdvrx1</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Axdvrx1" title="Reviewed Pull Requests">👀</a> <a href="#ideas-xdvrx1" title="Ideas, Planning, & Feedback">🤔</a></td>
<td align="center"><a href="http://subho.xyz"><img src="https://avatars0.githubusercontent.com/u/13291222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Subhrodip Mohanta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ohbus" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aohbus" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-ohbus" title="Maintenance">🚧</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/nahteb"><img src="https://avatars3.githubusercontent.com/u/13121570?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bethan Palmer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nahteb" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ToxicDreamz"><img src="https://avatars0.githubusercontent.com/u/45225562?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Toxic Dreamz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ToxicDreamz" title="Code">💻</a></td>
<td align="center"><a href="http://www.edycutjong.com"><img src="https://avatars1.githubusercontent.com/u/1098102?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Edy Cu Tjong</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=edycutjong" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/mkrzywanski"><img src="https://avatars0.githubusercontent.com/u/15279585?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michał Krzywański</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mkrzywanski" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://www.edycutjong.com"><img src="https://avatars1.githubusercontent.com/u/1098102?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Edy Cu Tjong</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=edycutjong" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/mkrzywanski"><img src="https://avatars0.githubusercontent.com/u/15279585?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michał Krzywański</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mkrzywanski" title="Code">💻</a></td>
<td align="center"><a href="https://www.stefan-birkner.de"><img src="https://avatars1.githubusercontent.com/u/711349?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Birkner</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=stefanbirkner" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/fedorskvorcov"><img src="https://avatars3.githubusercontent.com/u/43882212?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fedor Skvorcov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fedorskvorcov" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/samilAyoub"><img src="https://avatars0.githubusercontent.com/u/61546990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>samilAyoub</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samilAyoub" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/vdlald"><img src="https://avatars0.githubusercontent.com/u/29997701?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Golubinov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vdlald" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/swarajsaaj"><img src="https://avatars2.githubusercontent.com/u/6285049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Swaraj</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=swarajsaaj" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/swarajsaaj"><img src="https://avatars2.githubusercontent.com/u/6285049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Swaraj</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=swarajsaaj" title="Code">💻</a></td>
<td align="center"><a href="http://christophflick.de"><img src="https://avatars0.githubusercontent.com/u/4465376?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christoph Flick</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ChFlick" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/Ascenio"><img src="https://avatars1.githubusercontent.com/u/7662016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ascênio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AAscenio" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="https://www.linkedin.com/in/domenico-sibilio/"><img src="https://avatars2.githubusercontent.com/u/24280982?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Domenico Sibilio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dsibilio" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/akashchandwani"><img src="https://avatars2.githubusercontent.com/u/3483277?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akash Chandwani</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aakashchandwani" title="Reviewed Pull Requests">👀</a></td>
<td align="center"><a href="http://www.linkedin.com/in/manannikov"><img src="https://avatars2.githubusercontent.com/u/7019769?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavlo Manannikov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=manannikov" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/eimanip"><img src="https://avatars0.githubusercontent.com/u/20307301?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eiman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eimanip" title="Code">💻</a></td>
@ -290,38 +260,32 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://github.com/gkulkarni2020"><img src="https://avatars3.githubusercontent.com/u/5161548?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Girish Kulkarni</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gkulkarni2020" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/omk13"><img src="https://avatars0.githubusercontent.com/u/59054172?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Omar Karazoun</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=omk13" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/jeff303"><img src="https://avatars0.githubusercontent.com/u/3521562?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeff Evans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jeff303" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://viveksb007.github.io"><img src="https://avatars1.githubusercontent.com/u/12713808?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vivek Singh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=viveksb007" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/siavashsoleymani"><img src="https://avatars2.githubusercontent.com/u/18074419?v=4?s=100" width="100px;" alt=""/><br /><sub><b>siavash</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=siavashsoleymani" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/ruchpeanuts"><img src="https://avatars0.githubusercontent.com/u/29301900?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ruchpeanuts</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ruchpeanuts" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/warp125"><img src="https://avatars1.githubusercontent.com/u/48073115?v=4?s=100" width="100px;" alt=""/><br /><sub><b>warp125</b></sub></a><br /><a href="#translation-warp125" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/warp125"><img src="https://avatars1.githubusercontent.com/u/48073115?v=4?s=100" width="100px;" alt=""/><br /><sub><b>warp125</b></sub></a><br /><a href="#translation-warp125" title="Translation">🌍</a></td>
<td align="center"><a href="http://libkhadir.fr"><img src="https://avatars1.githubusercontent.com/u/45130488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>KHADIR Tayeb</b></sub></a><br /><a href="#translation-tkhadir" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/ignite1771"><img src="https://avatars2.githubusercontent.com/u/59446563?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ignite1771</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ignite1771" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/demirhalil"><img src="https://avatars1.githubusercontent.com/u/22895118?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Halil Demir</b></sub></a><br /><a href="#translation-demirhalil" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/rohit10000"><img src="https://avatars.githubusercontent.com/u/20845565?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rohit Singh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=rohit10000" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/byoungju94"><img src="https://avatars.githubusercontent.com/u/42516378?v=4?s=100" width="100px;" alt=""/><br /><sub><b>byoungju94</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=byoungju94" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/moustafafarhat"><img src="https://avatars.githubusercontent.com/u/38836727?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Moustafa Farhat</b></sub></a><br /><a href="#translation-moustafafarhat" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/richardmr36"><img src="https://avatars.githubusercontent.com/u/19147333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martel Richard</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=richardmr36" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/va1m"><img src="https://avatars.githubusercontent.com/u/17025445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>va1m</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=va1m" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/richardmr36"><img src="https://avatars.githubusercontent.com/u/19147333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martel Richard</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=richardmr36" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/va1m"><img src="https://avatars.githubusercontent.com/u/17025445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>va1m</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=va1m" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/noamgrinch"><img src="https://avatars.githubusercontent.com/u/31648669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Noam Greenshtain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=noamgrinch" title="Code">💻</a></td>
<td align="center"><a href="https://xuyonghong.cn/"><img src="https://avatars.githubusercontent.com/u/14086462?v=4?s=100" width="100px;" alt=""/><br /><sub><b>yonghong Xu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qfxl" title="Documentation">📖</a></td>
<td align="center"><a href="https://www.linkedin.com/in/jinisha-vora"><img src="https://avatars.githubusercontent.com/u/40777762?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jinishavora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Ajinishavora" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=jinishavora" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/eas5"><img src="https://avatars.githubusercontent.com/u/50836521?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Elvys Soares</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eas5" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/zWeBrain"><img src="https://avatars.githubusercontent.com/u/46642512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zWeBrain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zWeBrain" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/zWeBrain"><img src="https://avatars.githubusercontent.com/u/46642512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zWeBrain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zWeBrain" title="Code">💻</a></td>
<td align="center"><a href="https://al-assad.github.io/notion/"><img src="https://avatars.githubusercontent.com/u/22493821?v=4?s=100" width="100px;" alt=""/><br /><sub><b>余林颖</b></sub></a><br /><a href="#translation-Al-assad" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/STudio26"><img src="https://avatars.githubusercontent.com/u/6988911?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alain</b></sub></a><br /><a href="#translation-STudio26" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/DEV-VRUPER"><img src="https://avatars.githubusercontent.com/u/30525467?v=4?s=100" width="100px;" alt=""/><br /><sub><b>VR</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=DEV-VRUPER" title="Documentation">📖</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/JackieNim"><img src="https://avatars.githubusercontent.com/u/4138836?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JackieNim</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=JackieNim" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/EdisonE3"><img src="https://avatars.githubusercontent.com/u/52118917?v=4?s=100" width="100px;" alt=""/><br /><sub><b>EdisonE3</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=EdisonE3" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/tao-sun2"><img src="https://avatars.githubusercontent.com/u/66189688?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tao</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=tao-sun2" title="Code">💻</a></td>
@ -332,17 +296,40 @@ This project is licensed under the terms of the MIT license.
<td align="center"><a href="https://www.linkedin.com/in/souzasamuel/"><img src="https://avatars.githubusercontent.com/u/17254162?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Samuel Souza</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samuelpsouza" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/marlo2222"><img src="https://avatars.githubusercontent.com/u/40809563?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Marlo Henrique</b></sub></a><br /><a href="#translation-marlo2222" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/AndriyPyzh"><img src="https://avatars.githubusercontent.com/u/57706635?v=4?s=100" width="100px;" alt=""/><br /><sub><b>AndriyPyzh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=AndriyPyzh" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/karthikbhat13"><img src="https://avatars.githubusercontent.com/u/22431014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>karthikbhat13</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=karthikbhat13" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/mortezaadi"><img src="https://avatars.githubusercontent.com/u/1329687?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Morteza Adigozalpour</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mortezaadi" title="Code">💻</a></td>
<td align="center"><a href="https://stackoverflow.com/users/308565/nagaraj-tantri"><img src="https://avatars.githubusercontent.com/u/3784194?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nagaraj Tantri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=tan31989" title="Code">💻</a></td>
<td align="center"><a href="http://scuccimarri.it"><img src="https://avatars.githubusercontent.com/u/7107651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Francesco Scuccimarri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=frascu" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://scuccimarri.it"><img src="https://avatars.githubusercontent.com/u/7107651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Francesco Scuccimarri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=frascu" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Conhan93"><img src="https://avatars.githubusercontent.com/u/71334757?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conny Hansson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Conhan93" title="Documentation">📖</a></td>
<td align="center"><a href="http://muklasr.medium.com"><img src="https://avatars.githubusercontent.com/u/43443753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muklas Rahmanto</b></sub></a><br /><a href="#translation-muklasr" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/VxDxK"><img src="https://avatars.githubusercontent.com/u/38704817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vadim</b></sub></a><br /><a href="#translation-VxDxK" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/sims-keshri"><img src="https://avatars.githubusercontent.com/u/62168475?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simran Keshri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sims-keshri" title="Code">💻</a></td>
<td align="center"><a href="https://programacionymas.com"><img src="https://avatars.githubusercontent.com/u/3101238?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JCarlos</b></sub></a><br /><a href="#translation-JCarlosR" title="Translation">🌍</a></td>
<td align="center"><a href="https://www.mrmoshkel.ir"><img src="https://avatars.githubusercontent.com/u/60359433?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ali Ghasemi</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Dev-AliGhasemi" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="http://carlfx.wordpress.com"><img src="https://avatars.githubusercontent.com/u/1594624?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Carl Dea</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=carldea" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Mozartuss"><img src="https://avatars.githubusercontent.com/u/32893711?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mozartus</b></sub></a><br /><a href="#translation-Mozartuss" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/ManviGoel26"><img src="https://avatars.githubusercontent.com/u/55682355?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manvi Goel</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ManviGoel26" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/blueberry404"><img src="https://avatars.githubusercontent.com/u/39243539?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anum Amin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=blueberry404" title="Documentation">📖</a></td>
<td align="center"><a href="https://uh-zz.github.io/blog/"><img src="https://avatars.githubusercontent.com/u/47747828?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reo Uehara</b></sub></a><br /><a href="#translation-uh-zz" title="Translation">🌍</a></td>
<td align="center"><a href="https://github.com/Fiordy"><img src="https://avatars.githubusercontent.com/u/53420573?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fiordy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Fiordy" title="Documentation">📖</a></td>
<td align="center"><a href="https://github.com/harshalkh"><img src="https://avatars.githubusercontent.com/u/37841724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harshal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=harshalkh" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://www.linkedin.com/in/abhinav-vashisth-06613b208/"><img src="https://avatars.githubusercontent.com/u/89785800?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abhinav Vashisth</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vashisthabhinav" title="Documentation">📖</a></td>
<td align="center"><a href="http://no website"><img src="https://avatars.githubusercontent.com/u/47126749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AKevinyl3" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Kevinyl3" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Shrirang97"><img src="https://avatars.githubusercontent.com/u/28738668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shrirang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AShrirang97" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Shrirang97" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/interactwithankush"><img src="https://avatars.githubusercontent.com/u/18613127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>interactwithankush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=interactwithankush" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/yuhangbin"><img src="https://avatars.githubusercontent.com/u/17566866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CharlieYu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yuhangbin" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/Leisterbecker"><img src="https://avatars.githubusercontent.com/u/20650323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leisterbecker</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Leisterbecker" title="Code">💻</a></td>
<td align="center"><a href="http://rosaecrucis.cn"><img src="https://avatars.githubusercontent.com/u/35420129?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DragonDreamer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=castleKing1997" title="Code">💻</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/ShivanshCharak"><img src="https://avatars.githubusercontent.com/u/96943825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ShivanshCharak</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ShivanshCharak" title="Code">💻</a></td>
<td align="center"><a href="https://github.com/HattoriHenzo"><img src="https://avatars.githubusercontent.com/u/5141285?v=4?s=100" width="100px;" alt=""/><br /><sub><b>HattoriHenzo</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=HattoriHenzo" title="Code">💻</a></td>
</tr>
</table>

@ -23,42 +23,40 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-document</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-document</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.abstractdocument.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.abstractdocument.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -87,24 +87,36 @@ public interface KingdomFactory {
}
public class ElfKingdomFactory implements KingdomFactory {
@Override
public Castle createCastle() {
return new ElfCastle();
}
@Override
public King createKing() {
return new ElfKing();
}
@Override
public Army createArmy() {
return new ElfArmy();
}
}
public class OrcKingdomFactory implements KingdomFactory {
@Override
public Castle createCastle() {
return new OrcCastle();
}
@Override
public King createKing() {
return new OrcKing();
}
@Override
public Army createArmy() {
return new OrcArmy();
}

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,43 +23,40 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.abstractfactory.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.abstractfactory.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -124,3 +124,7 @@ Now, we can create multiple creatures such as Orcs, tell them to eat and roam, a
## Class diagram
![alt text](./etc/active-object.urm.png "Active Object class diagram")
## Tutorials
* [Android and Java Concurrency: The Active Object Pattern](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,43 +23,40 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>active-object</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>active-object</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.activeobject.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.activeobject.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -138,6 +138,10 @@ This pattern can be used:
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
## Tutorial
* [Acyclic Visitor Pattern Example](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
## Consequences
The good:

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -22,53 +23,47 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>acyclic-visitor</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.9.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/uk.org.lidalia/slf4j-test -->
<dependency>
<groupId>uk.org.lidalia</groupId>
<artifactId>slf4j-test</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>acyclic-visitor</artifactId>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>3.9.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/uk.org.lidalia/slf4j-test -->
<dependency>
<groupId>uk.org.lidalia</groupId>
<artifactId>slf4j-test</artifactId>
<version>1.2.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.10.19</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->

@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
* Hayes class implements its accept method.
*/
@Slf4j
public class Hayes extends Modem {
public class Hayes implements Modem {
/**
* Accepts all visitors but honors only HayesVisitor.

@ -24,8 +24,9 @@
package com.iluwatar.acyclicvisitor;
/**
* Modem abstract class.
* //Modem abstract class.
* converted to an interface
*/
public abstract class Modem {
public abstract void accept(ModemVisitor modemVisitor);
public interface Modem {
void accept(ModemVisitor modemVisitor);
}

@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
* Zoom class implements its accept method.
*/
@Slf4j
public class Zoom extends Modem {
public class Zoom implements Modem {
/**
* Accepts all visitors but honors only ZoomVisitor.

@ -18,10 +18,10 @@ couldn't otherwise because of incompatible interfaces.
## Explanation
Real world example
Real-world example
> Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter.
> Another example would be the famous power adapter; a three legged plug can't be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet.
> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter.
> Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets.
> Yet another example would be a translator translating words spoken by one person to another
In plain words
@ -36,7 +36,7 @@ Wikipedia says
Consider a captain that can only use rowing boats and cannot sail at all.
First we have interfaces `RowingBoat` and `FishingBoat`
First, we have interfaces `RowingBoat` and `FishingBoat`
```java
public interface RowingBoat {
@ -68,7 +68,7 @@ public class Captain {
}
```
Now let's say the pirates are coming and our captain needs to escape but there is only fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
Now let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
```java
@Slf4j
@ -100,25 +100,31 @@ captain.row();
## Applicability
Use the Adapter pattern when
* you want to use an existing class, and its interface does not match the one you need
* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
* most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
* You want to use an existing class, and its interface does not match the one you need
* You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
* You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class.
* Most of the applications using third-party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
## Tutorials
* [Dzone](https://dzone.com/articles/adapter-design-pattern-in-java)
* [Refactoring Guru](https://refactoring.guru/design-patterns/adapter/java/example)
* [Baeldung](https://www.baeldung.com/java-adapter-pattern)
## Consequences
Class and object adapters have different trade-offs. A class adapter
* adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter wont work when we want to adapt a class and all its subclasses.
* lets Adapter override some of Adaptees behavior, since Adapter is a subclass of Adaptee.
* introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
* Adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter wont work when we want to adapt a class and all its subclasses.
* Lets Adapter override some of Adaptees behavior since Adapter is a subclass of Adaptee.
* Introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
An object adapter
* lets a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
* makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself.
* Lets a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
* Makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making the Adapter refer to the subclass rather than the Adaptee itself.
## Known uses
## Real-world examples
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,48 +23,45 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.adapter.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.adapter.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -33,7 +33,7 @@ import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
/**
* Test class
* Tests for the adapter pattern.
*/
class AdapterPatternTest {

@ -33,9 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
* Check whether the execution of the main method in {@link App}
* throws an exception.
*/

@ -23,68 +23,65 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>aggregator-service</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Maven assembly plugin is invoked with default setting which we have
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>aggregator-service</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<!-- Maven assembly plugin is invoked with default setting which we have
in parent pom and specifying the class having main method -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.aggregator.microservices.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.aggregator.microservices.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -23,63 +23,58 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>information-microservice</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.information.microservices.InformationApplication</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>information-microservice</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.information.microservices.InformationApplication</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -23,62 +23,58 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>inventory-microservice</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.inventory.microservices.InventoryApplication</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>inventory-microservice</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.inventory.microservices.InventoryApplication</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -23,20 +23,18 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>aggregator-microservices</artifactId>
<packaging>pom</packaging>
<modules>
<module>information-microservice</module>
<module>aggregator-service</module>
<module>inventory-microservice</module>
</modules>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>aggregator-microservices</artifactId>
<packaging>pom</packaging>
<modules>
<module>information-microservice</module>
<module>aggregator-service</module>
<module>inventory-microservice</module>
</modules>
</project>

@ -23,40 +23,38 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ambassador</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.ambassador.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ambassador</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.ambassador.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -23,13 +23,11 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>api-gateway</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway-service</artifactId>
@ -54,7 +52,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

@ -23,13 +23,11 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>api-gateway</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>image-microservice</artifactId>
@ -49,7 +47,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

@ -23,13 +23,11 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway</artifactId>

@ -23,19 +23,15 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>api-gateway</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>price-microservice</artifactId>
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
@ -51,7 +47,6 @@
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

@ -23,16 +23,13 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>arrange-act-assert</artifactId>
<dependencies>
<dependency>
@ -41,4 +38,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>
</project>

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,46 +23,43 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.async.method.invocation.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.async.method.invocation.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -59,9 +59,12 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class App {
private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
/**
* Program entry point.
*/
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor();
@ -87,9 +90,9 @@ public class App {
asyncResult5.await();
// log the results of the tasks, callbacks log immediately when complete
log("Space rocket <" + result1 + "> launch complete");
log("Space rocket <" + result2 + "> launch complete");
log("Space rocket <" + result3 + "> launch complete");
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
}
/**
@ -102,7 +105,7 @@ public class App {
private static <T> Callable<T> lazyval(T value, long delayMillis) {
return () -> {
Thread.sleep(delayMillis);
log("Space rocket <" + value + "> launched successfully");
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
return value;
};
}

@ -23,41 +23,38 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>balking</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.balking.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>balking</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.balking.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,30 +23,27 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>bridge</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

@ -134,6 +134,12 @@ Use the Builder pattern when
* The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
* The construction process must allow different representations for the object that's constructed
## Tutorials
* [Refactoring Guru](https://refactoring.guru/design-patterns/builder)
* [Oracle Blog](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java)
* [Journal Dev](https://www.journaldev.com/1425/builder-design-pattern-in-java)
## Known uses
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,25 +23,22 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>builder</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,46 +23,43 @@
THE SOFTWARE.
-->
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>business-delegate</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.business.delegate.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>business-delegate</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.business.delegate.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -23,43 +23,38 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bytecode</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.bytecode.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>bytecode</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.bytecode.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -42,6 +42,14 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j
public class App {
private static final String LITERAL_0 = "LITERAL 0";
private static final String HEALTH_PATTERN = "%s_HEALTH";
private static final String GET_AGILITY = "GET_AGILITY";
private static final String GET_WISDOM = "GET_WISDOM";
private static final String ADD = "ADD";
private static final String LITERAL_2 = "LITERAL 2";
private static final String DIVIDE = "DIVIDE";
/**
* Main app method.
*
@ -53,17 +61,17 @@ public class App {
new Wizard(45, 7, 11, 0, 0),
new Wizard(36, 18, 8, 0, 0));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(GET_AGILITY));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
vm.execute(InstructionConverterUtil.convertToByteCode(GET_WISDOM));
vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_2));
vm.execute(InstructionConverterUtil.convertToByteCode(DIVIDE));
vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
}
}

Binary file not shown.

Before

(image error) Size: 137 KiB

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,13 +23,12 @@
THE SOFTWARE.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>caching</artifactId>
<dependencies>
@ -38,12 +37,6 @@
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.12.4</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
@ -53,7 +46,6 @@
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.1</version>
</dependency>
</dependencies>
<!--

@ -16,7 +16,6 @@ import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
class MongoDbTest {

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,13 +23,12 @@
THE SOFTWARE.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>callback</artifactId>
<dependencies>

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,13 +23,12 @@
THE SOFTWARE.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>chain-of-responsibility</artifactId>
<dependencies>

@ -23,11 +23,10 @@
THE SOFTWARE.
-->
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
<suppressions>
<suppress checks="AvoidStarImport" files="[\\/]src[\\/]test[\\/]java[\\/]"/>
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]test[\\/]resources[\\/]"/>
<suppress checks="[a-zA-Z0-9]*" files="[\\/]build[\\/]generated-sources[\\/]"/>
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]main[\\/]resources[\\/]"/>
<suppress checks="AvoidStarImport" files="[\\/]src[\\/]test[\\/]java[\\/]" />
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]test[\\/]resources[\\/]" />
<suppress checks="[a-zA-Z0-9]*" files="[\\/]build[\\/]generated-sources[\\/]" />
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]main[\\/]resources[\\/]" />
</suppressions>

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,21 +24,21 @@
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>circuit-breaker</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>circuit-breaker</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>

38
cloud-claim-check-pattern/.gitignore vendored Normal file

@ -0,0 +1,38 @@
# Build output
target/
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# IDE
.idea/
*.iml
.settings/
.project
.classpath
# macOS
.DS_Store
# Azure Functions
local.settings.json
bin/
obj/

@ -0,0 +1,83 @@
---
layout: pattern
title: Claim Check Pattern
folder: cloud-claim-check-pattern
permalink: /patterns/cloud-claim-check-pattern/
categories: Cloud
language: en
tags:
- Cloud distributed
- Microservices
---
## Name
[Claim Check Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check)
## Also known as
[Reference-Based Messaging](https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html)
## Intent
- Reduce the load of data transfer through the Internet. Instead of sending actual data directly, just send the message reference.
- Improve data security. As only message reference is shared, no data is exposed to the Internet.
## Explanation
Real-World Example
> Suppose if you want to build a photo processing system. A photo processing system takes an image as input, processes it, and outputs a different set of images. Consider system has one persistent storage, one input component, ten processing components, messaging platform. Once a photo is given to the input component, it stores that image on persistent storage. It then creates ten different messages/events with the same image location and publishes them to the messaging platform. The messaging platform triggers ten different processing components. The ten processing components extract information about image location from the received event and then read an image from persistent storage. They generate ten different images from the original image and drop these images again to persistent storage.
In Plain words
> Split a large message into a claim check and a payload. Send the claim check to the messaging platform and store the payload to an external service. This pattern allows large messages to be processed while protecting the message bus and the client from being overwhelmed or slowed down. This pattern also helps to reduce costs, as storage is usually cheaper than resource units used by the messaging platform.([ref](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check))
## Architecture Diagram
![alt text](./etc/Claim-Check-Pattern.png "Claim Check Pattern")
## Applicability
Use the Claim Check Pattern when
- Huge processing data causes a lot of bandwidth consumption to transfer data through the Internet.
- To secure your data transfer by storing in common persistent storage.
- Using a cloud platform - Azure Functions or AWS Lambda, Azure EventGrid or AWS Event Bridge, Azure Blob Storage or AWS S3 Bucket.
- Each service must be independent and idempotent. Output data is dropped to persistent storage by the service.
- Publish-subscribe messaging pattern needs to be used.
## Consequences
- This pattern is stateless. Any compute API will not store any data.
- You must have persistent storage and a reliable messaging platform.
## Tutorials
### Workflow
Suppose a telecom company wants to build call cost calculator system which generate the call cost daily. At the end of each day, details of the calls made by the consumers are stored somewhere. The call calculator system will read this data and generate call cost data for each user. Consumers will be billed using this generated data in case of postpaid service.
Producer class( `UsageDetailPublisherFunction` Azure Function) will generate call usage details (here we are generating call data in producer class itself. In real world scenario, it will read from storage). `UsageDetailPublisherFunction` creates a message. Message consists of message header and message body. Message header is basically an event grid event or claim or message reference. Message body contains actual data. `UsageDetailPublisherFunction` sends a message header to Event Grid topic `usage-detail` and drops an entire message to the blob storage. Event Grid then sent this message header to the `UsageCostProcessorFunction` Azure function. It will read the entire message from blob storage with the help of the header, will calculate call cost and drop the result to the blob storage.
### Class Diagrams
![alt text](./etc/class-diagram.png "Claim-Check-Class-Diagram")
### Setup
- Any operating system can be used macOS, Windows, Linux as everything is deployed on Azure.
- Install Java JDK 11 and set up Java environmental variables.
- Install Git.
- Install Visual Studio Code.
- Install [ Azure Functions extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions) to be able to deploy using Visual studio.
### Storage Data
The data is stored in the Azure blob storage in the container `callusageapp`. For every trigger, one GUID is created. Under the `GUID folder`, 2 files will be created `input.json` and `output.json`.
`Input.json` is dropped `producer` azure function which contains call usage details.` Output.json` contains call cost details which are dropped by the `consumer` azure function.
## Credits
- [Messaging Pattern - Claim Check](https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html)
- [Azure Architecture Pattern - Claim Check Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check)

@ -0,0 +1,38 @@
# Build output
target/
*.class
# Log file
*.log
# BlueJ files
*.ctxt
# Mobile Tools for Java (J2ME)
.mtj.tmp/
# Package Files #
*.jar
*.war
*.ear
*.zip
*.tar.gz
*.rar
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*
# IDE
.idea/
*.iml
.settings/
.project
.classpath
# macOS
.DS_Store
# Azure Functions
local.settings.json
bin/
obj/

@ -0,0 +1,117 @@
@startuml
package com.iluwatar.claimcheckpattern.producer.calldetails.functions {
class UsageDetailPublisherFunction {
- eventHandlerUtility : EventHandlerUtility<MessageHeader>
- messageHandlerUtility : MessageHandlerUtility<UsageDetail>
+ UsageDetailPublisherFunction()
+ UsageDetailPublisherFunction(messageHandlerUtility : MessageHandlerUtility<UsageDetail>, eventHandlerUtility : EventHandlerUtility<MessageHeader>)
+ run(request : HttpRequestMessage<Optional<String>>, context : ExecutionContext) : HttpResponseMessage
}
}
package com.iluwatar.claimcheckpattern.domain {
class Message<T> {
- messageBody : MessageBody<T>
- messageHeader : MessageHeader
+ Message<T>()
+ getMessageBody() : MessageBody<T>
+ getMessageHeader() : MessageHeader
+ setMessageBody(messageBody : MessageBody<T>)
+ setMessageHeader(messageHeader : MessageHeader)
}
class MessageBody<T> {
- data : List<T>
+ MessageBody<T>()
+ getData() : List<T>
+ setData(data : List<T>)
}
class MessageHeader {
- data : Object
- dataVersion : String
- eventTime : String
- eventType : String
- id : String
- subject : String
- topic : String
+ MessageHeader()
+ getData() : Object
+ getDataVersion() : String
+ getEventTime() : String
+ getEventType() : String
+ getId() : String
+ getSubject() : String
+ getTopic() : String
+ setData(data : Object)
+ setDataVersion(dataVersion : String)
+ setEventTime(eventTime : String)
+ setEventType(eventType : String)
+ setId(id : String)
+ setSubject(subject : String)
+ setTopic(topic : String)
}
class MessageReference {
- dataFileName : String
- dataLocation : String
+ MessageReference()
+ MessageReference(dataLocation : String, dataFileName : String)
+ getDataFileName() : String
+ getDataLocation() : String
+ setDataFileName(dataFileName : String)
+ setDataLocation(dataLocation : String)
}
class UsageCostDetail {
- callCost : double
- dataCost : double
- userId : String
+ UsageCostDetail()
+ getCallCost() : double
+ getDataCost() : double
+ getUserId() : String
+ setCallCost(callCost : double)
+ setDataCost(dataCost : double)
+ setUserId(userId : String)
}
class UsageDetail {
- data : int
- duration : int
- userId : String
+ UsageDetail()
+ getData() : int
+ getDuration() : int
+ getUserId() : String
+ setData(data : int)
+ setDuration(duration : int)
+ setUserId(userId : String)
}
}
package com.iluwatar.claimcheckpattern.utility {
class EventHandlerUtility<T> {
- customEventClient : EventGridPublisherClient<BinaryData>
+ EventHandlerUtility<T>()
+ EventHandlerUtility<T>(customEventClient : EventGridPublisherClient<BinaryData>)
+ publishEvent(customEvent : T, logger : Logger)
}
class MessageHandlerUtility<T> {
- blobServiceClient : BlobServiceClient
+ MessageHandlerUtility<T>()
+ MessageHandlerUtility<T>(blobServiceClient : BlobServiceClient)
+ dropToPersistantStorage(message : Message<T>, logger : Logger)
+ readFromPersistantStorage(messageReference : MessageReference, logger : Logger) : Message<T>
}
}
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions {
class UsageCostProcessorFunction {
- messageHandlerUtilityForUsageCostDetail : MessageHandlerUtility<UsageCostDetail>
- messageHandlerUtilityForUsageDetail : MessageHandlerUtility<UsageDetail>
+ UsageCostProcessorFunction()
+ UsageCostProcessorFunction(messageHandlerUtilityForUsageDetail : MessageHandlerUtility<UsageDetail>, messageHandlerUtilityForUsageCostDetail : MessageHandlerUtility<UsageCostDetail>)
- calculateUsageCostDetails(usageDetailsList : List<UsageDetail>) : List<UsageCostDetail>
+ run(request : HttpRequestMessage<Optional<String>>, context : ExecutionContext) : HttpResponseMessage
}
}
UsageCostProcessorFunction --> "-messageHandlerUtilityForUsageDetail" MessageHandlerUtility
Message --> "-messageBody" MessageBody
UsageDetailPublisherFunction --> "-eventHandlerUtility" EventHandlerUtility
Builder ..+ HttpResponseMessage
UsageDetailPublisherFunction --> "-messageHandlerUtility" MessageHandlerUtility
Message --> "-messageHeader" MessageHeader
@enduml

@ -0,0 +1,7 @@
{
"version": "2.0",
"extensionBundle": {
"id": "Microsoft.Azure.Functions.ExtensionBundle",
"version": "[1.*, 2.0.0)"
}
}

@ -0,0 +1,150 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>claim-check-pattern</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<artifactId>call-usage-app</artifactId>
<name>call-usage-app</name>
<packaging>jar</packaging>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<azure.functions.maven.plugin.version>1.14.0</azure.functions.maven.plugin.version>
<azure.functions.java.library.version>1.4.2</azure.functions.java.library.version>
<functionAppName>CallUsageApp</functionAppName>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-sdk-bom</artifactId>
<version>1.0.4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.microsoft.azure.functions</groupId>
<artifactId>azure-functions-java-library</artifactId>
<version>${azure.functions.java.library.version}</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-eventgrid</artifactId>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>12.13.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<scope>test</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-functions-maven-plugin</artifactId>
<version>${azure.functions.maven.plugin.version}</version>
<configuration>
<!-- function app name -->
<appName>${functionAppName}</appName>
<!-- function app resource group -->
<resourceGroup>java-functions-group</resourceGroup>
<!-- function app service plan name -->
<appServicePlanName>java-functions-app-service-plan</appServicePlanName>
<!-- function app region-->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions for all valid values -->
<region>westus</region>
<!-- function pricingTier, default to be consumption if not specified -->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-pricing-tiers for all valid values -->
<!-- <pricingTier></pricingTier> -->
<!-- Whether to disable application insights, default is false -->
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details for all valid configurations for application insights-->
<!-- <disableAppInsights></disableAppInsights> -->
<runtime>
<!-- runtime os, could be windows, linux or docker-->
<os>windows</os>
<javaVersion>11</javaVersion>
</runtime>
<appSettings>
<property>
<name>FUNCTIONS_EXTENSION_VERSION</name>
<value>~3</value>
</property>
</appSettings>
</configuration>
<executions>
<execution>
<id>package-functions</id>
<goals>
<goal>package</goal>
</goals>
</execution>
</executions>
</plugin>
<!--Remove obj folder generated by .NET SDK in maven clean-->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
<configuration>
<filesets>
<fileset>
<directory>obj</directory>
</fileset>
</filesets>
</configuration>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,168 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions;
import com.azure.core.util.BinaryData;
import com.azure.core.util.serializer.TypeReference;
import com.azure.messaging.eventgrid.EventGridEvent;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationEventData;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationResponse;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageBody;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import com.iluwatar.claimcheckpattern.domain.UsageCostDetail;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* Azure Functions with HTTP Trigger.
* This is Consumer class.
*/
public class UsageCostProcessorFunction {
private MessageHandlerUtility<UsageDetail> messageHandlerUtilityForUsageDetail;
private MessageHandlerUtility<UsageCostDetail> messageHandlerUtilityForUsageCostDetail;
public UsageCostProcessorFunction() {
this.messageHandlerUtilityForUsageDetail = new MessageHandlerUtility<>();
this.messageHandlerUtilityForUsageCostDetail = new MessageHandlerUtility<>();
}
public UsageCostProcessorFunction(
MessageHandlerUtility<UsageDetail> messageHandlerUtilityForUsageDetail,
MessageHandlerUtility<UsageCostDetail> messageHandlerUtilityForUsageCostDetail) {
this.messageHandlerUtilityForUsageDetail = messageHandlerUtilityForUsageDetail;
this.messageHandlerUtilityForUsageCostDetail = messageHandlerUtilityForUsageCostDetail;
}
/**
* Azure function which gets triggered when event grid event send event to it.
* After receiving event, it read input file from blob storage, calculate call cost details.
* It creates new message with cost details and drop message to blob storage.
* @param request represents HttpRequestMessage
* @param context represents ExecutionContext
* @return HttpResponseMessage
*/
@FunctionName("UsageCostProcessorFunction")
public HttpResponseMessage run(@HttpTrigger(name = "req", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
try {
var eventGridEvents = EventGridEvent.fromString(request.getBody().get());
for (var eventGridEvent : eventGridEvents) {
// Handle system events
if (eventGridEvent.getEventType()
.equals("Microsoft.EventGrid.SubscriptionValidationEvent")) {
SubscriptionValidationEventData subscriptionValidationEventData = eventGridEvent.getData()
.toObject(SubscriptionValidationEventData.class);
// Handle the subscription validation event
var responseData = new SubscriptionValidationResponse();
responseData.setValidationResponse(subscriptionValidationEventData.getValidationCode());
return request.createResponseBuilder(HttpStatus.OK).body(responseData).build();
} else if (eventGridEvent.getEventType().equals("UsageDetail")) {
// Get message header and reference
var messageReference = eventGridEvent.getData()
.toObject(MessageReference.class);
// Read message from persistent storage
var message = this.messageHandlerUtilityForUsageDetail
.readFromPersistantStorage(messageReference, context.getLogger());
// Get Data and generate cost details
List<UsageDetail> usageDetailsList = BinaryData.fromObject(
message.getMessageBody().getData())
.toObject(new TypeReference<>() {
});
var usageCostDetailsList = calculateUsageCostDetails(usageDetailsList);
// Create message body
var newMessageBody = new MessageBody<UsageCostDetail>();
newMessageBody.setData(usageCostDetailsList);
// Create message header
var newMessageReference = new MessageReference("callusageapp",
eventGridEvent.getId() + "/output.json");
var newMessageHeader = new MessageHeader();
newMessageHeader.setId(eventGridEvent.getId());
newMessageHeader.setSubject("UsageCostProcessor");
newMessageHeader.setTopic("");
newMessageHeader.setEventType("UsageCostDetail");
newMessageHeader.setEventTime(OffsetDateTime.now().toString());
newMessageHeader.setData(newMessageReference);
newMessageHeader.setDataVersion("v1.0");
// Create entire message
var newMessage = new Message<UsageCostDetail>();
newMessage.setMessageHeader(newMessageHeader);
newMessage.setMessageBody(newMessageBody);
// Drop data to persistent storage
this.messageHandlerUtilityForUsageCostDetail.dropToPersistantStorage(newMessage,
context.getLogger());
context.getLogger().info("Message is dropped successfully");
return request.createResponseBuilder(HttpStatus.OK)
.body("Message is dropped successfully").build();
}
}
} catch (Exception e) {
context.getLogger().warning(e.getMessage());
}
return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR).body(null).build();
}
private List<UsageCostDetail> calculateUsageCostDetails(List<UsageDetail> usageDetailsList) {
if (usageDetailsList == null) {
return null;
}
var usageCostDetailsList = new ArrayList<UsageCostDetail>();
usageDetailsList.forEach(usageDetail -> {
var usageCostDetail = new UsageCostDetail();
usageCostDetail.setUserId(usageDetail.getUserId());
usageCostDetail.setCallCost(usageDetail.getDuration() * 0.30); // 0.30 per minute
usageCostDetail.setDataCost(usageDetail.getData() * 0.20); // 0.20 per MB
usageCostDetailsList.add(usageCostDetail);
});
return usageCostDetailsList;
}
}

@ -0,0 +1,43 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* It is the message which gets dropped or read by Producer or Consumer Azure functions.
* It is stored in the json format.
* @param <T> represents UsageDetail or UsageCostDetail
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Message<T> {
private MessageHeader messageHeader;
private MessageBody<T> messageBody;
}

@ -0,0 +1,43 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* It is message body of the message.
* It stores actual data in our case UsageCostDetail or UsageDetail.
* @param <T> represents UsageDetail or UsageCostDetail
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageBody<T> {
private List<T> data;
}

@ -0,0 +1,47 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is message header or event which is sent to Event Grid.
* Its structure is same as Azure Event Grid Event Class.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageHeader {
private String id;
private String subject;
private String topic;
private String eventType;
private String eventTime;
private Object data;
private String dataVersion;
}

@ -0,0 +1,45 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is claim/message reference class.
* It contains the information about data where it is stored in persistent storage
* and file name.
* dataLocation is blob storage container name.
* dataFileName is file name in above container.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MessageReference {
private String dataLocation;
private String dataFileName;
}

@ -0,0 +1,43 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is call cost details class.
* It stores userId of the caller, call duration cost and data cost.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UsageCostDetail {
private String userId;
private double callCost;
private double dataCost;
}

@ -0,0 +1,44 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.domain;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* This is call usage detail calls.
* It stores userId of the caller, call duration and data used.
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class UsageDetail {
private String userId;
private int duration;
private int data;
}

@ -0,0 +1,146 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.producer.calldetails.functions;
import com.azure.messaging.eventgrid.EventGridEvent;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationEventData;
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationResponse;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageBody;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.EventHandlerUtility;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.UUID;
/**
* Azure Functions with HTTP Trigger.
* This is Producer class.
*/
public class UsageDetailPublisherFunction {
private MessageHandlerUtility<UsageDetail> messageHandlerUtility;
private EventHandlerUtility<MessageHeader> eventHandlerUtility;
public UsageDetailPublisherFunction() {
this.messageHandlerUtility = new MessageHandlerUtility<>();
this.eventHandlerUtility = new EventHandlerUtility<>();
}
public UsageDetailPublisherFunction(MessageHandlerUtility<UsageDetail> messageHandlerUtility,
EventHandlerUtility<MessageHeader> eventHandlerUtility) {
this.messageHandlerUtility = messageHandlerUtility;
this.eventHandlerUtility = eventHandlerUtility;
}
/**
* Azure function which create message, drop it in persistent storage
* and publish the event to Event Grid topic.
* @param request represents HttpRequestMessage
* @param context represents ExecutionContext
* @return HttpResponseMessage
*/
@FunctionName("UsageDetailPublisherFunction")
public HttpResponseMessage run(@HttpTrigger(name = "req", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) {
try {
var eventGridEvents = EventGridEvent.fromString(request.getBody().get());
for (EventGridEvent eventGridEvent : eventGridEvents) {
// Handle system events
if (eventGridEvent.getEventType()
.equals("Microsoft.EventGrid.SubscriptionValidationEvent")) {
SubscriptionValidationEventData subscriptionValidationEventData = eventGridEvent.getData()
.toObject(SubscriptionValidationEventData.class);
// Handle the subscription validation event
var responseData = new SubscriptionValidationResponse();
responseData.setValidationResponse(subscriptionValidationEventData.getValidationCode());
return request.createResponseBuilder(HttpStatus.OK).body(responseData).build();
} else if (eventGridEvent.getEventType().equals("UsageDetail")) {
// Create message body
var messageBody = new MessageBody<UsageDetail>();
var usageDetailsList = new ArrayList<UsageDetail>();
var random = new Random();
for (int i = 0; i < 51; i++) {
var usageDetail = new UsageDetail();
usageDetail.setUserId("userId" + i);
usageDetail.setData(random.nextInt(500));
usageDetail.setDuration(random.nextInt(500));
usageDetailsList.add(usageDetail);
}
messageBody.setData(usageDetailsList);
// Create message header
var messageHeader = new MessageHeader();
messageHeader.setId(UUID.randomUUID().toString());
messageHeader.setSubject("UsageDetailPublisher");
messageHeader.setTopic("usagecostprocessorfunction-topic");
messageHeader.setEventType("UsageDetail");
messageHeader.setEventTime(OffsetDateTime.now().toString());
var messageReference = new MessageReference("callusageapp",
messageHeader.getId() + "/input.json");
messageHeader.setData(messageReference);
messageHeader.setDataVersion("v1.0");
// Create entire message
var message = new Message<UsageDetail>();
message.setMessageHeader(messageHeader);
message.setMessageBody(messageBody);
// Drop data to persistent storage
this.messageHandlerUtility.dropToPersistantStorage(message, context.getLogger());
// Publish event to event grid topic
eventHandlerUtility.publishEvent(messageHeader, context.getLogger());
context.getLogger().info("Message is dropped and event is published successfully");
return request.createResponseBuilder(HttpStatus.OK).body(message).build();
}
}
} catch (Exception e) {
context.getLogger().warning(e.getMessage());
}
return request.createResponseBuilder(HttpStatus.OK).body(null).build();
}
}

@ -0,0 +1,66 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import com.azure.core.credential.AzureKeyCredential;
import com.azure.core.util.BinaryData;
import com.azure.messaging.eventgrid.EventGridPublisherClient;
import com.azure.messaging.eventgrid.EventGridPublisherClientBuilder;
import java.util.logging.Logger;
/**
* This class is event publisher utility which published message header to Event Grid topic.
* @param <T> represents UsageDetail or UsageCostDetail
*/
public class EventHandlerUtility<T> {
private EventGridPublisherClient<BinaryData> customEventClient;
/** Default constructor.
*/
public EventHandlerUtility() {
this.customEventClient = new EventGridPublisherClientBuilder()
.endpoint(System.getenv("EventGridURL"))
.credential(new AzureKeyCredential(System.getenv("EventGridKey")))
.buildCustomEventPublisherClient();
}
/**
Parameterized constructor.
*/
public EventHandlerUtility(EventGridPublisherClient<BinaryData> customEventClient) {
this.customEventClient = customEventClient;
}
/**
Method for publishing event to Event Grid Topic.
*/
public void publishEvent(T customEvent, Logger logger) {
try {
customEventClient.sendEvent(BinaryData.fromObject(customEvent));
} catch (Exception e) {
logger.info(e.getMessage());
}
}
}

@ -0,0 +1,127 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import com.azure.core.util.BinaryData;
import com.azure.core.util.serializer.TypeReference;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.azure.storage.blob.BlobServiceClientBuilder;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import java.util.logging.Logger;
/**
* This class read and drop message from Azure blob storage.
* @param <T> represents UsageDetail or UsageCostDetail
*/
public class MessageHandlerUtility<T> {
private BlobServiceClient blobServiceClient;
/**
* Parameterized constructor.
* @param blobServiceClient represents BlobServiceClient
*/
public MessageHandlerUtility(BlobServiceClient blobServiceClient) {
this.blobServiceClient = blobServiceClient;
}
/**
* Default constructor.
*/
public MessageHandlerUtility() {
// Create a BlobServiceClient object which will be used to create a container
// client
this.blobServiceClient = new BlobServiceClientBuilder()
.connectionString(System.getenv("BlobStorageConnectionString")).buildClient();
}
/**
* Read message from blob storage.
* @param messageReference represents MessageReference
* @param logger represents Logger
* @return Message
*/
public Message<T> readFromPersistantStorage(MessageReference messageReference, Logger logger) {
Message<T> message = null;
try {
// Get container name from message reference
String containerName = messageReference.getDataLocation();
// Get blob name from message reference
String blobName = messageReference.getDataFileName();
// Get container client
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName);
// Get a reference to a blob
BlobClient blobClient = containerClient.getBlobClient(blobName);
// download the blob
message = blobClient.downloadContent().toObject(new TypeReference<Message<T>>() {
});
} catch (Exception e) {
logger.info(e.getMessage());
}
return message;
}
/**
* Drop message to blob storage.
* @param message represents Message
* @param logger represents Logger
*/
public void dropToPersistantStorage(Message<T> message, Logger logger) {
try {
// Get message reference
MessageReference messageReference = (MessageReference) message.getMessageHeader().getData();
// Create a unique name for the container
String containerName = messageReference.getDataLocation();
// Create the container and return a container client object
BlobContainerClient containerClient = this.blobServiceClient
.getBlobContainerClient(containerName);
if (!containerClient.exists()) {
containerClient.create();
}
// Get a reference to a blob
BlobClient blobClient = containerClient.getBlobClient(messageReference.getDataFileName());
// Upload the blob
blobClient.upload(BinaryData.fromObject(message));
} catch (Exception e) {
logger.info(e.getMessage());
}
}
}

@ -0,0 +1,105 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.HttpStatusType;
import java.util.HashMap;
import java.util.Map;
/**
* The mock for HttpResponseMessage, can be used in unit tests to verify if the
* returned response by HTTP trigger function is correct or not.
*/
public class HttpResponseMessageMock implements HttpResponseMessage {
private int httpStatusCode;
private HttpStatusType httpStatus;
private Object body;
private Map<String, String> headers;
public HttpResponseMessageMock(HttpStatusType status, Map<String, String> headers, Object body) {
this.httpStatus = status;
this.httpStatusCode = status.value();
this.headers = headers;
this.body = body;
}
@Override
public HttpStatusType getStatus() {
return this.httpStatus;
}
@Override
public int getStatusCode() {
return httpStatusCode;
}
@Override
public String getHeader(String key) {
return this.headers.get(key);
}
@Override
public Object getBody() {
return this.body;
}
public static class HttpResponseMessageBuilderMock implements HttpResponseMessage.Builder {
private Object body;
private int httpStatusCode;
private Map<String, String> headers = new HashMap<>();
private HttpStatusType httpStatus;
public Builder status(HttpStatus status) {
this.httpStatusCode = status.value();
this.httpStatus = status;
return this;
}
@Override
public Builder status(HttpStatusType httpStatusType) {
this.httpStatusCode = httpStatusType.value();
this.httpStatus = httpStatusType;
return this;
}
@Override
public HttpResponseMessage.Builder header(String key, String value) {
this.headers.put(key, value);
return this;
}
@Override
public HttpResponseMessage.Builder body(Object body) {
this.body = body;
return this;
}
@Override
public HttpResponseMessage build() {
return new HttpResponseMessageMock(this.httpStatus, this.headers, this.body);
}
}
}

@ -0,0 +1,181 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions;
import com.iluwatar.claimcheckpattern.HttpResponseMessageMock;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.MessageBody;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.MessageReference;
import com.iluwatar.claimcheckpattern.domain.UsageCostDetail;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.time.OffsetDateTime;
import java.util.*;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* Unit test for Function class.
*/
@ExtendWith(MockitoExtension.class)
public class UsageCostProcessorFunctionTest {
@Mock
MessageHandlerUtility<UsageDetail> mockMessageHandlerUtilityForUsageADetail;
@Mock
MessageHandlerUtility<UsageCostDetail> mockMessageHandlerUtilityForUsageCostDetail;
@Mock
ExecutionContext context;
Message<UsageCostDetail> messageToDrop;
Message<UsageDetail> messageToRead;
MessageReference messageReference;
@InjectMocks
UsageCostProcessorFunction usageCostProcessorFunction;
@BeforeEach
public void setUp() {
var messageBodyUsageDetail = new MessageBody<UsageDetail>();
var usageDetailsList = new ArrayList<UsageDetail>();
var messageBodyUsageCostDetail = new MessageBody<UsageCostDetail>();
var usageCostDetailsList = new ArrayList<UsageCostDetail>();
for (int i = 0; i < 2; i++) {
var usageDetail = new UsageDetail();
usageDetail.setUserId("userId" + i);
usageDetail.setData(i + 1);
usageDetail.setDuration(i + 1);
usageDetailsList.add(usageDetail);
var usageCostDetail = new UsageCostDetail();
usageCostDetail.setUserId(usageDetail.getUserId());
usageCostDetail.setDataCost(usageDetail.getData() * 0.20);
usageCostDetail.setCallCost(usageDetail.getDuration() * 0.30);
usageCostDetailsList.add(usageCostDetail);
}
messageBodyUsageDetail.setData(usageDetailsList);
messageBodyUsageCostDetail.setData(usageCostDetailsList);
// Create message header
var messageHeader = new MessageHeader();
messageHeader.setId(UUID.randomUUID().toString());
messageHeader.setSubject("UsageDetailPublisher");
messageHeader.setTopic("usagecostprocessorfunction-topic");
messageHeader.setEventType("UsageDetail");
messageHeader.setEventTime(OffsetDateTime.now().toString());
this.messageReference = new MessageReference("callusageapp", "d8284456-dfff-4bd4-9cef-ea99f70f4835/input.json");
messageHeader.setData(messageReference);
messageHeader.setDataVersion("v1.0");
// Create entire message
messageToRead = new Message<>();
messageToRead.setMessageHeader(messageHeader);
messageToRead.setMessageBody(messageBodyUsageDetail);
messageToDrop = new Message<>();
messageToDrop.setMessageHeader(messageHeader);
messageToDrop.setMessageBody(messageBodyUsageCostDetail);
}
/**
* Unit test for HttpTriggerJava method.
*/
@Test
public void shouldTriggerHttpAzureFunctionJavaWithSubscriptionValidationEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/subscriptionValidationEvent.json").getPath()
.replaceAll("%20", " "), jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
// Invoke
final HttpResponseMessage ret = this.usageCostProcessorFunction.run(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.OK);
}
@Test
public void shouldTriggerHttpAzureFunctionJavaWithUsageDetailEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/usageDetailEvent.json").getPath().replaceAll("%20", " "),
jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doReturn(Logger.getGlobal()).when(context).getLogger();
when(this.mockMessageHandlerUtilityForUsageADetail.readFromPersistantStorage(any(MessageReference.class),
eq(Logger.getGlobal()))).thenReturn(messageToRead);
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
assertNotNull(this.mockMessageHandlerUtilityForUsageADetail);
assertEquals(this.messageToRead, this.mockMessageHandlerUtilityForUsageADetail
.readFromPersistantStorage(this.messageReference, Logger.getGlobal()));
// Invoke
final HttpResponseMessage ret = this.usageCostProcessorFunction.run(req, context);
// Verify
assertEquals(HttpStatus.OK, ret.getStatus());
assertEquals("Message is dropped successfully", ret.getBody());
}
}

@ -0,0 +1,119 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.producer.calldetails.functions;
import com.iluwatar.claimcheckpattern.HttpResponseMessageMock;
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import com.iluwatar.claimcheckpattern.utility.EventHandlerUtility;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.stubbing.Answer;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;
import java.util.logging.Logger;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* Unit test for Function class.
*/
@ExtendWith(MockitoExtension.class)
public class UsageDetailPublisherFunctionTest {
@Mock
MessageHandlerUtility<UsageDetail> mockMessageHandlerUtility;
@Mock
EventHandlerUtility<MessageHeader> mockEventHandlerUtility;
@InjectMocks
UsageDetailPublisherFunction usageDetailPublisherFunction;
/**
* Unit test for HttpTriggerJava method.
*/
@Test
public void shouldTriggerHttpAzureFunctionJavaWithSubscriptionValidationEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/subscriptionValidationEvent.json").getPath()
.replaceAll("%20", " "), jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
// Invoke
final HttpResponseMessage ret = this.usageDetailPublisherFunction.run(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.OK);
}
@Test
public void shouldTriggerHttpAzureFunctionJavaWithUsageDetailEventType() throws Exception {
// Setup
@SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
String fileAbsolutePath = getClass().getResource("/usageDetailEvent.json").getPath().replaceAll("%20", " "),
jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
doReturn(Optional.of(jsonBody)).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
@Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
doReturn(Logger.getGlobal()).when(context).getLogger();
// Invoke
final HttpResponseMessage ret = this.usageDetailPublisherFunction.run(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.OK);
}
}

@ -0,0 +1,70 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
import com.azure.core.util.BinaryData;
import com.azure.messaging.eventgrid.EventGridPublisherClient;
import com.iluwatar.claimcheckpattern.domain.Message;
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
import java.util.logging.Logger;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
public class EventHandlerUtilityTest {
@Mock
EventGridPublisherClient<BinaryData> mockCustomEventClient;
@InjectMocks
EventHandlerUtility<Message<UsageDetail>> eventHandlerUtility;
@BeforeEach
public void setUp() {
System.setProperty("EventGridURL", "https://www.dummyEndpoint.com/api/events");
System.setProperty("EventGridKey", "EventGridURL");
}
@Test
void shouldPublishEvent() {
doNothing().when(mockCustomEventClient).sendEvent(any(BinaryData.class));
eventHandlerUtility.publishEvent(null, Logger.getLogger("logger"));
verify(mockCustomEventClient, times(1)).sendEvent(any(BinaryData.class));
}
@Test
void shouldPublishEventWithNullLogger() {
eventHandlerUtility.publishEvent(null, null);
verify(mockCustomEventClient, times(1)).sendEvent(any(BinaryData.class));
}
}

@ -0,0 +1,114 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.claimcheckpattern.utility;
import com.azure.storage.blob.BlobClient;
import com.azure.storage.blob.BlobContainerClient;
import com.azure.storage.blob.BlobServiceClient;
import com.iluwatar.claimcheckpattern.domain.*;
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.UUID;
import java.util.logging.Logger;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.*;
@ExtendWith(MockitoExtension.class)
public class MessageHandlerUtilityTest {
@Mock
private BlobClient mockBlobClient;
@Mock
private BlobContainerClient mockContainerClient;
@Mock
private BlobServiceClient mockBlobServiceClient;
@InjectMocks
private MessageHandlerUtility<UsageDetail> messageHandlerUtility;
private Message<UsageDetail> messageToPublish;
private MessageReference messageReference;
@BeforeEach
public void setUp() {
System.setProperty("BlobStorageConnectionString", "https://www.dummyEndpoint.com/api/blobs");
var messageBody = new MessageBody<UsageDetail>();
var usageDetailsList = new ArrayList<UsageDetail>();
var random = new Random();
for (int i = 0; i < 51; i++) {
var usageDetail = new UsageDetail();
usageDetail.setUserId("userId" + i);
usageDetail.setData(random.nextInt(500));
usageDetail.setDuration(random.nextInt(500));
usageDetailsList.add(usageDetail);
}
messageBody.setData(usageDetailsList);
// Create message header
var messageHeader = new MessageHeader();
messageHeader.setId(UUID.randomUUID().toString());
messageHeader.setSubject("UsageDetailPublisher");
messageHeader.setTopic("usagecostprocessorfunction-topic");
messageHeader.setEventType("UsageDetail");
messageHeader.setEventTime(OffsetDateTime.now().toString());
this.messageReference = new MessageReference("callusageapp", messageHeader.getId() + "/input.json");
messageHeader.setData(messageReference);
messageHeader.setDataVersion("v1.0");
// Create entire message
this.messageToPublish = new Message<>();
this.messageToPublish.setMessageHeader(messageHeader);
this.messageToPublish.setMessageBody(messageBody);
when(mockContainerClient.getBlobClient(anyString())).thenReturn(mockBlobClient);
when(mockBlobServiceClient.getBlobContainerClient(anyString())).thenReturn(mockContainerClient);
}
@Test
void shouldDropMessageToPersistantStorage() {
messageHandlerUtility.dropToPersistantStorage(messageToPublish, Logger.getLogger("logger"));
verify(mockBlobServiceClient, times(1)).getBlobContainerClient(anyString());
// verify(mockContainerClient, times(0)).exists();
}
@Test
void shouldReadMessageFromPersistantStorage() {
messageHandlerUtility.readFromPersistantStorage(messageReference, Logger.getLogger("logger"));
verify(mockBlobServiceClient, times(1)).getBlobContainerClient(anyString());
}
}

@ -0,0 +1,15 @@
[
{
"data": {
"validationCode": "C12F266E-79D9-4C0A-9922-5EF6201A34C2",
"validationUrl": "https://rp-centralindia.eventgrid.azure.net:553/eventsubscriptions/usagedetailpublisherfunction-subscription/validate?idu003dC12F266E-79D9-4C0A-9922-5EF6201A34C2u0026tu003d2021-10-26T08:10:52.4999377Zu0026apiVersionu003d2020-10-15-previewu0026tokenu003d30kEVoL8rAOWzQv0buurhrKnbP%2bGMtHObbA%2bax6wb4Y%3d"
},
"dataVersion": "2",
"eventTime": "2021-10-26T08:10:52.4999377Z",
"eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
"id": "e2a8466b-3dc0-46b7-bb7d-b999e51a2848",
"metadataVersion": "1",
"subject": "",
"topic": "/subscriptions/0fef643d-a6b1-48f9-a256-53fbd0d22f48/resourceGroups/resource-group-ccp/providers/Microsoft.EventGrid/domains/event-grid-domains-ccp/topics/usagedetailpublisherfunction-topic"
}
]

@ -0,0 +1,15 @@
[
{
"data": {
"dataFileName": "d8284456-dfff-4bd4-9cef-ea99f70f4835/input.json",
"dataLocation": "callusageapp"
},
"dataVersion": "v1.0",
"eventTime": "2021-10-25T19:17:15.7468501Z",
"eventType": "UsageDetail",
"id": "d8284456-dfff-4bd4-9cef-ea99f70f4835",
"metadataVersion": "1",
"subject": "UsageDetailPublisher",
"topic": "/subscriptions/0fef643d-a6b1-48f9-a256-ea99f70f4835/resourceGroups/resource-group-ccp/providers/Microsoft.EventGrid/domains/event-grid-domains-ccp/topics/usagecostprocessorfunction-topic"
}
]

Binary file not shown.

After

(image error) Size: 52 KiB

@ -0,0 +1,117 @@
@startuml
class UsageDetailPublisherFunction [[java:com.iluwatar.producer.calldetails.functions]] {
-messageHandlerUtility: MessageHandlerUtility<UsageDetail>
-eventHandlerUtility: EventHandlerUtility<MessageHeader>
+run(): HttpResponseMessage
}
class UsageCostProcessorFunction [[java:com.iluwatar.consumer.callcostprocessor.functions]] {
-messageHandlerUtilityForUsageDetail: MessageHandlerUtility<UsageDetail>
-messageHandlerUtilityForUsageCostDetail: MessageHandlerUtility<UsageCostDetail>
+run(): HttpResponseMessage
}
class "MessageHandlerUtility<T>" as MessageHandlerUtility_T [[java:com.iluwatar.claimcheckpattern.utility]] {
+readFromPersistantStorage(messageReference: MessageReference, logger: Logger): Message
+dropToPersistantStorage(message: Message, logger: Logger): void
}
class "EventHandlerUtility<T>" as EventHandlerUtility_T [[java:com.callusage.utility.PersistentLocalStorageUtility]] {
+publishEvent(customEvent: T, logger: Logger): void
}
class "Message<T>" as Message_T [[java:com.iluwatar.claimcheckpattern.domain]] {
-messageHeader: MessageHeader
-messageData: MessageData<T>
+Message(messageHeader: MessageHeader, messageData: MessageData<T>)
+getMessageData(): MessageData<T>
+getMessageHeader(): MessageHeader
}
class MessageHeader [[java:com.iluwatar.claimcheckpattern.domain]] {
-id: String
-subject: String
-topic: String
-eventType: String
-eventTime: String
-data: Object
-dataVersion: String
+getId(): String
+setId(id: String): void
+getSubject(): String
+setSubject(subject: String): void
+getTopic(): String
+setTopic(topic: String): void
+getEventType(): String
+setEventType(eventType: String): void
+getEventTime(): String
+setEventTime(eventTime: String): void
+getData(): Object
+setData(data: Object): void
+getDataVersion(): String
+setDataVersion(dataVersion:String): void
}
class "MessageBody<T>" as MessageBody_T [[java:com.iluwatar.claimcheckpattern.domain]] {
-data: List[] T
+getData(): List[] T
+setData(data:List[] T): void
}
class MessageReference [[java:com.iluwatar.claimcheckpattern.domain]] {
-dataLocation: String
-dataFileName: String
+getDataLocation(): String
+setDataLocation(dataLocation:String): void
+getDataFileName(): String
+setDataFileName(dataFileName:String): void
}
class UsageDetail [[java:com.iluwatar.claimcheckpattern.domain]] {
-userId: String
-duration: int
-data: int
+getUserId(): String
+setUserId(userId: String): void
+getDuration(): long
+setDuration(duration: long): void
+getData(): long
+setData(data: long): void
}
class UsageCostDetail [[java:com.iluwatar.claimcheckpattern.domain]] {
-userId: String
-callCost: double
-dataCost: double
+getUserId(): String
+setUserId(userId: String): void
+getCallCost(): double
+setCallCost(callCost:double): void
+getDataCost(): double
+setDataCost(dataCost:double) : void
}
Message_T "1" *-- "1" MessageHeader : has
Message_T "1" *-- "1" MessageBody_T : has
MessageHeader "1" *-- "1" MessageReference : has as data object
MessageBody_T "1" *-- "1" UsageDetail: has
MessageBody_T "1" *-- "1" UsageCostDetail: has
EventHandlerUtility_T "1" *-- "1" MessageHeader: has
MessageHandlerUtility_T "1" *-- "1" Message_T: has
UsageDetailPublisherFunction "1" *-- "1" MessageHandlerUtility_T : has
UsageDetailPublisherFunction "1" *-- "1" EventHandlerUtility_T : has
UsageCostProcessorFunction "1" *-- "1" MessageHandlerUtility_T : has
UsageCostProcessorFunction "1" *-- "1" MessageHandlerUtility_T : has
@enduml

Binary file not shown.

After

(image error) Size: 108 KiB

@ -0,0 +1,69 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>claim-check-pattern</artifactId>
<packaging>pom</packaging>
<modules>
<module>call-usage-app</module>
</modules>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.8</version>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId>
<version>5.8.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -27,7 +28,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>collection-pipeline</artifactId>
<dependencies>

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,15 +23,13 @@
THE SOFTWARE.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>combinator</artifactId>
<dependencies>
<dependency>
@ -40,5 +38,4 @@
<scope>test</scope>
</dependency>
</dependencies>
</project>

@ -1,4 +1,4 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -23,13 +23,12 @@
THE SOFTWARE.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>command</artifactId>
<dependencies>

@ -36,9 +36,6 @@ public class Wizard {
private final Deque<Runnable> undoStack = new LinkedList<>();
private final Deque<Runnable> redoStack = new LinkedList<>();
public Wizard() {
}
/**
* Cast spell.
*/

@ -1,3 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
@ -27,7 +28,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.26.0-SNAPSHOT</version>
</parent>
<artifactId>commander</artifactId>
<dependencies>
@ -55,7 +56,7 @@
</configuration>
</execution>
<execution>
<id>Message</id>
<id>Message</id>
<configuration>
<archive>
<manifest>

@ -90,6 +90,13 @@ public class Commander {
private static final Logger LOG = LoggerFactory.getLogger(Commander.class);
//we could also have another db where it stores all orders
private static final String ORDER_ID = "Order {}";
private static final String REQUEST_ID = " request Id: {}";
private static final String ERROR_CONNECTING_MSG_SVC =
": Error in connecting to messaging service ";
private static final String TRY_CONNECTING_MSG_SVC =
": Trying to connect to messaging service..";
Commander(EmployeeHandle empDb, PaymentService paymentService, ShippingService shippingService,
MessagingService messagingService, QueueDatabase qdb, int numOfRetries,
long retryDuration, long queueTime, long queueTaskTime, long paymentTime,
@ -118,17 +125,17 @@ public class Commander {
Retry.Operation op = (l) -> {
if (!l.isEmpty()) {
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
LOG.debug("Order " + order.id + ": Error in connecting to shipping service, "
+ "trying again..");
LOG.debug(ORDER_ID + ": Error in connecting to shipping service, "
+ "trying again..", order.id);
} else {
LOG.debug("Order " + order.id + ": Error in creating shipping request..");
LOG.debug(ORDER_ID + ": Error in creating shipping request..", order.id);
}
throw l.remove(0);
}
String transactionId = shippingService.receiveRequest(order.item, order.user.address);
//could save this transaction id in a db too
LOG.info("Order " + order.id + ": Shipping placed successfully, transaction id: "
+ transactionId);
LOG.info(ORDER_ID + ": Shipping placed successfully, transaction id: {}",
order.id, transactionId);
LOG.info("Order has been placed and will be shipped to you. Please wait while we make your"
+ " payment... ");
sendPaymentRequest(order);
@ -138,19 +145,19 @@ public class Commander {
LOG.info("Shipping is currently not possible to your address. We are working on the problem"
+ " and will get back to you asap.");
finalSiteMsgShown = true;
LOG.info("Order " + order.id + ": Shipping not possible to address, trying to add problem "
+ "to employee db..");
LOG.info(ORDER_ID + ": Shipping not possible to address, trying to add problem "
+ "to employee db..", order.id);
employeeHandleIssue(o);
} else if (ItemUnavailableException.class.isAssignableFrom(err.getClass())) {
LOG.info("This item is currently unavailable. We will inform you as soon as the item "
+ "becomes available again.");
finalSiteMsgShown = true;
LOG.info("Order " + order.id + ": Item " + order.item + " unavailable, trying to add "
+ "problem to employee handle..");
LOG.info(ORDER_ID + ": Item {}" + " unavailable, trying to add "
+ "problem to employee handle..", order.id, order.item);
employeeHandleIssue(o);
} else {
LOG.info("Sorry, there was a problem in creating your order. Please try later.");
LOG.error("Order " + order.id + ": Shipping service unavailable, order not placed..");
LOG.error(ORDER_ID + ": Shipping service unavailable, order not placed..", order.id);
finalSiteMsgShown = true;
}
};
@ -164,7 +171,7 @@ public class Commander {
if (order.paid.equals(PaymentStatus.TRYING)) {
order.paid = PaymentStatus.NOT_DONE;
sendPaymentFailureMessage(order);
LOG.error("Order " + order.id + ": Payment time for order over, failed and returning..");
LOG.error(ORDER_ID + ": Payment time for order over, failed and returning..", order.id);
} //if succeeded or failed, would have been dequeued, no attempt to make payment
return;
}
@ -173,17 +180,18 @@ public class Commander {
Retry.Operation op = (l) -> {
if (!l.isEmpty()) {
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
LOG.debug("Order " + order.id + ": Error in connecting to payment service,"
+ " trying again..");
LOG.debug(ORDER_ID + ": Error in connecting to payment service,"
+ " trying again..", order.id);
} else {
LOG.debug("Order " + order.id + ": Error in creating payment request..");
LOG.debug(ORDER_ID + ": Error in creating payment request..", order.id);
}
throw l.remove(0);
}
if (order.paid.equals(PaymentStatus.TRYING)) {
var transactionId = paymentService.receiveRequest(order.price);
order.paid = PaymentStatus.DONE;
LOG.info("Order " + order.id + ": Payment successful, transaction Id: " + transactionId);
LOG.info(ORDER_ID + ": Payment successful, transaction Id: {}",
order.id, transactionId);
if (!finalSiteMsgShown) {
LOG.info("Payment made successfully, thank you for shopping with us!!");
finalSiteMsgShown = true;
@ -199,7 +207,7 @@ public class Commander {
+ "Meanwhile, your order has been converted to COD and will be shipped.");
finalSiteMsgShown = true;
}
LOG.error("Order " + order.id + ": Payment details incorrect, failed..");
LOG.error(ORDER_ID + ": Payment details incorrect, failed..", order.id);
o.paid = PaymentStatus.NOT_DONE;
sendPaymentFailureMessage(o);
} else {
@ -209,7 +217,7 @@ public class Commander {
+ "asap. Don't worry, your order has been placed and will be shipped.");
finalSiteMsgShown = true;
}
LOG.warn("Order " + order.id + ": Payment error, going to queue..");
LOG.warn(ORDER_ID + ": Payment error, going to queue..", order.id);
sendPaymentPossibleErrorMsg(o);
}
if (o.paid.equals(PaymentStatus.TRYING) && System
@ -234,7 +242,7 @@ public class Commander {
if (System.currentTimeMillis() - qt.order.createdTime >= this.queueTime) {
// since payment time is lesser than queuetime it would have already failed..
// additional check not needed
LOG.trace("Order " + qt.order.id + ": Queue time for order over, failed..");
LOG.trace(ORDER_ID + ": Queue time for order over, failed..", qt.order.id);
return;
} else if (qt.taskType.equals(TaskType.PAYMENT) && !qt.order.paid.equals(PaymentStatus.TRYING)
|| qt.taskType.equals(TaskType.MESSAGING) && (qt.messageType == 1
@ -242,30 +250,30 @@ public class Commander {
|| qt.order.messageSent.equals(MessageSent.PAYMENT_FAIL)
|| qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL))
|| qt.taskType.equals(TaskType.EMPLOYEE_DB) && qt.order.addedToEmployeeHandle) {
LOG.trace("Order " + qt.order.id + ": Not queueing task since task already done..");
LOG.trace(ORDER_ID + ": Not queueing task since task already done..", qt.order.id);
return;
}
var list = queue.exceptionsList;
Thread t = new Thread(() -> {
Retry.Operation op = (list1) -> {
if (!list1.isEmpty()) {
LOG.warn("Order " + qt.order.id + ": Error in connecting to queue db, trying again..");
LOG.warn(ORDER_ID + ": Error in connecting to queue db, trying again..", qt.order.id);
throw list1.remove(0);
}
queue.add(qt);
queueItems++;
LOG.info("Order " + qt.order.id + ": " + qt.getType() + " task enqueued..");
LOG.info(ORDER_ID + ": {}" + " task enqueued..", qt.order.id, qt.getType());
tryDoingTasksInQueue();
};
Retry.HandleErrorIssue<QueueTask> handleError = (qt1, err) -> {
if (qt1.taskType.equals(TaskType.PAYMENT)) {
qt1.order.paid = PaymentStatus.NOT_DONE;
sendPaymentFailureMessage(qt1.order);
LOG.error("Order " + qt1.order.id + ": Unable to enqueue payment task,"
+ " payment failed..");
LOG.error(ORDER_ID + ": Unable to enqueue payment task,"
+ " payment failed..", qt1.order.id);
}
LOG.error("Order " + qt1.order.id + ": Unable to enqueue task of type " + qt1.getType()
+ ", trying to add to employee handle..");
LOG.error(ORDER_ID + ": Unable to enqueue task of type {}"
+ ", trying to add to employee handle..", qt1.order.id, qt1.getType());
employeeHandleIssue(qt1.order);
};
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
@ -328,7 +336,7 @@ public class Commander {
private void sendSuccessMessage(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.messageTime) {
LOG.trace("Order " + order.id + ": Message time for order over, returning..");
LOG.trace(ORDER_ID + ": Message time for order over, returning..", order.id);
return;
}
var list = messagingService.exceptionsList;
@ -354,8 +362,8 @@ public class Commander {
&& System.currentTimeMillis() - o.createdTime < messageTime) {
var qt = new QueueTask(order, TaskType.MESSAGING, 2);
updateQueue(qt);
LOG.info("Order " + order.id + ": Error in sending Payment Success message, trying to"
+ " queue task and add to employee handle..");
LOG.info(ORDER_ID + ": Error in sending Payment Success message, trying to"
+ " queue task and add to employee handle..", order.id);
employeeHandleIssue(order);
}
}
@ -364,11 +372,11 @@ public class Commander {
return (l) -> {
if (!l.isEmpty()) {
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
+ "(Payment Success msg), trying again..");
LOG.debug(ORDER_ID + ERROR_CONNECTING_MSG_SVC
+ "(Payment Success msg), trying again..", order.id);
} else {
LOG.debug("Order " + order.id + ": Error in creating Payment Success"
+ " messaging request..");
LOG.debug(ORDER_ID + ": Error in creating Payment Success"
+ " messaging request..", order.id);
}
throw l.remove(0);
}
@ -376,15 +384,15 @@ public class Commander {
&& !order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
var requestId = messagingService.receiveRequest(2);
order.messageSent = MessageSent.PAYMENT_SUCCESSFUL;
LOG.info("Order " + order.id + ": Payment Success message sent,"
+ " request Id: " + requestId);
LOG.info(ORDER_ID + ": Payment Success message sent,"
+ REQUEST_ID, order.id, requestId);
}
};
}
private void sendPaymentFailureMessage(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.messageTime) {
LOG.trace("Order " + order.id + ": Message time for order over, returning..");
LOG.trace(ORDER_ID + ": Message time for order over, returning..", order.id);
return;
}
var list = messagingService.exceptionsList;
@ -412,8 +420,8 @@ public class Commander {
&& System.currentTimeMillis() - o.createdTime < messageTime) {
var qt = new QueueTask(order, TaskType.MESSAGING, 0);
updateQueue(qt);
LOG.warn("Order " + order.id + ": Error in sending Payment Failure message, "
+ "trying to queue task and add to employee handle..");
LOG.warn(ORDER_ID + ": Error in sending Payment Failure message, "
+ "trying to queue task and add to employee handle..", order.id);
employeeHandleIssue(o);
}
}
@ -421,11 +429,11 @@ public class Commander {
private void handlePaymentFailureRetryOperation(Order order, List<Exception> l) throws Exception {
if (!l.isEmpty()) {
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
+ "(Payment Failure msg), trying again..");
LOG.debug(ORDER_ID + ERROR_CONNECTING_MSG_SVC
+ "(Payment Failure msg), trying again..", order.id);
} else {
LOG.debug("Order " + order.id + ": Error in creating Payment Failure"
+ " message request..");
LOG.debug(ORDER_ID + ": Error in creating Payment Failure"
+ " message request..", order.id);
}
throw l.remove(0);
}
@ -433,8 +441,8 @@ public class Commander {
&& !order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
var requestId = messagingService.receiveRequest(0);
order.messageSent = MessageSent.PAYMENT_FAIL;
LOG.info("Order " + order.id + ": Payment Failure message sent successfully,"
+ " request Id: " + requestId);
LOG.info(ORDER_ID + ": Payment Failure message sent successfully,"
+ REQUEST_ID, order.id, requestId);
}
}
@ -469,7 +477,7 @@ public class Commander {
var qt = new QueueTask(order, TaskType.MESSAGING, 1);
updateQueue(qt);
LOG.warn("Order " + order.id + ": Error in sending Payment Error message, "
+ "trying to queue task and add to employee handle..");
+ "trying to queue task and add to employee handle..");
employeeHandleIssue(o);
}
}
@ -478,11 +486,11 @@ public class Commander {
throws Exception {
if (!l.isEmpty()) {
if (DatabaseUnavailableException.class.isAssignableFrom(l.get(0).getClass())) {
LOG.debug("Order " + order.id + ": Error in connecting to messaging service "
+ "(Payment Error msg), trying again..");
LOG.debug(ORDER_ID + ERROR_CONNECTING_MSG_SVC
+ "(Payment Error msg), trying again..", order.id);
} else {
LOG.debug("Order " + order.id + ": Error in creating Payment Error"
+ " messaging request..");
LOG.debug(ORDER_ID + ": Error in creating Payment Error"
+ " messaging request..", order.id);
}
throw l.remove(0);
}
@ -490,28 +498,28 @@ public class Commander {
.equals(MessageSent.NONE_SENT)) {
var requestId = messagingService.receiveRequest(1);
order.messageSent = MessageSent.PAYMENT_TRYING;
LOG.info("Order " + order.id + ": Payment Error message sent successfully,"
+ " request Id: " + requestId);
LOG.info(ORDER_ID + ": Payment Error message sent successfully,"
+ REQUEST_ID, order.id, requestId);
}
}
private void employeeHandleIssue(Order order) {
if (System.currentTimeMillis() - order.createdTime >= this.employeeTime) {
LOG.trace("Order " + order.id + ": Employee handle time for order over, returning..");
LOG.trace(ORDER_ID + ": Employee handle time for order over, returning..", order.id);
return;
}
var list = employeeDb.exceptionsList;
var t = new Thread(() -> {
Retry.Operation op = (l) -> {
if (!l.isEmpty()) {
LOG.warn("Order " + order.id + ": Error in connecting to employee handle,"
+ " trying again..");
LOG.warn(ORDER_ID + ": Error in connecting to employee handle,"
+ " trying again..", order.id);
throw l.remove(0);
}
if (!order.addedToEmployeeHandle) {
employeeDb.receiveRequest(order);
order.addedToEmployeeHandle = true;
LOG.info("Order " + order.id + ": Added order to employee database");
LOG.info(ORDER_ID + ": Added order to employee database", order.id);
}
};
Retry.HandleErrorIssue<Order> handleError = (o, err) -> {
@ -519,8 +527,8 @@ public class Commander {
.currentTimeMillis() - order.createdTime < employeeTime) {
var qt = new QueueTask(order, TaskType.EMPLOYEE_DB, -1);
updateQueue(qt);
LOG.warn("Order " + order.id + ": Error in adding to employee db,"
+ " trying to queue task..");
LOG.warn(ORDER_ID + ": Error in adding to employee db,"
+ " trying to queue task..", order.id);
}
};
var r = new Retry<>(op, handleError, numOfRetries, retryDuration,
@ -538,51 +546,51 @@ public class Commander {
if (queueItems != 0) {
var qt = queue.peek(); //this should probably be cloned here
//this is why we have retry for doTasksInQueue
LOG.trace("Order " + qt.order.id + ": Started doing task of type " + qt.getType());
LOG.trace(ORDER_ID + ": Started doing task of type {}", qt.order.id, qt.getType());
if (qt.getFirstAttemptTime() == -1) {
qt.setFirstAttemptTime(System.currentTimeMillis());
}
if (System.currentTimeMillis() - qt.getFirstAttemptTime() >= queueTaskTime) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This queue task of type " + qt.getType()
+ " does not need to be done anymore (timeout), dequeue..");
LOG.trace(ORDER_ID + ": This queue task of type {}"
+ " does not need to be done anymore (timeout), dequeue..", qt.order.id, qt.getType());
} else {
if (qt.taskType.equals(TaskType.PAYMENT)) {
if (!qt.order.paid.equals(PaymentStatus.TRYING)) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This payment task already done, dequeueing..");
LOG.trace(ORDER_ID + ": This payment task already done, dequeueing..", qt.order.id);
} else {
sendPaymentRequest(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to payment service..");
LOG.debug(ORDER_ID + ": Trying to connect to payment service..", qt.order.id);
}
} else if (qt.taskType.equals(TaskType.MESSAGING)) {
if (qt.order.messageSent.equals(MessageSent.PAYMENT_FAIL)
|| qt.order.messageSent.equals(MessageSent.PAYMENT_SUCCESSFUL)) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This messaging task already done, dequeue..");
LOG.trace(ORDER_ID + ": This messaging task already done, dequeue..", qt.order.id);
} else if (qt.messageType == 1 && (!qt.order.messageSent.equals(MessageSent.NONE_SENT)
|| !qt.order.paid.equals(PaymentStatus.TRYING))) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This messaging task does not need to be done,"
+ " dequeue..");
LOG.trace(ORDER_ID + ": This messaging task does not need to be done,"
+ " dequeue..", qt.order.id);
} else if (qt.messageType == 0) {
sendPaymentFailureMessage(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to messaging service..");
LOG.debug(ORDER_ID + TRY_CONNECTING_MSG_SVC, qt.order.id);
} else if (qt.messageType == 1) {
sendPaymentPossibleErrorMsg(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to messaging service..");
LOG.debug(ORDER_ID + TRY_CONNECTING_MSG_SVC, qt.order.id);
} else if (qt.messageType == 2) {
sendSuccessMessage(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to messaging service..");
LOG.debug(ORDER_ID + TRY_CONNECTING_MSG_SVC, qt.order.id);
}
} else if (qt.taskType.equals(TaskType.EMPLOYEE_DB)) {
if (qt.order.addedToEmployeeHandle) {
tryDequeue();
LOG.trace("Order " + qt.order.id + ": This employee handle task already done,"
+ " dequeue..");
LOG.trace(ORDER_ID + ": This employee handle task already done,"
+ " dequeue..", qt.order.id);
} else {
employeeHandleIssue(qt.order);
LOG.debug("Order " + qt.order.id + ": Trying to connect to employee handle..");
LOG.debug(ORDER_ID + ": Trying to connect to employee handle..", qt.order.id);
}
}
}

@ -0,0 +1,526 @@
package com.iluwatar.commander;
import com.iluwatar.commander.employeehandle.EmployeeDatabase;
import com.iluwatar.commander.employeehandle.EmployeeHandle;
import com.iluwatar.commander.exceptions.DatabaseUnavailableException;
import com.iluwatar.commander.exceptions.ItemUnavailableException;
import com.iluwatar.commander.exceptions.PaymentDetailsErrorException;
import com.iluwatar.commander.exceptions.ShippingNotPossibleException;
import com.iluwatar.commander.messagingservice.MessagingDatabase;
import com.iluwatar.commander.messagingservice.MessagingService;
import com.iluwatar.commander.paymentservice.PaymentDatabase;
import com.iluwatar.commander.paymentservice.PaymentService;
import com.iluwatar.commander.queue.QueueDatabase;
import com.iluwatar.commander.shippingservice.ShippingDatabase;
import com.iluwatar.commander.shippingservice.ShippingService;
import org.junit.jupiter.api.Test;
import org.junit.platform.commons.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertFalse;
class CommanderTest {
private final int numOfRetries = 1;
private final long retryDuration = 1_000;
private long queueTime = 1_00;
private long queueTaskTime = 1_000;
private long paymentTime = 6_000;
private long messageTime = 5_000;
private long employeeTime = 2_000;
private static final List<Exception> exceptionList = new ArrayList<>();
static {
exceptionList.add(new DatabaseUnavailableException());
exceptionList.add(new ShippingNotPossibleException());
exceptionList.add(new ItemUnavailableException());
exceptionList.add(new PaymentDetailsErrorException());
exceptionList.add(new IllegalStateException());
}
private Commander buildCommanderObject() {
return buildCommanderObject(false);
}
private Commander buildCommanderObject(boolean nonPaymentException) {
PaymentService paymentService = new PaymentService
(new PaymentDatabase(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException());
ShippingService shippingService;
MessagingService messagingService;
if (nonPaymentException) {
shippingService = new ShippingService(new ShippingDatabase(), new DatabaseUnavailableException());
messagingService = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
} else {
shippingService = new ShippingService(new ShippingDatabase(), new DatabaseUnavailableException());
messagingService = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
}
var employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException());
var qdb = new QueueDatabase
(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException());
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, qdb, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectVanilla() {
PaymentService paymentService = new PaymentService
(new PaymentDatabase(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException());
var shippingService = new ShippingService(new ShippingDatabase());
var messagingService = new MessagingService(new MessagingDatabase());
var employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException());
var qdb = new QueueDatabase
(new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException(),
new DatabaseUnavailableException(), new DatabaseUnavailableException());
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, qdb, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectUnknownException() {
PaymentService paymentService = new PaymentService
(new PaymentDatabase(), new IllegalStateException());
var shippingService = new ShippingService(new ShippingDatabase());
var messagingService = new MessagingService(new MessagingDatabase());
var employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), new IllegalStateException());
var qdb = new QueueDatabase
(new DatabaseUnavailableException(), new IllegalStateException());
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, qdb, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectNoPaymentException1() {
PaymentService paymentService = new PaymentService
(new PaymentDatabase());
var shippingService = new ShippingService(new ShippingDatabase());
var messagingService = new MessagingService(new MessagingDatabase());
var employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), new IllegalStateException());
var qdb = new QueueDatabase
(new DatabaseUnavailableException(), new IllegalStateException());
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, qdb, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectNoPaymentException2() {
PaymentService paymentService = new PaymentService
(new PaymentDatabase());
var shippingService = new ShippingService(new ShippingDatabase());
var messagingService = new MessagingService(new MessagingDatabase(), new IllegalStateException());
var employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), new IllegalStateException());
var qdb = new QueueDatabase
(new DatabaseUnavailableException(), new IllegalStateException());
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, qdb, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectNoPaymentException3() {
PaymentService paymentService = new PaymentService
(new PaymentDatabase());
var shippingService = new ShippingService(new ShippingDatabase());
var messagingService = new MessagingService(new MessagingDatabase(), new DatabaseUnavailableException());
var employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), new IllegalStateException());
var qdb = new QueueDatabase
(new DatabaseUnavailableException(), new IllegalStateException());
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, qdb, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectWithDB() {
return buildCommanderObjectWithoutDB(false, false, new IllegalStateException());
}
private Commander buildCommanderObjectWithDB(boolean includeException, boolean includeDBException, Exception e) {
var l = includeDBException ? new DatabaseUnavailableException() : e;
PaymentService paymentService;
ShippingService shippingService;
MessagingService messagingService;
EmployeeHandle employeeHandle;
if (includeException) {
paymentService = new PaymentService
(new PaymentDatabase(), l);
shippingService = new ShippingService(new ShippingDatabase(), l);
messagingService = new MessagingService(new MessagingDatabase(), l);
employeeHandle = new EmployeeHandle
(new EmployeeDatabase(), l);
} else {
paymentService = new PaymentService
(null);
shippingService = new ShippingService(null);
messagingService = new MessagingService(null);
employeeHandle = new EmployeeHandle
(null);
}
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, null, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
private Commander buildCommanderObjectWithoutDB() {
return buildCommanderObjectWithoutDB(false, false, new IllegalStateException());
}
private Commander buildCommanderObjectWithoutDB(boolean includeException, boolean includeDBException, Exception e) {
var l = includeDBException ? new DatabaseUnavailableException() : e;
PaymentService paymentService;
ShippingService shippingService;
MessagingService messagingService;
EmployeeHandle employeeHandle;
if (includeException) {
paymentService = new PaymentService
(null, l);
shippingService = new ShippingService(null, l);
messagingService = new MessagingService(null, l);
employeeHandle = new EmployeeHandle
(null, l);
} else {
paymentService = new PaymentService
(null);
shippingService = new ShippingService(null);
messagingService = new MessagingService(null);
employeeHandle = new EmployeeHandle
(null);
}
return new Commander(employeeHandle, paymentService, shippingService,
messagingService, null, numOfRetries, retryDuration,
queueTime, queueTaskTime, paymentTime, messageTime, employeeTime);
}
@Test
void testPlaceOrderVanilla() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObjectVanilla();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrder() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObject(true);
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrder2() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObject(false);
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderNoException1() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObjectNoPaymentException1();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderNoException2() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObjectNoPaymentException2();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderNoException3() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObjectNoPaymentException3();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderNoException4() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
Commander c = buildCommanderObjectNoPaymentException3();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
c.placeOrder(order);
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderUnknownException() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObjectUnknownException();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderShortDuration() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObject(true);
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderShortDuration2() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObject(false);
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderNoExceptionShortMsgDuration() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObjectNoPaymentException1();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderNoExceptionShortQueueDuration() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObjectUnknownException();
var order = new Order(new User("K", "J"), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderWithDatabase() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObjectWithDB();
var order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderWithDatabaseAndExceptions() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
for (Exception e : exceptionList) {
Commander c = buildCommanderObjectWithDB(true, true, e);
var order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
c = buildCommanderObjectWithDB(true, false, e);
order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
c = buildCommanderObjectWithDB(false, false, e);
order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
c = buildCommanderObjectWithDB(false, true, e);
order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
}
@Test
void testPlaceOrderWithoutDatabase() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
Commander c = buildCommanderObjectWithoutDB();
var order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
@Test
void testPlaceOrderWithoutDatabaseAndExceptions() throws Exception {
for (double d = 0.1; d < 2; d = d + 0.1) {
paymentTime *= d;
queueTaskTime *= d;
messageTime *= d;
employeeTime *= d;
queueTime *= d;
for (Exception e : exceptionList) {
Commander c = buildCommanderObjectWithoutDB(true, true, e);
var order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
c = buildCommanderObjectWithoutDB(true, false, e);
order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
c = buildCommanderObjectWithoutDB(false, false, e);
order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
c = buildCommanderObjectWithoutDB(false, true, e);
order = new Order(new User("K", null), "pen", 1f);
for (Order.MessageSent ms : Order.MessageSent.values()) {
c.placeOrder(order);
assertFalse(StringUtils.isBlank(order.id));
}
}
}
}
}

@ -23,41 +23,38 @@
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>composite-entity</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.composite-entity.com.iluwatar.compositeentity.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>composite-entity</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.composite-entity.com.iluwatar.compositeentity.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

325
composite-view/README.md Normal file

@ -0,0 +1,325 @@
---
layout: pattern
title: Composite View
folder: composite-view
permalink: /patterns/composite-view/
categories: Structural
language: en
tags:
- Enterprise Integration Pattern
- Presentation
---
## Name
**Composite View**
## Intent
The purpose of the Composite View Pattern is to increase re-usability and flexibility when creating views for websites/webapps.
This pattern seeks to decouple the content of the page from its layout, allowing changes to be made to either the content
or layout of the page without impacting the other. This pattern also allows content to be easily reused across different views easily.
## Explanation
Real World Example
> A news site wants to display the current date and news to different users
> based on that user's preferences. The news site will substitute in different news feed
> components depending on the user's interest, defaulting to local news.
In Plain Words
> Composite View Pattern is having a main view being composed of smaller subviews.
> The layout of this composite view is based on a template. A View-manager then decides which
> subviews to include in this template.
Wikipedia Says
> Composite views that are composed of multiple atomic subviews. Each component of
> the template may be included dynamically into the whole and the layout of the page may be managed independently of the content.
> This solution provides for the creation of a composite view based on the inclusion and substitution of
> modular dynamic and static template fragments.
> It promotes the reuse of atomic portions of the view by encouraging modular design.
**Programmatic Example**
Since this is a web development pattern, a server is required to demonstrate it.
This example uses Tomcat 10.0.13 to run the servlet, and this programmatic example will only work with Tomcat 10+.
Firstly there is `AppServlet` which is an `HttpServlet` that runs on Tomcat 10+.
```java
public class AppServlet extends HttpServlet {
private String msgPartOne = "<h1>This Server Doesn't Support";
private String msgPartTwo = "Requests</h1>\n"
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
private String destination = "newsDisplay.jsp";
public AppServlet() {
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
req.setAttribute("properties", reqParams);
requestDispatcher.forward(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(msgPartOne + " Post " + msgPartTwo);
}
@Override
public void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(msgPartOne + " Delete " + msgPartTwo);
}
@Override
public void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
out.println(msgPartOne + " Put " + msgPartTwo);
}
}
```
This servlet is not part of the pattern, and simply forwards GET requests to the correct JSP.
PUT, POST, and DELETE requests are not supported and will simply show an error message.
The view management in this example is done via a javabean class: `ClientPropertiesBean`, which stores user preferences.
```java
public class ClientPropertiesBean implements Serializable {
private static final String WORLD_PARAM = "world";
private static final String SCIENCE_PARAM = "sci";
private static final String SPORTS_PARAM = "sport";
private static final String BUSINESS_PARAM = "bus";
private static final String NAME_PARAM = "name";
private static final String DEFAULT_NAME = "DEFAULT_NAME";
private boolean worldNewsInterest;
private boolean sportsInterest;
private boolean businessInterest;
private boolean scienceNewsInterest;
private String name;
public ClientPropertiesBean() {
worldNewsInterest = true;
sportsInterest = true;
businessInterest = true;
scienceNewsInterest = true;
name = DEFAULT_NAME;
}
public ClientPropertiesBean(HttpServletRequest req) {
worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
String tempName = req.getParameter(NAME_PARAM);
if (tempName == null || tempName == "") {
tempName = DEFAULT_NAME;
}
name = tempName;
}
// getters and setters generated by Lombok
}
```
This javabean has a default constructor, and another that takes an `HttpServletRequest`.
This second constructor takes the request object, parses out the request parameters which contain the
user preferences for different types of news.
The template for the news page is in `newsDisplay.jsp`
```html
<html>
<head>
<style>
h1 { text-align: center;}
h2 { text-align: center;}
h3 { text-align: center;}
.centerTable {
margin-left: auto;
margin-right: auto;
}
table {border: 1px solid black;}
tr {text-align: center;}
td {text-align: center;}
</style>
</head>
<body>
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
<h1>Welcome <%= propertiesBean.getName()%></h1>
<jsp:include page="header.jsp"></jsp:include>
<table class="centerTable">
<tr>
<td></td>
<% if(propertiesBean.isWorldNewsInterest()) { %>
<td><%@include file="worldNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
</tr>
<tr>
<% if(propertiesBean.isBusinessInterest()) { %>
<td><%@include file="businessNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
<% if(propertiesBean.isSportsInterest()) { %>
<td><%@include file="sportsNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
</tr>
<tr>
<td></td>
<% if(propertiesBean.isScienceNewsInterest()) { %>
<td><%@include file="scienceNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
</tr>
</table>
</body>
</html>
```
This JSP page is the template. It declares a table with three rows, with one component in the first row,
two components in the second row, and one component in the third row.
The scriplets in the file are part of the
view management strategy that include different atomic subviews based on the user preferences in the Javabean.
Here are two examples of the mock atomic subviews used in the composite:
`businessNews.jsp`
```html
<html>
<head>
<style>
h2 { text-align: center;}
table {border: 1px solid black;}
tr {text-align: center;}
td {text-align: center;}
</style>
</head>
<body>
<h2>
Generic Business News
</h2>
<table style="margin-right: auto; margin-left: auto">
<tr>
<td>Stock prices up across the world</td>
<td>New tech companies to invest in</td>
</tr>
<tr>
<td>Industry leaders unveil new project</td>
<td>Price fluctuations and what they mean</td>
</tr>
</table>
</body>
</html>
```
`localNews.jsp`
```html
<html>
<body>
<div style="text-align: center">
<h3>
Generic Local News
</h3>
<ul style="list-style-type: none">
<li>
Mayoral elections coming up in 2 weeks
</li>
<li>
New parking meter rates downtown coming tomorrow
</li>
<li>
Park renovations to finish by the next year
</li>
<li>
Annual marathon sign ups available online
</li>
</ul>
</div>
</body>
</html>
```
The results are as such:
1) The user has put their name as `Tammy` in the request parameters and no preferences:
![alt text](etc/images/noparam.PNG)
2) The user has put their name as `Johnny` in the request parameters and has a preference for world, business, and science news:
![alt text](etc/images/threeparams.PNG)
The different subviews such as `worldNews.jsp`, `businessNews.jsp`, etc. are included conditionally
based on the request parameters.
**How To Use**
To try this example, make sure you have Tomcat 10+ installed.
Set up your IDE to build a WAR file from the module and deploy that file to the server
IntelliJ:
Under `Run` and `edit configurations` Make sure Tomcat server is one of the run configurations.
Go to the deployment tab, and make sure there is one artifact being built called `composite-view:war exploded`.
If not present, add one.
Ensure that the artifact is being built from the content of the `web` directory and the compilation results of the module.
Point the output of the artifact to a convenient place. Run the configuration and view the landing page,
follow instructions on that page to continue.
## Class diagram
![alt text](./etc/composite_view.png)
The class diagram here displays the Javabean which is the view manager.
The views are JSP's held inside the web directory.
## Applicability
This pattern is applicable to most websites that require content to be displayed dynamically/conditionally.
If there are components that need to be re-used for multiple views, or if the project requires reusing a template,
or if it needs to include content depending on certain conditions, then this pattern is a good choice.
## Known uses
Most modern websites use composite views in some shape or form, as they have templates for views and small atomic components
that are included in the page dynamically. Most modern Javascript libraries, like React, support this design pattern
with components.
## Consequences
**Pros**
* Easy to re-use components
* Change layout/content without affecting the other
* Reduce code duplication
* Code is more maintainable and modular
**Cons**
* Overhead cost at runtime
* Slower response compared to directly embedding elements
* Increases potential for display errors
## Related patterns
* [Composite (GoF)](https://java-design-patterns.com/patterns/composite/)
* [View Helper](https://www.oracle.com/java/technologies/viewhelper.html)
## Credits
* [Core J2EE Patterns - Composite View](https://www.oracle.com/java/technologies/composite-view.html)
* [Composite View Design Pattern Core J2EE Patterns](https://www.dineshonjava.com/composite-view-design-pattern/)

@ -0,0 +1,29 @@
@startuml
package com.iluwatar.compositeview {
class ClientPropertiesBean {
- BUSINESS_PARAM : String {static}
- DEFAULT_NAME : String {static}
- NAME_PARAM : String {static}
- SCIENCE_PARAM : String {static}
- SPORTS_PARAM : String {static}
- WORLD_PARAM : String {static}
- businessInterest : boolean
- name : String
- scienceNewsInterest : boolean
- sportsInterest : boolean
- worldNewsInterest : boolean
+ ClientPropertiesBean()
+ ClientPropertiesBean(req : HttpServletRequest)
+ getName() : String
+ isBusinessInterest() : boolean
+ isScienceNewsInterest() : boolean
+ isSportsInterest() : boolean
+ isWorldNewsInterest() : boolean
+ setBusinessInterest(businessInterest : boolean)
+ setName(name : String)
+ setScienceNewsInterest(scienceNewsInterest : boolean)
+ setSportsInterest(sportsInterest : boolean)
+ setWorldNewsInterest(worldNewsInterest : boolean)
}
}
@enduml

Binary file not shown.

After

(image error) Size: 19 KiB

Binary file not shown.

After

(image error) Size: 71 KiB

Binary file not shown.

After

(image error) Size: 77 KiB

80
composite-view/pom.xml Normal file

@ -0,0 +1,80 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.26.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>composite-view</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>4.1.0</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<configuration>
<archive>
<manifest>
<mainClass>com.iluwatar.compositeview.App</mainClass>
</manifest>
</archive>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>

@ -0,0 +1,64 @@
package com.iluwatar.compositeview;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* A servlet object that extends HttpServlet.
* Runs on Tomcat 10 and handles Http requests
*/
public final class AppServlet extends HttpServlet {
private String msgPartOne = "<h1>This Server Doesn't Support";
private String msgPartTwo = "Requests</h1>\n"
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
private String destination = "newsDisplay.jsp";
public AppServlet() {
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
RequestDispatcher requestDispatcher = req.getRequestDispatcher(destination);
ClientPropertiesBean reqParams = new ClientPropertiesBean(req);
req.setAttribute("properties", reqParams);
requestDispatcher.forward(req, resp);
}
@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
try (PrintWriter out = resp.getWriter()) {
out.println(msgPartOne + " Post " + msgPartTwo);
}
}
@Override
public void doDelete(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
try (PrintWriter out = resp.getWriter()) {
out.println(msgPartOne + " Delete " + msgPartTwo);
}
}
@Override
public void doPut(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
resp.setContentType("text/html");
try (PrintWriter out = resp.getWriter()) {
out.println(msgPartOne + " Put " + msgPartTwo);
}
}
}

@ -0,0 +1,53 @@
package com.iluwatar.compositeview;
import jakarta.servlet.http.HttpServletRequest;
import java.io.Serializable;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* A Java beans class that parses a http request and stores parameters.
* Java beans used in JSP's to dynamically include elements in view.
* DEFAULT_NAME = a constant, default name to be used for the default constructor
* worldNewsInterest = whether current request has world news interest
* sportsInterest = whether current request has a sportsInterest
* businessInterest = whether current request has a businessInterest
* scienceNewsInterest = whether current request has a scienceNewsInterest
*/
@Getter
@Setter
@NoArgsConstructor
public class ClientPropertiesBean implements Serializable {
private static final String WORLD_PARAM = "world";
private static final String SCIENCE_PARAM = "sci";
private static final String SPORTS_PARAM = "sport";
private static final String BUSINESS_PARAM = "bus";
private static final String NAME_PARAM = "name";
private static final String DEFAULT_NAME = "DEFAULT_NAME";
private boolean worldNewsInterest = true;
private boolean sportsInterest = true;
private boolean businessInterest = true;
private boolean scienceNewsInterest = true;
private String name = DEFAULT_NAME;
/**
* Constructor that parses an HttpServletRequest and stores all the request parameters.
*
* @param req the HttpServletRequest object that is passed in
*/
public ClientPropertiesBean(HttpServletRequest req) {
worldNewsInterest = Boolean.parseBoolean(req.getParameter(WORLD_PARAM));
sportsInterest = Boolean.parseBoolean(req.getParameter(SPORTS_PARAM));
businessInterest = Boolean.parseBoolean(req.getParameter(BUSINESS_PARAM));
scienceNewsInterest = Boolean.parseBoolean(req.getParameter(SCIENCE_PARAM));
String tempName = req.getParameter(NAME_PARAM);
if (tempName == null || tempName.equals("")) {
tempName = DEFAULT_NAME;
}
name = tempName;
}
}

@ -0,0 +1,83 @@
package com.iluwatar.compositeview;
import jakarta.servlet.RequestDispatcher;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import java.io.PrintWriter;
import java.io.StringWriter;
import static org.junit.Assert.*;
/* Written with reference from https://stackoverflow.com/questions/5434419/how-to-test-my-servlet-using-junit
and https://stackoverflow.com/questions/50211433/servlets-unit-testing
*/
public class AppServletTest extends Mockito{
private String msgPartOne = "<h1>This Server Doesn't Support";
private String msgPartTwo = "Requests</h1>\n"
+ "<h2>Use a GET request with boolean values for the following parameters<h2>\n"
+ "<h3>'name'</h3>\n<h3>'bus'</h3>\n<h3>'sports'</h3>\n<h3>'sci'</h3>\n<h3>'world'</h3>";
private String destination = "newsDisplay.jsp";
@Test
public void testDoGet() throws Exception {
HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
RequestDispatcher mockDispatcher = Mockito.mock(RequestDispatcher.class);
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
when(mockResp.getWriter()).thenReturn(printWriter);
when(mockReq.getRequestDispatcher(destination)).thenReturn(mockDispatcher);
AppServlet curServlet = new AppServlet();
curServlet.doGet(mockReq, mockResp);
verify(mockReq, times(1)).getRequestDispatcher(destination);
verify(mockDispatcher).forward(mockReq, mockResp);
}
@Test
public void testDoPost() throws Exception {
HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
when(mockResp.getWriter()).thenReturn(printWriter);
AppServlet curServlet = new AppServlet();
curServlet.doPost(mockReq, mockResp);
printWriter.flush();
assertTrue(stringWriter.toString().contains(msgPartOne + " Post " + msgPartTwo));
}
@Test
public void testDoPut() throws Exception {
HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
when(mockResp.getWriter()).thenReturn(printWriter);
AppServlet curServlet = new AppServlet();
curServlet.doPut(mockReq, mockResp);
printWriter.flush();
assertTrue(stringWriter.toString().contains(msgPartOne + " Put " + msgPartTwo));
}
@Test
public void testDoDelete() throws Exception {
HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
HttpServletResponse mockResp = Mockito.mock(HttpServletResponse.class);
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
when(mockResp.getWriter()).thenReturn(printWriter);
AppServlet curServlet = new AppServlet();
curServlet.doDelete(mockReq, mockResp);
printWriter.flush();
assertTrue(stringWriter.toString().contains(msgPartOne + " Delete " + msgPartTwo));
}
}

@ -0,0 +1,71 @@
package com.iluwatar.compositeview;
import jakarta.servlet.http.HttpServletRequest;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.junit.Assert.*;
public class JavaBeansTest {
@Test
public void testDefaultConstructor() {
ClientPropertiesBean newBean = new ClientPropertiesBean();
assertEquals("DEFAULT_NAME", newBean.getName());
assertTrue(newBean.isBusinessInterest());
assertTrue(newBean.isScienceNewsInterest());
assertTrue(newBean.isSportsInterest());
assertTrue(newBean.isWorldNewsInterest());
}
@Test
public void testNameGetterSetter() {
ClientPropertiesBean newBean = new ClientPropertiesBean();
assertEquals("DEFAULT_NAME", newBean.getName());
newBean.setName("TEST_NAME_ONE");
assertEquals("TEST_NAME_ONE", newBean.getName());
}
@Test
public void testBusinessSetterGetter() {
ClientPropertiesBean newBean = new ClientPropertiesBean();
assertTrue(newBean.isBusinessInterest());
newBean.setBusinessInterest(false);
assertFalse(newBean.isBusinessInterest());
}
@Test
public void testScienceSetterGetter() {
ClientPropertiesBean newBean = new ClientPropertiesBean();
assertTrue(newBean.isScienceNewsInterest());
newBean.setScienceNewsInterest(false);
assertFalse(newBean.isScienceNewsInterest());
}
@Test
public void testSportsSetterGetter() {
ClientPropertiesBean newBean = new ClientPropertiesBean();
assertTrue(newBean.isSportsInterest());
newBean.setSportsInterest(false);
assertFalse(newBean.isSportsInterest());
}
@Test
public void testWorldSetterGetter() {
ClientPropertiesBean newBean = new ClientPropertiesBean();
assertTrue(newBean.isWorldNewsInterest());
newBean.setWorldNewsInterest(false);
assertFalse(newBean.isWorldNewsInterest());
}
@Test
public void testRequestConstructor(){
HttpServletRequest mockReq = Mockito.mock(HttpServletRequest.class);
ClientPropertiesBean newBean = new ClientPropertiesBean((mockReq));
assertEquals("DEFAULT_NAME", newBean.getName());
assertFalse(newBean.isWorldNewsInterest());
assertFalse(newBean.isBusinessInterest());
assertFalse(newBean.isScienceNewsInterest());
assertFalse(newBean.isSportsInterest());
}
}

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>appServlet</servlet-name>
<servlet-class>com.iluwatar.compositeview.AppServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>appServlet</servlet-name>
<url-pattern>/news</url-pattern>
</servlet-mapping>
</web-app>

@ -0,0 +1,33 @@
<%--
Created by IntelliJ IDEA.
User: Kevin
Date: 11/29/2021
Time: 2:51 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
h2 { text-align: center;}
table {border: 1px solid black;}
tr {text-align: center;}
td {text-align: center;}
</style>
</head>
<body>
<h2>
Generic Business News
</h2>
<table style="margin-right: auto; margin-left: auto">
<tr>
<td>Stock prices up across the world</td>
<td>New tech companies to invest in</td>
</tr>
<tr>
<td>Industry leaders unveil new project</td>
<td>Price fluctuations and what they mean</td>
</tr>
</table>
</body>
</html>

@ -0,0 +1,23 @@
<%--
Created by IntelliJ IDEA.
User: Kevin
Date: 11/29/2021
Time: 1:28 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="java.util.Date"%>
<html>
<head>
<style>
h1 { text-align: center;}
h2 { text-align: center;}
h3 { text-align: center;}
</style>
</head>
<body>
<% String todayDateStr = (new Date().toString()); %>
<h1>Today's Personalized Frontpage</h1>
<h2><%=todayDateStr%></h2>
</body>
</html>

@ -0,0 +1,20 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
h1 { text-align: center;}
h2 { text-align: center;}
h3 { text-align: center;}
</style>
</head>
<body>
<h1>Welcome To The Composite Patterns Mock News Site</h1>
<h2>Send a GET request to the "/news" path to see the composite view with mock news</h2>
<h2>Use the following parameters:</h2>
<h3>name: string name to be dynamically displayed</h3>
<h3>bus: boolean for whether you want to see the mock business news</h3>
<h3>world: boolean for whether you want to see the mock world news</h3>
<h3>sci: boolean for whether you want to see the mock world news</h3>
<h3>sport: boolean for whether you want to see the mock world news</h3>
</body>
</html>

@ -0,0 +1,25 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<div style="text-align: center">
<h3>
Generic Local News
</h3>
<ul style="list-style-type: none">
<li>
Mayoral elections coming up in 2 weeks
</li>
<li>
New parking meter rates downtown coming tomorrow
</li>
<li>
Park renovations to finish by the next year
</li>
<li>
Annual marathon sign ups available online
</li>
</ul>
</div>
</body>
</html>

@ -0,0 +1,57 @@
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page import="com.iluwatar.compositeview.ClientPropertiesBean"%>
<html>
<head>
<style>
h1 { text-align: center;}
h2 { text-align: center;}
h3 { text-align: center;}
.centerTable {
margin-left: auto;
margin-right: auto;
}
table {border: 1px solid black;}
tr {text-align: center;}
td {text-align: center;}
</style>
</head>
<body>
<%ClientPropertiesBean propertiesBean = (ClientPropertiesBean) request.getAttribute("properties");%>
<h1>Welcome <%= propertiesBean.getName()%></h1>
<jsp:include page="header.jsp"></jsp:include>
<table class="centerTable">
<tr>
<td></td>
<% if(propertiesBean.isWorldNewsInterest()) { %>
<td><%@include file="worldNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
</tr>
<tr>
<% if(propertiesBean.isBusinessInterest()) { %>
<td><%@include file="businessNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
<% if(propertiesBean.isSportsInterest()) { %>
<td><%@include file="sportsNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
</tr>
<tr>
<td></td>
<% if(propertiesBean.isScienceNewsInterest()) { %>
<td><%@include file="scienceNews.jsp"%></td>
<% } else { %>
<td><%@include file="localNews.jsp"%></td>
<% } %>
<td></td>
</tr>
</table>
</body>
</html>

@ -0,0 +1,34 @@
<%--
Created by IntelliJ IDEA.
User: Kevin
Date: 11/29/2021
Time: 4:18 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<body>
<div style="text-align: center">
<h3>
Generic Science News
</h3>
<ul>
<li>
New model of gravity proposed for dark matter
</li>
<li>
Genetic modifying technique proved on bacteria
</li>
<li>
Neurology study maps brain with new precision
</li>
<li>
Survey of rainforest discovers 15 new species
</li>
<li>
New signalling pathway for immune system discovered
</li>
</ul>
</div>
</body>
</html>

@ -0,0 +1,32 @@
<%--
Created by IntelliJ IDEA.
User: Kevin
Date: 11/29/2021
Time: 3:53 PM
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<style>
h2 { text-align: center;}
table {border: 1px solid black;}
tr {text-align: center;}
td {text-align: center;}
</style>
</head>
<body>
<h2>
Generic Sports News
</h2>
<div style="margin-left: auto; margin-right: auto; padding: 20px">
International football match delayed due to weather, will be held next week
</div>
<div style="margin-left: auto; margin-right: auto; padding: 20px">
New rising stars in winter sports, ten new athletes that will shake up the scene
</div>
<div style="margin-left: auto; margin-right: auto; padding: 20px">
Biggest upset in basketball history, upstart team sweeps competition
</div>
</body>
</html>

Some files were not shown because too many files have changed in this diff Show More