Compare commits

..

1 Commits

Author SHA1 Message Date
bc801bae20 format readme.md 2021-02-14 23:21:49 +02:00
1284 changed files with 7763 additions and 21682 deletions
.all-contributorsrc
.circleci
.github/workflows
.mvn/wrapper
LICENSE.mdREADME.md
abstract-document
README.mdpom.xml
src
main
java
com
iluwatar
abstractdocument
test
java
com
abstract-factory
active-object
acyclic-visitor
adapter
aggregator-microservices
README.md
aggregator-service
pom.xml
src
main
test
java
com
iluwatar
aggregator
microservices
information-microservice
pom.xml
src
main
java
com
iluwatar
information
test
java
com
iluwatar
information
inventory-microservice
pom.xml
src
main
java
com
iluwatar
inventory
test
java
com
iluwatar
inventory
pom.xml
ambassador
api-gateway
README.md
api-gateway-service
image-microservice
pom.xml
src
main
java
com
iluwatar
image
test
java
com
iluwatar
image
pom.xml
price-microservice
pom.xml
src
main
java
com
iluwatar
price
test
java
com
iluwatar
price
ar
arrange-act-assert
README.mdpom.xml
src
main
java
com
iluwatar
arrangeactassert
test
java
com
iluwatar
assets/flags
async-method-invocation
README.mdpom.xml
src
main
java
com
iluwatar
async
method
invocation
test
java
com
iluwatar
async
balking
bridge
builder
README.mdpom.xml
src
main
test
java
com
iluwatar
business-delegate
bytecode
caching
callback
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
chain
circuit-breaker
cloud-static-content-hosting
collection-pipeline
README.mdpom.xml
src
main
java
com
test
java
com
iluwatar
collectionpipeline
combinator
README.mdpom.xml
src
main
java
com
iluwatar
test
command
commander
composite-entity
composite
README.mdpom.xml
src
main
java
test
java
com
iluwatar
converter
README.mdpom.xml
src
main
test
java
com
iluwatar
cqrs
README.mdpom.xml
src
main
test
java
com
dao
data-bus
data-locality
data-mapper
README.mdpom.xml
src
main
java
com
iluwatar
test
java
data-transfer-object
README.mdpom.xml
src
main
java
com
test
java
com
iluwatar
decorator
delegation
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
delegation
dependency-injection
dirty-flag
domain-model
double-buffer
README.mdpom.xml
src
main
java
com
iluwatar
test
double-checked-locking
README.mdpom.xml
src
main
java
com
iluwatar
doublechecked
test
java
com
iluwatar
doublechecked
double-dispatch
eip-aggregator
eip-message-channel
README.mdpom.xml
src
main
java
com
iluwatar
eip
message
channel
eip-publish-subscribe
README.mdpom.xml
src
main
java
com
iluwatar
eip
publish
subscribe
eip-splitter
README.mdpom.xml
src
test
java
com
iluwatar
eip
eip-wire-tap
README.mdpom.xml
src
test
java
com
iluwatar
eip
event-aggregator
event-asynchronous
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
event-driven-architecture
event-queue
README.mdpom.xml
src
main
java
test
java
com
iluwatar
event
event-sourcing
execute-around
README.mdpom.xml
src
test
java
com
iluwatar
extension-objects
facade
factory-kit
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
factorykit
factory-method
factory
fanout-fanin
feature-toggle
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
filterer
fluentinterface
README.mdpom.xml
src
main
java
com
iluwatar
fluentinterface
test
java
flux
flyweight
fr
front-controller
README.mdpom.xml
src
main
test
java
com
iluwatar
game-loop
gpl-3.0.txt
guarded-suspension
README.mdpom.xml
src
main
java
com
iluwatar
guarded
test
java
com
iluwatar
guarded
half-sync-half-async
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
hexagonal
index.md
intercepting-filter
README.mdpom.xml
src
test
java
com
iluwatar
interpreter
README.mdpom.xml
src
main
java
com
iluwatar
interpreter
iterator
README.mdpom.xml
src
main
java
com
iluwatar
iterator
ko
README.md
adapter
factory
observer
prototype
singleton
layers
lazy-loading
leader-election
leader-followers
lgpl-3.0.txt
localization
es
fr
id
ko
pt
ru
tr
zh
abstract-document
abstract-factory
active-object
acyclic-visitor
adapter
aggregator-microservices
ambassador
api-gateway
arrange-act-assert
async-method-invocation
balking
bridge
builder
business-delegate
bytecode
caching
callback
chain
circuit-breaker
cloud-static-content-hosting
collection-pipeline
command
composite-entity
composite
converter
dao
data-bus
data-mapper
data-transfer-object
decorator
delegation
dependency-injection
dirty-flag
double-checked-locking
facade
factory-kit
factory-method
factory
index.md
interpreter
iterator
observer
private-class-data
producer-consumer
proxy
sharding
state
strategy
template-method
version-number
visitor
lockable-object
lombok.config
marker
master-worker-pattern
README.mdpom.xml
src
main
java
com
mediator
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
memento
README.mdpom.xml
src
main
java
com
iluwatar
memento
test
java
com
iluwatar
model-view-controller
README.mdpom.xml
src
main
java
com
iluwatar
model
view
controller
test
model-view-presenter
model-view-viewmodel
module
monad
README.mdpom.xml
src
main
java
com
iluwatar
test
java
monostate
README.mdpom.xml
src
main
java
com
iluwatar
monostate
test
java
multiton
README.mdpom.xml
src
main
java
com
iluwatar
multiton
test
mute-idiom
README.mdpom.xml
src
main
java
com
iluwatar
mvnwmvnw.cmd
naked-objects
README.md
dom
pom.xml
src
test
java
fixture
integtests
pom.xml
src
test
java
pom.xml
webapp
pom.xml
src
main
java
domainapp
null-object
README.mdpom.xml
src
main
java
com
iluwatar
nullobject
test
java
com
iluwatar
nullobject
object-mother
README.mdpom.xml
src
test
java
com
iluwatar
object-pool
README.mdpom.xml
src
main
java
com
iluwatar
object
test
java
observer
page-object
README.mdpom.xml
sample-application
pom.xml
src
main
java
com
iluwatar
pageobject
src
test-automation
parameter-object
partial-response
README.mdpom.xml
src
main
java
com
iluwatar
partialresponse
test
java
com
iluwatar
partialresponse
pipeline
README.mdpom.xml
src
test
java
com
iluwatar
poison-pill
pom.xml
presentation
priority-queue
private-class-data
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
producer-consumer
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
promise
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
property
README.mdpom.xml
src
main
java
com
iluwatar
property
test
java
com
iluwatar
prototype
proxy
queue-load-leveling
reactor
reader-writer-lock
registry
README.mdpom.xml
src
test
java
com
repository
resource-acquisition-is-initialization
README.mdpom.xml
src
main
java
com
iluwatar
resource
test
java
com
iluwatar
resource
acquisition
is
initialization
retry
role-object
saga
separated-interface
README.mdpom.xml
src
main
java
com
iluwatar
separatedinterface
test
servant
README.mdpom.xml
src
main
java
com
iluwatar
servant
test
serverless
service-layer
README.mdpom.xml
src
main
java
com
iluwatar
servicelayer
test
service-locator
README.mdpom.xml
src
main
test
java
com
iluwatar
sharding
singleton
spatial-partition
README.mdpom.xml
src
main
java
com
iluwatar
spatialpartition
special-case
README.mdpom.xml
src
main
java
com
iluwatar
specification
state
step-builder
README.mdpom.xml
src
main
java
com
iluwatar
stepbuilder
test
java
com
strangler
strategy
subclass-sandbox
README.mdpom.xml
src
main
java
com
iluwatar
subclasssandbox
test
table-module
template-method
thread-pool
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
throttling
README.mdpom.xml
src
main
java
com
iluwatar
test
java
tls
tolerant-reader
tr
trampoline
README.mdpom.xml
src
main
java
test
java
com
iluwatar
transaction-script
README.mdpom.xml
src
main
java
test
java
twin
typeobjectpattern
unit-of-work
update-method
value-object
README.mdpom.xml
src
main
java
com
iluwatar
test
java
com
iluwatar
version-number
visitor
zh
README.md
adapter
bridge
builder
caching
callback
chain
command
composite
converter
dao
data-transfer-object
decorator
delegation
dependency-injection
dirty-flag
facade
factory-method
interpreter
iterator
observer
private-class-data
producer-consumer
proxy
state
strategy
template-method
visitor

@ -1132,8 +1132,7 @@
"profile": "http://subho.xyz",
"contributions": [
"code",
"review",
"maintenance"
"review"
]
},
{
@ -1405,232 +1404,6 @@
"contributions": [
"translation"
]
},
{
"login": "richardmr36",
"name": "Martel Richard",
"avatar_url": "https://avatars.githubusercontent.com/u/19147333?v=4",
"profile": "https://github.com/richardmr36",
"contributions": [
"code"
]
},
{
"login": "va1m",
"name": "va1m",
"avatar_url": "https://avatars.githubusercontent.com/u/17025445?v=4",
"profile": "https://github.com/va1m",
"contributions": [
"code"
]
},
{
"login": "noamgrinch",
"name": "Noam Greenshtain",
"avatar_url": "https://avatars.githubusercontent.com/u/31648669?v=4",
"profile": "https://github.com/noamgrinch",
"contributions": [
"code"
]
},
{
"login": "qfxl",
"name": "yonghong Xu",
"avatar_url": "https://avatars.githubusercontent.com/u/14086462?v=4",
"profile": "https://xuyonghong.cn/",
"contributions": [
"doc"
]
},
{
"login": "jinishavora",
"name": "jinishavora",
"avatar_url": "https://avatars.githubusercontent.com/u/40777762?v=4",
"profile": "https://www.linkedin.com/in/jinisha-vora",
"contributions": [
"review",
"code"
]
},
{
"login": "eas5",
"name": "Elvys Soares",
"avatar_url": "https://avatars.githubusercontent.com/u/50836521?v=4",
"profile": "https://github.com/eas5",
"contributions": [
"code"
]
},
{
"login": "zWeBrain",
"name": "zWeBrain",
"avatar_url": "https://avatars.githubusercontent.com/u/46642512?v=4",
"profile": "https://github.com/zWeBrain",
"contributions": [
"code"
]
},
{
"login": "Al-assad",
"name": "余林颖",
"avatar_url": "https://avatars.githubusercontent.com/u/22493821?v=4",
"profile": "https://al-assad.github.io/notion/",
"contributions": [
"translation"
]
},
{
"login": "STudio26",
"name": "Alain",
"avatar_url": "https://avatars.githubusercontent.com/u/6988911?v=4",
"profile": "https://github.com/STudio26",
"contributions": [
"translation"
]
},
{
"login": "DEV-VRUPER",
"name": "VR",
"avatar_url": "https://avatars.githubusercontent.com/u/30525467?v=4",
"profile": "https://github.com/DEV-VRUPER",
"contributions": [
"doc"
]
},
{
"login": "JackieNim",
"name": "JackieNim",
"avatar_url": "https://avatars.githubusercontent.com/u/4138836?v=4",
"profile": "https://github.com/JackieNim",
"contributions": [
"code"
]
},
{
"login": "EdisonE3",
"name": "EdisonE3",
"avatar_url": "https://avatars.githubusercontent.com/u/52118917?v=4",
"profile": "https://github.com/EdisonE3",
"contributions": [
"code"
]
},
{
"login": "tao-sun2",
"name": "Tao",
"avatar_url": "https://avatars.githubusercontent.com/u/66189688?v=4",
"profile": "https://github.com/tao-sun2",
"contributions": [
"code"
]
},
{
"login": "JuanManuelAbate",
"name": "Juan Manuel Abate",
"avatar_url": "https://avatars.githubusercontent.com/u/16357060?v=4",
"profile": "https://github.com/JuanManuelAbate",
"contributions": [
"translation"
]
},
{
"login": "Xenilo137",
"name": "Xenilo137",
"avatar_url": "https://avatars.githubusercontent.com/u/24865069?v=4",
"profile": "https://github.com/Xenilo137",
"contributions": [
"code"
]
},
{
"login": "samuelpsouza",
"name": "Samuel Souza",
"avatar_url": "https://avatars.githubusercontent.com/u/17254162?v=4",
"profile": "https://www.linkedin.com/in/souzasamuel/",
"contributions": [
"code"
]
},
{
"login": "marlo2222",
"name": "Marlo Henrique",
"avatar_url": "https://avatars.githubusercontent.com/u/40809563?v=4",
"profile": "https://github.com/marlo2222",
"contributions": [
"translation"
]
},
{
"login": "AndriyPyzh",
"name": "AndriyPyzh",
"avatar_url": "https://avatars.githubusercontent.com/u/57706635?v=4",
"profile": "https://github.com/AndriyPyzh",
"contributions": [
"code"
]
},
{
"login": "karthikbhat13",
"name": "karthikbhat13",
"avatar_url": "https://avatars.githubusercontent.com/u/22431014?v=4",
"profile": "https://github.com/karthikbhat13",
"contributions": [
"code"
]
},
{
"login": "mortezaadi",
"name": "Morteza Adigozalpour",
"avatar_url": "https://avatars.githubusercontent.com/u/1329687?v=4",
"profile": "https://github.com/mortezaadi",
"contributions": [
"code"
]
},
{
"login": "tan31989",
"name": "Nagaraj Tantri",
"avatar_url": "https://avatars.githubusercontent.com/u/3784194?v=4",
"profile": "https://stackoverflow.com/users/308565/nagaraj-tantri",
"contributions": [
"code"
]
},
{
"login": "frascu",
"name": "Francesco Scuccimarri",
"avatar_url": "https://avatars.githubusercontent.com/u/7107651?v=4",
"profile": "http://scuccimarri.it",
"contributions": [
"code"
]
},
{
"login": "Conhan93",
"name": "Conny Hansson",
"avatar_url": "https://avatars.githubusercontent.com/u/71334757?v=4",
"profile": "https://github.com/Conhan93",
"contributions": [
"doc"
]
},
{
"login": "muklasr",
"name": "Muklas Rahmanto",
"avatar_url": "https://avatars.githubusercontent.com/u/43443753?v=4",
"profile": "http://muklasr.medium.com",
"contributions": [
"translation"
]
},
{
"login": "VxDxK",
"name": "Vadim",
"avatar_url": "https://avatars.githubusercontent.com/u/38704817?v=4",
"profile": "https://github.com/VxDxK",
"contributions": [
"translation"
]
}
],
"contributorsPerLine": 4,

@ -1,52 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
version: 2
jobs:
sonar-pr:
docker:
- image: circleci/openjdk:11-node
steps:
- checkout
- restore_cache:
key: jdp-sonar-pr-{{ checksum "pom.xml" }}
- run: |
if [ -n "${CIRCLE_PR_NUMBER}" ]; then
MAVEN_OPTS="-Xmx3000m" xvfb-run ./mvnw -B clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \
-Dsonar.pullrequest.key=${CIRCLE_PR_NUMBER} \
-Dsonar.pullrequest.branch=${CIRCLE_BRANCH} \
-Dsonar.pullrequest.base=master
else
echo "No Sonar PR analysis as this is not a pull request"
fi
- save_cache:
key: jdp-sonar-pr-{{ checksum "pom.xml" }}
paths:
- ~/.m2
workflows:
version: 2
all:
jobs:
- sonar-pr

@ -46,10 +46,9 @@ jobs:
fetch-depth: 0
- name: Set up JDK 11
uses: actions/setup-java@v2
uses: actions/setup-java@v1
with:
java-version: 11
distribution: 'adopt'
- name: Cache SonarCloud packages
uses: actions/cache@v2
@ -70,8 +69,11 @@ jobs:
- name: Install xvfb
run: sudo apt-get install -y xvfb
# The SonarQube analysis is only for the master branch of the main repository.
# SonarQube scan does not work for forked repositories try changing it to xvfb-run mvn clean verify
# See https://jira.sonarsource.com/browse/MMF-1371
- name: Build with Maven and run SonarQube analysis
run: xvfb-run ./mvnw clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
run: xvfb-run mvn clean verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar
env:
# These two env variables are needed for sonar analysis
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

@ -29,7 +29,7 @@ name: Java PR Builder
on:
pull_request:
branches: [ master ]
types: [ opened, reopened, synchronize ]
types: [ opened, reopened, synchronize, labeled, unlabeled ]
jobs:
build:
@ -41,10 +41,9 @@ jobs:
uses: actions/checkout@v2
- name: Set up JDK 11
uses: actions/setup-java@v2
uses: actions/setup-java@v1
with:
java-version: 11
distribution: 'adopt'
- name: Cache Maven Dependecies
uses: actions/cache@v2
@ -58,5 +57,8 @@ jobs:
- name: Install xvfb
run: sudo apt-get install -y xvfb
# This worflow is only for building Pull Requests, the master branch runs Sonar analysis on the main repository.
# SonarQube scan does not work for forked repositories.
# See https://jira.sonarsource.com/browse/MMF-1371
- name: Build with Maven
run: xvfb-run ./mvnw clean verify
run: xvfb-run mvn clean verify

@ -1,125 +0,0 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
import java.net.*;
import java.io.*;
import java.nio.channels.*;
import java.util.Properties;
public class MavenWrapperDownloader {
private static final String WRAPPER_VERSION = "0.5.6";
/**
* Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
*/
private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
/**
* Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
* use instead of the default one.
*/
private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
".mvn/wrapper/maven-wrapper.properties";
/**
* Path where the maven-wrapper.jar will be saved to.
*/
private static final String MAVEN_WRAPPER_JAR_PATH =
".mvn/wrapper/maven-wrapper.jar";
/**
* Name of the property which should be used to override the default download url for the wrapper.
*/
private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
public static void main(String args[]) {
System.out.println("- Downloader started");
File baseDirectory = new File(args[0]);
System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
// If the maven-wrapper.properties exists, read it and check if it contains a custom
// wrapperUrl parameter.
File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
String url = DEFAULT_DOWNLOAD_URL;
if(mavenWrapperPropertyFile.exists()) {
FileInputStream mavenWrapperPropertyFileInputStream = null;
try {
mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
Properties mavenWrapperProperties = new Properties();
mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
} catch (IOException e) {
System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
} finally {
try {
if(mavenWrapperPropertyFileInputStream != null) {
mavenWrapperPropertyFileInputStream.close();
}
} catch (IOException e) {
// Ignore ...
}
}
}
System.out.println("- Downloading from: " + url);
File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
if(!outputFile.getParentFile().exists()) {
if(!outputFile.getParentFile().mkdirs()) {
System.out.println(
"- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
}
}
System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
try {
downloadFileFromURL(url, outputFile);
System.out.println("Done");
System.exit(0);
} catch (Throwable e) {
System.out.println("- Error downloading");
e.printStackTrace();
System.exit(1);
}
}
private static void downloadFileFromURL(String urlString, File destination) throws Exception {
if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
String username = System.getenv("MVNW_USERNAME");
char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
Authenticator.setDefault(new Authenticator() {
@Override
protected PasswordAuthentication getPasswordAuthentication() {
return new PasswordAuthentication(username, password);
}
});
}
URL website = new URL(urlString);
ReadableByteChannel rbc;
rbc = Channels.newChannel(website.openStream());
FileOutputStream fos = new FileOutputStream(destination);
fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
fos.close();
rbc.close();
}
}

@ -1,25 +0,0 @@
#
# The MIT License
# Copyright © 2014-2021 Ilkka Seppälä
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
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

@ -1,6 +1,6 @@
The MIT License (MIT)
Copyright <EFBFBD> 2014-2021 Ilkka Sepp<EFBFBD>l<EFBFBD>
Copyright (c) 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
@ -19,6 +19,3 @@ 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.
Module Model-view-viewmodel is using ZK framework
ZK framework is licensed under LGPL and the license can be found at lgpl-3.0.txt

@ -4,18 +4,18 @@
# Design patterns implemented in Java
![Java CI](https://github.com/iluwatar/java-design-patterns/workflows/Java%20CI/badge.svg)
![Java CI with Maven](https://github.com/iluwatar/java-design-patterns/workflows/Java%20CI%20with%20Maven/badge.svg)
[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=ncloc)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=iluwatar_java-design-patterns&metric=ncloc)](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
[![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-154-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 : [![CN](/assets/flags/CN.png)**CN**](/zh/README.md),[![KR](/assets/flags/KR.png)**KR**](/ko/README.md),[![FR](/assets/flags/FR.png)**FR**](/fr/README.md),[![TR](/assets/flags/TR.png)**TR**](/tr/README.md),[![AR](/assets/flags/AR.png)**AR**](/ar/README.md),
<br/>
@ -34,7 +34,7 @@ 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
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
programming tutorials on how to implement a specific pattern. We use the most
@ -49,7 +49,7 @@ patterns should only be introduced when they are needed for practical
extensibility.
Once you are familiar with these concepts you can start drilling down into the
[available design patterns](https://java-design-patterns.com/patterns/) by any
[available design patterns](https://java-design-patterns.com/patterns/) by any
of the following approaches
- Search for a specific pattern by name. Can't find one? Please report a new pattern [here](https://github.com/iluwatar/java-design-patterns/issues).
@ -61,8 +61,8 @@ in your architectures and have as much fun learning them as we had developing th
# How to contribute
If you are willing to contribute to the project you will find the relevant information in
our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). We will help
If you are willing to contribute to the project you will find the relevant information in
our [developer wiki](https://github.com/iluwatar/java-design-patterns/wiki). We will help
you and answer your questions in the [Gitter chatroom](https://gitter.im/iluwatar/java-design-patterns).
# License
@ -259,7 +259,7 @@ This project is licensed under the terms of the MIT license.
<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>
<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></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>
@ -306,43 +306,6 @@ This project is licensed under the terms of the MIT license.
<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/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>
</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>
<td align="center"><a href="https://github.com/JuanManuelAbate"><img src="https://avatars.githubusercontent.com/u/16357060?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juan Manuel Abate</b></sub></a><br /><a href="#translation-JuanManuelAbate" title="Translation">🌍</a></td>
</tr>
<tr>
<td align="center"><a href="https://github.com/Xenilo137"><img src="https://avatars.githubusercontent.com/u/24865069?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Xenilo137</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Xenilo137" title="Code">💻</a></td>
<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="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>
</tr>
</table>

@ -1,7 +1,9 @@
---
layout: pattern
title: Abstract Document
category: Structural
language: en
folder: abstract-document
permalink: /patterns/abstract-document/
categories: Structural
tags:
- Extensibility
---

@ -30,7 +30,7 @@
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-document</artifactId>
<dependencies>

@ -27,7 +27,8 @@ import com.iluwatar.abstractdocument.domain.Car;
import com.iluwatar.abstractdocument.domain.enums.Property;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Abstract Document pattern enables handling additional, non-static properties. This pattern
@ -37,9 +38,10 @@ import lombok.extern.slf4j.Slf4j;
* <p>In Abstract Document pattern,({@link AbstractDocument}) fully implements {@link Document})
* interface. Traits are then defined to enable access to properties in usual, static way.
*/
@Slf4j
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point.
*

@ -23,18 +23,19 @@
package com.iluwatar.abstractdocument;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.*;
import org.junit.jupiter.api.Test;
/**
* AbstractDocument test class
*/
class AbstractDocumentTest {
public class AbstractDocumentTest {
private static final String KEY = "key";
private static final String VALUE = "value";
@ -49,13 +50,13 @@ class AbstractDocumentTest {
private final DocumentImplementation document = new DocumentImplementation(new HashMap<>());
@Test
void shouldPutAndGetValue() {
public void shouldPutAndGetValue() {
document.put(KEY, VALUE);
assertEquals(VALUE, document.get(KEY));
}
@Test
void shouldRetrieveChildren() {
public void shouldRetrieveChildren() {
var children = List.of(Map.of(), Map.of());
document.put(KEY, children);
@ -66,14 +67,14 @@ class AbstractDocumentTest {
}
@Test
void shouldRetrieveEmptyStreamForNonExistingChildren() {
public void shouldRetrieveEmptyStreamForNonExistingChildren() {
var children = document.children(KEY, DocumentImplementation::new);
assertNotNull(children);
assertEquals(0, children.count());
}
@Test
void shouldIncludePropsInToString() {
public void shouldIncludePropsInToString() {
var props = Map.of(KEY, (Object) VALUE);
var document = new DocumentImplementation(props);
assertTrue(document.toString().contains(KEY));

@ -23,20 +23,19 @@
package com.iluwatar.abstractdocument;
import static org.junit.jupiter.api.Assertions.assertEquals;
import com.iluwatar.abstractdocument.domain.Car;
import com.iluwatar.abstractdocument.domain.Part;
import com.iluwatar.abstractdocument.domain.enums.Property;
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Map;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* Test for Part and Car
*/
class DomainTest {
public class DomainTest {
private static final String TEST_PART_TYPE = "test-part-type";
private static final String TEST_PART_MODEL = "test-part-model";
@ -46,7 +45,7 @@ class DomainTest {
private static final long TEST_CAR_PRICE = 1L;
@Test
void shouldConstructPart() {
public void shouldConstructPart() {
var partProperties = Map.of(
Property.TYPE.toString(), TEST_PART_TYPE,
Property.MODEL.toString(), TEST_PART_MODEL,
@ -59,7 +58,7 @@ class DomainTest {
}
@Test
void shouldConstructCar() {
public void shouldConstructCar() {
var carProperties = Map.of(
Property.MODEL.toString(), TEST_CAR_MODEL,
Property.PRICE.toString(), TEST_CAR_PRICE,

@ -1,7 +1,9 @@
---
layout: pattern
title: Abstract Factory
category: Creational
language: en
folder: abstract-factory
permalink: /patterns/abstract-factory/
categories: Creational
tags:
- Gang of Four
---
@ -17,9 +19,9 @@ objects without specifying their concrete classes.
## Explanation
Real-world example
Real world example
> To create a kingdom we need objects with a common theme. The elven kingdom needs an elven king, elven castle, and elven army whereas the orcish kingdom needs an orcish king, orcish castle, and orcish army. There is a dependency between the objects in the kingdom.
> To create a kingdom we need objects with a common theme. Elven kingdom needs an Elven king, Elven castle and Elven army whereas Orcish kingdom needs an Orcish king, Orcish castle and Orcish army. There is a dependency between the objects in the kingdom.
In plain words
@ -31,7 +33,7 @@ Wikipedia says
**Programmatic Example**
Translating the kingdom example above. First of all, we have some interfaces and implementation for the objects in the
Translating the kingdom example above. First of all we have some interfaces and implementation for the objects in the
kingdom.
```java
@ -49,21 +51,21 @@ public interface Army {
// Elven implementations ->
public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the elven castle!";
static final String DESCRIPTION = "This is the Elven castle!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfKing implements King {
static final String DESCRIPTION = "This is the elven king!";
static final String DESCRIPTION = "This is the Elven king!";
@Override
public String getDescription() {
return DESCRIPTION;
}
}
public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the elven Army!";
static final String DESCRIPTION = "This is the Elven Army!";
@Override
public String getDescription() {
return DESCRIPTION;
@ -74,7 +76,7 @@ public class ElfArmy implements Army {
```
Then we have the abstraction and implementations for the kingdom factory.
Then we have the abstraction and implementations for the kingdom factory
```java
public interface KingdomFactory {
@ -108,7 +110,7 @@ public class OrcKingdomFactory implements KingdomFactory {
}
```
Now we have the abstract factory that lets us make a family of related objects i.e. elven kingdom factory creates elven castle, king and army, etc.
Now we have our abstract factory that lets us make family of related objects i.e. Elven kingdom factory creates Elven castle, king and army etc.
```java
var factory = new ElfKingdomFactory();
@ -124,13 +126,13 @@ army.getDescription();
Program output:
```java
This is the elven castle!
This is the elven king!
This is the elven Army!
This is the Elven castle!
This is the Elven king!
This is the Elven Army!
```
Now, we can design a factory for our different kingdom factories. In this example, we created `FactoryMaker`, responsible for returning an instance of either `ElfKingdomFactory` or `OrcKingdomFactory`.
The client can use `FactoryMaker` to create the desired concrete factory which, in turn, will produce different concrete objects (derived from `Army`, `King`, `Castle`).
Now, we can design a factory for our different kingdom factories. In this example, we created FactoryMaker, responsible for returning an instance of either ElfKingdomFactory or OrcKingdomFactory.
The client can use FactoryMaker to create the desired concrete factory which, in turn, will produce different concrete objects (Army, King, Castle).
In this example, we also used an enum to parameterize which type of kingdom factory the client will ask for.
```java
@ -176,8 +178,8 @@ public static void main(String[] args) {
Use the Abstract Factory pattern when
* The system should be independent of how its products are created, composed, and represented
* The system should be configured with one of the multiple families of products
* The system should be independent of how its products are created, composed and represented
* The system should be configured with one of multiple families of products
* The family of related product objects is designed to be used together, and you need to enforce this constraint
* You want to provide a class library of products, and you want to reveal just their interfaces, not their implementations
* The lifetime of the dependency is conceptually shorter than the lifetime of the consumer.
@ -193,13 +195,13 @@ Example use cases
* Unit test case writing becomes much easier
* UI tools for different OS
## Consequences
## Consequences:
* Dependency injection in java hides the service class dependencies that can lead to runtime errors that would have been caught at compile time.
* While the pattern is great when creating predefined objects, adding the new ones might be challenging.
* The code becomes more complicated than it should be since a lot of new interfaces and classes are introduced along with the pattern.
* The code becomes more complicated than it should be, since a lot of new interfaces and classes are introduced along with the pattern.
## Tutorials
## Tutorial
* [Abstract Factory Pattern Tutorial](https://www.journaldev.com/1418/abstract-factory-design-pattern-in-java)

@ -31,7 +31,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>abstract-factory</artifactId>
<dependencies>

@ -23,7 +23,8 @@
package com.iluwatar.abstractfactory;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Abstract Factory pattern provides a way to encapsulate a group of individual factories that
@ -37,11 +38,12 @@ import lombok.extern.slf4j.Slf4j;
*
* <p>The essence of the Abstract Factory pattern is a factory interface ({@link KingdomFactory})
* and its implementations ( {@link ElfKingdomFactory}, {@link OrcKingdomFactory}). The example uses
* both concrete implementations to create a king, a castle, and an army.
* both concrete implementations to create a king, a castle and an army.
*/
@Slf4j
public class App implements Runnable {
private static Logger log = LoggerFactory.getLogger(App.class);
private final Kingdom kingdom = new Kingdom();
public Kingdom getKingdom() {
@ -60,17 +62,17 @@ public class App implements Runnable {
@Override
public void run() {
LOGGER.info("elf kingdom");
log.info("Elf Kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
LOGGER.info(kingdom.getArmy().getDescription());
LOGGER.info(kingdom.getCastle().getDescription());
LOGGER.info(kingdom.getKing().getDescription());
log.info(kingdom.getArmy().getDescription());
log.info(kingdom.getCastle().getDescription());
log.info(kingdom.getKing().getDescription());
LOGGER.info("orc kingdom");
log.info("Orc Kingdom");
createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
LOGGER.info(kingdom.getArmy().getDescription());
LOGGER.info(kingdom.getCastle().getDescription());
LOGGER.info(kingdom.getKing().getDescription());
log.info(kingdom.getArmy().getDescription());
log.info(kingdom.getCastle().getDescription());
log.info(kingdom.getKing().getDescription());
}
/**

@ -28,7 +28,7 @@ package com.iluwatar.abstractfactory;
*/
public class ElfArmy implements Army {
static final String DESCRIPTION = "This is the elven army!";
static final String DESCRIPTION = "This is the Elven Army!";
@Override
public String getDescription() {

@ -28,7 +28,7 @@ package com.iluwatar.abstractfactory;
*/
public class ElfCastle implements Castle {
static final String DESCRIPTION = "This is the elven castle!";
static final String DESCRIPTION = "This is the Elven castle!";
@Override
public String getDescription() {

@ -28,7 +28,7 @@ package com.iluwatar.abstractfactory;
*/
public class ElfKing implements King {
static final String DESCRIPTION = "This is the elven king!";
static final String DESCRIPTION = "This is the Elven king!";
@Override
public String getDescription() {

@ -23,17 +23,36 @@
package com.iluwatar.abstractfactory;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class Kingdom {
private King king;
private Castle castle;
private Army army;
public King getKing() {
return king;
}
public Castle getCastle() {
return castle;
}
public Army getArmy() {
return army;
}
public void setKing(King king) {
this.king = king;
}
public void setCastle(Castle castle) {
this.castle = castle;
}
public void setArmy(Army army) {
this.army = army;
}
/**
* The factory of kingdom factories.
*/

@ -28,7 +28,7 @@ package com.iluwatar.abstractfactory;
*/
public class OrcArmy implements Army {
static final String DESCRIPTION = "This is the orc army!";
static final String DESCRIPTION = "This is the Orc Army!";
@Override
public String getDescription() {

@ -28,7 +28,7 @@ package com.iluwatar.abstractfactory;
*/
public class OrcCastle implements Castle {
static final String DESCRIPTION = "This is the orc castle!";
static final String DESCRIPTION = "This is the Orc castle!";
@Override
public String getDescription() {

@ -28,7 +28,7 @@ package com.iluwatar.abstractfactory;
*/
public class OrcKing implements King {
static final String DESCRIPTION = "This is the orc king!";
static final String DESCRIPTION = "This is the Orc king!";
@Override
public String getDescription() {

@ -29,14 +29,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Tests for abstract factory.
* Test for abstract factory.
*/
class AbstractFactoryTest {
public class AbstractFactoryTest {
private final App app = new App();
@Test
void verifyKingCreation() {
public void king() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@ -51,7 +51,7 @@ class AbstractFactoryTest {
}
@Test
void verifyCastleCreation() {
public void castle() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@ -66,7 +66,7 @@ class AbstractFactoryTest {
}
@Test
void verifyArmyCreation() {
public void army() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@ -81,7 +81,7 @@ class AbstractFactoryTest {
}
@Test
void verifyElfKingdomCreation() {
public void createElfKingdom() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ELF);
final var kingdom = app.getKingdom();
@ -97,7 +97,7 @@ class AbstractFactoryTest {
}
@Test
void verifyOrcKingdomCreation() {
public void createOrcKingdom() {
app.createKingdom(Kingdom.FactoryMaker.KingdomType.ORC);
final var kingdom = app.getKingdom();

@ -28,13 +28,20 @@ import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
/**
* Check whether the execution of the main method in {@link App} throws an exception.
* Tests that Abstract Factory example runs without errors.
*/
class AppTest {
/**
* Issue: Add at least one assertion to this test case.
*
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
* throws an exception.
*/
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}

@ -1,123 +0,0 @@
---
title: Active Object
category: Concurrency
language: en
tags:
- Performance
---
## Intent
The active object design pattern decouples method execution from method invocation for objects that each reside in their thread of control. The goal is to introduce concurrency, by using asynchronous method invocation, and a scheduler for handling requests.
## Explanation
The class that implements the active object pattern will contain a self-synchronization mechanism without using 'synchronized' methods.
Real-world example
>The Orcs are known for their wildness and untameable soul. It seems like they have their own thread of control based on previous behavior.
To implement a creature that has its own thread of control mechanism and expose its API only and not the execution itself, we can use the Active Object pattern.
**Programmatic Example**
```java
public abstract class ActiveCreature{
private final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
private BlockingQueue<Runnable> requests;
private String name;
private Thread thread;
public ActiveCreature(String name) {
this.name = name;
this.requests = new LinkedBlockingQueue<Runnable>();
thread = new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
requests.take().run();
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
}
}
}
);
thread.start();
}
public void eat() throws InterruptedException {
requests.put(new Runnable() {
@Override
public void run() {
logger.info("{} is eating!",name());
logger.info("{} has finished eating!",name());
}
}
);
}
public void roam() throws InterruptedException {
requests.put(new Runnable() {
@Override
public void run() {
logger.info("{} has started to roam the wastelands.",name());
}
}
);
}
public String name() {
return this.name;
}
}
```
We can see that any class that will extend the ActiveCreature class will have its own thread of control to invoke and execute methods.
For example, the Orc class:
```java
public class Orc extends ActiveCreature {
public Orc(String name) {
super(name);
}
}
```
Now, we can create multiple creatures such as Orcs, tell them to eat and roam, and they will execute it on their own thread of control:
```java
public static void main(String[] args) {
var app = new App();
app.run();
}
@Override
public void run() {
ActiveCreature creature;
try {
for (int i = 0;i < creatures;i++) {
creature = new Orc(Orc.class.getSimpleName().toString() + i);
creature.eat();
creature.roam();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error(e.getMessage());
}
Runtime.getRuntime().exit(1);
}
```
## Class diagram
![alt text](./etc/active-object.urm.png "Active Object class diagram")

Binary file not shown.

Before

(image error) Size: 19 KiB

@ -1,25 +0,0 @@
@startuml
package com.iluwatar.activeobject {
abstract class ActiveCreature {
- logger : Logger
- name : String
- requests : BlockingQueue<Runnable>
- thread : Thread
+ ActiveCreature(name : String)
+ eat()
+ name() : String
+ roam()
}
class App {
- creatures : Integer
- logger : Logger
+ App()
+ main(args : String[]) {static}
+ run()
}
class Orc {
+ Orc(name : String)
}
}
Orc --|> ActiveCreature
@enduml

@ -1,65 +0,0 @@
<?xml version="1.0"?>
<!--
The MIT License
Copyright © 2014-2021 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<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
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>
</project>

@ -1,118 +0,0 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.activeobject;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ActiveCreature class is the base of the active object example.
* @author Noam Greenshtain
*
*/
public abstract class ActiveCreature {
private static final Logger logger = LoggerFactory.getLogger(ActiveCreature.class.getName());
private BlockingQueue<Runnable> requests;
private String name;
private Thread thread; // Thread of execution.
private int status; // status of the thread of execution.
/**
* Constructor and initialization.
*/
protected ActiveCreature(String name) {
this.name = name;
this.status = 0;
this.requests = new LinkedBlockingQueue<>();
thread = new Thread(() -> {
boolean infinite = true;
while (infinite) {
try {
requests.take().run();
} catch (InterruptedException e) {
if (this.status != 0) {
logger.error("Thread was interrupted. --> {}", e.getMessage());
}
infinite = false;
Thread.currentThread().interrupt();
}
}
});
thread.start();
}
/**
* Eats the porridge.
* @throws InterruptedException due to firing a new Runnable.
*/
public void eat() throws InterruptedException {
requests.put(() -> {
logger.info("{} is eating!",name());
logger.info("{} has finished eating!",name());
});
}
/**
* Roam the wastelands.
* @throws InterruptedException due to firing a new Runnable.
*/
public void roam() throws InterruptedException {
requests.put(() ->
logger.info("{} has started to roam in the wastelands.",name())
);
}
/**
* Returns the name of the creature.
* @return the name of the creature.
*/
public String name() {
return this.name;
}
/**
* Kills the thread of execution.
* @param status of the thread of execution. 0 == OK, the rest is logging an error.
*/
public void kill(int status) {
this.status = status;
this.thread.interrupt();
}
/**
* Returns the status of the thread of execution.
* @return the status of the thread of execution.
*/
public int getStatus() {
return this.status;
}
}

@ -1,75 +0,0 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.activeobject;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* The Active Object pattern helps to solve synchronization difficulties without using
* 'synchronized' methods. The active object will contain a thread-safe data structure
* (such as BlockingQueue) and use to synchronize method calls by moving the logic of the method
* into an invocator(usually a Runnable) and store it in the DSA.
*
* <p>In this example, we fire 20 threads to modify a value in the target class.
*/
public class App implements Runnable {
private static final Logger logger = LoggerFactory.getLogger(App.class.getName());
private static final int NUM_CREATURES = 3;
/**
* Program entry point.
*
* @param args command line arguments.
*/
public static void main(String[] args) {
var app = new App();
app.run();
}
@Override
public void run() {
List<ActiveCreature> creatures = new ArrayList<>();
try {
for (int i = 0;i < NUM_CREATURES;i++) {
creatures.add(new Orc(Orc.class.getSimpleName() + i));
creatures.get(i).eat();
creatures.get(i).roam();
}
Thread.sleep(1000);
} catch (InterruptedException e) {
logger.error(e.getMessage());
Thread.currentThread().interrupt();
} finally {
for (int i = 0;i < NUM_CREATURES;i++) {
creatures.get(i).kill(0);
}
}
}
}

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

@ -1,42 +0,0 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.activeobject;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
class ActiveCreatureTest {
@Test
void executionTest() throws InterruptedException {
ActiveCreature orc = new Orc("orc1");
assertEquals("orc1",orc.name());
assertEquals(0,orc.getStatus());
orc.eat();
orc.roam();
orc.kill(0);
}
}

@ -1,37 +0,0 @@
/*
* The MIT License
* Copyright © 2014-2021 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package com.iluwatar.activeobject;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import org.junit.jupiter.api.Test;
class AppTest {
@Test
void shouldExecuteApplicationWithoutException() {
assertDoesNotThrow(() -> App.main(new String[]{}));
}
}

@ -1,7 +1,9 @@
---
layout: pattern
title: Acyclic Visitor
category: Behavioral
language: en
folder: acyclic-visitor
permalink: /patterns/acyclic-visitor/
categories: Behavioral
tags:
- Extensibility
---

@ -30,7 +30,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>acyclic-visitor</artifactId>

@ -23,15 +23,17 @@
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ConfigureForDosVisitor class implements both zoom's and hayes' visit method for Dos
* manufacturer.
*/
@Slf4j
public class ConfigureForDosVisitor implements AllModemVisitor {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class);
@Override
public void visit(Hayes hayes) {
LOGGER.info(hayes + " used with Dos configurator.");

@ -23,15 +23,17 @@
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ConfigureForUnixVisitor class implements zoom's visit method for Unix manufacturer, unlike
* traditional visitor pattern, this class may selectively implement visit for other modems.
*/
@Slf4j
public class ConfigureForUnixVisitor implements ZoomVisitor {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForUnixVisitor.class);
@Override
public void visit(Zoom zoom) {
LOGGER.info(zoom + " used with Unix configurator.");

@ -23,14 +23,16 @@
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Hayes class implements its accept method.
*/
@Slf4j
public class Hayes extends Modem {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class);
/**
* Accepts all visitors but honors only HayesVisitor.
*/

@ -23,14 +23,16 @@
package com.iluwatar.acyclicvisitor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Zoom class implements its accept method.
*/
@Slf4j
public class Zoom extends Modem {
private static final Logger LOGGER = LoggerFactory.getLogger(ConfigureForDosVisitor.class);
/**
* Accepts all visitors but honors only ZoomVisitor.
*/

@ -35,34 +35,34 @@ import uk.org.lidalia.slf4jtest.TestLoggerFactory;
/**
* ConfigureForDosVisitor test class
*/
class ConfigureForDosVisitorTest {
public class ConfigureForDosVisitorTest {
private final TestLogger logger = TestLoggerFactory.getTestLogger(ConfigureForDosVisitor.class);
@Test
void testVisitForZoom() {
public void testVisitForZoom() {
var conDos = new ConfigureForDosVisitor();
var zoom = new Zoom();
conDos.visit(zoom);
assertThat(logger.getLoggingEvents())
.extracting("level", "message")
.contains(tuple(INFO, zoom + " used with Dos configurator."));
}
@Test
void testVisitForHayes() {
public void testVisitForHayes() {
var conDos = new ConfigureForDosVisitor();
var hayes = new Hayes();
conDos.visit(hayes);
assertThat(logger.getLoggingEvents())
.extracting("level", "message")
.contains(tuple(INFO, hayes + " used with Dos configurator."));
}
@AfterEach
public void clearLoggers() {
TestLoggerFactory.clear();

@ -23,34 +23,35 @@
package com.iluwatar.acyclicvisitor;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import uk.org.lidalia.slf4jtest.TestLogger;
import uk.org.lidalia.slf4jtest.TestLoggerFactory;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.groups.Tuple.tuple;
import static uk.org.lidalia.slf4jext.Level.INFO;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import uk.org.lidalia.slf4jtest.TestLogger;
import uk.org.lidalia.slf4jtest.TestLoggerFactory;
/**
* ConfigureForUnixVisitor test class
*/
class ConfigureForUnixVisitorTest {
public class ConfigureForUnixVisitorTest {
private static final TestLogger LOGGER = TestLoggerFactory.getTestLogger(ConfigureForUnixVisitor.class);
@AfterEach
public void clearLoggers() {
TestLoggerFactory.clear();
}
@Test
void testVisitForZoom() {
public void testVisitForZoom() {
var conUnix = new ConfigureForUnixVisitor();
var zoom = new Zoom();
conUnix.visit(zoom);
assertThat(LOGGER.getLoggingEvents())
.extracting("level", "message")
.contains(tuple(INFO, zoom + " used with Unix configurator."));

@ -23,32 +23,34 @@
package com.iluwatar.acyclicvisitor;
import org.junit.jupiter.api.Test;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyZeroInteractions;
import org.junit.jupiter.api.Test;
/**
* Hayes test class
*/
class HayesTest {
public class HayesTest {
@Test
void testAcceptForDos() {
public void testAcceptForDos() {
var hayes = new Hayes();
var mockVisitor = mock(ConfigureForDosVisitor.class);
hayes.accept(mockVisitor);
verify((HayesVisitor) mockVisitor).visit(eq(hayes));
verify((HayesVisitor)mockVisitor).visit(eq(hayes));
}
@Test
void testAcceptForUnix() {
public void testAcceptForUnix() {
var hayes = new Hayes();
var mockVisitor = mock(ConfigureForUnixVisitor.class);
hayes.accept(mockVisitor);
verifyZeroInteractions(mockVisitor);
}
}

@ -24,32 +24,32 @@
package com.iluwatar.acyclicvisitor;
import org.junit.jupiter.api.Test;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.mock;
import org.junit.jupiter.api.Test;
/**
* Zoom test class
*/
class ZoomTest {
public class ZoomTest {
@Test
void testAcceptForDos() {
public void testAcceptForDos() {
var zoom = new Zoom();
var mockVisitor = mock(ConfigureForDosVisitor.class);
zoom.accept(mockVisitor);
verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
verify((ZoomVisitor)mockVisitor).visit(eq(zoom));
}
@Test
void testAcceptForUnix() {
public void testAcceptForUnix() {
var zoom = new Zoom();
var mockVisitor = mock(ConfigureForUnixVisitor.class);
zoom.accept(mockVisitor);
verify((ZoomVisitor) mockVisitor).visit(eq(zoom));
verify((ZoomVisitor)mockVisitor).visit(eq(zoom));
}
}

@ -1,7 +1,9 @@
---
layout: pattern
title: Adapter
category: Structural
language: en
folder: adapter
permalink: /patterns/adapter/
categories: Structural
tags:
- Gang of Four
---
@ -40,8 +42,8 @@ public interface RowingBoat {
void row();
}
@Slf4j
public class FishingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoat.class);
public void sail() {
LOGGER.info("The fishing boat is sailing");
}
@ -68,9 +70,10 @@ 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.
```java
@Slf4j
public class FishingBoatAdapter implements RowingBoat {
private static final Logger LOGGER = LoggerFactory.getLogger(FishingBoatAdapter.class);
private final FishingBoat boat;
public FishingBoatAdapter() {
@ -102,7 +105,7 @@ Use the Adapter pattern when
* 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.
## Consequences
## 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.
@ -115,7 +118,7 @@ An object adapter
* makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making 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-)

@ -31,7 +31,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>adapter</artifactId>
<dependencies>

@ -23,20 +23,24 @@
package com.iluwatar.adapter;
import lombok.AllArgsConstructor;
import lombok.NoArgsConstructor;
import lombok.Setter;
/**
* The Captain uses {@link RowingBoat} to sail. <br> This is the client in the pattern.
*/
@Setter
@NoArgsConstructor
@AllArgsConstructor
public final class Captain {
private RowingBoat rowingBoat;
public Captain() {
}
public Captain(final RowingBoat boat) {
this.rowingBoat = boat;
}
void setRowingBoat(final RowingBoat boat) {
this.rowingBoat = boat;
}
void row() {
rowingBoat.row();
}

@ -23,15 +23,18 @@
package com.iluwatar.adapter;
import lombok.extern.slf4j.Slf4j;
import static org.slf4j.LoggerFactory.getLogger;
import org.slf4j.Logger;
/**
* Device class (adaptee in the pattern). We want to reuse this class. Fishing boat moves by
* sailing.
*/
@Slf4j
final class FishingBoat {
private static final Logger LOGGER = getLogger(FishingBoat.class);
void sail() {
LOGGER.info("The fishing boat is sailing");
}

@ -29,7 +29,11 @@ package com.iluwatar.adapter;
*/
public class FishingBoatAdapter implements RowingBoat {
private final FishingBoat boat = new FishingBoat();
private final FishingBoat boat;
public FishingBoatAdapter() {
boat = new FishingBoat();
}
public final void row() {
boat.sail();

@ -23,19 +23,18 @@
package com.iluwatar.adapter;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.util.HashMap;
import java.util.Map;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
/**
* Test class
*/
class AdapterPatternTest {
public class AdapterPatternTest {
private Map<String, Object> beans;
@ -65,7 +64,7 @@ class AdapterPatternTest {
* by the client ({@link Captain} ).
*/
@Test
void testAdapter() {
public void testAdapter() {
var captain = (Captain) beans.get(ROWING_BEAN);
// when captain moves

@ -1,7 +1,9 @@
---
layout: pattern
title: Aggregator Microservices
category: Architectural
language: en
folder: aggregator-microservices
permalink: /patterns/aggregator-microservices/
categories: Architectural
tags:
- Cloud distributed
- Decoupling

@ -29,7 +29,7 @@
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>aggregator-service</artifactId>

@ -26,7 +26,8 @@ package com.iluwatar.aggregator.microservices;
import static java.util.Objects.requireNonNullElse;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
@ -36,18 +37,20 @@ import org.springframework.web.bind.annotation.RestController;
@RestController
public class Aggregator {
@Resource
private ProductInformationClient informationClient;
@Resource
private ProductInventoryClient inventoryClient;
/**
* Retrieves product data.
*
* @return a Product.
*/
@GetMapping("/product")
@RequestMapping(path = "/product", method = RequestMethod.GET)
public Product getProduct() {
var product = new Product();

@ -23,14 +23,9 @@
package com.iluwatar.aggregator.microservices;
import lombok.Getter;
import lombok.Setter;
/**
* Encapsulates all the data for a Product that clients will request.
*/
@Getter
@Setter
public class Product {
/**
@ -44,4 +39,20 @@ public class Product {
*/
private int productInventories;
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getProductInventories() {
return productInventories;
}
public void setProductInventories(int productInventories) {
this.productInventories = productInventories;
}
}

@ -28,16 +28,18 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* An adapter to communicate with information micro-service.
*/
@Slf4j
@Component
public class ProductInformationClientImpl implements ProductInformationClient {
private static final Logger LOGGER = LoggerFactory.getLogger(ProductInformationClientImpl.class);
@Override
public String getProductTitle() {
var request = HttpRequest.newBuilder()
@ -52,7 +54,6 @@ public class ProductInformationClientImpl implements ProductInformationClient {
LOGGER.error("IOException Occurred", ioe);
} catch (InterruptedException ie) {
LOGGER.error("InterruptedException Occurred", ie);
Thread.currentThread().interrupt();
}
return null;
}

@ -28,16 +28,18 @@ import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
/**
* An adapter to communicate with inventory micro-service.
*/
@Slf4j
@Component
public class ProductInventoryClientImpl implements ProductInventoryClient {
private static final Logger LOGGER = LoggerFactory.getLogger(ProductInventoryClientImpl.class);
@Override
public Integer getProductInventories() {
var response = "";
@ -54,7 +56,6 @@ public class ProductInventoryClientImpl implements ProductInventoryClient {
LOGGER.error("IOException Occurred", ioe);
} catch (InterruptedException ie) {
LOGGER.error("InterruptedException Occurred", ie);
Thread.currentThread().interrupt();
}
if ("".equalsIgnoreCase(response)) {
return null;

@ -23,19 +23,19 @@
package com.iluwatar.aggregator.microservices;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
/**
* Test Aggregation of domain objects
*/
class AggregatorTest {
public class AggregatorTest {
@InjectMocks
private Aggregator aggregator;
@ -48,14 +48,14 @@ class AggregatorTest {
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
MockitoAnnotations.initMocks(this);
}
/**
* Tests getting the data for a desktop client
*/
@Test
void testGetProduct() {
public void testGetProduct() {
var title = "The Product Title.";
var inventories = 5;

@ -29,7 +29,7 @@
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -23,7 +23,8 @@
package com.iluwatar.information.microservice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
@ -37,7 +38,7 @@ public class InformationController {
*
* @return product inventory.
*/
@GetMapping("/information")
@RequestMapping(value = "/information", method = RequestMethod.GET)
public String getProductTitle() {
return "The Product Title.";
}

@ -23,17 +23,17 @@
package com.iluwatar.information.microservice;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* Test for Information Rest Controller
*/
class InformationControllerTest {
public class InformationControllerTest {
@Test
void shouldGetProductTitle() {
public void shouldGetProductTitle() {
var infoController = new InformationController();
var title = infoController.getProductTitle();
assertEquals("The Product Title.", title);

@ -29,7 +29,7 @@
<parent>
<artifactId>aggregator-microservices</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>inventory-microservice</artifactId>

@ -23,7 +23,8 @@
package com.iluwatar.inventory.microservice;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
@ -37,7 +38,7 @@ public class InventoryController {
*
* @return product inventory.
*/
@GetMapping("/inventories")
@RequestMapping(value = "/inventories", method = RequestMethod.GET)
public int getProductInventories() {
return 5;
}

@ -23,17 +23,16 @@
package com.iluwatar.inventory.microservice;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* Test Inventory Rest Controller
*/
class InventoryControllerTest {
public class InventoryControllerTest {
@Test
void testGetProductInventories() {
public void testGetProductInventories() {
var inventoryController = new InventoryController();
var numberOfInventories = inventoryController.getProductInventories();
assertEquals(5, numberOfInventories);

@ -29,7 +29,7 @@
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>aggregator-microservices</artifactId>

@ -1,7 +1,9 @@
---
layout: pattern
title: Ambassador
category: Structural
language: en
folder: ambassador
permalink: /patterns/ambassador/
categories: Structural
tags:
- Decoupling
- Cloud distributed
@ -46,8 +48,9 @@ interface RemoteServiceInterface {
A remote services represented as a singleton.
```java
@Slf4j
public class RemoteService implements RemoteServiceInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class);
private static RemoteService service = null;
static synchronized RemoteService getRemoteService() {
@ -77,8 +80,9 @@ public class RemoteService implements RemoteServiceInterface {
A service ambassador adding additional features such as logging, latency checks
```java
@Slf4j
public class ServiceAmbassador implements RemoteServiceInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
private static final int RETRIES = 3;
private static final int DELAY_MS = 3000;
@ -128,8 +132,9 @@ public class ServiceAmbassador implements RemoteServiceInterface {
A client has a local service ambassador used to interact with the remote service:
```java
@Slf4j
public class Client {
private static final Logger LOGGER = LoggerFactory.getLogger(Client.class);
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
long useService(int value) {

@ -29,7 +29,7 @@
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>ambassador</artifactId>

@ -23,19 +23,20 @@
package com.iluwatar.ambassador;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple Client.
*/
@Slf4j
public class Client {
private static final Logger LOGGER = LoggerFactory.getLogger(Client.class);
private final ServiceAmbassador serviceAmbassador = new ServiceAmbassador();
long useService(int value) {
var result = serviceAmbassador.doRemoteFunction(value);
LOGGER.info("Service result: {}", result);
LOGGER.info("Service result: " + result);
return result;
}
}

@ -26,14 +26,15 @@ package com.iluwatar.ambassador;
import static java.lang.Thread.sleep;
import com.iluwatar.ambassador.util.RandomProvider;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A remote legacy application represented by a Singleton implementation.
*/
@Slf4j
public class RemoteService implements RemoteServiceInterface {
private static final int THRESHOLD = 200;
private static final Logger LOGGER = LoggerFactory.getLogger(RemoteService.class);
private static RemoteService service = null;
private final RandomProvider randomProvider;
@ -72,9 +73,8 @@ public class RemoteService implements RemoteServiceInterface {
sleep(waitTime);
} catch (InterruptedException e) {
LOGGER.error("Thread sleep state interrupted", e);
Thread.currentThread().interrupt();
}
return waitTime <= THRESHOLD ? value * 10
: RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
: RemoteServiceStatus.FAILURE.getRemoteServiceStatusValue();
}
}

@ -33,7 +33,8 @@ package com.iluwatar.ambassador;
*/
public enum RemoteServiceStatus {
FAILURE(-1);
FAILURE(-1)
;
private final long remoteServiceStatusValue;

@ -26,16 +26,17 @@ package com.iluwatar.ambassador;
import static com.iluwatar.ambassador.RemoteServiceStatus.FAILURE;
import static java.lang.Thread.sleep;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* ServiceAmbassador provides an interface for a ({@link Client}) to access ({@link RemoteService}).
* The interface adds logging, latency testing and usage of the service in a safe way that will not
* add stress to the remote service when connectivity issues occur.
*/
@Slf4j
public class ServiceAmbassador implements RemoteServiceInterface {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceAmbassador.class);
private static final int RETRIES = 3;
private static final int DELAY_MS = 3000;
@ -52,7 +53,7 @@ public class ServiceAmbassador implements RemoteServiceInterface {
var result = RemoteService.getRemoteService().doRemoteFunction(value);
var timeTaken = System.currentTimeMillis() - startTime;
LOGGER.info("Time taken (ms): {}", timeTaken);
LOGGER.info("Time taken (ms): " + timeTaken);
return result;
}
@ -66,13 +67,12 @@ public class ServiceAmbassador implements RemoteServiceInterface {
}
if ((result = checkLatency(value)) == FAILURE.getRemoteServiceStatusValue()) {
LOGGER.info("Failed to reach remote: ({})", i + 1);
LOGGER.info("Failed to reach remote: (" + (i + 1) + ")");
retries++;
try {
sleep(DELAY_MS);
} catch (InterruptedException e) {
LOGGER.error("Thread sleep state interrupted", e);
Thread.currentThread().interrupt();
}
} else {
break;

@ -1,7 +1,9 @@
---
layout: pattern
title: API Gateway
category: Architectural
language: en
folder: api-gateway
permalink: /patterns/api-gateway/
categories: Architectural
tags:
- Cloud distributed
- Decoupling

@ -29,7 +29,7 @@
<parent>
<artifactId>api-gateway</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway-service</artifactId>

@ -24,7 +24,8 @@
package com.iluwatar.api.gateway;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
@ -44,7 +45,7 @@ public class ApiGateway {
*
* @return Product information for clients on a desktop
*/
@GetMapping("/desktop")
@RequestMapping(path = "/desktop", method = RequestMethod.GET)
public DesktopProduct getProductDesktop() {
var desktopProduct = new DesktopProduct();
desktopProduct.setImagePath(imageClient.getImagePath());
@ -57,7 +58,7 @@ public class ApiGateway {
*
* @return Product information for clients on a mobile device
*/
@GetMapping("/mobile")
@RequestMapping(path = "/mobile", method = RequestMethod.GET)
public MobileProduct getProductMobile() {
var mobileProduct = new MobileProduct();
mobileProduct.setPrice(priceClient.getPrice());

@ -23,16 +23,10 @@
package com.iluwatar.api.gateway;
import lombok.Getter;
import lombok.Setter;
/**
* Encapsulates all of the information that a desktop client needs to display a product.
*/
@Getter
@Setter
public class DesktopProduct {
/**
* The price of the product.
*/
@ -43,4 +37,19 @@ public class DesktopProduct {
*/
private String imagePath;
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getImagePath() {
return imagePath;
}
public void setImagePath(String imagePath) {
this.imagePath = imagePath;
}
}

@ -23,21 +23,24 @@
package com.iluwatar.api.gateway;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
/**
* An adapter to communicate with the Image microservice.
*/
@Slf4j
@Component
public class ImageClientImpl implements ImageClient {
private static final Logger LOGGER = getLogger(ImageClientImpl.class);
/**
* Makes a simple HTTP Get request to the Image microservice.
@ -57,11 +60,8 @@ public class ImageClientImpl implements ImageClient {
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
logResponse(httpResponse);
return httpResponse.body();
} catch (IOException ioe) {
LOGGER.error("Failure occurred while getting image path", ioe);
} catch (InterruptedException ie) {
LOGGER.error("Failure occurred while getting image path", ie);
Thread.currentThread().interrupt();
} catch (IOException | InterruptedException e) {
LOGGER.error("Failure occurred while getting image path", e);
}
return null;

@ -23,17 +23,20 @@
package com.iluwatar.api.gateway;
import lombok.Getter;
import lombok.Setter;
/**
* Encapsulates all of the information that mobile client needs to display a product.
*/
@Getter
@Setter
public class MobileProduct {
/**
* The price of the product.
*/
private String price;
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
}

@ -23,22 +23,25 @@
package com.iluwatar.api.gateway;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.IOException;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandlers;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.springframework.stereotype.Component;
/**
* An adapter to communicate with the Price microservice.
*/
@Slf4j
@Component
public class PriceClientImpl implements PriceClient {
private static final Logger LOGGER = getLogger(PriceClientImpl.class);
/**
* Makes a simple HTTP Get request to the Price microservice.
@ -58,11 +61,8 @@ public class PriceClientImpl implements PriceClient {
var httpResponse = httpClient.send(httpGet, BodyHandlers.ofString());
logResponse(httpResponse);
return httpResponse.body();
} catch (IOException e) {
} catch (IOException | InterruptedException e) {
LOGGER.error("Failure occurred while getting price info", e);
} catch (InterruptedException e) {
LOGGER.error("Failure occurred while getting price info", e);
Thread.currentThread().interrupt();
}
return null;

@ -35,7 +35,7 @@ import org.mockito.MockitoAnnotations;
/**
* Test API Gateway Pattern
*/
class ApiGatewayTest {
public class ApiGatewayTest {
@InjectMocks
private ApiGateway apiGateway;
@ -48,14 +48,14 @@ class ApiGatewayTest {
@BeforeEach
public void setup() {
MockitoAnnotations.openMocks(this);
MockitoAnnotations.initMocks(this);
}
/**
* Tests getting the data for a desktop client
*/
@Test
void testGetProductDesktop() {
public void testGetProductDesktop() {
var imagePath = "/product-image.png";
var price = "20";
when(imageClient.getImagePath()).thenReturn(imagePath);
@ -71,7 +71,7 @@ class ApiGatewayTest {
* Tests getting the data for a mobile client
*/
@Test
void testGetProductMobile() {
public void testGetProductMobile() {
var price = "20";
when(priceClient.getPrice()).thenReturn(price);

@ -29,7 +29,7 @@
<parent>
<artifactId>api-gateway</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>image-microservice</artifactId>

@ -23,24 +23,27 @@
package com.iluwatar.image.microservice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import static org.slf4j.LoggerFactory.getLogger;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Exposes the Image microservice's endpoints.
*/
@Slf4j
@RestController
public class ImageController {
private static final Logger LOGGER = getLogger(ImageController.class);
/**
* An endpoint for a user to retrieve an image path.
*
* @return An image path
*/
@GetMapping("/image-path")
@RequestMapping(value = "/image-path", method = RequestMethod.GET)
public String getImagePath() {
LOGGER.info("Successfully found image path");
return "/product-image.png";

@ -23,17 +23,16 @@
package com.iluwatar.image.microservice;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* Test for Image Rest Controller
*/
class ImageControllerTest {
public class ImageControllerTest {
@Test
void testGetImagePath() {
public void testGetImagePath() {
var imageController = new ImageController();
var imagePath = imageController.getImagePath();
assertEquals("/product-image.png", imagePath);

@ -29,7 +29,7 @@
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>api-gateway</artifactId>

@ -29,7 +29,7 @@
<parent>
<artifactId>api-gateway</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

@ -23,24 +23,27 @@
package com.iluwatar.price.microservice;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import static org.slf4j.LoggerFactory.getLogger;
import org.slf4j.Logger;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
/**
* Exposes the Price microservice's endpoints.
*/
@Slf4j
@RestController
public class PriceController {
private static final Logger LOGGER = getLogger(PriceController.class);
/**
* An endpoint for a user to retrieve a product's price.
*
* @return A product's price
*/
@GetMapping("/price")
@RequestMapping(value = "/price", method = RequestMethod.GET)
public String getPrice() {
LOGGER.info("Successfully found price info");
return "20";

@ -23,17 +23,16 @@
package com.iluwatar.price.microservice;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import org.junit.jupiter.api.Test;
/**
* Test for Price Rest Controller
*/
class PriceControllerTest {
public class PriceControllerTest {
@Test
void testgetPrice() {
public void testgetPrice() {
var priceController = new PriceController();
var price = priceController.getPrice();
assertEquals("20", price);

@ -1,7 +1,9 @@
---
layout: pattern
title: Arrange/Act/Assert
category: Idiom
language: en
folder: arrange-act-assert
permalink: /patterns/arrange-act-assert/
categories: Idiom
tags:
- Testing
---
@ -79,10 +81,10 @@ Then we write our unit tests according to Arrange/Act/Assert pattern. Notice the
separated steps for each unit test.
```java
class CashAAATest {
public class CashAAATest {
@Test
void testPlus() {
public void testPlus() {
//Arrange
var cash = new Cash(3);
//Act
@ -92,7 +94,7 @@ class CashAAATest {
}
@Test
void testMinus() {
public void testMinus() {
//Arrange
var cash = new Cash(8);
//Act
@ -103,7 +105,7 @@ class CashAAATest {
}
@Test
void testInsufficientMinus() {
public void testInsufficientMinus() {
//Arrange
var cash = new Cash(1);
//Act
@ -114,7 +116,7 @@ class CashAAATest {
}
@Test
void testUpdate() {
public void testUpdate() {
//Arrange
var cash = new Cash(5);
//Act

@ -29,15 +29,15 @@
<parent>
<artifactId>java-design-patterns</artifactId>
<groupId>com.iluwatar</groupId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>arrange-act-assert</artifactId>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

@ -23,17 +23,18 @@
package com.iluwatar.arrangeactassert;
import lombok.AllArgsConstructor;
/**
* Arrange/Act/Assert (AAA) is a unit test pattern. In this simple example, we have a ({@link Cash})
* object for plus, minus and counting amount.
*/
@AllArgsConstructor
public class Cash {
private int amount;
Cash(int amount) {
this.amount = amount;
}
//plus
void plus(int addend) {
amount += addend;

@ -23,11 +23,11 @@
package com.iluwatar.arrangeactassert;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.Test;
/**
* Arrange/Act/Assert (AAA) is a pattern for organizing unit tests. It is a way to structure your
@ -51,11 +51,10 @@ import org.junit.jupiter.api.Test;
* change and one reason to fail. In a large and complicated code base, tests that honor the single
* responsibility principle are much easier to troubleshoot.
*/
class CashAAATest {
public class CashAAATest {
@Test
void testPlus() {
public void testPlus() {
//Arrange
var cash = new Cash(3);
//Act
@ -65,7 +64,7 @@ class CashAAATest {
}
@Test
void testMinus() {
public void testMinus() {
//Arrange
var cash = new Cash(8);
//Act
@ -76,7 +75,7 @@ class CashAAATest {
}
@Test
void testInsufficientMinus() {
public void testInsufficientMinus() {
//Arrange
var cash = new Cash(1);
//Act
@ -87,7 +86,7 @@ class CashAAATest {
}
@Test
void testUpdate() {
public void testUpdate() {
//Arrange
var cash = new Cash(5);
//Act

@ -23,24 +23,23 @@
package com.iluwatar.arrangeactassert;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.jupiter.api.Test;
import org.junit.Test;
/**
* ({@link CashAAATest}) is an anti-example of AAA pattern. This test is functionally correct, but
* with the addition of a new feature, it needs refactoring. There are an awful lot of steps in that
* with the addition of new feature, it needs refactoring. There are an awful lot of steps in that
* test method, but it verifies the class' important behavior in just eleven lines. It violates the
* single responsibility principle. If this test method failed after a small code change, it might
* take some digging to discover why.
*/
class CashAntiAAATest {
public class CashAntiAAATest {
@Test
void testCash() {
public void testCash() {
//initialize
var cash = new Cash(3);
//test plus

BIN
assets/flags/AR.png Normal file

Binary file not shown.

After

(image error) Size: 252 B

BIN
assets/flags/CN.png Normal file

Binary file not shown.

After

(image error) Size: 310 B

BIN
assets/flags/FR.png Normal file

Binary file not shown.

After

(image error) Size: 100 B

BIN
assets/flags/KR.png Normal file

Binary file not shown.

After

(image error) Size: 413 B

BIN
assets/flags/TR.png Normal file

Binary file not shown.

After

(image error) Size: 802 B

@ -1,166 +1,29 @@
---
layout: pattern
title: Async Method Invocation
category: Concurrency
language: en
folder: async-method-invocation
permalink: /patterns/async-method-invocation/
categories: Concurrency
tags:
- Reactive
---
## Intent
Asynchronous method invocation is a pattern where the calling thread
Asynchronous method invocation is pattern where the calling thread
is not blocked while waiting results of tasks. The pattern provides parallel
processing of multiple independent tasks and retrieving the results via
callbacks or waiting until everything is done.
## Explanation
Real world example
> Launching space rockets is an exciting business. The mission command gives an order to launch and
> after some undetermined time, the rocket either launches successfully or fails miserably.
In plain words
> Asynchronous method invocation starts task processing and returns immediately before the task is
> ready. The results of the task processing are returned to the caller later.
Wikipedia says
> In multithreaded computer programming, asynchronous method invocation (AMI), also known as
> asynchronous method calls or the asynchronous pattern is a design pattern in which the call site
> is not blocked while waiting for the called code to finish. Instead, the calling thread is
> notified when the reply arrives. Polling for a reply is an undesired option.
**Programmatic Example**
In this example, we are launching space rockets and deploying lunar rovers.
The application demonstrates the async method invocation pattern. The key parts of the pattern are
`AsyncResult` which is an intermediate container for an asynchronously evaluated value,
`AsyncCallback` which can be provided to be executed on task completion and `AsyncExecutor` that
manages the execution of the async tasks.
```java
public interface AsyncResult<T> {
boolean isCompleted();
T getValue() throws ExecutionException;
void await() throws InterruptedException;
}
```
```java
public interface AsyncCallback<T> {
void onComplete(T value, Optional<Exception> ex);
}
```
```java
public interface AsyncExecutor {
<T> AsyncResult<T> startProcess(Callable<T> task);
<T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback);
<T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException;
}
```
`ThreadAsyncExecutor` is an implementation of `AsyncExecutor`. Some of its key parts are highlighted
next.
```java
public class ThreadAsyncExecutor implements AsyncExecutor {
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task) {
return startProcess(task, null);
}
@Override
public <T> AsyncResult<T> startProcess(Callable<T> task, AsyncCallback<T> callback) {
var result = new CompletableResult<>(callback);
new Thread(
() -> {
try {
result.setValue(task.call());
} catch (Exception ex) {
result.setException(ex);
}
},
"executor-" + idx.incrementAndGet())
.start();
return result;
}
@Override
public <T> T endProcess(AsyncResult<T> asyncResult)
throws ExecutionException, InterruptedException {
if (!asyncResult.isCompleted()) {
asyncResult.await();
}
return asyncResult.getValue();
}
}
```
Then we are ready to launch some rockets to see how everything works together.
```java
public static void main(String[] args) throws Exception {
// construct a new executor that will run async tasks
var executor = new ThreadAsyncExecutor();
// start few async tasks with varying processing times, two last with callback handlers
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Deploying lunar rover"));
final var asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
// emulate processing in the current thread while async tasks are running in their own threads
Thread.sleep(350); // Oh boy, we are working hard here
log("Mission command is sipping coffee");
// wait for completion of the tasks
final var result1 = executor.endProcess(asyncResult1);
final var result2 = executor.endProcess(asyncResult2);
final var result3 = executor.endProcess(asyncResult3);
asyncResult4.await();
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");
}
```
Here's the program console output.
```java
21:47:08.227 [executor-2] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launched successfully
21:47:08.269 [main] INFO com.iluwatar.async.method.invocation.App - Mission command is sipping coffee
21:47:08.318 [executor-4] INFO com.iluwatar.async.method.invocation.App - Space rocket <20> launched successfully
21:47:08.335 [executor-4] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <20>
21:47:08.414 [executor-1] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launched successfully
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Space rocket <callback> launched successfully
21:47:08.519 [executor-5] INFO com.iluwatar.async.method.invocation.App - Deploying lunar rover <callback>
21:47:08.616 [executor-3] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launched successfully
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <10> launch complete
21:47:08.617 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <test> launch complete
21:47:08.618 [main] INFO com.iluwatar.async.method.invocation.App - Space rocket <50> launch complete
```
# Class diagram
![alt text](./etc/async-method-invocation.png "Async Method Invocation")
## Applicability
Use the async method invocation pattern when
Use async method invocation pattern when
* You have multiple independent tasks that can run in parallel
* You need to improve the performance of a group of sequential tasks
* You have a limited amount of processing capacity or long-running tasks and the caller should not wait for the tasks to be ready
* You have limited amount of processing capacity or long running tasks and the
caller should not wait the tasks to be ready
## Real world examples

@ -31,7 +31,7 @@
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.25.0-SNAPSHOT</version>
<version>1.24.0-SNAPSHOT</version>
</parent>
<artifactId>async-method-invocation</artifactId>
<dependencies>
@ -45,6 +45,11 @@
<artifactId>mockito-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>

@ -24,15 +24,14 @@
package com.iluwatar.async.method.invocation;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* In this example, we are launching space rockets and deploying lunar rovers.
*
* <p>The application demonstrates the async method invocation pattern. The key parts of the
* pattern are <code>AsyncResult</code> which is an intermediate container for an asynchronously
* evaluated value, <code>AsyncCallback</code> which can be provided to be executed on task
* completion and <code>AsyncExecutor</code> that manages the execution of the async tasks.
* This application demonstrates the async method invocation pattern. Key parts of the pattern are
* <code>AsyncResult</code> which is an intermediate container for an asynchronously evaluated
* value, <code>AsyncCallback</code> which can be provided to be executed on task completion and
* <code>AsyncExecutor</code> that manages the execution of the async tasks.
*
* <p>The main method shows example flow of async invocations. The main thread starts multiple
* tasks with variable durations and then continues its own work. When the main thread has done it's
@ -56,9 +55,10 @@ import lombok.extern.slf4j.Slf4j;
* @see java.util.concurrent.CompletableFuture
* @see java.util.concurrent.ExecutorService
*/
@Slf4j
public class App {
private static final Logger LOGGER = LoggerFactory.getLogger(App.class);
/**
* Program entry point.
*/
@ -70,14 +70,13 @@ public class App {
final var asyncResult1 = executor.startProcess(lazyval(10, 500));
final var asyncResult2 = executor.startProcess(lazyval("test", 300));
final var asyncResult3 = executor.startProcess(lazyval(50L, 700));
final var asyncResult4 = executor.startProcess(lazyval(20, 400),
callback("Deploying lunar rover"));
final var asyncResult4 = executor.startProcess(lazyval(20, 400), callback("Callback result 4"));
final var asyncResult5 =
executor.startProcess(lazyval("callback", 600), callback("Deploying lunar rover"));
executor.startProcess(lazyval("callback", 600), callback("Callback result 5"));
// emulate processing in the current thread while async tasks are running in their own threads
Thread.sleep(350); // Oh boy, we are working hard here
log("Mission command is sipping coffee");
Thread.sleep(350); // Oh boy I'm working hard here
log("Some hard work done");
// wait for completion of the tasks
final var result1 = executor.endProcess(asyncResult1);
@ -87,9 +86,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("Result 1: " + result1);
log("Result 2: " + result2);
log("Result 3: " + result3);
}
/**
@ -102,7 +101,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("Task completed with: " + value);
return value;
};
}
@ -118,7 +117,7 @@ public class App {
if (ex.isPresent()) {
log(name + " failed: " + ex.map(Exception::getMessage).orElse(""));
} else {
log(name + " <" + value + ">");
log(name + ": " + value);
}
};
}

@ -68,7 +68,7 @@ class ThreadAsyncExecutorTest {
@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);
MockitoAnnotations.initMocks(this);
}
/**

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