Compare commits
85 Commits
all-contri
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
3b87565fb6 | ||
|
da9f26bbc0 | ||
|
dde31bfe34 | ||
|
1596e9048d | ||
|
a4aff05123 | ||
|
c87689b247 | ||
|
5ce0419b44 | ||
|
58ce3a2ab2 | ||
|
7652b11bca | ||
|
07ee94d671 | ||
|
c5492184b7 | ||
|
4f8007d674 | ||
|
2d2dec98e8 | ||
|
3cc9bc2dea | ||
|
11f20593b2 | ||
|
c66ca67201 | ||
|
2679f7aa6f | ||
|
8403fdacdd | ||
|
f670ae547b | ||
|
df73d80365 | ||
|
4588e09939 | ||
|
69883196d2 | ||
|
4dcc20b733 | ||
|
600227d2e4 | ||
|
fee898cd27 | ||
|
b1242629c8 | ||
|
5d78a77b97 | ||
|
9063336687 | ||
|
b22c8bc32f | ||
|
f7fc48d6b7 | ||
|
414e3263b1 | ||
|
2674cb9523 | ||
|
925755fa35 | ||
|
89b2072131 | ||
|
0a7b524bd1 | ||
|
36c6ce1df7 | ||
|
22ddd57146 | ||
|
72bb189dc0 | ||
|
bee1283371 | ||
|
785cbf42b7 | ||
|
c51eb66c89 | ||
|
1eb74203fc | ||
|
c6803f920d | ||
|
b80ac39e1f | ||
|
da6d20e997 | ||
|
5dbb176a33 | ||
|
e3c3b02efd | ||
|
988c478ad4 | ||
|
221daf5d74 | ||
|
90b1b922e1 | ||
|
bd71bc1311 | ||
|
54d19d4c87 | ||
|
464b166fa3 | ||
|
78abdb4e13 | ||
|
d1c26f9d84 | ||
|
ba51a49802 | ||
|
73eca64b9f | ||
|
8aac45ab69 | ||
|
2c93c81cf9 | ||
|
656b441e29 | ||
|
37d9ff3342 | ||
|
d9dae3ae6a | ||
|
1354bf0f55 | ||
|
dc7c99c32f | ||
|
c2786e5dc4 | ||
|
a1f3c6fe20 | ||
|
0ad44ced24 | ||
|
4c5c0fd63e | ||
|
5a644f1092 | ||
|
cab9048e06 | ||
|
d247b6ed69 | ||
|
0a73ead12d | ||
|
ddb9b14eed | ||
|
0255111b4e | ||
|
9513d2be58 | ||
|
119abf3ee4 | ||
|
87cc4df14b | ||
|
57f9c2e968 | ||
|
42eb7950ae | ||
|
be72a96cd6 | ||
|
be25c0b433 | ||
|
be59e50205 | ||
|
ec90320eda | ||
|
3126ad3106 | ||
|
e2ebb59fe7 |
@ -16,6 +16,17 @@
|
|||||||
"content"
|
"content"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"login": "ohbus",
|
||||||
|
"name": "Subhrodip Mohanta",
|
||||||
|
"avatar_url": "https://avatars0.githubusercontent.com/u/13291222?v=4",
|
||||||
|
"profile": "http://subho.xyz",
|
||||||
|
"contributions": [
|
||||||
|
"code",
|
||||||
|
"review",
|
||||||
|
"maintenance"
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"login": "amit1307",
|
"login": "amit1307",
|
||||||
"name": "amit1307",
|
"name": "amit1307",
|
||||||
@ -1125,17 +1136,6 @@
|
|||||||
"ideas"
|
"ideas"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"login": "ohbus",
|
|
||||||
"name": "Subhrodip Mohanta",
|
|
||||||
"avatar_url": "https://avatars0.githubusercontent.com/u/13291222?v=4",
|
|
||||||
"profile": "http://subho.xyz",
|
|
||||||
"contributions": [
|
|
||||||
"code",
|
|
||||||
"review",
|
|
||||||
"maintenance"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"login": "nahteb",
|
"login": "nahteb",
|
||||||
"name": "Bethan Palmer",
|
"name": "Bethan Palmer",
|
||||||
@ -1568,9 +1568,245 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"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"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "sims-keshri",
|
||||||
|
"name": "Simran Keshri",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/62168475?v=4",
|
||||||
|
"profile": "https://github.com/sims-keshri",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "JCarlosR",
|
||||||
|
"name": "JCarlos",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/3101238?v=4",
|
||||||
|
"profile": "https://programacionymas.com",
|
||||||
|
"contributions": [
|
||||||
|
"translation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Dev-AliGhasemi",
|
||||||
|
"name": "Ali Ghasemi",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/60359433?v=4",
|
||||||
|
"profile": "https://www.mrmoshkel.ir",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "carldea",
|
||||||
|
"name": "Carl Dea",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/1594624?v=4",
|
||||||
|
"profile": "http://carlfx.wordpress.com",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Mozartuss",
|
||||||
|
"name": "Mozartus",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/32893711?v=4",
|
||||||
|
"profile": "https://github.com/Mozartuss",
|
||||||
|
"contributions": [
|
||||||
|
"translation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ManviGoel26",
|
||||||
|
"name": "Manvi Goel",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/55682355?v=4",
|
||||||
|
"profile": "https://github.com/ManviGoel26",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "blueberry404",
|
||||||
|
"name": "Anum Amin",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/39243539?v=4",
|
||||||
|
"profile": "https://github.com/blueberry404",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "uh-zz",
|
||||||
|
"name": "Reo Uehara",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/47747828?v=4",
|
||||||
|
"profile": "https://uh-zz.github.io/blog/",
|
||||||
|
"contributions": [
|
||||||
|
"translation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Fiordy",
|
||||||
|
"name": "Fiordy",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/53420573?v=4",
|
||||||
|
"profile": "https://github.com/Fiordy",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "harshalkh",
|
||||||
|
"name": "Harshal",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/37841724?v=4",
|
||||||
|
"profile": "https://github.com/harshalkh",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "vashisthabhinav",
|
||||||
|
"name": "Abhinav Vashisth",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/89785800?v=4",
|
||||||
|
"profile": "https://www.linkedin.com/in/abhinav-vashisth-06613b208/",
|
||||||
|
"contributions": [
|
||||||
|
"doc"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Kevinyl3",
|
||||||
|
"name": "Kevin",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/47126749?v=4",
|
||||||
|
"profile": "http://no website",
|
||||||
|
"contributions": [
|
||||||
|
"review",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Shrirang97",
|
||||||
|
"name": "Shrirang",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/28738668?v=4",
|
||||||
|
"profile": "https://github.com/Shrirang97",
|
||||||
|
"contributions": [
|
||||||
|
"review",
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "interactwithankush",
|
||||||
|
"name": "interactwithankush",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/18613127?v=4",
|
||||||
|
"profile": "https://github.com/interactwithankush",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "yuhangbin",
|
||||||
|
"name": "CharlieYu",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/17566866?v=4",
|
||||||
|
"profile": "https://github.com/yuhangbin",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "Leisterbecker",
|
||||||
|
"name": "Leisterbecker",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/20650323?v=4",
|
||||||
|
"profile": "https://github.com/Leisterbecker",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "castleKing1997",
|
||||||
|
"name": "DragonDreamer",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/35420129?v=4",
|
||||||
|
"profile": "http://rosaecrucis.cn",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "ShivanshCharak",
|
||||||
|
"name": "ShivanshCharak",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/96943825?v=4",
|
||||||
|
"profile": "https://github.com/ShivanshCharak",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"login": "HattoriHenzo",
|
||||||
|
"name": "HattoriHenzo",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/u/5141285?v=4",
|
||||||
|
"profile": "https://github.com/HattoriHenzo",
|
||||||
|
"contributions": [
|
||||||
|
"code"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 4,
|
"contributorsPerLine": 7,
|
||||||
"projectName": "java-design-patterns",
|
"projectName": "java-design-patterns",
|
||||||
"projectOwner": "iluwatar",
|
"projectOwner": "iluwatar",
|
||||||
"repoType": "github",
|
"repoType": "github",
|
||||||
|
49
.github/workflows/maven-ci.yml
vendored
49
.github/workflows/maven-ci.yml
vendored
@ -24,6 +24,9 @@
|
|||||||
# This workflow will build a Java project with Maven
|
# This workflow will build a Java project with Maven
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
# For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
|
||||||
|
|
||||||
|
# We are using two jobs here for testing our code on the latest JDK 11 build as well as a more satble build version of 11.0.3
|
||||||
|
# You can see the full discussion here https://github.com/iluwatar/java-design-patterns/pull/1868#issue-1029459688
|
||||||
|
|
||||||
name: Java CI
|
name: Java CI
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@ -33,10 +36,11 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
build:
|
# This Workflow Job will build this project and run Sonar analysis using JDK 11.0.3
|
||||||
|
build-and-analyze:
|
||||||
|
|
||||||
|
name: Build and Run Sonar analysis on JDK 11.0.3
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
@ -45,12 +49,14 @@ jobs:
|
|||||||
# Disabling shallow clone for improving relevancy of SonarQube reporting
|
# Disabling shallow clone for improving relevancy of SonarQube reporting
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
|
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK 11.0.3
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: 11.0.3
|
||||||
distribution: 'adopt'
|
distribution: 'zulu'
|
||||||
|
cache: 'maven'
|
||||||
|
|
||||||
|
# Cache Sonar packages which as used to run anaylysis and collect metrics
|
||||||
- name: Cache SonarCloud packages
|
- name: Cache SonarCloud packages
|
||||||
uses: actions/cache@v2
|
uses: actions/cache@v2
|
||||||
with:
|
with:
|
||||||
@ -58,14 +64,6 @@ jobs:
|
|||||||
key: ${{ runner.os }}-sonar
|
key: ${{ runner.os }}-sonar
|
||||||
restore-keys: ${{ runner.os }}-sonar
|
restore-keys: ${{ runner.os }}-sonar
|
||||||
|
|
||||||
- name: Cache Maven dependencies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository
|
|
||||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-maven-
|
|
||||||
|
|
||||||
# Some tests need screen access
|
# Some tests need screen access
|
||||||
- name: Install xvfb
|
- name: Install xvfb
|
||||||
run: sudo apt-get install -y xvfb
|
run: sudo apt-get install -y xvfb
|
||||||
@ -76,3 +74,28 @@ jobs:
|
|||||||
# These two env variables are needed for sonar analysis
|
# These two env variables are needed for sonar analysis
|
||||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
|
||||||
|
|
||||||
|
|
||||||
|
# This Workflow Job is going to build the project on the latest stable JDK 11
|
||||||
|
build:
|
||||||
|
|
||||||
|
name: Build and Test on JDK 11
|
||||||
|
runs-on: ubuntu-20.04
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: Checkout Code
|
||||||
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
|
- name: Set up JDK 11 (Latest)
|
||||||
|
uses: actions/setup-java@v2
|
||||||
|
with:
|
||||||
|
java-version: 11
|
||||||
|
distribution: 'zulu'
|
||||||
|
cache: 'maven'
|
||||||
|
|
||||||
|
# Some tests need screen access
|
||||||
|
- name: Install xvfb
|
||||||
|
run: sudo apt-get install -y xvfb
|
||||||
|
|
||||||
|
- name: Build with Maven
|
||||||
|
run: xvfb-run ./mvnw clean verify
|
||||||
|
21
.github/workflows/maven-pr-builder.yml
vendored
21
.github/workflows/maven-pr-builder.yml
vendored
@ -34,25 +34,22 @@ on:
|
|||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
|
name: Build JDP
|
||||||
runs-on: ubuntu-20.04
|
runs-on: ubuntu-20.04
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
java-version: [ 11.0.3, 11 ]
|
||||||
steps:
|
steps:
|
||||||
|
|
||||||
- name: Checkout Code
|
- name: Checkout Code
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v2
|
||||||
|
|
||||||
- name: Set up JDK 11
|
- name: Set up JDK ${{ matrix.java-version }}
|
||||||
uses: actions/setup-java@v2
|
uses: actions/setup-java@v2
|
||||||
with:
|
with:
|
||||||
java-version: 11
|
java-version: ${{ matrix.java-version }}
|
||||||
distribution: 'adopt'
|
distribution: 'zulu'
|
||||||
|
cache: 'maven'
|
||||||
- name: Cache Maven Dependecies
|
|
||||||
uses: actions/cache@v2
|
|
||||||
with:
|
|
||||||
path: ~/.m2/repository
|
|
||||||
key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
|
|
||||||
restore-keys: |
|
|
||||||
${{ runner.os }}-maven-
|
|
||||||
|
|
||||||
# Some tests need screen access
|
# Some tests need screen access
|
||||||
- name: Install xvfb
|
- name: Install xvfb
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -12,6 +12,7 @@ tmp/
|
|||||||
local.properties
|
local.properties
|
||||||
.loadpath
|
.loadpath
|
||||||
.recommenders
|
.recommenders
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
####### Java annotation processor (APT) ########
|
####### Java annotation processor (APT) ########
|
||||||
.factorypath
|
.factorypath
|
||||||
|
43
.mvn/wrapper/maven-wrapper.properties
vendored
43
.mvn/wrapper/maven-wrapper.properties
vendored
@ -1,25 +1,18 @@
|
|||||||
#
|
# Licensed to the Apache Software Foundation (ASF) under one
|
||||||
# The MIT License
|
# or more contributor license agreements. See the NOTICE file
|
||||||
# Copyright © 2014-2021 Ilkka Seppälä
|
# distributed with this work for additional information
|
||||||
#
|
# regarding copyright ownership. The ASF licenses this file
|
||||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
# to you under the Apache License, Version 2.0 (the
|
||||||
# of this software and associated documentation files (the "Software"), to deal
|
# "License"); you may not use this file except in compliance
|
||||||
# in the Software without restriction, including without limitation the rights
|
# with the License. You may obtain a copy of the License at
|
||||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
#
|
||||||
# copies of the Software, and to permit persons to whom the Software is
|
# http://www.apache.org/licenses/LICENSE-2.0
|
||||||
# furnished to do so, subject to the following conditions:
|
#
|
||||||
#
|
# Unless required by applicable law or agreed to in writing,
|
||||||
# The above copyright notice and this permission notice shall be included in
|
# software distributed under the License is distributed on an
|
||||||
# all copies or substantial portions of the Software.
|
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||||
#
|
# KIND, either express or implied. See the License for the
|
||||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
# specific language governing permissions and limitations
|
||||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
# under the License.
|
||||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.4/apache-maven-3.8.4-bin.zip
|
||||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar
|
||||||
# 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
|
|
||||||
|
140
README.md
140
README.md
@ -10,18 +10,18 @@
|
|||||||
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
[](https://sonarcloud.io/dashboard?id=iluwatar_java-design-patterns)
|
||||||
[](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
[](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-BADGE:START - Do not remove or modify this section -->
|
||||||
[](#contributors-)
|
[](#contributors-)
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||||
|
|
||||||
<br/>
|
<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)
|
Read in different language : [**zh**](localization/zh/README.md), [**ko**](localization/ko/README.md), [**fr**](localization/fr/README.md), [**tr**](localization/tr/README.md), [**ar**](localization/ar/README.md), [**es**](localization/es/README.md), [**pt**](localization/pt/README.md), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md), [**de**](localization/de/README.md), [**ja**](localization/ja/README.md)
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
# Introduction
|
# Introduction
|
||||||
|
|
||||||
Design patterns are the best formalized practices a programmer can use to
|
Design patterns are the best, formalized practices a programmer can use to
|
||||||
solve common problems when designing an application or system.
|
solve common problems when designing an application or system.
|
||||||
|
|
||||||
Design patterns can speed up the development process by providing tested, proven
|
Design patterns can speed up the development process by providing tested, proven
|
||||||
@ -34,11 +34,11 @@ are familiar with the patterns.
|
|||||||
# Getting started
|
# Getting started
|
||||||
|
|
||||||
This site showcases Java Design Patterns. The solutions have been developed by
|
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
|
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
|
source code. The source code examples are well commented and can be thought of as
|
||||||
programming tutorials on how to implement a specific pattern. We use the most
|
programming tutorials on how to implement a specific pattern. We use the most
|
||||||
popular battle-proven open source Java technologies.
|
popular battle-proven open-source Java technologies.
|
||||||
|
|
||||||
Before you dive into the material, you should be familiar with various
|
Before you dive into the material, you should be familiar with various
|
||||||
[Software Design Principles](https://java-design-patterns.com/principles/).
|
[Software Design Principles](https://java-design-patterns.com/principles/).
|
||||||
@ -56,8 +56,8 @@ of the following approaches
|
|||||||
- Using tags such as `Performance`, `Gang of Four` or `Data access`.
|
- Using tags such as `Performance`, `Gang of Four` or `Data access`.
|
||||||
- Using pattern categories, `Creational`, `Behavioral`, and others.
|
- Using pattern categories, `Creational`, `Behavioral`, and others.
|
||||||
|
|
||||||
Hopefully you find the object oriented solutions presented on this site useful
|
Hopefully, you find the object-oriented solutions presented on this site useful
|
||||||
in your architectures and have as much fun learning them as we had developing them.
|
in your architectures and have as much fun learning them as we had while developing them.
|
||||||
|
|
||||||
# How to contribute
|
# How to contribute
|
||||||
|
|
||||||
@ -77,29 +77,26 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/iluwatar"><img src="https://avatars1.githubusercontent.com/u/582346?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ilkka Seppälä</b></sub></a><br /><a href="#projectManagement-iluwatar" title="Project Management">📆</a> <a href="#maintenance-iluwatar" title="Maintenance">🚧</a> <a href="#content-iluwatar" title="Content">🖋</a></td>
|
<td align="center"><a href="https://github.com/iluwatar"><img src="https://avatars1.githubusercontent.com/u/582346?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ilkka Seppälä</b></sub></a><br /><a href="#projectManagement-iluwatar" title="Project Management">📆</a> <a href="#maintenance-iluwatar" title="Maintenance">🚧</a> <a href="#content-iluwatar" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="http://subho.xyz"><img src="https://avatars0.githubusercontent.com/u/13291222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Subhrodip Mohanta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ohbus" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aohbus" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-ohbus" title="Maintenance">🚧</a></td>
|
||||||
<td align="center"><a href="https://github.com/amit1307"><img src="https://avatars0.githubusercontent.com/u/23420222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>amit1307</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit1307" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/amit1307"><img src="https://avatars0.githubusercontent.com/u/23420222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>amit1307</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit1307" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/npathai"><img src="https://avatars2.githubusercontent.com/u/1792515?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Narendra Pathai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npathai" title="Code">💻</a> <a href="#ideas-npathai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Anpathai" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://github.com/npathai"><img src="https://avatars2.githubusercontent.com/u/1792515?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Narendra Pathai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npathai" title="Code">💻</a> <a href="#ideas-npathai" title="Ideas, Planning, & Feedback">🤔</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Anpathai" title="Reviewed Pull Requests">👀</a></td>
|
||||||
<td align="center"><a href="https://github.com/fluxw42"><img src="https://avatars1.githubusercontent.com/u/1545460?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeroen Meulemeester</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fluxw42" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/fluxw42"><img src="https://avatars1.githubusercontent.com/u/1545460?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeroen Meulemeester</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fluxw42" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="http://www.joemccarthy.co.uk"><img src="https://avatars0.githubusercontent.com/u/4526195?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joseph McCarthy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mikulucky" title="Code">💻</a></td>
|
<td align="center"><a href="http://www.joemccarthy.co.uk"><img src="https://avatars0.githubusercontent.com/u/4526195?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joseph McCarthy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mikulucky" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/thomasoss"><img src="https://avatars1.githubusercontent.com/u/22516154?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thomas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=thomasoss" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/thomasoss"><img src="https://avatars1.githubusercontent.com/u/22516154?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Thomas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=thomasoss" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/anuragagarwal561994"><img src="https://avatars1.githubusercontent.com/u/6075379?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag Agarwal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anuragagarwal561994" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://markusmo3.github.io"><img src="https://avatars1.githubusercontent.com/u/3317416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Markus Moser</b></sub></a><br /><a href="#design-markusmo3" title="Design">🎨</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=markusmo3" title="Code">💻</a> <a href="#ideas-markusmo3" title="Ideas, Planning, & Feedback">🤔</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/anuragagarwal561994"><img src="https://avatars1.githubusercontent.com/u/6075379?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag Agarwal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anuragagarwal561994" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://markusmo3.github.io"><img src="https://avatars1.githubusercontent.com/u/3317416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Markus Moser</b></sub></a><br /><a href="#design-markusmo3" title="Design">🎨</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=markusmo3" title="Code">💻</a> <a href="#ideas-markusmo3" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
<td align="center"><a href="https://twitter.com/i_sabiq"><img src="https://avatars1.githubusercontent.com/u/19510920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sabiq Ihab</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=isabiq" title="Code">💻</a></td>
|
<td align="center"><a href="https://twitter.com/i_sabiq"><img src="https://avatars1.githubusercontent.com/u/19510920?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sabiq Ihab</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=isabiq" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://inbravo.github.io"><img src="https://avatars3.githubusercontent.com/u/5253764?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Dixit</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=inbravo" title="Code">💻</a></td>
|
<td align="center"><a href="http://inbravo.github.io"><img src="https://avatars3.githubusercontent.com/u/5253764?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Dixit</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=inbravo" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/piyushchaudhari04"><img src="https://avatars3.githubusercontent.com/u/10268029?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piyush Kailash Chaudhari</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=piyushchaudhari04" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/piyushchaudhari04"><img src="https://avatars3.githubusercontent.com/u/10268029?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Piyush Kailash Chaudhari</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=piyushchaudhari04" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/joshzambales"><img src="https://avatars1.githubusercontent.com/u/8704552?v=4?s=100" width="100px;" alt=""/><br /><sub><b>joshzambales</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joshzambales" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/joshzambales"><img src="https://avatars1.githubusercontent.com/u/8704552?v=4?s=100" width="100px;" alt=""/><br /><sub><b>joshzambales</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joshzambales" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Crossy147"><img src="https://avatars2.githubusercontent.com/u/7272996?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kamil Pietruszka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Crossy147" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/Crossy147"><img src="https://avatars2.githubusercontent.com/u/7272996?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kamil Pietruszka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Crossy147" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://cs.joensuu.fi/~zkhayda"><img src="https://avatars2.githubusercontent.com/u/660742?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zafar Khaydarov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Documentation">📖</a></td>
|
<td align="center"><a href="http://cs.joensuu.fi/~zkhayda"><img src="https://avatars2.githubusercontent.com/u/660742?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zafar Khaydarov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=zafarella" title="Documentation">📖</a></td>
|
||||||
<td align="center"><a href="https://kemitix.github.io/"><img src="https://avatars1.githubusercontent.com/u/1147749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul Campbell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kemitix" title="Code">💻</a></td>
|
<td align="center"><a href="https://kemitix.github.io/"><img src="https://avatars1.githubusercontent.com/u/1147749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Paul Campbell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kemitix" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Argyro-Sioziou"><img src="https://avatars0.githubusercontent.com/u/22822639?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Argyro Sioziou</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Argyro-Sioziou" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/Argyro-Sioziou"><img src="https://avatars0.githubusercontent.com/u/22822639?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Argyro Sioziou</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Argyro-Sioziou" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/TylerMcConville"><img src="https://avatars0.githubusercontent.com/u/4946449?v=4?s=100" width="100px;" alt=""/><br /><sub><b>TylerMcConville</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=TylerMcConville" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/TylerMcConville"><img src="https://avatars0.githubusercontent.com/u/4946449?v=4?s=100" width="100px;" alt=""/><br /><sub><b>TylerMcConville</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=TylerMcConville" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/saksham93"><img src="https://avatars1.githubusercontent.com/u/37399540?v=4?s=100" width="100px;" alt=""/><br /><sub><b>saksham93</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=saksham93" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/saksham93"><img src="https://avatars1.githubusercontent.com/u/37399540?v=4?s=100" width="100px;" alt=""/><br /><sub><b>saksham93</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=saksham93" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/nikhilbarar"><img src="https://avatars2.githubusercontent.com/u/37332144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nikhilbarar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nikhilbarar" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/nikhilbarar"><img src="https://avatars2.githubusercontent.com/u/37332144?v=4?s=100" width="100px;" alt=""/><br /><sub><b>nikhilbarar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nikhilbarar" title="Code">💻</a></td>
|
||||||
@ -110,38 +107,32 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://github.com/JuhoKang"><img src="https://avatars1.githubusercontent.com/u/4745294?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juho Kang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=JuhoKang" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/JuhoKang"><img src="https://avatars1.githubusercontent.com/u/4745294?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juho Kang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=JuhoKang" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/dheeraj-mummareddy"><img src="https://avatars2.githubusercontent.com/u/7002230?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dheeraj Mummareddy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dheeraj-mummareddy" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/dheeraj-mummareddy"><img src="https://avatars2.githubusercontent.com/u/7002230?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dheeraj Mummareddy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dheeraj-mummareddy" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.bernardosulzbach.com"><img src="https://avatars0.githubusercontent.com/u/8271090?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bernardo Sulzbach</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=bernardosulzbach" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.bernardosulzbach.com"><img src="https://avatars0.githubusercontent.com/u/8271090?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bernardo Sulzbach</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=bernardosulzbach" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/4lexis"><img src="https://avatars0.githubusercontent.com/u/19871727?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandar Dudukovic</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=4lexis" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/4lexis"><img src="https://avatars0.githubusercontent.com/u/19871727?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aleksandar Dudukovic</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=4lexis" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.yusufaytas.com"><img src="https://avatars2.githubusercontent.com/u/1049483?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yusuf Aytaş</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yusufaytas" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.yusufaytas.com"><img src="https://avatars2.githubusercontent.com/u/1049483?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yusuf Aytaş</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yusufaytas" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://futurehomes.hu"><img src="https://avatars2.githubusercontent.com/u/1001491?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihály Kuprivecz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qpi" title="Code">💻</a></td>
|
<td align="center"><a href="http://futurehomes.hu"><img src="https://avatars2.githubusercontent.com/u/1001491?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mihály Kuprivecz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qpi" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/kapinuss"><img src="https://avatars0.githubusercontent.com/u/17639945?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stanislav Kapinus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kapinuss" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/kapinuss"><img src="https://avatars0.githubusercontent.com/u/17639945?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stanislav Kapinus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kapinuss" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/gvsharma"><img src="https://avatars1.githubusercontent.com/u/6648152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GVSharma</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gvsharma" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/gvsharma"><img src="https://avatars1.githubusercontent.com/u/6648152?v=4?s=100" width="100px;" alt=""/><br /><sub><b>GVSharma</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gvsharma" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/SrdjanPaunovic"><img src="https://avatars1.githubusercontent.com/u/22815104?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Srđan Paunović</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=SrdjanPaunovic" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/SrdjanPaunovic"><img src="https://avatars1.githubusercontent.com/u/22815104?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Srđan Paunović</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=SrdjanPaunovic" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://sideris.xyz/"><img src="https://avatars3.githubusercontent.com/u/5484694?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Petros G. Sideris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sideris" title="Code">💻</a></td>
|
<td align="center"><a href="https://sideris.xyz/"><img src="https://avatars3.githubusercontent.com/u/5484694?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Petros G. Sideris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sideris" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.linkedin.com/in/pramodgupta3/"><img src="https://avatars1.githubusercontent.com/u/2184241?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pramod Gupta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AIAmPramod" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://www.linkedin.com/in/pramodgupta3/"><img src="https://avatars1.githubusercontent.com/u/2184241?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pramod Gupta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AIAmPramod" title="Reviewed Pull Requests">👀</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://amarnath510.github.io/portfolio"><img src="https://avatars0.githubusercontent.com/u/4599623?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amarnath Chandana</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Amarnath510" title="Code">💻</a></td>
|
<td align="center"><a href="https://amarnath510.github.io/portfolio"><img src="https://avatars0.githubusercontent.com/u/4599623?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amarnath Chandana</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Amarnath510" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Anurag870"><img src="https://avatars1.githubusercontent.com/u/6295975?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag870</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Anurag870" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Anurag870" title="Documentation">📖</a></td>
|
<td align="center"><a href="https://github.com/Anurag870"><img src="https://avatars1.githubusercontent.com/u/6295975?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anurag870</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Anurag870" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Anurag870" title="Documentation">📖</a></td>
|
||||||
<td align="center"><a href="http://theerroris.me"><img src="https://avatars0.githubusercontent.com/u/1685953?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wes Gilleland</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Deathnerd" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/Harshrajsinh"><img src="https://avatars2.githubusercontent.com/u/22811531?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harshraj Thakor</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Harshrajsinh" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="http://theerroris.me"><img src="https://avatars0.githubusercontent.com/u/1685953?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wes Gilleland</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Deathnerd" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Harshrajsinh"><img src="https://avatars2.githubusercontent.com/u/22811531?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harshraj Thakor</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Harshrajsinh" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/MaVdbussche"><img src="https://avatars1.githubusercontent.com/u/26136934?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin Vandenbussche</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MaVdbussche" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/MaVdbussche"><img src="https://avatars1.githubusercontent.com/u/26136934?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martin Vandenbussche</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MaVdbussche" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://alexsomai.com"><img src="https://avatars1.githubusercontent.com/u/5720977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandru Somai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=alexsomai" title="Code">💻</a></td>
|
<td align="center"><a href="https://alexsomai.com"><img src="https://avatars1.githubusercontent.com/u/5720977?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexandru Somai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=alexsomai" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/amogozov"><img src="https://avatars3.githubusercontent.com/u/7372215?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artur Mogozov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amogozov" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/amogozov"><img src="https://avatars3.githubusercontent.com/u/7372215?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Artur Mogozov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amogozov" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/anthonycampbell"><img src="https://avatars3.githubusercontent.com/u/10249255?v=4?s=100" width="100px;" alt=""/><br /><sub><b>anthony</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anthonycampbell" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/anthonycampbell"><img src="https://avatars3.githubusercontent.com/u/10249255?v=4?s=100" width="100px;" alt=""/><br /><sub><b>anthony</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=anthonycampbell" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://ccygnus.com/"><img src="https://avatars1.githubusercontent.com/u/9342724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Cygnus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christophercolumbusdog" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="http://ccygnus.com/"><img src="https://avatars1.githubusercontent.com/u/9342724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christian Cygnus</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christophercolumbusdog" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://about.me/dzmitryh"><img src="https://avatars2.githubusercontent.com/u/5390492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dima Gubin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dzmitryh" title="Code">💻</a></td>
|
<td align="center"><a href="https://about.me/dzmitryh"><img src="https://avatars2.githubusercontent.com/u/5390492?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dima Gubin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dzmitryh" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/jjjimenez100"><img src="https://avatars3.githubusercontent.com/u/22243493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joshua Jimenez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jjjimenez100" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/jjjimenez100"><img src="https://avatars3.githubusercontent.com/u/22243493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Joshua Jimenez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jjjimenez100" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://about.me/kaiwinter"><img src="https://avatars0.githubusercontent.com/u/110982?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Winter</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kaiwinter" title="Code">💻</a></td>
|
<td align="center"><a href="http://about.me/kaiwinter"><img src="https://avatars0.githubusercontent.com/u/110982?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kai Winter</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kaiwinter" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/lbroman"><img src="https://avatars1.githubusercontent.com/u/86007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lbroman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=lbroman" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/lbroman"><img src="https://avatars1.githubusercontent.com/u/86007?v=4?s=100" width="100px;" alt=""/><br /><sub><b>lbroman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=lbroman" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://przemeknowak.com"><img src="https://avatars1.githubusercontent.com/u/3254609?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Przemek</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pnowy" title="Code">💻</a></td>
|
<td align="center"><a href="https://przemeknowak.com"><img src="https://avatars1.githubusercontent.com/u/3254609?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Przemek</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pnowy" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/prafful1"><img src="https://avatars0.githubusercontent.com/u/14350274?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Prafful Agarwal</b></sub></a><br /><a href="#content-prafful1" title="Content">🖋</a></td>
|
<td align="center"><a href="https://github.com/prafful1"><img src="https://avatars0.githubusercontent.com/u/14350274?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Prafful Agarwal</b></sub></a><br /><a href="#content-prafful1" title="Content">🖋</a></td>
|
||||||
@ -152,38 +143,32 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://github.com/valdar-hu"><img src="https://avatars3.githubusercontent.com/u/17962817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krisztián Nagy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=valdar-hu" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/valdar-hu"><img src="https://avatars3.githubusercontent.com/u/17962817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Krisztián Nagy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=valdar-hu" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.vanogrid.com"><img src="https://avatars0.githubusercontent.com/u/4307918?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexander Ivanov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vanogrid" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.vanogrid.com"><img src="https://avatars0.githubusercontent.com/u/4307918?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Alexander Ivanov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vanogrid" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/yosfik"><img src="https://avatars3.githubusercontent.com/u/4850270?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yosfik Alqadri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yosfik" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/yosfik"><img src="https://avatars3.githubusercontent.com/u/4850270?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yosfik Alqadri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yosfik" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/7agustibm"><img src="https://avatars0.githubusercontent.com/u/8149332?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Agustí Becerra Milà</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=7agustibm" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/7agustibm"><img src="https://avatars0.githubusercontent.com/u/8149332?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Agustí Becerra Milà</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=7agustibm" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Juaanma"><img src="https://avatars3.githubusercontent.com/u/7390500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juan Manuel Suárez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Juaanma" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/Juaanma"><img src="https://avatars3.githubusercontent.com/u/7390500?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Juan Manuel Suárez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Juaanma" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://www.devsedge.net/"><img src="https://avatars0.githubusercontent.com/u/9956006?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luigi Cortese</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=LuigiCortese" title="Code">💻</a></td>
|
<td align="center"><a href="http://www.devsedge.net/"><img src="https://avatars0.githubusercontent.com/u/9956006?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Luigi Cortese</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=LuigiCortese" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Rzeposlaw"><img src="https://avatars2.githubusercontent.com/u/18425745?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Katarzyna Rzepecka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Rzeposlaw" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/Rzeposlaw"><img src="https://avatars2.githubusercontent.com/u/18425745?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Katarzyna Rzepecka</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Rzeposlaw" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://adamski.pro"><img src="https://avatars1.githubusercontent.com/u/6537430?v=4?s=100" width="100px;" alt=""/><br /><sub><b>adamski.pro</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=akrystian" title="Code">💻</a></td>
|
<td align="center"><a href="http://adamski.pro"><img src="https://avatars1.githubusercontent.com/u/6537430?v=4?s=100" width="100px;" alt=""/><br /><sub><b>adamski.pro</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=akrystian" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/baislsl"><img src="https://avatars0.githubusercontent.com/u/17060584?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shengli Bai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=baislsl" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/baislsl"><img src="https://avatars0.githubusercontent.com/u/17060584?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shengli Bai</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=baislsl" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/besok"><img src="https://avatars2.githubusercontent.com/u/29834592?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=besok" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/besok"><img src="https://avatars2.githubusercontent.com/u/29834592?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=besok" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/dmitraver"><img src="https://avatars3.githubusercontent.com/u/1798156?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dmitry Avershin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dmitraver" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/dmitraver"><img src="https://avatars3.githubusercontent.com/u/1798156?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Dmitry Avershin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dmitraver" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/fanofxiaofeng"><img src="https://avatars0.githubusercontent.com/u/3983683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>靳阳</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fanofxiaofeng" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/fanofxiaofeng"><img src="https://avatars0.githubusercontent.com/u/3983683?v=4?s=100" width="100px;" alt=""/><br /><sub><b>靳阳</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fanofxiaofeng" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/hoangnam2261"><img src="https://avatars2.githubusercontent.com/u/31692990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hoangnam2261</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoangnam2261" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/hoangnam2261"><img src="https://avatars2.githubusercontent.com/u/31692990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>hoangnam2261</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoangnam2261" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/jarpit96"><img src="https://avatars2.githubusercontent.com/u/10098713?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Jain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jarpit96" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://joningi.net"><img src="https://avatars2.githubusercontent.com/u/6115148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jón Ingi Sveinbjörnsson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joningiwork" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/jarpit96"><img src="https://avatars2.githubusercontent.com/u/10098713?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Arpit Jain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jarpit96" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://joningi.net"><img src="https://avatars2.githubusercontent.com/u/6115148?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jón Ingi Sveinbjörnsson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=joningiwork" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/kirill-vlasov"><img src="https://avatars3.githubusercontent.com/u/16112495?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kirill Vlasov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kirill-vlasov" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/kirill-vlasov"><img src="https://avatars3.githubusercontent.com/u/16112495?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kirill Vlasov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kirill-vlasov" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://mitchell-irvin.com"><img src="https://avatars0.githubusercontent.com/u/16233245?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mitchell Irvin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mitchellirvin" title="Code">💻</a></td>
|
<td align="center"><a href="http://mitchell-irvin.com"><img src="https://avatars0.githubusercontent.com/u/16233245?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mitchell Irvin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mitchellirvin" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://ranjeet-floyd.github.io"><img src="https://avatars0.githubusercontent.com/u/1992972?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ranjeet</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ranjeet-floyd" title="Code">💻</a></td>
|
<td align="center"><a href="https://ranjeet-floyd.github.io"><img src="https://avatars0.githubusercontent.com/u/1992972?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ranjeet</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ranjeet-floyd" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://alwayswithme.github.io"><img src="https://avatars3.githubusercontent.com/u/3234786?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PhoenixYip</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Alwayswithme" title="Code">💻</a></td>
|
<td align="center"><a href="https://alwayswithme.github.io"><img src="https://avatars3.githubusercontent.com/u/3234786?v=4?s=100" width="100px;" alt=""/><br /><sub><b>PhoenixYip</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Alwayswithme" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/MSaifAsif"><img src="https://avatars1.githubusercontent.com/u/6280554?v=4?s=100" width="100px;" alt=""/><br /><sub><b>M Saif Asif</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MSaifAsif" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/MSaifAsif"><img src="https://avatars1.githubusercontent.com/u/6280554?v=4?s=100" width="100px;" alt=""/><br /><sub><b>M Saif Asif</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=MSaifAsif" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/kanwarpreet25"><img src="https://avatars0.githubusercontent.com/u/39183641?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kanwarpreet25</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kanwarpreet25" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/kanwarpreet25"><img src="https://avatars0.githubusercontent.com/u/39183641?v=4?s=100" width="100px;" alt=""/><br /><sub><b>kanwarpreet25</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=kanwarpreet25" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://leonmak.me"><img src="https://avatars3.githubusercontent.com/u/13071508?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leon Mak</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=leonmak" title="Code">💻</a></td>
|
<td align="center"><a href="http://leonmak.me"><img src="https://avatars3.githubusercontent.com/u/13071508?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leon Mak</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=leonmak" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://www.wramdemark.se"><img src="https://avatars2.githubusercontent.com/u/7052193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Per Wramdemark</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=perwramdemark" title="Code">💻</a></td>
|
<td align="center"><a href="http://www.wramdemark.se"><img src="https://avatars2.githubusercontent.com/u/7052193?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Per Wramdemark</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=perwramdemark" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/waisuan"><img src="https://avatars2.githubusercontent.com/u/10975700?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Evan Sia Wai Suan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=waisuan" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/waisuan"><img src="https://avatars2.githubusercontent.com/u/10975700?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Evan Sia Wai Suan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=waisuan" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/AnaghaSasikumar"><img src="https://avatars2.githubusercontent.com/u/42939261?v=4?s=100" width="100px;" alt=""/><br /><sub><b>AnaghaSasikumar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=AnaghaSasikumar" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/AnaghaSasikumar"><img src="https://avatars2.githubusercontent.com/u/42939261?v=4?s=100" width="100px;" alt=""/><br /><sub><b>AnaghaSasikumar</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=AnaghaSasikumar" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://christofferh.com"><img src="https://avatars1.githubusercontent.com/u/767643?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christoffer Hamberg</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christofferh" title="Code">💻</a></td>
|
<td align="center"><a href="https://christofferh.com"><img src="https://avatars1.githubusercontent.com/u/767643?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christoffer Hamberg</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=christofferh" title="Code">💻</a></td>
|
||||||
@ -194,38 +179,32 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://github.com/leogtzr"><img src="https://avatars0.githubusercontent.com/u/1211969?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leo Gutiérrez Ramírez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=leogtzr" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/leogtzr"><img src="https://avatars0.githubusercontent.com/u/1211969?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leo Gutiérrez Ramírez</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=leogtzr" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/npczwh"><img src="https://avatars0.githubusercontent.com/u/14066422?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zhang WH</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npczwh" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/npczwh"><img src="https://avatars0.githubusercontent.com/u/14066422?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zhang WH</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=npczwh" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/oconnelc"><img src="https://avatars0.githubusercontent.com/u/1112973?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christopher O'Connell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=oconnelc" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/oconnelc"><img src="https://avatars0.githubusercontent.com/u/1112973?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christopher O'Connell</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=oconnelc" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/giorgosmav21"><img src="https://avatars2.githubusercontent.com/u/22855493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Mavroeidis</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=giorgosmav21" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/giorgosmav21"><img src="https://avatars2.githubusercontent.com/u/22855493?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Mavroeidis</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=giorgosmav21" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/hbothra15"><img src="https://avatars1.githubusercontent.com/u/7418012?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hemant Bothra</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hbothra15" title="Code">💻</a> <a href="#design-hbothra15" title="Design">🎨</a></td>
|
<td align="center"><a href="https://github.com/hbothra15"><img src="https://avatars1.githubusercontent.com/u/7418012?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hemant Bothra</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hbothra15" title="Code">💻</a> <a href="#design-hbothra15" title="Design">🎨</a></td>
|
||||||
<td align="center"><a href="https://www.kevinpeters.net/about/"><img src="https://avatars1.githubusercontent.com/u/12736734?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Peters</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=igeligel" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.kevinpeters.net/about/"><img src="https://avatars1.githubusercontent.com/u/12736734?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin Peters</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=igeligel" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://llorllale.github.io/"><img src="https://avatars1.githubusercontent.com/u/2019896?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Aristy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=llorllale" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://llorllale.github.io/"><img src="https://avatars1.githubusercontent.com/u/2019896?v=4?s=100" width="100px;" alt=""/><br /><sub><b>George Aristy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=llorllale" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/mookkiah"><img src="https://avatars1.githubusercontent.com/u/8975264?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mahendran Mookkiah</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mookkiah" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/mookkiah"><img src="https://avatars1.githubusercontent.com/u/8975264?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mahendran Mookkiah</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mookkiah" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Azureyjt"><img src="https://avatars2.githubusercontent.com/u/18476317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Azureyjt</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Azureyjt" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/Azureyjt"><img src="https://avatars2.githubusercontent.com/u/18476317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Azureyjt</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Azureyjt" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/vehpsr"><img src="https://avatars2.githubusercontent.com/u/3133265?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vehpsr" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/vehpsr"><img src="https://avatars2.githubusercontent.com/u/3133265?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vehpsr" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/ThatGuyWithTheHat"><img src="https://avatars0.githubusercontent.com/u/24470582?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt</b></sub></a><br /><a href="#content-ThatGuyWithTheHat" title="Content">🖋</a></td>
|
<td align="center"><a href="https://github.com/ThatGuyWithTheHat"><img src="https://avatars0.githubusercontent.com/u/24470582?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt</b></sub></a><br /><a href="#content-ThatGuyWithTheHat" title="Content">🖋</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://www.linkedin.com/in/gopinathlangote/"><img src="https://avatars2.githubusercontent.com/u/10210778?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gopinath Langote</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gopinath-langote" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.linkedin.com/in/gopinathlangote/"><img src="https://avatars2.githubusercontent.com/u/10210778?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Gopinath Langote</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gopinath-langote" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/hoswey"><img src="https://avatars3.githubusercontent.com/u/3689445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hoswey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoswey" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/hoswey"><img src="https://avatars3.githubusercontent.com/u/3689445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Hoswey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=hoswey" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/amit2103"><img src="https://avatars3.githubusercontent.com/u/7566692?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Pandey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit2103" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/gwildor28"><img src="https://avatars0.githubusercontent.com/u/16000365?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gwildor28</b></sub></a><br /><a href="#content-gwildor28" title="Content">🖋</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/amit2103"><img src="https://avatars3.githubusercontent.com/u/7566692?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Amit Pandey</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=amit2103" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/gwildor28"><img src="https://avatars0.githubusercontent.com/u/16000365?v=4?s=100" width="100px;" alt=""/><br /><sub><b>gwildor28</b></sub></a><br /><a href="#content-gwildor28" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://t.me/paul_docker"><img src="https://avatars1.githubusercontent.com/u/2404785?v=4?s=100" width="100px;" alt=""/><br /><sub><b>田浩</b></sub></a><br /><a href="#content-llitfkitfk" title="Content">🖋</a></td>
|
<td align="center"><a href="https://t.me/paul_docker"><img src="https://avatars1.githubusercontent.com/u/2404785?v=4?s=100" width="100px;" alt=""/><br /><sub><b>田浩</b></sub></a><br /><a href="#content-llitfkitfk" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://twitter.com/StPitsios"><img src="https://avatars1.githubusercontent.com/u/6773603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stamatis Pitsios</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pitsios-s" title="Code">💻</a></td>
|
<td align="center"><a href="https://twitter.com/StPitsios"><img src="https://avatars1.githubusercontent.com/u/6773603?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stamatis Pitsios</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=pitsios-s" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/qza"><img src="https://avatars3.githubusercontent.com/u/233149?v=4?s=100" width="100px;" alt=""/><br /><sub><b>qza</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qza" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/qza"><img src="https://avatars3.githubusercontent.com/u/233149?v=4?s=100" width="100px;" alt=""/><br /><sub><b>qza</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=qza" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://tschis.github.io"><img src="https://avatars1.githubusercontent.com/u/20662669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodolfo Forte</b></sub></a><br /><a href="#content-Tschis" title="Content">🖋</a></td>
|
<td align="center"><a href="http://tschis.github.io"><img src="https://avatars1.githubusercontent.com/u/20662669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rodolfo Forte</b></sub></a><br /><a href="#content-Tschis" title="Content">🖋</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/ankurkaushal"><img src="https://avatars2.githubusercontent.com/u/2236616?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ankur Kaushal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ankurkaushal" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/ankurkaushal"><img src="https://avatars2.githubusercontent.com/u/2236616?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ankur Kaushal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ankurkaushal" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://www.linkedin.com/in/ovidijus-okinskas/"><img src="https://avatars0.githubusercontent.com/u/20372387?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ovidijus Okinskas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=okinskas" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.linkedin.com/in/ovidijus-okinskas/"><img src="https://avatars0.githubusercontent.com/u/20372387?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ovidijus Okinskas</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=okinskas" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/robertt240"><img src="https://avatars1.githubusercontent.com/u/9137432?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert Kasperczyk</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=robertt240" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/robertt240"><img src="https://avatars1.githubusercontent.com/u/9137432?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Robert Kasperczyk</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=robertt240" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/trautonen"><img src="https://avatars3.githubusercontent.com/u/1641063?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tapio Rautonen</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=trautonen" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/trautonen"><img src="https://avatars3.githubusercontent.com/u/1641063?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tapio Rautonen</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=trautonen" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="http://vk.com/yuri.orlov"><img src="https://avatars0.githubusercontent.com/u/1595733?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yuri Orlov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yorlov" title="Code">💻</a></td>
|
<td align="center"><a href="http://vk.com/yuri.orlov"><img src="https://avatars0.githubusercontent.com/u/1595733?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Yuri Orlov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yorlov" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.linkedin.com/in/varunu28/"><img src="https://avatars0.githubusercontent.com/u/7676016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Varun Upadhyay</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=varunu28" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.linkedin.com/in/varunu28/"><img src="https://avatars0.githubusercontent.com/u/7676016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Varun Upadhyay</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=varunu28" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/PalAditya"><img src="https://avatars2.githubusercontent.com/u/25523604?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aditya Pal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=PalAditya" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/PalAditya"><img src="https://avatars2.githubusercontent.com/u/25523604?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Aditya Pal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=PalAditya" title="Code">💻</a></td>
|
||||||
@ -236,50 +215,41 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://github.com/d4gg4d"><img src="https://avatars2.githubusercontent.com/u/99457?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sami Airaksinen</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=d4gg4d" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/d4gg4d"><img src="https://avatars2.githubusercontent.com/u/99457?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Sami Airaksinen</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=d4gg4d" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/vertti"><img src="https://avatars0.githubusercontent.com/u/557751?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Janne Sinivirta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vertti" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/vertti"><img src="https://avatars0.githubusercontent.com/u/557751?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Janne Sinivirta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vertti" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/Bobo1239"><img src="https://avatars1.githubusercontent.com/u/2302947?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris-Chengbiao Zhou</b></sub></a><br /><a href="#content-Bobo1239" title="Content">🖋</a></td>
|
<td align="center"><a href="https://github.com/Bobo1239"><img src="https://avatars1.githubusercontent.com/u/2302947?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Boris-Chengbiao Zhou</b></sub></a><br /><a href="#content-Bobo1239" title="Content">🖋</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://jahhein.github.io"><img src="https://avatars2.githubusercontent.com/u/10779515?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jacob Hein</b></sub></a><br /><a href="#content-Jahhein" title="Content">🖋</a></td>
|
<td align="center"><a href="https://jahhein.github.io"><img src="https://avatars2.githubusercontent.com/u/10779515?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jacob Hein</b></sub></a><br /><a href="#content-Jahhein" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://github.com/iamrichardjones"><img src="https://avatars3.githubusercontent.com/u/14842151?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Jones</b></sub></a><br /><a href="#content-iamrichardjones" title="Content">🖋</a></td>
|
<td align="center"><a href="https://github.com/iamrichardjones"><img src="https://avatars3.githubusercontent.com/u/14842151?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Jones</b></sub></a><br /><a href="#content-iamrichardjones" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://rachelcarmena.github.io"><img src="https://avatars0.githubusercontent.com/u/22792183?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rachel M. Carmena</b></sub></a><br /><a href="#content-rachelcarmena" title="Content">🖋</a></td>
|
<td align="center"><a href="https://rachelcarmena.github.io"><img src="https://avatars0.githubusercontent.com/u/22792183?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rachel M. Carmena</b></sub></a><br /><a href="#content-rachelcarmena" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://zd-zero.github.io"><img src="https://avatars0.githubusercontent.com/u/21978370?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zaerald Denze Lungos</b></sub></a><br /><a href="#content-zd-zero" title="Content">🖋</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://zd-zero.github.io"><img src="https://avatars0.githubusercontent.com/u/21978370?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Zaerald Denze Lungos</b></sub></a><br /><a href="#content-zd-zero" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://webpro.nl"><img src="https://avatars1.githubusercontent.com/u/456426?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lars Kappert</b></sub></a><br /><a href="#content-webpro" title="Content">🖋</a></td>
|
<td align="center"><a href="https://webpro.nl"><img src="https://avatars1.githubusercontent.com/u/456426?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Lars Kappert</b></sub></a><br /><a href="#content-webpro" title="Content">🖋</a></td>
|
||||||
<td align="center"><a href="https://xiaod.info"><img src="https://avatars2.githubusercontent.com/u/21277644?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike Liu</b></sub></a><br /><a href="#translation-xiaod-dev" title="Translation">🌍</a></td>
|
<td align="center"><a href="https://xiaod.info"><img src="https://avatars2.githubusercontent.com/u/21277644?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mike Liu</b></sub></a><br /><a href="#translation-xiaod-dev" title="Translation">🌍</a></td>
|
||||||
<td align="center"><a href="https://github.com/charlesfinley"><img src="https://avatars1.githubusercontent.com/u/6307904?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Dolan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=charlesfinley" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Acharlesfinley" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://github.com/charlesfinley"><img src="https://avatars1.githubusercontent.com/u/6307904?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Matt Dolan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=charlesfinley" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Acharlesfinley" title="Reviewed Pull Requests">👀</a></td>
|
||||||
<td align="center"><a href="https://github.com/MananS77"><img src="https://avatars3.githubusercontent.com/u/21033516?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AMananS77" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://github.com/MananS77"><img src="https://avatars3.githubusercontent.com/u/21033516?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manan</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AMananS77" title="Reviewed Pull Requests">👀</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/nishant"><img src="https://avatars2.githubusercontent.com/u/15331971?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nishant Arora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nishant" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/nishant"><img src="https://avatars2.githubusercontent.com/u/15331971?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nishant Arora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nishant" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/raja-peeyush-kumar-singh"><img src="https://avatars0.githubusercontent.com/u/5496024?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peeyush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=raja-peeyush-kumar-singh" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/raja-peeyush-kumar-singh"><img src="https://avatars0.githubusercontent.com/u/5496024?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Peeyush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=raja-peeyush-kumar-singh" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aravening" title="Reviewed Pull Requests">👀</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/vINCENT8888801"><img src="https://avatars0.githubusercontent.com/u/8037883?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wei Seng</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vINCENT8888801" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/ravening"><img src="https://avatars1.githubusercontent.com/u/10645273?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rakesh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ravening" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aravening" title="Reviewed Pull Requests">👀</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/vINCENT8888801"><img src="https://avatars0.githubusercontent.com/u/8037883?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Wei Seng</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vINCENT8888801" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.linkedin.com/in/ashish-trivedi-218379135/"><img src="https://avatars3.githubusercontent.com/u/23194128?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ashish Trivedi</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ashishtrivedi16" title="Code">💻</a></td>
|
<td align="center"><a href="https://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://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="https://xdvrx1.github.io/"><img src="https://avatars0.githubusercontent.com/u/47092464?v=4?s=100" width="100px;" alt=""/><br /><sub><b>xdvrx1</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Axdvrx1" title="Reviewed Pull Requests">👀</a> <a href="#ideas-xdvrx1" title="Ideas, Planning, & Feedback">🤔</a></td>
|
||||||
<td align="center"><a href="http://subho.xyz"><img src="https://avatars0.githubusercontent.com/u/13291222?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Subhrodip Mohanta</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ohbus" title="Code">💻</a> <a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aohbus" title="Reviewed Pull Requests">👀</a> <a href="#maintenance-ohbus" title="Maintenance">🚧</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/nahteb"><img src="https://avatars3.githubusercontent.com/u/13121570?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bethan Palmer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nahteb" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/nahteb"><img src="https://avatars3.githubusercontent.com/u/13121570?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Bethan Palmer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=nahteb" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/ToxicDreamz"><img src="https://avatars0.githubusercontent.com/u/45225562?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Toxic Dreamz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ToxicDreamz" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/ToxicDreamz"><img src="https://avatars0.githubusercontent.com/u/45225562?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Toxic Dreamz</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ToxicDreamz" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="http://www.edycutjong.com"><img src="https://avatars1.githubusercontent.com/u/1098102?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Edy Cu Tjong</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=edycutjong" title="Documentation">📖</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/mkrzywanski"><img src="https://avatars0.githubusercontent.com/u/15279585?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michał Krzywański</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mkrzywanski" title="Code">💻</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="http://www.edycutjong.com"><img src="https://avatars1.githubusercontent.com/u/1098102?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Edy Cu Tjong</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=edycutjong" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/mkrzywanski"><img src="https://avatars0.githubusercontent.com/u/15279585?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Michał Krzywański</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mkrzywanski" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://www.stefan-birkner.de"><img src="https://avatars1.githubusercontent.com/u/711349?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Birkner</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=stefanbirkner" title="Code">💻</a></td>
|
<td align="center"><a href="https://www.stefan-birkner.de"><img src="https://avatars1.githubusercontent.com/u/711349?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Stefan Birkner</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=stefanbirkner" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/fedorskvorcov"><img src="https://avatars3.githubusercontent.com/u/43882212?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fedor Skvorcov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fedorskvorcov" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/fedorskvorcov"><img src="https://avatars3.githubusercontent.com/u/43882212?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fedor Skvorcov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=fedorskvorcov" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/samilAyoub"><img src="https://avatars0.githubusercontent.com/u/61546990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>samilAyoub</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samilAyoub" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/samilAyoub"><img src="https://avatars0.githubusercontent.com/u/61546990?v=4?s=100" width="100px;" alt=""/><br /><sub><b>samilAyoub</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samilAyoub" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/vdlald"><img src="https://avatars0.githubusercontent.com/u/29997701?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Golubinov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vdlald" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/vdlald"><img src="https://avatars0.githubusercontent.com/u/29997701?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vladislav Golubinov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vdlald" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/swarajsaaj"><img src="https://avatars2.githubusercontent.com/u/6285049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Swaraj</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=swarajsaaj" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/swarajsaaj"><img src="https://avatars2.githubusercontent.com/u/6285049?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Swaraj</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=swarajsaaj" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://christophflick.de"><img src="https://avatars0.githubusercontent.com/u/4465376?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christoph Flick</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ChFlick" title="Documentation">📖</a></td>
|
<td align="center"><a href="http://christophflick.de"><img src="https://avatars0.githubusercontent.com/u/4465376?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Christoph Flick</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ChFlick" title="Documentation">📖</a></td>
|
||||||
<td align="center"><a href="https://github.com/Ascenio"><img src="https://avatars1.githubusercontent.com/u/7662016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ascênio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AAscenio" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://github.com/Ascenio"><img src="https://avatars1.githubusercontent.com/u/7662016?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ascênio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AAscenio" title="Reviewed Pull Requests">👀</a></td>
|
||||||
<td align="center"><a href="https://www.linkedin.com/in/domenico-sibilio/"><img src="https://avatars2.githubusercontent.com/u/24280982?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Domenico Sibilio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dsibilio" title="Documentation">📖</a></td>
|
<td align="center"><a href="https://www.linkedin.com/in/domenico-sibilio/"><img src="https://avatars2.githubusercontent.com/u/24280982?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Domenico Sibilio</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=dsibilio" title="Documentation">📖</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/akashchandwani"><img src="https://avatars2.githubusercontent.com/u/3483277?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akash Chandwani</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aakashchandwani" title="Reviewed Pull Requests">👀</a></td>
|
<td align="center"><a href="https://github.com/akashchandwani"><img src="https://avatars2.githubusercontent.com/u/3483277?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Akash Chandwani</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Aakashchandwani" title="Reviewed Pull Requests">👀</a></td>
|
||||||
<td align="center"><a href="http://www.linkedin.com/in/manannikov"><img src="https://avatars2.githubusercontent.com/u/7019769?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavlo Manannikov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=manannikov" title="Code">💻</a></td>
|
<td align="center"><a href="http://www.linkedin.com/in/manannikov"><img src="https://avatars2.githubusercontent.com/u/7019769?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Pavlo Manannikov</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=manannikov" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/eimanip"><img src="https://avatars0.githubusercontent.com/u/20307301?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eiman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eimanip" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/eimanip"><img src="https://avatars0.githubusercontent.com/u/20307301?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Eiman</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eimanip" title="Code">💻</a></td>
|
||||||
@ -290,38 +260,32 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://github.com/gkulkarni2020"><img src="https://avatars3.githubusercontent.com/u/5161548?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Girish Kulkarni</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gkulkarni2020" title="Documentation">📖</a></td>
|
<td align="center"><a href="https://github.com/gkulkarni2020"><img src="https://avatars3.githubusercontent.com/u/5161548?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Girish Kulkarni</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=gkulkarni2020" title="Documentation">📖</a></td>
|
||||||
<td align="center"><a href="https://github.com/omk13"><img src="https://avatars0.githubusercontent.com/u/59054172?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Omar Karazoun</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=omk13" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/omk13"><img src="https://avatars0.githubusercontent.com/u/59054172?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Omar Karazoun</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=omk13" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/jeff303"><img src="https://avatars0.githubusercontent.com/u/3521562?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeff Evans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jeff303" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/jeff303"><img src="https://avatars0.githubusercontent.com/u/3521562?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Jeff Evans</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=jeff303" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://viveksb007.github.io"><img src="https://avatars1.githubusercontent.com/u/12713808?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vivek Singh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=viveksb007" title="Code">💻</a></td>
|
<td align="center"><a href="https://viveksb007.github.io"><img src="https://avatars1.githubusercontent.com/u/12713808?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vivek Singh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=viveksb007" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/siavashsoleymani"><img src="https://avatars2.githubusercontent.com/u/18074419?v=4?s=100" width="100px;" alt=""/><br /><sub><b>siavash</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=siavashsoleymani" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/siavashsoleymani"><img src="https://avatars2.githubusercontent.com/u/18074419?v=4?s=100" width="100px;" alt=""/><br /><sub><b>siavash</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=siavashsoleymani" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/ruchpeanuts"><img src="https://avatars0.githubusercontent.com/u/29301900?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ruchpeanuts</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ruchpeanuts" title="Documentation">📖</a></td>
|
<td align="center"><a href="https://github.com/ruchpeanuts"><img src="https://avatars0.githubusercontent.com/u/29301900?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ruchpeanuts</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ruchpeanuts" title="Documentation">📖</a></td>
|
||||||
<td align="center"><a href="https://github.com/warp125"><img src="https://avatars1.githubusercontent.com/u/48073115?v=4?s=100" width="100px;" alt=""/><br /><sub><b>warp125</b></sub></a><br /><a href="#translation-warp125" title="Translation">🌍</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/warp125"><img src="https://avatars1.githubusercontent.com/u/48073115?v=4?s=100" width="100px;" alt=""/><br /><sub><b>warp125</b></sub></a><br /><a href="#translation-warp125" title="Translation">🌍</a></td>
|
||||||
<td align="center"><a href="http://libkhadir.fr"><img src="https://avatars1.githubusercontent.com/u/45130488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>KHADIR Tayeb</b></sub></a><br /><a href="#translation-tkhadir" title="Translation">🌍</a></td>
|
<td align="center"><a href="http://libkhadir.fr"><img src="https://avatars1.githubusercontent.com/u/45130488?v=4?s=100" width="100px;" alt=""/><br /><sub><b>KHADIR Tayeb</b></sub></a><br /><a href="#translation-tkhadir" title="Translation">🌍</a></td>
|
||||||
<td align="center"><a href="https://github.com/ignite1771"><img src="https://avatars2.githubusercontent.com/u/59446563?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ignite1771</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ignite1771" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/ignite1771"><img src="https://avatars2.githubusercontent.com/u/59446563?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ignite1771</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ignite1771" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/demirhalil"><img src="https://avatars1.githubusercontent.com/u/22895118?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Halil Demir</b></sub></a><br /><a href="#translation-demirhalil" title="Translation">🌍</a></td>
|
<td align="center"><a href="https://github.com/demirhalil"><img src="https://avatars1.githubusercontent.com/u/22895118?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Halil Demir</b></sub></a><br /><a href="#translation-demirhalil" title="Translation">🌍</a></td>
|
||||||
<td align="center"><a href="https://github.com/rohit10000"><img src="https://avatars.githubusercontent.com/u/20845565?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rohit Singh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=rohit10000" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/rohit10000"><img src="https://avatars.githubusercontent.com/u/20845565?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Rohit Singh</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=rohit10000" title="Code">💻</a></td>
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/byoungju94"><img src="https://avatars.githubusercontent.com/u/42516378?v=4?s=100" width="100px;" alt=""/><br /><sub><b>byoungju94</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=byoungju94" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/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/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>
|
||||||
<tr>
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/richardmr36"><img src="https://avatars.githubusercontent.com/u/19147333?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Martel Richard</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=richardmr36" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/va1m"><img src="https://avatars.githubusercontent.com/u/17025445?v=4?s=100" width="100px;" alt=""/><br /><sub><b>va1m</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=va1m" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/noamgrinch"><img src="https://avatars.githubusercontent.com/u/31648669?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Noam Greenshtain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=noamgrinch" title="Code">💻</a></td>
|
<td align="center"><a href="https://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://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://www.linkedin.com/in/jinisha-vora"><img src="https://avatars.githubusercontent.com/u/40777762?v=4?s=100" width="100px;" alt=""/><br /><sub><b>jinishavora</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3Ajinishavora" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=jinishavora" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/eas5"><img src="https://avatars.githubusercontent.com/u/50836521?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Elvys Soares</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eas5" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/eas5"><img src="https://avatars.githubusercontent.com/u/50836521?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Elvys Soares</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=eas5" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/zWeBrain"><img src="https://avatars.githubusercontent.com/u/46642512?v=4?s=100" width="100px;" alt=""/><br /><sub><b>zWeBrain</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=zWeBrain" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
<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://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/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>
|
<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/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/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/tao-sun2"><img src="https://avatars.githubusercontent.com/u/66189688?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tao</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=tao-sun2" title="Code">💻</a></td>
|
||||||
@ -332,6 +296,40 @@ This project is licensed under the terms of the MIT license.
|
|||||||
<td align="center"><a href="https://www.linkedin.com/in/souzasamuel/"><img src="https://avatars.githubusercontent.com/u/17254162?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Samuel Souza</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=samuelpsouza" title="Code">💻</a></td>
|
<td align="center"><a href="https://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/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>
|
<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>
|
||||||
|
<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>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="http://scuccimarri.it"><img src="https://avatars.githubusercontent.com/u/7107651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Francesco Scuccimarri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=frascu" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Conhan93"><img src="https://avatars.githubusercontent.com/u/71334757?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conny Hansson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Conhan93" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="http://muklasr.medium.com"><img src="https://avatars.githubusercontent.com/u/43443753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muklas Rahmanto</b></sub></a><br /><a href="#translation-muklasr" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/VxDxK"><img src="https://avatars.githubusercontent.com/u/38704817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vadim</b></sub></a><br /><a href="#translation-VxDxK" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/sims-keshri"><img src="https://avatars.githubusercontent.com/u/62168475?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Simran Keshri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=sims-keshri" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://programacionymas.com"><img src="https://avatars.githubusercontent.com/u/3101238?v=4?s=100" width="100px;" alt=""/><br /><sub><b>JCarlos</b></sub></a><br /><a href="#translation-JCarlosR" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://www.mrmoshkel.ir"><img src="https://avatars.githubusercontent.com/u/60359433?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Ali Ghasemi</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Dev-AliGhasemi" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="http://carlfx.wordpress.com"><img src="https://avatars.githubusercontent.com/u/1594624?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Carl Dea</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=carldea" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Mozartuss"><img src="https://avatars.githubusercontent.com/u/32893711?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Mozartus</b></sub></a><br /><a href="#translation-Mozartuss" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/ManviGoel26"><img src="https://avatars.githubusercontent.com/u/55682355?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Manvi Goel</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ManviGoel26" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/blueberry404"><img src="https://avatars.githubusercontent.com/u/39243539?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Anum Amin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=blueberry404" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://uh-zz.github.io/blog/"><img src="https://avatars.githubusercontent.com/u/47747828?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Reo Uehara</b></sub></a><br /><a href="#translation-uh-zz" title="Translation">🌍</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Fiordy"><img src="https://avatars.githubusercontent.com/u/53420573?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Fiordy</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Fiordy" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/harshalkh"><img src="https://avatars.githubusercontent.com/u/37841724?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Harshal</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=harshalkh" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://www.linkedin.com/in/abhinav-vashisth-06613b208/"><img src="https://avatars.githubusercontent.com/u/89785800?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Abhinav Vashisth</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=vashisthabhinav" title="Documentation">📖</a></td>
|
||||||
|
<td align="center"><a href="http://no website"><img src="https://avatars.githubusercontent.com/u/47126749?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Kevin</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AKevinyl3" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Kevinyl3" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Shrirang97"><img src="https://avatars.githubusercontent.com/u/28738668?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Shrirang</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/pulls?q=is%3Apr+reviewed-by%3AShrirang97" title="Reviewed Pull Requests">👀</a> <a href="https://github.com/iluwatar/java-design-patterns/commits?author=Shrirang97" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/interactwithankush"><img src="https://avatars.githubusercontent.com/u/18613127?v=4?s=100" width="100px;" alt=""/><br /><sub><b>interactwithankush</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=interactwithankush" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/yuhangbin"><img src="https://avatars.githubusercontent.com/u/17566866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>CharlieYu</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=yuhangbin" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/Leisterbecker"><img src="https://avatars.githubusercontent.com/u/20650323?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Leisterbecker</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Leisterbecker" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="http://rosaecrucis.cn"><img src="https://avatars.githubusercontent.com/u/35420129?v=4?s=100" width="100px;" alt=""/><br /><sub><b>DragonDreamer</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=castleKing1997" title="Code">💻</a></td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td align="center"><a href="https://github.com/ShivanshCharak"><img src="https://avatars.githubusercontent.com/u/96943825?v=4?s=100" width="100px;" alt=""/><br /><sub><b>ShivanshCharak</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=ShivanshCharak" title="Code">💻</a></td>
|
||||||
|
<td align="center"><a href="https://github.com/HattoriHenzo"><img src="https://avatars.githubusercontent.com/u/5141285?v=4?s=100" width="100px;" alt=""/><br /><sub><b>HattoriHenzo</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=HattoriHenzo" title="Code">💻</a></td>
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -23,42 +23,40 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<artifactId>abstract-document</artifactId>
|
||||||
</parent>
|
<dependencies>
|
||||||
<artifactId>abstract-document</artifactId>
|
<dependency>
|
||||||
<dependencies>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependency>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<scope>test</scope>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependency>
|
||||||
<scope>test</scope>
|
</dependencies>
|
||||||
</dependency>
|
<build>
|
||||||
</dependencies>
|
<plugins>
|
||||||
<build>
|
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||||
<plugins>
|
|
||||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
|
||||||
in parent pom and specifying the class having main method -->
|
in parent pom and specifying the class having main method -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<mainClass>com.iluwatar.abstractdocument.App</mainClass>
|
<mainClass>com.iluwatar.abstractdocument.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -87,24 +87,36 @@ public interface KingdomFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public class ElfKingdomFactory implements KingdomFactory {
|
public class ElfKingdomFactory implements KingdomFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Castle createCastle() {
|
public Castle createCastle() {
|
||||||
return new ElfCastle();
|
return new ElfCastle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public King createKing() {
|
public King createKing() {
|
||||||
return new ElfKing();
|
return new ElfKing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Army createArmy() {
|
public Army createArmy() {
|
||||||
return new ElfArmy();
|
return new ElfArmy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class OrcKingdomFactory implements KingdomFactory {
|
public class OrcKingdomFactory implements KingdomFactory {
|
||||||
|
|
||||||
|
@Override
|
||||||
public Castle createCastle() {
|
public Castle createCastle() {
|
||||||
return new OrcCastle();
|
return new OrcCastle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public King createKing() {
|
public King createKing() {
|
||||||
return new OrcKing();
|
return new OrcKing();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public Army createArmy() {
|
public Army createArmy() {
|
||||||
return new OrcArmy();
|
return new OrcArmy();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,43 +23,40 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>abstract-factory</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>abstract-factory</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
|
||||||
in parent pom and specifying the class having main method -->
|
in parent pom and specifying the class having main method -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<mainClass>com.iluwatar.abstractfactory.App</mainClass>
|
<mainClass>com.iluwatar.abstractfactory.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -11,7 +11,7 @@ tags:
|
|||||||
|
|
||||||
|
|
||||||
## Intent
|
## 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.
|
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
|
## Explanation
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ public abstract class ActiveCreature{
|
|||||||
requests.put(new Runnable() {
|
requests.put(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
logger.info("{} has started to roam and the wastelands.",name());
|
logger.info("{} has started to roam the wastelands.",name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -82,7 +82,7 @@ public abstract class ActiveCreature{
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods.
|
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:
|
For example, the Orc class:
|
||||||
|
|
||||||
@ -96,7 +96,7 @@ public class Orc extends ActiveCreature {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
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:
|
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
|
```java
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
@ -123,4 +123,8 @@ Now, we can create multiple creatures such as Orcs, tell them to eat and roam an
|
|||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
* [Android and Java Concurrency: The Active Object Pattern](https://www.youtube.com/watch?v=Cd8t2u5Qmvc)
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,43 +23,40 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>active-object</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>active-object</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
|
||||||
in parent pom and specifying the class having main method -->
|
in parent pom and specifying the class having main method -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<mainClass>com.iluwatar.activeobject.App</mainClass>
|
<mainClass>com.iluwatar.activeobject.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -82,7 +82,7 @@ public abstract class ActiveCreature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Roam in the wastelands.
|
* Roam the wastelands.
|
||||||
* @throws InterruptedException due to firing a new Runnable.
|
* @throws InterruptedException due to firing a new Runnable.
|
||||||
*/
|
*/
|
||||||
public void roam() throws InterruptedException {
|
public void roam() throws InterruptedException {
|
||||||
|
@ -138,6 +138,10 @@ This pattern can be used:
|
|||||||
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
|
* When the visited class hierarchy will be frequently extended with new derivatives of the Element class.
|
||||||
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
|
* When the recompilation, relinking, retesting or redistribution of the derivatives of Element is very expensive.
|
||||||
|
|
||||||
|
## Tutorial
|
||||||
|
|
||||||
|
* [Acyclic Visitor Pattern Example](https://codecrafter.blogspot.com/2012/12/the-acyclic-visitor-pattern.html)
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
|
|
||||||
The good:
|
The good:
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -22,53 +23,47 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>acyclic-visitor</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<properties>
|
||||||
</parent>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<maven.compiler.source>1.8</maven.compiler.source>
|
||||||
<artifactId>acyclic-visitor</artifactId>
|
<maven.compiler.target>1.8</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
<properties>
|
<dependencies>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
|
||||||
<maven.compiler.source>1.8</maven.compiler.source>
|
<dependency>
|
||||||
<maven.compiler.target>1.8</maven.compiler.target>
|
<groupId>org.assertj</groupId>
|
||||||
</properties>
|
<artifactId>assertj-core</artifactId>
|
||||||
|
<version>3.9.1</version>
|
||||||
<dependencies>
|
<scope>test</scope>
|
||||||
<!-- https://mvnrepository.com/artifact/org.assertj/assertj-core -->
|
</dependency>
|
||||||
<dependency>
|
<!-- https://mvnrepository.com/artifact/uk.org.lidalia/slf4j-test -->
|
||||||
<groupId>org.assertj</groupId>
|
<dependency>
|
||||||
<artifactId>assertj-core</artifactId>
|
<groupId>uk.org.lidalia</groupId>
|
||||||
<version>3.9.1</version>
|
<artifactId>slf4j-test</artifactId>
|
||||||
<scope>test</scope>
|
<version>1.2.0</version>
|
||||||
</dependency>
|
<scope>test</scope>
|
||||||
<!-- https://mvnrepository.com/artifact/uk.org.lidalia/slf4j-test -->
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>uk.org.lidalia</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>slf4j-test</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<version>1.2.0</version>
|
<scope>test</scope>
|
||||||
<scope>test</scope>
|
</dependency>
|
||||||
</dependency>
|
<dependency>
|
||||||
<dependency>
|
<groupId>org.mockito</groupId>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<artifactId>mockito-all</artifactId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<version>1.10.19</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
</dependencies>
|
||||||
<groupId>org.mockito</groupId>
|
<build>
|
||||||
<artifactId>mockito-all</artifactId>
|
|
||||||
<version>1.10.19</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||||
in parent pom and specifying the class having main method -->
|
in parent pom and specifying the class having main method -->
|
||||||
|
@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* Hayes class implements its accept method.
|
* Hayes class implements its accept method.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Hayes extends Modem {
|
public class Hayes implements Modem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts all visitors but honors only HayesVisitor.
|
* Accepts all visitors but honors only HayesVisitor.
|
||||||
|
@ -24,8 +24,9 @@
|
|||||||
package com.iluwatar.acyclicvisitor;
|
package com.iluwatar.acyclicvisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Modem abstract class.
|
* //Modem abstract class.
|
||||||
|
* converted to an interface
|
||||||
*/
|
*/
|
||||||
public abstract class Modem {
|
public interface Modem {
|
||||||
public abstract void accept(ModemVisitor modemVisitor);
|
void accept(ModemVisitor modemVisitor);
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
* Zoom class implements its accept method.
|
* Zoom class implements its accept method.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class Zoom extends Modem {
|
public class Zoom implements Modem {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Accepts all visitors but honors only ZoomVisitor.
|
* Accepts all visitors but honors only ZoomVisitor.
|
||||||
|
@ -18,10 +18,10 @@ couldn't otherwise because of incompatible interfaces.
|
|||||||
|
|
||||||
## Explanation
|
## Explanation
|
||||||
|
|
||||||
Real world example
|
Real-world example
|
||||||
|
|
||||||
> Consider that you have some pictures in your memory card and you need to transfer them to your computer. In order to transfer them you need some kind of adapter that is compatible with your computer ports so that you can attach memory card to your computer. In this case card reader is an adapter.
|
> Consider that you have some pictures on your memory card and you need to transfer them to your computer. To transfer them, you need some kind of adapter that is compatible with your computer ports so that you can attach a memory card to your computer. In this case card reader is an adapter.
|
||||||
> Another example would be the famous power adapter; a three legged plug can't be connected to a two pronged outlet, it needs to use a power adapter that makes it compatible with the two pronged outlet.
|
> Another example would be the famous power adapter; a three-legged plug can't be connected to a two-pronged outlet, it needs to use a power adapter that makes it compatible with the two-pronged outlets.
|
||||||
> Yet another example would be a translator translating words spoken by one person to another
|
> Yet another example would be a translator translating words spoken by one person to another
|
||||||
|
|
||||||
In plain words
|
In plain words
|
||||||
@ -36,7 +36,7 @@ Wikipedia says
|
|||||||
|
|
||||||
Consider a captain that can only use rowing boats and cannot sail at all.
|
Consider a captain that can only use rowing boats and cannot sail at all.
|
||||||
|
|
||||||
First we have interfaces `RowingBoat` and `FishingBoat`
|
First, we have interfaces `RowingBoat` and `FishingBoat`
|
||||||
|
|
||||||
```java
|
```java
|
||||||
public interface RowingBoat {
|
public interface RowingBoat {
|
||||||
@ -68,7 +68,7 @@ public class Captain {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Now let's say the pirates are coming and our captain needs to escape but there is only fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
|
Now let's say the pirates are coming and our captain needs to escape but there is only a fishing boat available. We need to create an adapter that allows the captain to operate the fishing boat with his rowing boat skills.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@ -100,25 +100,31 @@ captain.row();
|
|||||||
## Applicability
|
## Applicability
|
||||||
Use the Adapter pattern when
|
Use the Adapter pattern when
|
||||||
|
|
||||||
* you want to use an existing class, and its interface does not match the one you need
|
* You want to use an existing class, and its interface does not match the one you need
|
||||||
* you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
|
* You want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces
|
||||||
* you need to use several existing subclasses, but it's impractical to adapt their interface by subclassing every one. An object adapter can adapt the interface of its parent class.
|
* You need to use several existing subclasses, but it's impractical to adapt their interface by subclassing everyone. An object adapter can adapt the interface of its parent class.
|
||||||
* most of the applications using third party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
|
* Most of the applications using third-party libraries use adapters as a middle layer between the application and the 3rd party library to decouple the application from the library. If another library has to be used only an adapter for the new library is required without having to change the application code.
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
* [Dzone](https://dzone.com/articles/adapter-design-pattern-in-java)
|
||||||
|
* [Refactoring Guru](https://refactoring.guru/design-patterns/adapter/java/example)
|
||||||
|
* [Baeldung](https://www.baeldung.com/java-adapter-pattern)
|
||||||
|
|
||||||
## Consequences
|
## Consequences
|
||||||
Class and object adapters have different trade-offs. A class adapter
|
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 won’t work when we want to adapt a class and all its subclasses.
|
* Adapts Adaptee to Target by committing to a concrete Adaptee class. As a consequence, a class adapter won’t work when we want to adapt a class and all its subclasses.
|
||||||
* let’s Adapter override some of Adaptee’s behavior, since Adapter is a subclass of Adaptee.
|
* Let’s Adapter override some of Adaptee’s behavior since Adapter is a subclass of Adaptee.
|
||||||
* introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
|
* Introduces only one object, and no additional pointer indirection is needed to get to the adaptee.
|
||||||
|
|
||||||
An object adapter
|
An object adapter
|
||||||
|
|
||||||
* let’s a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
|
* Lets a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once.
|
||||||
* makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself.
|
* Makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making the Adapter refer to the subclass rather than the Adaptee itself.
|
||||||
|
|
||||||
|
|
||||||
## Known uses
|
## Real-world examples
|
||||||
|
|
||||||
* [java.util.Arrays#asList()](http://docs.oracle.com/javase/8/docs/api/java/util/Arrays.html#asList%28T...%29)
|
* [java.util.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-)
|
* [java.util.Collections#list()](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#list-java.util.Enumeration-)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,48 +23,45 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>adapter</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>adapter</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<dependency>
|
||||||
<scope>test</scope>
|
<groupId>org.mockito</groupId>
|
||||||
</dependency>
|
<artifactId>mockito-core</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.mockito</groupId>
|
</dependency>
|
||||||
<artifactId>mockito-core</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
|
||||||
in parent pom and specifying the class having main method -->
|
in parent pom and specifying the class having main method -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<mainClass>com.iluwatar.adapter.App</mainClass>
|
<mainClass>com.iluwatar.adapter.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -33,7 +33,7 @@ import static org.mockito.Mockito.spy;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test class
|
* Tests for the adapter pattern.
|
||||||
*/
|
*/
|
||||||
class AdapterPatternTest {
|
class AdapterPatternTest {
|
||||||
|
|
||||||
|
@ -33,9 +33,7 @@ import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* Check whether the execution of the main method in {@link App}
|
||||||
*
|
|
||||||
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
|
|
||||||
* throws an exception.
|
* throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -23,68 +23,65 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>aggregator-microservices</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>aggregator-microservices</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>aggregator-service</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<packaging>jar</packaging>
|
||||||
<artifactId>aggregator-service</artifactId>
|
<dependencies>
|
||||||
<packaging>jar</packaging>
|
<dependency>
|
||||||
<dependencies>
|
<groupId>org.springframework</groupId>
|
||||||
<dependency>
|
<artifactId>spring-webmvc</artifactId>
|
||||||
<groupId>org.springframework</groupId>
|
</dependency>
|
||||||
<artifactId>spring-webmvc</artifactId>
|
<dependency>
|
||||||
</dependency>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<dependency>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
<groupId>org.springframework.boot</groupId>
|
</dependency>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<dependency>
|
||||||
</dependency>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependency>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<scope>test</scope>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependency>
|
||||||
<scope>test</scope>
|
<dependency>
|
||||||
</dependency>
|
<groupId>org.mockito</groupId>
|
||||||
<dependency>
|
<artifactId>mockito-core</artifactId>
|
||||||
<groupId>org.mockito</groupId>
|
<scope>test</scope>
|
||||||
<artifactId>mockito-core</artifactId>
|
</dependency>
|
||||||
<scope>test</scope>
|
</dependencies>
|
||||||
</dependency>
|
<build>
|
||||||
</dependencies>
|
<plugins>
|
||||||
|
<plugin>
|
||||||
<build>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<plugins>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<plugin>
|
<executions>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<execution>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<goals>
|
||||||
<executions>
|
<goal>repackage</goal>
|
||||||
<execution>
|
</goals>
|
||||||
<goals>
|
</execution>
|
||||||
<goal>repackage</goal>
|
</executions>
|
||||||
</goals>
|
</plugin>
|
||||||
</execution>
|
<!-- Maven assembly plugin is invoked with default setting which we have
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<!-- Maven assembly plugin is invoked with default setting which we have
|
|
||||||
in parent pom and specifying the class having main method -->
|
in parent pom and specifying the class having main method -->
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<executions>
|
<executions>
|
||||||
<execution>
|
<execution>
|
||||||
<configuration>
|
<configuration>
|
||||||
<archive>
|
<archive>
|
||||||
<manifest>
|
<manifest>
|
||||||
<mainClass>com.iluwatar.aggregator.microservices.App</mainClass>
|
<mainClass>com.iluwatar.aggregator.microservices.App</mainClass>
|
||||||
</manifest>
|
</manifest>
|
||||||
</archive>
|
</archive>
|
||||||
</configuration>
|
</configuration>
|
||||||
</execution>
|
</execution>
|
||||||
</executions>
|
</executions>
|
||||||
</plugin>
|
</plugin>
|
||||||
</plugins>
|
</plugins>
|
||||||
</build>
|
</build>
|
||||||
</project>
|
</project>
|
||||||
|
@ -48,7 +48,7 @@ class AggregatorTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,63 +23,58 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>aggregator-microservices</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>aggregator-microservices</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>information-microservice</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<packaging>jar</packaging>
|
||||||
|
<dependencies>
|
||||||
<artifactId>information-microservice</artifactId>
|
<dependency>
|
||||||
<packaging>jar</packaging>
|
<groupId>org.springframework</groupId>
|
||||||
|
<artifactId>spring-webmvc</artifactId>
|
||||||
<dependencies>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
<artifactId>spring-webmvc</artifactId>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
</dependency>
|
<scope>test</scope>
|
||||||
<dependency>
|
</dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependencies>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<build>
|
||||||
<scope>test</scope>
|
<plugins>
|
||||||
</dependency>
|
<plugin>
|
||||||
</dependencies>
|
<groupId>org.springframework.boot</groupId>
|
||||||
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<build>
|
<executions>
|
||||||
<plugins>
|
<execution>
|
||||||
<plugin>
|
<goals>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<goal>repackage</goal>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
</goals>
|
||||||
<executions>
|
</execution>
|
||||||
<execution>
|
</executions>
|
||||||
<goals>
|
</plugin>
|
||||||
<goal>repackage</goal>
|
<plugin>
|
||||||
</goals>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
</execution>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
</executions>
|
<executions>
|
||||||
</plugin>
|
<execution>
|
||||||
<plugin>
|
<configuration>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<archive>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<manifest>
|
||||||
<executions>
|
<mainClass>com.iluwatar.information.microservices.InformationApplication</mainClass>
|
||||||
<execution>
|
</manifest>
|
||||||
<configuration>
|
</archive>
|
||||||
<archive>
|
</configuration>
|
||||||
<manifest>
|
</execution>
|
||||||
<mainClass>com.iluwatar.information.microservices.InformationApplication</mainClass>
|
</executions>
|
||||||
</manifest>
|
</plugin>
|
||||||
</archive>
|
</plugins>
|
||||||
</configuration>
|
</build>
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -23,62 +23,58 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>aggregator-microservices</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>aggregator-microservices</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>inventory-microservice</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<packaging>jar</packaging>
|
||||||
<artifactId>inventory-microservice</artifactId>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
<packaging>jar</packaging>
|
<groupId>org.springframework</groupId>
|
||||||
<dependencies>
|
<artifactId>spring-webmvc</artifactId>
|
||||||
<dependency>
|
</dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<dependency>
|
||||||
<artifactId>spring-webmvc</artifactId>
|
<groupId>org.springframework.boot</groupId>
|
||||||
</dependency>
|
<artifactId>spring-boot-starter-web</artifactId>
|
||||||
<dependency>
|
</dependency>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<dependency>
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
</dependency>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<plugin>
|
||||||
|
<groupId>org.springframework.boot</groupId>
|
||||||
<build>
|
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||||
<plugins>
|
<executions>
|
||||||
<plugin>
|
<execution>
|
||||||
<groupId>org.springframework.boot</groupId>
|
<goals>
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
<goal>repackage</goal>
|
||||||
<executions>
|
</goals>
|
||||||
<execution>
|
</execution>
|
||||||
<goals>
|
</executions>
|
||||||
<goal>repackage</goal>
|
</plugin>
|
||||||
</goals>
|
<plugin>
|
||||||
</execution>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
</executions>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
</plugin>
|
<executions>
|
||||||
<plugin>
|
<execution>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<configuration>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<archive>
|
||||||
<executions>
|
<manifest>
|
||||||
<execution>
|
<mainClass>com.iluwatar.inventory.microservices.InventoryApplication</mainClass>
|
||||||
<configuration>
|
</manifest>
|
||||||
<archive>
|
</archive>
|
||||||
<manifest>
|
</configuration>
|
||||||
<mainClass>com.iluwatar.inventory.microservices.InventoryApplication</mainClass>
|
</execution>
|
||||||
</manifest>
|
</executions>
|
||||||
</archive>
|
</plugin>
|
||||||
</configuration>
|
</plugins>
|
||||||
</execution>
|
</build>
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -23,20 +23,18 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>aggregator-microservices</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<packaging>pom</packaging>
|
||||||
<artifactId>aggregator-microservices</artifactId>
|
<modules>
|
||||||
<packaging>pom</packaging>
|
<module>information-microservice</module>
|
||||||
<modules>
|
<module>aggregator-service</module>
|
||||||
<module>information-microservice</module>
|
<module>inventory-microservice</module>
|
||||||
<module>aggregator-service</module>
|
</modules>
|
||||||
<module>inventory-microservice</module>
|
|
||||||
</modules>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -23,40 +23,38 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>ambassador</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<dependencies>
|
||||||
<artifactId>ambassador</artifactId>
|
<dependency>
|
||||||
<dependencies>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependency>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<scope>test</scope>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependency>
|
||||||
<scope>test</scope>
|
</dependencies>
|
||||||
</dependency>
|
<build>
|
||||||
</dependencies>
|
<plugins>
|
||||||
<build>
|
<plugin>
|
||||||
<plugins>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<plugin>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<executions>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<execution>
|
||||||
<executions>
|
<configuration>
|
||||||
<execution>
|
<archive>
|
||||||
<configuration>
|
<manifest>
|
||||||
<archive>
|
<mainClass>com.iluwatar.ambassador.App</mainClass>
|
||||||
<manifest>
|
</manifest>
|
||||||
<mainClass>com.iluwatar.ambassador.App</mainClass>
|
</archive>
|
||||||
</manifest>
|
</configuration>
|
||||||
</archive>
|
</execution>
|
||||||
</configuration>
|
</executions>
|
||||||
</execution>
|
</plugin>
|
||||||
</executions>
|
</plugins>
|
||||||
</plugin>
|
</build>
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -23,13 +23,11 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>api-gateway</artifactId>
|
<artifactId>api-gateway</artifactId>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>api-gateway-service</artifactId>
|
<artifactId>api-gateway-service</artifactId>
|
||||||
@ -54,7 +52,6 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -48,7 +48,7 @@ class ApiGatewayTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,13 +23,11 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>api-gateway</artifactId>
|
<artifactId>api-gateway</artifactId>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>image-microservice</artifactId>
|
<artifactId>image-microservice</artifactId>
|
||||||
@ -49,7 +47,6 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -23,13 +23,11 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>api-gateway</artifactId>
|
<artifactId>api-gateway</artifactId>
|
||||||
|
@ -23,19 +23,15 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>api-gateway</artifactId>
|
<artifactId>api-gateway</artifactId>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<artifactId>price-microservice</artifactId>
|
<artifactId>price-microservice</artifactId>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.springframework</groupId>
|
<groupId>org.springframework</groupId>
|
||||||
@ -51,7 +47,6 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
|
@ -23,16 +23,13 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<parent>
|
<parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
<artifactId>arrange-act-assert</artifactId>
|
<artifactId>arrange-act-assert</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -41,4 +38,4 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,46 +23,43 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>async-method-invocation</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>async-method-invocation</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<dependency>
|
||||||
<scope>test</scope>
|
<groupId>org.mockito</groupId>
|
||||||
</dependency>
|
<artifactId>mockito-core</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.mockito</groupId>
|
</dependency>
|
||||||
<artifactId>mockito-core</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<plugin>
|
||||||
<build>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<plugins>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<plugin>
|
<executions>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<execution>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<configuration>
|
||||||
<executions>
|
<archive>
|
||||||
<execution>
|
<manifest>
|
||||||
<configuration>
|
<mainClass>com.iluwatar.async.method.invocation.App</mainClass>
|
||||||
<archive>
|
</manifest>
|
||||||
<manifest>
|
</archive>
|
||||||
<mainClass>com.iluwatar.async.method.invocation.App</mainClass>
|
</configuration>
|
||||||
</manifest>
|
</execution>
|
||||||
</archive>
|
</executions>
|
||||||
</configuration>
|
</plugin>
|
||||||
</execution>
|
</plugins>
|
||||||
</executions>
|
</build>
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -59,9 +59,12 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
|
private static final String ROCKET_LAUNCH_LOG_PATTERN = "Space rocket <%s> launched successfully";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
public static void main(String[] args) throws Exception {
|
||||||
// construct a new executor that will run async tasks
|
// construct a new executor that will run async tasks
|
||||||
var executor = new ThreadAsyncExecutor();
|
var executor = new ThreadAsyncExecutor();
|
||||||
@ -87,9 +90,9 @@ public class App {
|
|||||||
asyncResult5.await();
|
asyncResult5.await();
|
||||||
|
|
||||||
// log the results of the tasks, callbacks log immediately when complete
|
// log the results of the tasks, callbacks log immediately when complete
|
||||||
log("Space rocket <" + result1 + "> launch complete");
|
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result1));
|
||||||
log("Space rocket <" + result2 + "> launch complete");
|
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result2));
|
||||||
log("Space rocket <" + result3 + "> launch complete");
|
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, result3));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -102,7 +105,7 @@ public class App {
|
|||||||
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
private static <T> Callable<T> lazyval(T value, long delayMillis) {
|
||||||
return () -> {
|
return () -> {
|
||||||
Thread.sleep(delayMillis);
|
Thread.sleep(delayMillis);
|
||||||
log("Space rocket <" + value + "> launched successfully");
|
log(String.format(ROCKET_LAUNCH_LOG_PATTERN, value));
|
||||||
return value;
|
return value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -68,7 +68,7 @@ class ThreadAsyncExecutorTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.openMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,41 +23,38 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>balking</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
<artifactId>balking</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<plugin>
|
||||||
<build>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<plugins>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<plugin>
|
<executions>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<execution>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<configuration>
|
||||||
<executions>
|
<archive>
|
||||||
<execution>
|
<manifest>
|
||||||
<configuration>
|
<mainClass>com.iluwatar.balking.App</mainClass>
|
||||||
<archive>
|
</manifest>
|
||||||
<manifest>
|
</archive>
|
||||||
<mainClass>com.iluwatar.balking.App</mainClass>
|
</configuration>
|
||||||
</manifest>
|
</execution>
|
||||||
</archive>
|
</executions>
|
||||||
</configuration>
|
</plugin>
|
||||||
</execution>
|
</plugins>
|
||||||
</executions>
|
</build>
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,30 +23,27 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>bridge</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>bridge</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<dependency>
|
||||||
<scope>test</scope>
|
<groupId>org.mockito</groupId>
|
||||||
</dependency>
|
<artifactId>mockito-core</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.mockito</groupId>
|
</dependency>
|
||||||
<artifactId>mockito-core</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
@ -134,6 +134,12 @@ Use the Builder pattern when
|
|||||||
* The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
|
* The algorithm for creating a complex object should be independent of the parts that make up the object and how they're assembled
|
||||||
* The construction process must allow different representations for the object that's constructed
|
* The construction process must allow different representations for the object that's constructed
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
* [Refactoring Guru](https://refactoring.guru/design-patterns/builder)
|
||||||
|
* [Oracle Blog](https://blogs.oracle.com/javamagazine/post/exploring-joshua-blochs-builder-design-pattern-in-java)
|
||||||
|
* [Journal Dev](https://www.journaldev.com/1425/builder-design-pattern-in-java)
|
||||||
|
|
||||||
## Known uses
|
## Known uses
|
||||||
|
|
||||||
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
|
* [java.lang.StringBuilder](http://docs.oracle.com/javase/8/docs/api/java/lang/StringBuilder.html)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,25 +23,22 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>builder</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>builder</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
<build>
|
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,46 +23,43 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
<modelVersion>4.0.0</modelVersion>
|
||||||
xmlns="http://maven.apache.org/POM/4.0.0"
|
<parent>
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
<groupId>com.iluwatar</groupId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>business-delegate</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<dependencies>
|
||||||
</parent>
|
<dependency>
|
||||||
<artifactId>business-delegate</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<dependency>
|
||||||
<scope>test</scope>
|
<groupId>org.mockito</groupId>
|
||||||
</dependency>
|
<artifactId>mockito-core</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.mockito</groupId>
|
</dependency>
|
||||||
<artifactId>mockito-core</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<plugin>
|
||||||
<build>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<plugins>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<plugin>
|
<executions>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<execution>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<configuration>
|
||||||
<executions>
|
<archive>
|
||||||
<execution>
|
<manifest>
|
||||||
<configuration>
|
<mainClass>com.iluwatar.business.delegate.App</mainClass>
|
||||||
<archive>
|
</manifest>
|
||||||
<manifest>
|
</archive>
|
||||||
<mainClass>com.iluwatar.business.delegate.App</mainClass>
|
</configuration>
|
||||||
</manifest>
|
</execution>
|
||||||
</archive>
|
</executions>
|
||||||
</configuration>
|
</plugin>
|
||||||
</execution>
|
</plugins>
|
||||||
</executions>
|
</build>
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
</project>
|
</project>
|
||||||
|
@ -23,43 +23,38 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
<parent>
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<parent>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
<groupId>com.iluwatar</groupId>
|
</parent>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
</parent>
|
<artifactId>bytecode</artifactId>
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
<artifactId>bytecode</artifactId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<dependencies>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<dependency>
|
<scope>test</scope>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
</dependency>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
</dependencies>
|
||||||
<scope>test</scope>
|
<build>
|
||||||
</dependency>
|
<plugins>
|
||||||
</dependencies>
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
<build>
|
<artifactId>maven-assembly-plugin</artifactId>
|
||||||
<plugins>
|
<executions>
|
||||||
<plugin>
|
<execution>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<configuration>
|
||||||
<artifactId>maven-assembly-plugin</artifactId>
|
<archive>
|
||||||
<executions>
|
<manifest>
|
||||||
<execution>
|
<mainClass>com.iluwatar.bytecode.App</mainClass>
|
||||||
<configuration>
|
</manifest>
|
||||||
<archive>
|
</archive>
|
||||||
<manifest>
|
</configuration>
|
||||||
<mainClass>com.iluwatar.bytecode.App</mainClass>
|
</execution>
|
||||||
</manifest>
|
</executions>
|
||||||
</archive>
|
</plugin>
|
||||||
</configuration>
|
</plugins>
|
||||||
</execution>
|
</build>
|
||||||
</executions>
|
</project>
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
||||||
|
@ -42,6 +42,14 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
|
private static final String LITERAL_0 = "LITERAL 0";
|
||||||
|
private static final String HEALTH_PATTERN = "%s_HEALTH";
|
||||||
|
private static final String GET_AGILITY = "GET_AGILITY";
|
||||||
|
private static final String GET_WISDOM = "GET_WISDOM";
|
||||||
|
private static final String ADD = "ADD";
|
||||||
|
private static final String LITERAL_2 = "LITERAL 2";
|
||||||
|
private static final String DIVIDE = "DIVIDE";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Main app method.
|
* Main app method.
|
||||||
*
|
*
|
||||||
@ -53,17 +61,17 @@ public class App {
|
|||||||
new Wizard(45, 7, 11, 0, 0),
|
new Wizard(45, 7, 11, 0, 0),
|
||||||
new Wizard(36, 18, 8, 0, 0));
|
new Wizard(36, 18, 8, 0, 0));
|
||||||
|
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_HEALTH"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "GET")));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_AGILITY"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(GET_AGILITY));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 0"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_0));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("GET_WISDOM"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(GET_WISDOM));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("LITERAL 2"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(LITERAL_2));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("DIVIDE"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(DIVIDE));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("ADD"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(ADD));
|
||||||
vm.execute(InstructionConverterUtil.convertToByteCode("SET_HEALTH"));
|
vm.execute(InstructionConverterUtil.convertToByteCode(String.format(HEALTH_PATTERN, "SET")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -43,39 +43,29 @@ Wikipedia says:
|
|||||||
**Programmatic Example**
|
**Programmatic Example**
|
||||||
|
|
||||||
Let's first look at the data layer of our application. The interesting classes are `UserAccount`
|
Let's first look at the data layer of our application. The interesting classes are `UserAccount`
|
||||||
which is a simple Java object containing the user account details, and `DbManager` which handles
|
which is a simple Java object containing the user account details, and `DbManager` interface which handles
|
||||||
reading and writing of these objects to/from MongoDB database.
|
reading and writing of these objects to/from database.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Setter
|
@Data
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ToString
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
public class UserAccount {
|
public class UserAccount {
|
||||||
private String userId;
|
private String userId;
|
||||||
private String userName;
|
private String userName;
|
||||||
private String additionalInfo;
|
private String additionalInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Slf4j
|
public interface DbManager {
|
||||||
public final class DbManager {
|
|
||||||
|
|
||||||
private static MongoClient mongoClient;
|
void connect();
|
||||||
private static MongoDatabase db;
|
void disconnect();
|
||||||
|
|
||||||
private DbManager() { /*...*/ }
|
UserAccount readFromDb(String userId);
|
||||||
|
UserAccount writeToDb(UserAccount userAccount);
|
||||||
public static void createVirtualDb() { /*...*/ }
|
UserAccount updateDb(UserAccount userAccount);
|
||||||
|
UserAccount upsertDb(UserAccount userAccount);
|
||||||
public static void connect() throws ParseException { /*...*/ }
|
|
||||||
|
|
||||||
public static UserAccount readFromDb(String userId) { /*...*/ }
|
|
||||||
|
|
||||||
public static void writeToDb(UserAccount userAccount) { /*...*/ }
|
|
||||||
|
|
||||||
public static void updateDb(UserAccount userAccount) { /*...*/ }
|
|
||||||
|
|
||||||
public static void upsertDb(UserAccount userAccount) { /*...*/ }
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -171,30 +161,43 @@ strategies.
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class CacheStore {
|
public class CacheStore {
|
||||||
|
|
||||||
|
private static final int CAPACITY = 3;
|
||||||
private static LruCache cache;
|
private static LruCache cache;
|
||||||
|
private final DbManager dbManager;
|
||||||
|
|
||||||
/* ... details omitted ... */
|
/* ... details omitted ... */
|
||||||
|
|
||||||
public static UserAccount readThrough(String userId) {
|
public UserAccount readThrough(final String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Cache Hit!");
|
LOGGER.info("# Found in Cache!");
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
LOGGER.info("# Cache Miss!");
|
LOGGER.info("# Not found in cache! Go to DB!!");
|
||||||
UserAccount userAccount = DbManager.readFromDb(userId);
|
UserAccount userAccount = dbManager.readFromDb(userId);
|
||||||
cache.set(userId, userAccount);
|
cache.set(userId, userAccount);
|
||||||
return userAccount;
|
return userAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void writeThrough(UserAccount userAccount) {
|
public void writeThrough(final UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserId())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
DbManager.updateDb(userAccount);
|
dbManager.updateDb(userAccount);
|
||||||
} else {
|
} else {
|
||||||
DbManager.writeToDb(userAccount);
|
dbManager.writeToDb(userAccount);
|
||||||
}
|
}
|
||||||
cache.set(userAccount.getUserId(), userAccount);
|
cache.set(userAccount.getUserId(), userAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void writeAround(final UserAccount userAccount) {
|
||||||
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
|
dbManager.updateDb(userAccount);
|
||||||
|
// Cache data has been updated -- remove older
|
||||||
|
cache.invalidate(userAccount.getUserId());
|
||||||
|
// version from cache.
|
||||||
|
} else {
|
||||||
|
dbManager.writeToDb(userAccount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void clearCache() {
|
public static void clearCache() {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
@ -225,34 +228,39 @@ class.
|
|||||||
public final class AppManager {
|
public final class AppManager {
|
||||||
|
|
||||||
private static CachingPolicy cachingPolicy;
|
private static CachingPolicy cachingPolicy;
|
||||||
|
private final DbManager dbManager;
|
||||||
|
private final CacheStore cacheStore;
|
||||||
|
|
||||||
private AppManager() {
|
private AppManager() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void initDb(boolean useMongoDb) { /* ... */ }
|
public void initDb() { /* ... */ }
|
||||||
|
|
||||||
public static void initCachingPolicy(CachingPolicy policy) { /* ... */ }
|
public static void initCachingPolicy(CachingPolicy policy) { /* ... */ }
|
||||||
|
|
||||||
public static void initCacheCapacity(int capacity) { /* ... */ }
|
public static void initCacheCapacity(int capacity) { /* ... */ }
|
||||||
|
|
||||||
public static UserAccount find(String userId) {
|
public UserAccount find(final String userId) {
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
LOGGER.info("Trying to find {} in cache", userId);
|
||||||
return CacheStore.readThrough(userId);
|
if (cachingPolicy == CachingPolicy.THROUGH
|
||||||
|
|| cachingPolicy == CachingPolicy.AROUND) {
|
||||||
|
return cacheStore.readThrough(userId);
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
return CacheStore.readThroughWithWriteBackPolicy(userId);
|
return cacheStore.readThroughWithWriteBackPolicy(userId);
|
||||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
||||||
return findAside(userId);
|
return findAside(userId);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void save(UserAccount userAccount) {
|
public void save(final UserAccount userAccount) {
|
||||||
|
LOGGER.info("Save record!");
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH) {
|
if (cachingPolicy == CachingPolicy.THROUGH) {
|
||||||
CacheStore.writeThrough(userAccount);
|
cacheStore.writeThrough(userAccount);
|
||||||
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
||||||
CacheStore.writeAround(userAccount);
|
cacheStore.writeAround(userAccount);
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
CacheStore.writeBehind(userAccount);
|
cacheStore.writeBehind(userAccount);
|
||||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
||||||
saveAside(userAccount);
|
saveAside(userAccount);
|
||||||
}
|
}
|
||||||
@ -272,24 +280,35 @@ Here is what we do in the main class of the application.
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(final String[] args) {
|
||||||
AppManager.initDb(false);
|
boolean isDbMongo = isDbMongo(args);
|
||||||
AppManager.initCacheCapacity(3);
|
if(isDbMongo){
|
||||||
var app = new App();
|
LOGGER.info("Using the Mongo database engine to run the application.");
|
||||||
|
} else {
|
||||||
|
LOGGER.info("Using the 'in Memory' database to run the application.");
|
||||||
|
}
|
||||||
|
App app = new App(isDbMongo);
|
||||||
app.useReadAndWriteThroughStrategy();
|
app.useReadAndWriteThroughStrategy();
|
||||||
|
String splitLine = "==============================================";
|
||||||
|
LOGGER.info(splitLine);
|
||||||
app.useReadThroughAndWriteAroundStrategy();
|
app.useReadThroughAndWriteAroundStrategy();
|
||||||
|
LOGGER.info(splitLine);
|
||||||
app.useReadThroughAndWriteBehindStrategy();
|
app.useReadThroughAndWriteBehindStrategy();
|
||||||
|
LOGGER.info(splitLine);
|
||||||
app.useCacheAsideStategy();
|
app.useCacheAsideStategy();
|
||||||
|
LOGGER.info(splitLine);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void useReadAndWriteThroughStrategy() {
|
public void useReadAndWriteThroughStrategy() {
|
||||||
LOGGER.info("# CachingPolicy.THROUGH");
|
LOGGER.info("# CachingPolicy.THROUGH");
|
||||||
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
|
appManager.initCachingPolicy(CachingPolicy.THROUGH);
|
||||||
|
|
||||||
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
|
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
|
||||||
AppManager.save(userAccount1);
|
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
appManager.save(userAccount1);
|
||||||
AppManager.find("001");
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("001");
|
appManager.find("001");
|
||||||
|
appManager.find("001");
|
||||||
}
|
}
|
||||||
|
|
||||||
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
||||||
@ -300,16 +319,6 @@ public class App {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
Finally, here is some of the console output from the program.
|
|
||||||
|
|
||||||
```
|
|
||||||
12:32:53.845 [main] INFO com.iluwatar.caching.App - # CachingPolicy.THROUGH
|
|
||||||
12:32:53.900 [main] INFO com.iluwatar.caching.App -
|
|
||||||
--CACHE CONTENT--
|
|
||||||
UserAccount(userId=001, userName=John, additionalInfo=He is a boy.)
|
|
||||||
----
|
|
||||||
```
|
|
||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
11
caching/docker-compose.yml
Normal file
11
caching/docker-compose.yml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
version: '3.7'
|
||||||
|
services:
|
||||||
|
mongodb_container:
|
||||||
|
image: mongo:latest
|
||||||
|
environment:
|
||||||
|
MONGO_INITDB_ROOT_USERNAME: root
|
||||||
|
MONGO_INITDB_ROOT_PASSWORD: rootpassword
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
volumes:
|
||||||
|
- ./mongo-data/:/data/db
|
Binary file not shown.
Before Width: | Height: | Size: 112 KiB |
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,13 +23,12 @@
|
|||||||
THE SOFTWARE.
|
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"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>caching</artifactId>
|
<artifactId>caching</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
@ -39,19 +38,14 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mockito</groupId>
|
||||||
<artifactId>mongodb-driver</artifactId>
|
<artifactId>mockito-all</artifactId>
|
||||||
<version>3.12.1</version>
|
<version>1.10.19</version>
|
||||||
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
<artifactId>mongodb-driver-core</artifactId>
|
<artifactId>mongo-java-driver</artifactId>
|
||||||
<version>3.0.4</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mongodb</groupId>
|
|
||||||
<artifactId>bson</artifactId>
|
|
||||||
<version>3.0.4</version>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<!--
|
<!--
|
||||||
|
@ -1,58 +1,62 @@
|
|||||||
/*
|
|
||||||
* 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.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
|
import com.iluwatar.caching.database.DbManager;
|
||||||
|
import com.iluwatar.caching.database.DbManagerFactory;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
|
* The Caching pattern describes how to avoid expensive re-acquisition of
|
||||||
* the resources immediately after their use. The resources retain their identity, are kept in some
|
* resources by not releasing the resources immediately after their use.
|
||||||
* fast-access storage, and are re-used to avoid having to acquire them again. There are four main
|
* The resources retain their identity, are kept in some fast-access storage,
|
||||||
* caching strategies/techniques in this pattern; each with their own pros and cons. They are;
|
* and are re-used to avoid having to acquire them again. There are four main
|
||||||
* <code>write-through</code> which writes data to the cache and DB in a single transaction,
|
* caching strategies/techniques in this pattern; each with their own pros and
|
||||||
* <code>write-around</code> which writes data immediately into the DB instead of the cache,
|
* cons. They are <code>write-through</code> which writes data to the cache and
|
||||||
* <code>write-behind</code> which writes data into the cache initially whilst the data is only
|
* DB in a single transaction, <code>write-around</code> which writes data
|
||||||
* written into the DB when the cache is full, and <code>cache-aside</code> which pushes the
|
* immediately into the DB instead of the cache, <code>write-behind</code>
|
||||||
* responsibility of keeping the data synchronized in both data sources to the application itself.
|
* which writes data into the cache initially whilst the data is only
|
||||||
* The <code>read-through</code> strategy is also included in the mentioned four strategies --
|
* written into the DB when the cache is full, and <code>cache-aside</code>
|
||||||
* returns data from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and
|
* which pushes the responsibility of keeping the data synchronized in both
|
||||||
* stores it into the cache for future use. These strategies determine when the data in the cache
|
* data sources to the application itself. The <code>read-through</code>
|
||||||
* should be written back to the backing store (i.e. Database) and help keep both data sources
|
* strategy is also included in the mentioned four strategies --
|
||||||
* synchronized/up-to-date. This pattern can improve performance and also helps to maintain
|
* returns data from the cache to the caller <b>if</b> it exists <b>else</b>
|
||||||
* consistency between data held in the cache and the data in the underlying data store.
|
* queries from DB and stores it into the cache for future use. These strategies
|
||||||
|
* determine when the data in the cache should be written back to the backing
|
||||||
|
* store (i.e. Database) and help keep both data sources
|
||||||
|
* synchronized/up-to-date. This pattern can improve performance and also helps
|
||||||
|
* to maintainconsistency between data held in the cache and the data in
|
||||||
|
* the underlying data store.
|
||||||
*
|
*
|
||||||
* <p>In this example, the user account ({@link UserAccount}) entity is used as the underlying
|
* <p>In this example, the user account ({@link UserAccount}) entity is used
|
||||||
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
|
* as the underlying application data. The cache itself is implemented as an
|
||||||
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four
|
* internal (Java) data structure. It adopts a Least-Recently-Used (LRU)
|
||||||
* strategies are individually tested. The testing of the cache is restricted towards saving and
|
* strategy for evicting data from itself when its full. The four
|
||||||
* querying of user accounts from the underlying data store ( {@link DbManager}). The main class (
|
* strategies are individually tested. The testing of the cache is restricted
|
||||||
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
|
* towards saving and querying of user accounts from the
|
||||||
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
|
* underlying data store( {@link DbManager}). The main class ( {@link App}
|
||||||
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
|
* is not aware of the underlying mechanics of the application
|
||||||
* (depending on the preferred caching policy/strategy).
|
* (i.e. save and query) and whether the data is coming from the cache or the
|
||||||
|
* DB (i.e. separation of concern). The AppManager ({@link AppManager}) handles
|
||||||
|
* the transaction of data to-and-from the underlying data store (depending on
|
||||||
|
* the preferred caching policy/strategy).
|
||||||
* <p>
|
* <p>
|
||||||
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i>
|
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy -->
|
||||||
|
* DBManager} </i>
|
||||||
|
* </p>
|
||||||
|
*
|
||||||
|
* <p>
|
||||||
|
* There are 2 ways to launch the application.
|
||||||
|
* - to use "in Memory" database.
|
||||||
|
* - to use the MongoDb as a database
|
||||||
|
*
|
||||||
|
* To run the application with "in Memory" database, just launch it without parameters
|
||||||
|
* Example: 'java -jar app.jar'
|
||||||
|
*
|
||||||
|
* To run the application with MongoDb you need to be installed the MongoDb
|
||||||
|
* in your system, or to launch it in the docker container.
|
||||||
|
* You may launch docker container from the root of current module with command:
|
||||||
|
* 'docker-compose up'
|
||||||
|
* Then you can start the application with parameter --mongo
|
||||||
|
* Example: 'java -jar app.jar --mongo'
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
* @see CacheStore
|
* @see CacheStore
|
||||||
@ -61,23 +65,67 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
/**
|
||||||
|
* Constant parameter name to use mongoDB.
|
||||||
|
*/
|
||||||
|
private static final String USE_MONGO_DB = "--mongo";
|
||||||
|
/**
|
||||||
|
* Application manager.
|
||||||
|
*/
|
||||||
|
private final AppManager appManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor of current App.
|
||||||
|
*
|
||||||
|
* @param isMongo boolean
|
||||||
|
*/
|
||||||
|
public App(final boolean isMongo) {
|
||||||
|
DbManager dbManager = DbManagerFactory.initDb(isMongo);
|
||||||
|
appManager = new AppManager(dbManager);
|
||||||
|
appManager.initDb();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Program entry point.
|
* Program entry point.
|
||||||
*
|
*
|
||||||
* @param args command line args
|
* @param args command line args
|
||||||
*/
|
*/
|
||||||
public static void main(String[] args) {
|
public static void main(final String[] args) {
|
||||||
AppManager.initDb(false); // VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
// VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
||||||
// and the App class to avoid Maven compilation errors. Set flag to
|
// and the App class to avoid Maven compilation errors. Set flag to
|
||||||
// true to run the tests with MongoDB (provided that MongoDB is
|
// true to run the tests with MongoDB (provided that MongoDB is
|
||||||
// installed and socket connection is open).
|
// installed and socket connection is open).
|
||||||
AppManager.initCacheCapacity(3);
|
boolean isDbMongo = isDbMongo(args);
|
||||||
var app = new App();
|
if (isDbMongo) {
|
||||||
|
LOGGER.info("Using the Mongo database engine to run the application.");
|
||||||
|
} else {
|
||||||
|
LOGGER.info("Using the 'in Memory' database to run the application.");
|
||||||
|
}
|
||||||
|
App app = new App(isDbMongo);
|
||||||
app.useReadAndWriteThroughStrategy();
|
app.useReadAndWriteThroughStrategy();
|
||||||
|
String splitLine = "==============================================";
|
||||||
|
LOGGER.info(splitLine);
|
||||||
app.useReadThroughAndWriteAroundStrategy();
|
app.useReadThroughAndWriteAroundStrategy();
|
||||||
|
LOGGER.info(splitLine);
|
||||||
app.useReadThroughAndWriteBehindStrategy();
|
app.useReadThroughAndWriteBehindStrategy();
|
||||||
|
LOGGER.info(splitLine);
|
||||||
app.useCacheAsideStategy();
|
app.useCacheAsideStategy();
|
||||||
|
LOGGER.info(splitLine);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the input parameters. if
|
||||||
|
*
|
||||||
|
* @param args input params
|
||||||
|
* @return true if there is "--mongo" parameter in arguments
|
||||||
|
*/
|
||||||
|
private static boolean isDbMongo(final String[] args) {
|
||||||
|
for (String arg : args) {
|
||||||
|
if (arg.equals(USE_MONGO_DB)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,14 +133,14 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public void useReadAndWriteThroughStrategy() {
|
public void useReadAndWriteThroughStrategy() {
|
||||||
LOGGER.info("# CachingPolicy.THROUGH");
|
LOGGER.info("# CachingPolicy.THROUGH");
|
||||||
AppManager.initCachingPolicy(CachingPolicy.THROUGH);
|
appManager.initCachingPolicy(CachingPolicy.THROUGH);
|
||||||
|
|
||||||
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
|
var userAccount1 = new UserAccount("001", "John", "He is a boy.");
|
||||||
|
|
||||||
AppManager.save(userAccount1);
|
appManager.save(userAccount1);
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("001");
|
appManager.find("001");
|
||||||
AppManager.find("001");
|
appManager.find("001");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -100,21 +148,21 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public void useReadThroughAndWriteAroundStrategy() {
|
public void useReadThroughAndWriteAroundStrategy() {
|
||||||
LOGGER.info("# CachingPolicy.AROUND");
|
LOGGER.info("# CachingPolicy.AROUND");
|
||||||
AppManager.initCachingPolicy(CachingPolicy.AROUND);
|
appManager.initCachingPolicy(CachingPolicy.AROUND);
|
||||||
|
|
||||||
var userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
|
var userAccount2 = new UserAccount("002", "Jane", "She is a girl.");
|
||||||
|
|
||||||
AppManager.save(userAccount2);
|
appManager.save(userAccount2);
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("002");
|
appManager.find("002");
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
userAccount2 = AppManager.find("002");
|
userAccount2 = appManager.find("002");
|
||||||
userAccount2.setUserName("Jane G.");
|
userAccount2.setUserName("Jane G.");
|
||||||
AppManager.save(userAccount2);
|
appManager.save(userAccount2);
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("002");
|
appManager.find("002");
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("002");
|
appManager.find("002");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -122,23 +170,31 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public void useReadThroughAndWriteBehindStrategy() {
|
public void useReadThroughAndWriteBehindStrategy() {
|
||||||
LOGGER.info("# CachingPolicy.BEHIND");
|
LOGGER.info("# CachingPolicy.BEHIND");
|
||||||
AppManager.initCachingPolicy(CachingPolicy.BEHIND);
|
appManager.initCachingPolicy(CachingPolicy.BEHIND);
|
||||||
|
|
||||||
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
var userAccount3 = new UserAccount("003",
|
||||||
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
"Adam",
|
||||||
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
"He likes food.");
|
||||||
|
var userAccount4 = new UserAccount("004",
|
||||||
|
"Rita",
|
||||||
|
"She hates cats.");
|
||||||
|
var userAccount5 = new UserAccount("005",
|
||||||
|
"Isaac",
|
||||||
|
"He is allergic to mustard.");
|
||||||
|
|
||||||
AppManager.save(userAccount3);
|
appManager.save(userAccount3);
|
||||||
AppManager.save(userAccount4);
|
appManager.save(userAccount4);
|
||||||
AppManager.save(userAccount5);
|
appManager.save(userAccount5);
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("003");
|
appManager.find("003");
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
|
UserAccount userAccount6 = new UserAccount("006",
|
||||||
AppManager.save(userAccount6);
|
"Yasha",
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
"She is an only child.");
|
||||||
AppManager.find("004");
|
appManager.save(userAccount6);
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
|
appManager.find("004");
|
||||||
|
LOGGER.info(appManager.printCacheContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -146,20 +202,26 @@ public class App {
|
|||||||
*/
|
*/
|
||||||
public void useCacheAsideStategy() {
|
public void useCacheAsideStategy() {
|
||||||
LOGGER.info("# CachingPolicy.ASIDE");
|
LOGGER.info("# CachingPolicy.ASIDE");
|
||||||
AppManager.initCachingPolicy(CachingPolicy.ASIDE);
|
appManager.initCachingPolicy(CachingPolicy.ASIDE);
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
|
|
||||||
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
var userAccount3 = new UserAccount("003",
|
||||||
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
"Adam",
|
||||||
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
"He likes food.");
|
||||||
AppManager.save(userAccount3);
|
var userAccount4 = new UserAccount("004",
|
||||||
AppManager.save(userAccount4);
|
"Rita",
|
||||||
AppManager.save(userAccount5);
|
"She hates cats.");
|
||||||
|
var userAccount5 = new UserAccount("005",
|
||||||
|
"Isaac",
|
||||||
|
"He is allergic to mustard.");
|
||||||
|
appManager.save(userAccount3);
|
||||||
|
appManager.save(userAccount4);
|
||||||
|
appManager.save(userAccount5);
|
||||||
|
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("003");
|
appManager.find("003");
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
AppManager.find("004");
|
appManager.find("004");
|
||||||
LOGGER.info(AppManager.printCacheContent());
|
LOGGER.info(appManager.printCacheContent());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,65 +23,80 @@
|
|||||||
|
|
||||||
package com.iluwatar.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
import java.text.ParseException;
|
import com.iluwatar.caching.database.DbManager;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* AppManager helps to bridge the gap in communication between the main class and the application's
|
* AppManager helps to bridge the gap in communication between the main class
|
||||||
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
|
* and the application's back-end. DB connection is initialized through this
|
||||||
* also initialized here. Before the cache can be used, the size of the cache has to be set.
|
* class. The chosen caching strategy/policy is also initialized here.
|
||||||
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
|
* Before the cache can be used, the size of the cache has to be set.
|
||||||
* CacheStore class.
|
* Depending on the chosen caching policy, AppManager will call the
|
||||||
|
* appropriate function in the CacheStore class.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public final class AppManager {
|
public class AppManager {
|
||||||
|
/**
|
||||||
|
* Caching Policy.
|
||||||
|
*/
|
||||||
|
private CachingPolicy cachingPolicy;
|
||||||
|
/**
|
||||||
|
* Database Manager.
|
||||||
|
*/
|
||||||
|
private final DbManager dbManager;
|
||||||
|
/**
|
||||||
|
* Cache Store.
|
||||||
|
*/
|
||||||
|
private final CacheStore cacheStore;
|
||||||
|
|
||||||
private static CachingPolicy cachingPolicy;
|
/**
|
||||||
|
* Constructor.
|
||||||
private AppManager() {
|
*
|
||||||
|
* @param newDbManager database manager
|
||||||
|
*/
|
||||||
|
public AppManager(final DbManager newDbManager) {
|
||||||
|
this.dbManager = newDbManager;
|
||||||
|
this.cacheStore = new CacheStore(newDbManager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
|
* Developer/Tester is able to choose whether the application should use
|
||||||
* data storage or a simple Java data structure to (temporarily) store the data/objects during
|
* MongoDB as its underlying data storage or a simple Java data structure
|
||||||
* runtime.
|
* to (temporarily) store the data/objects during runtime.
|
||||||
*/
|
*/
|
||||||
public static void initDb(boolean useMongoDb) {
|
public void initDb() {
|
||||||
if (useMongoDb) {
|
dbManager.connect();
|
||||||
try {
|
|
||||||
DbManager.connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOGGER.error("Error connecting to MongoDB", e);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DbManager.createVirtualDb();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize caching policy.
|
* Initialize caching policy.
|
||||||
|
*
|
||||||
|
* @param policy is a {@link CachingPolicy}
|
||||||
*/
|
*/
|
||||||
public static void initCachingPolicy(CachingPolicy policy) {
|
public void initCachingPolicy(final CachingPolicy policy) {
|
||||||
cachingPolicy = policy;
|
cachingPolicy = policy;
|
||||||
if (cachingPolicy == CachingPolicy.BEHIND) {
|
if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache));
|
Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache));
|
||||||
}
|
}
|
||||||
CacheStore.clearCache();
|
cacheStore.clearCache();
|
||||||
}
|
|
||||||
|
|
||||||
public static void initCacheCapacity(int capacity) {
|
|
||||||
CacheStore.initCapacity(capacity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Find user account.
|
* Find user account.
|
||||||
|
*
|
||||||
|
* @param userId String
|
||||||
|
* @return {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static UserAccount find(String userId) {
|
public UserAccount find(final String userId) {
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
LOGGER.info("Trying to find {} in cache", userId);
|
||||||
return CacheStore.readThrough(userId);
|
if (cachingPolicy == CachingPolicy.THROUGH
|
||||||
|
|| cachingPolicy == CachingPolicy.AROUND) {
|
||||||
|
return cacheStore.readThrough(userId);
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
return CacheStore.readThroughWithWriteBackPolicy(userId);
|
return cacheStore.readThroughWithWriteBackPolicy(userId);
|
||||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
||||||
return findAside(userId);
|
return findAside(userId);
|
||||||
}
|
}
|
||||||
@ -90,41 +105,55 @@ public final class AppManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Save user account.
|
* Save user account.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static void save(UserAccount userAccount) {
|
public void save(final UserAccount userAccount) {
|
||||||
|
LOGGER.info("Save record!");
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH) {
|
if (cachingPolicy == CachingPolicy.THROUGH) {
|
||||||
CacheStore.writeThrough(userAccount);
|
cacheStore.writeThrough(userAccount);
|
||||||
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
} else if (cachingPolicy == CachingPolicy.AROUND) {
|
||||||
CacheStore.writeAround(userAccount);
|
cacheStore.writeAround(userAccount);
|
||||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||||
CacheStore.writeBehind(userAccount);
|
cacheStore.writeBehind(userAccount);
|
||||||
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
} else if (cachingPolicy == CachingPolicy.ASIDE) {
|
||||||
saveAside(userAccount);
|
saveAside(userAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String printCacheContent() {
|
/**
|
||||||
return CacheStore.print();
|
* Returns String.
|
||||||
|
*
|
||||||
|
* @return String
|
||||||
|
*/
|
||||||
|
public String printCacheContent() {
|
||||||
|
return cacheStore.print();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache-Aside save user account helper.
|
* Cache-Aside save user account helper.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
private static void saveAside(UserAccount userAccount) {
|
private void saveAside(final UserAccount userAccount) {
|
||||||
DbManager.updateDb(userAccount);
|
dbManager.updateDb(userAccount);
|
||||||
CacheStore.invalidate(userAccount.getUserId());
|
cacheStore.invalidate(userAccount.getUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cache-Aside find user account helper.
|
* Cache-Aside find user account helper.
|
||||||
|
*
|
||||||
|
* @param userId String
|
||||||
|
* @return {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
private static UserAccount findAside(String userId) {
|
private UserAccount findAside(final String userId) {
|
||||||
return Optional.ofNullable(CacheStore.get(userId))
|
return Optional.ofNullable(cacheStore.get(userId))
|
||||||
.or(() -> {
|
.or(() -> {
|
||||||
Optional<UserAccount> userAccount = Optional.ofNullable(DbManager.readFromDb(userId));
|
Optional<UserAccount> userAccount =
|
||||||
userAccount.ifPresent(account -> CacheStore.set(userId, account));
|
Optional.ofNullable(dbManager.readFromDb(userId));
|
||||||
return userAccount;
|
userAccount.ifPresent(account -> cacheStore.set(userId, account));
|
||||||
})
|
return userAccount;
|
||||||
.orElse(null);
|
})
|
||||||
|
.orElse(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,9 +23,11 @@
|
|||||||
|
|
||||||
package com.iluwatar.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
|
import com.iluwatar.caching.database.DbManager;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -33,16 +35,34 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class CacheStore {
|
public class CacheStore {
|
||||||
|
/**
|
||||||
|
* Cache capacity.
|
||||||
|
*/
|
||||||
|
private static final int CAPACITY = 3;
|
||||||
|
|
||||||
private static LruCache cache;
|
/**
|
||||||
|
* Lru cache see {@link LruCache}.
|
||||||
|
*/
|
||||||
|
private LruCache cache;
|
||||||
|
/**
|
||||||
|
* DbManager.
|
||||||
|
*/
|
||||||
|
private final DbManager dbManager;
|
||||||
|
|
||||||
private CacheStore() {
|
/**
|
||||||
|
* Cache Store.
|
||||||
|
* @param dataBaseManager {@link DbManager}
|
||||||
|
*/
|
||||||
|
public CacheStore(final DbManager dataBaseManager) {
|
||||||
|
this.dbManager = dataBaseManager;
|
||||||
|
initCapacity(CAPACITY);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Init cache capacity.
|
* Init cache capacity.
|
||||||
|
* @param capacity int
|
||||||
*/
|
*/
|
||||||
public static void initCapacity(int capacity) {
|
public void initCapacity(final int capacity) {
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
cache = new LruCache(capacity);
|
cache = new LruCache(capacity);
|
||||||
} else {
|
} else {
|
||||||
@ -52,57 +72,64 @@ public class CacheStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user account using read-through cache.
|
* Get user account using read-through cache.
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static UserAccount readThrough(String userId) {
|
public UserAccount readThrough(final String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Cache Hit!");
|
LOGGER.info("# Found in Cache!");
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
LOGGER.info("# Cache Miss!");
|
LOGGER.info("# Not found in cache! Go to DB!!");
|
||||||
UserAccount userAccount = DbManager.readFromDb(userId);
|
UserAccount userAccount = dbManager.readFromDb(userId);
|
||||||
cache.set(userId, userAccount);
|
cache.set(userId, userAccount);
|
||||||
return userAccount;
|
return userAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user account using write-through cache.
|
* Get user account using write-through cache.
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static void writeThrough(UserAccount userAccount) {
|
public void writeThrough(final UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserId())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
DbManager.updateDb(userAccount);
|
dbManager.updateDb(userAccount);
|
||||||
} else {
|
} else {
|
||||||
DbManager.writeToDb(userAccount);
|
dbManager.writeToDb(userAccount);
|
||||||
}
|
}
|
||||||
cache.set(userAccount.getUserId(), userAccount);
|
cache.set(userAccount.getUserId(), userAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user account using write-around cache.
|
* Get user account using write-around cache.
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static void writeAround(UserAccount userAccount) {
|
public void writeAround(final UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserId())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
DbManager.updateDb(userAccount);
|
dbManager.updateDb(userAccount);
|
||||||
cache.invalidate(userAccount.getUserId()); // Cache data has been updated -- remove older
|
// Cache data has been updated -- remove older
|
||||||
|
cache.invalidate(userAccount.getUserId());
|
||||||
// version from cache.
|
// version from cache.
|
||||||
} else {
|
} else {
|
||||||
DbManager.writeToDb(userAccount);
|
dbManager.writeToDb(userAccount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user account using read-through cache with write-back policy.
|
* Get user account using read-through cache with write-back policy.
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static UserAccount readThroughWithWriteBackPolicy(String userId) {
|
public UserAccount readThroughWithWriteBackPolicy(final String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Cache Hit!");
|
LOGGER.info("# Found in cache!");
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
LOGGER.info("# Cache Miss!");
|
LOGGER.info("# Not found in Cache!");
|
||||||
UserAccount userAccount = DbManager.readFromDb(userId);
|
UserAccount userAccount = dbManager.readFromDb(userId);
|
||||||
if (cache.isFull()) {
|
if (cache.isFull()) {
|
||||||
LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
|
LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
|
||||||
UserAccount toBeWrittenToDb = cache.getLruData();
|
UserAccount toBeWrittenToDb = cache.getLruData();
|
||||||
DbManager.upsertDb(toBeWrittenToDb);
|
dbManager.upsertDb(toBeWrittenToDb);
|
||||||
}
|
}
|
||||||
cache.set(userId, userAccount);
|
cache.set(userId, userAccount);
|
||||||
return userAccount;
|
return userAccount;
|
||||||
@ -110,12 +137,13 @@ public class CacheStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set user account.
|
* Set user account.
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static void writeBehind(UserAccount userAccount) {
|
public void writeBehind(final UserAccount userAccount) {
|
||||||
if (cache.isFull() && !cache.contains(userAccount.getUserId())) {
|
if (cache.isFull() && !cache.contains(userAccount.getUserId())) {
|
||||||
LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
|
LOGGER.info("# Cache is FULL! Writing LRU data to DB...");
|
||||||
UserAccount toBeWrittenToDb = cache.getLruData();
|
UserAccount toBeWrittenToDb = cache.getLruData();
|
||||||
DbManager.upsertDb(toBeWrittenToDb);
|
dbManager.upsertDb(toBeWrittenToDb);
|
||||||
}
|
}
|
||||||
cache.set(userAccount.getUserId(), userAccount);
|
cache.set(userAccount.getUserId(), userAccount);
|
||||||
}
|
}
|
||||||
@ -123,7 +151,7 @@ public class CacheStore {
|
|||||||
/**
|
/**
|
||||||
* Clears cache.
|
* Clears cache.
|
||||||
*/
|
*/
|
||||||
public static void clearCache() {
|
public void clearCache() {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
@ -132,44 +160,51 @@ public class CacheStore {
|
|||||||
/**
|
/**
|
||||||
* Writes remaining content in the cache into the DB.
|
* Writes remaining content in the cache into the DB.
|
||||||
*/
|
*/
|
||||||
public static void flushCache() {
|
public void flushCache() {
|
||||||
LOGGER.info("# flushCache...");
|
LOGGER.info("# flushCache...");
|
||||||
Optional.ofNullable(cache)
|
Optional.ofNullable(cache)
|
||||||
.map(LruCache::getCacheDataInListForm)
|
.map(LruCache::getCacheDataInListForm)
|
||||||
.orElse(List.of())
|
.orElse(List.of())
|
||||||
.forEach(DbManager::updateDb);
|
.forEach(dbManager::updateDb);
|
||||||
|
dbManager.disconnect();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Print user accounts.
|
* Print user accounts.
|
||||||
|
* @return {@link String}
|
||||||
*/
|
*/
|
||||||
public static String print() {
|
public String print() {
|
||||||
return Optional.ofNullable(cache)
|
return Optional.ofNullable(cache)
|
||||||
.map(LruCache::getCacheDataInListForm)
|
.map(LruCache::getCacheDataInListForm)
|
||||||
.orElse(List.of())
|
.orElse(List.of())
|
||||||
.stream()
|
.stream()
|
||||||
.map(userAccount -> userAccount.toString() + "\n")
|
.map(userAccount -> userAccount.toString() + "\n")
|
||||||
.collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----\n"));
|
.collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate to backing cache store.
|
* Delegate to backing cache store.
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static UserAccount get(String userId) {
|
public UserAccount get(final String userId) {
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate to backing cache store.
|
* Delegate to backing cache store.
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public static void set(String userId, UserAccount userAccount) {
|
public void set(final String userId, final UserAccount userAccount) {
|
||||||
cache.set(userId, userAccount);
|
cache.set(userId, userAccount);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate to backing cache store.
|
* Delegate to backing cache store.
|
||||||
|
* @param userId {@link String}
|
||||||
*/
|
*/
|
||||||
public static void invalidate(String userId) {
|
public void invalidate(final String userId) {
|
||||||
cache.invalidate(userId);
|
cache.invalidate(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,10 +32,25 @@ import lombok.Getter;
|
|||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@Getter
|
@Getter
|
||||||
public enum CachingPolicy {
|
public enum CachingPolicy {
|
||||||
|
/**
|
||||||
|
* Through.
|
||||||
|
*/
|
||||||
THROUGH("through"),
|
THROUGH("through"),
|
||||||
|
/**
|
||||||
|
* AROUND.
|
||||||
|
*/
|
||||||
AROUND("around"),
|
AROUND("around"),
|
||||||
|
/**
|
||||||
|
* BEHIND.
|
||||||
|
*/
|
||||||
BEHIND("behind"),
|
BEHIND("behind"),
|
||||||
|
/**
|
||||||
|
* ASIDE.
|
||||||
|
*/
|
||||||
ASIDE("aside");
|
ASIDE("aside");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Policy value.
|
||||||
|
*/
|
||||||
private final String policy;
|
private final String policy;
|
||||||
}
|
}
|
||||||
|
@ -1,171 +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.caching;
|
|
||||||
|
|
||||||
import com.iluwatar.caching.constants.CachingConstants;
|
|
||||||
import com.mongodb.MongoClient;
|
|
||||||
import com.mongodb.client.MongoDatabase;
|
|
||||||
import com.mongodb.client.model.UpdateOptions;
|
|
||||||
import java.text.ParseException;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
|
||||||
import org.bson.Document;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>DBManager handles the communication with the underlying data store i.e. Database. It contains
|
|
||||||
* the implemented methods for querying, inserting, and updating data. MongoDB was used as the
|
|
||||||
* database for the application.</p>
|
|
||||||
*
|
|
||||||
* <p>Developer/Tester is able to choose whether the application should use MongoDB as its
|
|
||||||
* underlying data storage (connect()) or a simple Java data structure to (temporarily) store the
|
|
||||||
* data/objects during runtime (createVirtualDB()).</p>
|
|
||||||
*/
|
|
||||||
@Slf4j
|
|
||||||
public final class DbManager {
|
|
||||||
|
|
||||||
private static MongoClient mongoClient;
|
|
||||||
private static MongoDatabase db;
|
|
||||||
private static boolean useMongoDB;
|
|
||||||
|
|
||||||
private static Map<String, UserAccount> virtualDB;
|
|
||||||
|
|
||||||
private DbManager() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create DB.
|
|
||||||
*/
|
|
||||||
public static void createVirtualDb() {
|
|
||||||
useMongoDB = false;
|
|
||||||
virtualDB = new HashMap<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Connect to DB.
|
|
||||||
*/
|
|
||||||
public static void connect() throws ParseException {
|
|
||||||
useMongoDB = true;
|
|
||||||
mongoClient = new MongoClient();
|
|
||||||
db = mongoClient.getDatabase("test");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read user account from DB.
|
|
||||||
*/
|
|
||||||
public static UserAccount readFromDb(String userId) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
if (virtualDB.containsKey(userId)) {
|
|
||||||
return virtualDB.get(userId);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (db == null) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOGGER.error("Error connecting to MongoDB", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var iterable = db
|
|
||||||
.getCollection(CachingConstants.USER_ACCOUNT)
|
|
||||||
.find(new Document(CachingConstants.USER_ID, userId));
|
|
||||||
if (iterable == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
Document doc = iterable.first();
|
|
||||||
String userName = doc.getString(CachingConstants.USER_NAME);
|
|
||||||
String appInfo = doc.getString(CachingConstants.ADD_INFO);
|
|
||||||
return new UserAccount(userId, userName, appInfo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Write user account to DB.
|
|
||||||
*/
|
|
||||||
public static void writeToDb(UserAccount userAccount) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
virtualDB.put(userAccount.getUserId(), userAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (db == null) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOGGER.error("Error connecting to MongoDB", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.getCollection(CachingConstants.USER_ACCOUNT).insertOne(
|
|
||||||
new Document(CachingConstants.USER_ID, userAccount.getUserId())
|
|
||||||
.append(CachingConstants.USER_NAME, userAccount.getUserName())
|
|
||||||
.append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update DB.
|
|
||||||
*/
|
|
||||||
public static void updateDb(UserAccount userAccount) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
virtualDB.put(userAccount.getUserId(), userAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (db == null) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOGGER.error("Error connecting to MongoDB", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
|
|
||||||
new Document(CachingConstants.USER_ID, userAccount.getUserId()),
|
|
||||||
new Document("$set", new Document(CachingConstants.USER_NAME, userAccount.getUserName())
|
|
||||||
.append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Insert data into DB if it does not exist. Else, update it.
|
|
||||||
*/
|
|
||||||
public static void upsertDb(UserAccount userAccount) {
|
|
||||||
if (!useMongoDB) {
|
|
||||||
virtualDB.put(userAccount.getUserId(), userAccount);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (db == null) {
|
|
||||||
try {
|
|
||||||
connect();
|
|
||||||
} catch (ParseException e) {
|
|
||||||
LOGGER.error("Error connecting to MongoDB", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
|
|
||||||
new Document(CachingConstants.USER_ID, userAccount.getUserId()),
|
|
||||||
new Document("$set",
|
|
||||||
new Document(CachingConstants.USER_ID, userAccount.getUserId())
|
|
||||||
.append(CachingConstants.USER_NAME, userAccount.getUserName())
|
|
||||||
.append(CachingConstants.ADD_INFO, userAccount.getAdditionalInfo())
|
|
||||||
),
|
|
||||||
new UpdateOptions().upsert(true)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
@ -29,41 +29,83 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data structure/implementation of the application's cache. The data structure consists of a hash
|
* Data structure/implementation of the application's cache. The data structure
|
||||||
* table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
|
* consists of a hash table attached with a doubly linked-list. The linked-list
|
||||||
* LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
|
* helps in capturing and maintaining the LRU data in the cache. When a data is
|
||||||
* the data is moved to the front of the list to depict itself as the most-recently-used data. The
|
* queried (from the cache), added (to the cache), or updated, the data is
|
||||||
* LRU data is always at the end of the list.
|
* moved to the front of the list to depict itself as the most-recently-used
|
||||||
|
* data. The LRU data is always at the end of the list.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class LruCache {
|
public class LruCache {
|
||||||
|
/**
|
||||||
|
* Static class Node.
|
||||||
|
*/
|
||||||
static class Node {
|
static class Node {
|
||||||
String userId;
|
/**
|
||||||
UserAccount userAccount;
|
* user id.
|
||||||
Node previous;
|
*/
|
||||||
Node next;
|
private final String userId;
|
||||||
|
/**
|
||||||
|
* User Account.
|
||||||
|
*/
|
||||||
|
private UserAccount userAccount;
|
||||||
|
/**
|
||||||
|
* previous.
|
||||||
|
*/
|
||||||
|
private Node previous;
|
||||||
|
/**
|
||||||
|
* next.
|
||||||
|
*/
|
||||||
|
private Node next;
|
||||||
|
|
||||||
public Node(String userId, UserAccount userAccount) {
|
/**
|
||||||
this.userId = userId;
|
* Node definition.
|
||||||
this.userAccount = userAccount;
|
*
|
||||||
|
* @param id String
|
||||||
|
* @param account {@link UserAccount}
|
||||||
|
*/
|
||||||
|
Node(final String id, final UserAccount account) {
|
||||||
|
this.userId = id;
|
||||||
|
this.userAccount = account;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int capacity;
|
/**
|
||||||
Map<String, Node> cache = new HashMap<>();
|
* Capacity of Cache.
|
||||||
Node head;
|
*/
|
||||||
Node end;
|
private int capacity;
|
||||||
|
/**
|
||||||
|
* Cache {@link HashMap}.
|
||||||
|
*/
|
||||||
|
private Map<String, Node> cache = new HashMap<>();
|
||||||
|
/**
|
||||||
|
* Head.
|
||||||
|
*/
|
||||||
|
private Node head;
|
||||||
|
/**
|
||||||
|
* End.
|
||||||
|
*/
|
||||||
|
private Node end;
|
||||||
|
|
||||||
public LruCache(int capacity) {
|
/**
|
||||||
this.capacity = capacity;
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param cap Integer.
|
||||||
|
*/
|
||||||
|
public LruCache(final int cap) {
|
||||||
|
this.capacity = cap;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get user account.
|
* Get user account.
|
||||||
|
*
|
||||||
|
* @param userId String
|
||||||
|
* @return {@link UserAccount}
|
||||||
*/
|
*/
|
||||||
public UserAccount get(String userId) {
|
public UserAccount get(final String userId) {
|
||||||
if (cache.containsKey(userId)) {
|
if (cache.containsKey(userId)) {
|
||||||
var node = cache.get(userId);
|
var node = cache.get(userId);
|
||||||
remove(node);
|
remove(node);
|
||||||
@ -75,8 +117,10 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove node from linked list.
|
* Remove node from linked list.
|
||||||
|
*
|
||||||
|
* @param node {@link Node}
|
||||||
*/
|
*/
|
||||||
public void remove(Node node) {
|
public void remove(final Node node) {
|
||||||
if (node.previous != null) {
|
if (node.previous != null) {
|
||||||
node.previous.next = node.next;
|
node.previous.next = node.next;
|
||||||
} else {
|
} else {
|
||||||
@ -91,8 +135,10 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Move node to the front of the list.
|
* Move node to the front of the list.
|
||||||
|
*
|
||||||
|
* @param node {@link Node}
|
||||||
*/
|
*/
|
||||||
public void setHead(Node node) {
|
public void setHead(final Node node) {
|
||||||
node.next = head;
|
node.next = head;
|
||||||
node.previous = null;
|
node.previous = null;
|
||||||
if (head != null) {
|
if (head != null) {
|
||||||
@ -106,8 +152,11 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set user account.
|
* Set user account.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @param userId {@link String}
|
||||||
*/
|
*/
|
||||||
public void set(String userId, UserAccount userAccount) {
|
public void set(final String userId, final UserAccount userAccount) {
|
||||||
if (cache.containsKey(userId)) {
|
if (cache.containsKey(userId)) {
|
||||||
var old = cache.get(userId);
|
var old = cache.get(userId);
|
||||||
old.userAccount = userAccount;
|
old.userAccount = userAccount;
|
||||||
@ -127,25 +176,43 @@ public class LruCache {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean contains(String userId) {
|
/**
|
||||||
|
* Check if Cache contains the userId.
|
||||||
|
*
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public boolean contains(final String userId) {
|
||||||
return cache.containsKey(userId);
|
return cache.containsKey(userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invalidate cache for user.
|
* Invalidate cache for user.
|
||||||
|
*
|
||||||
|
* @param userId {@link String}
|
||||||
*/
|
*/
|
||||||
public void invalidate(String userId) {
|
public void invalidate(final String userId) {
|
||||||
var toBeRemoved = cache.remove(userId);
|
var toBeRemoved = cache.remove(userId);
|
||||||
if (toBeRemoved != null) {
|
if (toBeRemoved != null) {
|
||||||
LOGGER.info("# {} has been updated! Removing older version from cache...", userId);
|
LOGGER.info("# {} has been updated! "
|
||||||
|
+ "Removing older version from cache...", userId);
|
||||||
remove(toBeRemoved);
|
remove(toBeRemoved);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the cache is full.
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
public boolean isFull() {
|
public boolean isFull() {
|
||||||
return cache.size() >= capacity;
|
return cache.size() >= capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get LRU data.
|
||||||
|
*
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
public UserAccount getLruData() {
|
public UserAccount getLruData() {
|
||||||
return end.userAccount;
|
return end.userAccount;
|
||||||
}
|
}
|
||||||
@ -161,6 +228,8 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns cache data in list form.
|
* Returns cache data in list form.
|
||||||
|
*
|
||||||
|
* @return {@link List}
|
||||||
*/
|
*/
|
||||||
public List<UserAccount> getCacheDataInListForm() {
|
public List<UserAccount> getCacheDataInListForm() {
|
||||||
var listOfCacheData = new ArrayList<UserAccount>();
|
var listOfCacheData = new ArrayList<UserAccount>();
|
||||||
@ -174,10 +243,14 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set cache capacity.
|
* Set cache capacity.
|
||||||
|
*
|
||||||
|
* @param newCapacity int
|
||||||
*/
|
*/
|
||||||
public void setCapacity(int newCapacity) {
|
public void setCapacity(final int newCapacity) {
|
||||||
if (capacity > newCapacity) {
|
if (capacity > newCapacity) {
|
||||||
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
|
// Behavior can be modified to accommodate
|
||||||
|
// for decrease in cache size. For now, we'll
|
||||||
|
clear();
|
||||||
// just clear the cache.
|
// just clear the cache.
|
||||||
} else {
|
} else {
|
||||||
this.capacity = newCapacity;
|
this.capacity = newCapacity;
|
||||||
|
@ -24,19 +24,28 @@
|
|||||||
package com.iluwatar.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Getter;
|
import lombok.Data;
|
||||||
import lombok.Setter;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.ToString;
|
import lombok.ToString;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity class (stored in cache and DB) used in the application.
|
* Entity class (stored in cache and DB) used in the application.
|
||||||
*/
|
*/
|
||||||
@Setter
|
@Data
|
||||||
@Getter
|
|
||||||
@AllArgsConstructor
|
@AllArgsConstructor
|
||||||
@ToString
|
@ToString
|
||||||
|
@EqualsAndHashCode
|
||||||
public class UserAccount {
|
public class UserAccount {
|
||||||
|
/**
|
||||||
|
* User Id.
|
||||||
|
*/
|
||||||
private String userId;
|
private String userId;
|
||||||
|
/**
|
||||||
|
* User Name.
|
||||||
|
*/
|
||||||
private String userName;
|
private String userName;
|
||||||
|
/**
|
||||||
|
* Additional Info.
|
||||||
|
*/
|
||||||
private String additionalInfo;
|
private String additionalInfo;
|
||||||
}
|
}
|
||||||
|
@ -26,11 +26,27 @@ package com.iluwatar.caching.constants;
|
|||||||
/**
|
/**
|
||||||
* Constant class for defining constants.
|
* Constant class for defining constants.
|
||||||
*/
|
*/
|
||||||
public class CachingConstants {
|
public final class CachingConstants {
|
||||||
|
/**
|
||||||
|
* User Account.
|
||||||
|
*/
|
||||||
public static final String USER_ACCOUNT = "user_accounts";
|
public static final String USER_ACCOUNT = "user_accounts";
|
||||||
|
/**
|
||||||
|
* User ID.
|
||||||
|
*/
|
||||||
public static final String USER_ID = "userID";
|
public static final String USER_ID = "userID";
|
||||||
|
/**
|
||||||
|
* User Name.
|
||||||
|
*/
|
||||||
public static final String USER_NAME = "userName";
|
public static final String USER_NAME = "userName";
|
||||||
|
/**
|
||||||
|
* Additional Info.
|
||||||
|
*/
|
||||||
public static final String ADD_INFO = "additionalInfo";
|
public static final String ADD_INFO = "additionalInfo";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*/
|
||||||
|
private CachingConstants() {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Constants.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.caching.constants;
|
@ -0,0 +1,52 @@
|
|||||||
|
package com.iluwatar.caching.database;
|
||||||
|
|
||||||
|
import com.iluwatar.caching.UserAccount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>DBManager handles the communication with the underlying data store i.e.
|
||||||
|
* Database. It contains the implemented methods for querying, inserting,
|
||||||
|
* and updating data. MongoDB was used as the database for the application.</p>
|
||||||
|
*/
|
||||||
|
public interface DbManager {
|
||||||
|
/**
|
||||||
|
* Connect to DB.
|
||||||
|
*/
|
||||||
|
void connect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disconnect from DB.
|
||||||
|
*/
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from DB.
|
||||||
|
*
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
UserAccount readFromDb(String userId);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to DB.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
UserAccount writeToDb(UserAccount userAccount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update record.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
UserAccount updateDb(UserAccount userAccount);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update record or Insert if not exists.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
UserAccount upsertDb(UserAccount userAccount);
|
||||||
|
}
|
@ -0,0 +1,25 @@
|
|||||||
|
package com.iluwatar.caching.database;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the database connection accroding the input parameter.
|
||||||
|
*/
|
||||||
|
public final class DbManagerFactory {
|
||||||
|
/**
|
||||||
|
* Private constructor.
|
||||||
|
*/
|
||||||
|
private DbManagerFactory() {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Init database.
|
||||||
|
*
|
||||||
|
* @param isMongo boolean
|
||||||
|
* @return {@link DbManager}
|
||||||
|
*/
|
||||||
|
public static DbManager initDb(final boolean isMongo) {
|
||||||
|
if (isMongo) {
|
||||||
|
return new MongoDb();
|
||||||
|
}
|
||||||
|
return new VirtualDb();
|
||||||
|
}
|
||||||
|
}
|
128
caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
Normal file
128
caching/src/main/java/com/iluwatar/caching/database/MongoDb.java
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package com.iluwatar.caching.database;
|
||||||
|
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.USER_ACCOUNT;
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
|
||||||
|
|
||||||
|
import com.iluwatar.caching.UserAccount;
|
||||||
|
import com.iluwatar.caching.constants.CachingConstants;
|
||||||
|
import com.mongodb.MongoClient;
|
||||||
|
import com.mongodb.MongoClientOptions;
|
||||||
|
import com.mongodb.MongoCredential;
|
||||||
|
import com.mongodb.ServerAddress;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import com.mongodb.client.model.UpdateOptions;
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.bson.Document;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of DatabaseManager.
|
||||||
|
* implements base methods to work with MongoDb.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class MongoDb implements DbManager {
|
||||||
|
private static final String DATABASE_NAME = "admin";
|
||||||
|
private static final String MONGO_USER = "root";
|
||||||
|
private static final String MONGO_PASSWORD = "rootpassword";
|
||||||
|
private MongoClient client;
|
||||||
|
private MongoDatabase db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to Db. Check th connection
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void connect() {
|
||||||
|
MongoCredential mongoCredential = MongoCredential.createCredential(MONGO_USER,
|
||||||
|
DATABASE_NAME,
|
||||||
|
MONGO_PASSWORD.toCharArray());
|
||||||
|
MongoClientOptions options = MongoClientOptions.builder().build();
|
||||||
|
client = new MongoClient(new ServerAddress(), List.of(mongoCredential), options);
|
||||||
|
db = client.getDatabase(DATABASE_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read data from DB.
|
||||||
|
*
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount readFromDb(final String userId) {
|
||||||
|
var iterable = db
|
||||||
|
.getCollection(CachingConstants.USER_ACCOUNT)
|
||||||
|
.find(new Document(USER_ID, userId));
|
||||||
|
if (iterable.first() == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Document doc = iterable.first();
|
||||||
|
if (doc != null) {
|
||||||
|
String userName = doc.getString(USER_NAME);
|
||||||
|
String appInfo = doc.getString(ADD_INFO);
|
||||||
|
return new UserAccount(userId, userName, appInfo);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write data to DB.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount writeToDb(final UserAccount userAccount) {
|
||||||
|
db.getCollection(USER_ACCOUNT).insertOne(
|
||||||
|
new Document(USER_ID, userAccount.getUserId())
|
||||||
|
.append(USER_NAME, userAccount.getUserName())
|
||||||
|
.append(ADD_INFO, userAccount.getAdditionalInfo())
|
||||||
|
);
|
||||||
|
return userAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update DB.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount updateDb(final UserAccount userAccount) {
|
||||||
|
Document id = new Document(USER_ID, userAccount.getUserId());
|
||||||
|
Document dataSet = new Document(USER_NAME, userAccount.getUserName())
|
||||||
|
.append(ADD_INFO, userAccount.getAdditionalInfo());
|
||||||
|
db.getCollection(CachingConstants.USER_ACCOUNT)
|
||||||
|
.updateOne(id, new Document("$set", dataSet));
|
||||||
|
return userAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update data if exists.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount upsertDb(final UserAccount userAccount) {
|
||||||
|
String userId = userAccount.getUserId();
|
||||||
|
String userName = userAccount.getUserName();
|
||||||
|
String additionalInfo = userAccount.getAdditionalInfo();
|
||||||
|
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
|
||||||
|
new Document(USER_ID, userId),
|
||||||
|
new Document("$set",
|
||||||
|
new Document(USER_ID, userId)
|
||||||
|
.append(USER_NAME, userName)
|
||||||
|
.append(ADD_INFO, additionalInfo)
|
||||||
|
),
|
||||||
|
new UpdateOptions().upsert(true)
|
||||||
|
);
|
||||||
|
return userAccount;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
package com.iluwatar.caching.database;
|
||||||
|
|
||||||
|
import com.iluwatar.caching.UserAccount;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implementation of DatabaseManager.
|
||||||
|
* implements base methods to work with hashMap as database.
|
||||||
|
*/
|
||||||
|
public class VirtualDb implements DbManager {
|
||||||
|
/**
|
||||||
|
* Virtual DataBase.
|
||||||
|
*/
|
||||||
|
private Map<String, UserAccount> db;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates new HashMap.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void connect() {
|
||||||
|
db = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
db = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read from Db.
|
||||||
|
*
|
||||||
|
* @param userId {@link String}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount readFromDb(final String userId) {
|
||||||
|
if (db.containsKey(userId)) {
|
||||||
|
return db.get(userId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Write to DB.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount writeToDb(final UserAccount userAccount) {
|
||||||
|
db.put(userAccount.getUserId(), userAccount);
|
||||||
|
return userAccount;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update reecord in DB.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount updateDb(final UserAccount userAccount) {
|
||||||
|
return writeToDb(userAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update.
|
||||||
|
*
|
||||||
|
* @param userAccount {@link UserAccount}
|
||||||
|
* @return {@link UserAccount}
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public UserAccount upsertDb(final UserAccount userAccount) {
|
||||||
|
return updateDb(userAccount);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
/**
|
||||||
|
* Database classes.
|
||||||
|
*/
|
||||||
|
package com.iluwatar.caching.database;
|
20
caching/src/main/java/com/iluwatar/caching/package-info.java
Normal file
20
caching/src/main/java/com/iluwatar/caching/package-info.java
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**
|
||||||
|
* 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.caching;
|
@ -25,25 +25,21 @@ package com.iluwatar.caching;
|
|||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests that Caching example runs without errors.
|
* Tests that Caching example runs without errors.
|
||||||
*/
|
*/
|
||||||
class AppTest {
|
class AppTest {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Issue: Add at least one assertion to this test case.
|
* Issue: Add at least one assertion to this test case.
|
||||||
*
|
* <p>
|
||||||
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
|
* Solution: Inserted assertion to check whether the execution of the main method in {@link App}
|
||||||
* throws an exception.
|
* throws an exception.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void shouldExecuteApplicationWithoutException() {
|
void shouldExecuteApplicationWithoutException() {
|
||||||
|
|
||||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,11 @@
|
|||||||
|
|
||||||
package com.iluwatar.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Application test
|
* Application test
|
||||||
*/
|
*/
|
||||||
@ -43,32 +43,30 @@ class CachingTest {
|
|||||||
// to avoid Maven compilation errors. Set flag to true to run the
|
// to avoid Maven compilation errors. Set flag to true to run the
|
||||||
// tests with MongoDB (provided that MongoDB is installed and socket
|
// tests with MongoDB (provided that MongoDB is installed and socket
|
||||||
// connection is open).
|
// connection is open).
|
||||||
AppManager.initDb(false);
|
app = new App(false);
|
||||||
AppManager.initCacheCapacity(3);
|
|
||||||
app = new App();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testReadAndWriteThroughStrategy() {
|
void testReadAndWriteThroughStrategy() {
|
||||||
assertNotNull(app);
|
assertNotNull(app);
|
||||||
app.useReadAndWriteThroughStrategy();
|
app.useReadAndWriteThroughStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testReadThroughAndWriteAroundStrategy() {
|
void testReadThroughAndWriteAroundStrategy() {
|
||||||
assertNotNull(app);
|
assertNotNull(app);
|
||||||
app.useReadThroughAndWriteAroundStrategy();
|
app.useReadThroughAndWriteAroundStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testReadThroughAndWriteBehindStrategy() {
|
void testReadThroughAndWriteBehindStrategy() {
|
||||||
assertNotNull(app);
|
assertNotNull(app);
|
||||||
app.useReadThroughAndWriteBehindStrategy();
|
app.useReadThroughAndWriteBehindStrategy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void testCacheAsideStrategy() {
|
void testCacheAsideStrategy() {
|
||||||
assertNotNull(app);
|
assertNotNull(app);
|
||||||
app.useCacheAsideStategy();
|
app.useCacheAsideStategy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
package com.iluwatar.caching.database;
|
||||||
|
|
||||||
|
import com.iluwatar.caching.UserAccount;
|
||||||
|
import com.iluwatar.caching.constants.CachingConstants;
|
||||||
|
import com.mongodb.client.FindIterable;
|
||||||
|
import com.mongodb.client.MongoCollection;
|
||||||
|
import com.mongodb.client.MongoDatabase;
|
||||||
|
import org.bson.Document;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.internal.util.reflection.Whitebox;
|
||||||
|
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
|
||||||
|
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
class MongoDbTest {
|
||||||
|
private static final String ID = "123";
|
||||||
|
private static final String NAME = "Some user";
|
||||||
|
private static final String ADDITIONAL_INFO = "Some app Info";
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
MongoDatabase db;
|
||||||
|
private MongoDb mongoDb = new MongoDb();
|
||||||
|
|
||||||
|
private UserAccount userAccount;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
void init() {
|
||||||
|
db = mock(MongoDatabase.class);
|
||||||
|
Whitebox.setInternalState(mongoDb, "db", db);
|
||||||
|
userAccount = new UserAccount(ID, NAME, ADDITIONAL_INFO);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void connect() {
|
||||||
|
assertDoesNotThrow(() -> mongoDb.connect());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void readFromDb() {
|
||||||
|
Document document = new Document(USER_ID, ID)
|
||||||
|
.append(USER_NAME, NAME)
|
||||||
|
.append(ADD_INFO, ADDITIONAL_INFO);
|
||||||
|
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
|
||||||
|
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
|
||||||
|
|
||||||
|
FindIterable<Document> findIterable = mock(FindIterable.class);
|
||||||
|
when(mongoCollection.find(any(Document.class))).thenReturn(findIterable);
|
||||||
|
|
||||||
|
when(findIterable.first()).thenReturn(document);
|
||||||
|
|
||||||
|
assertEquals(mongoDb.readFromDb(ID),userAccount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void writeToDb() {
|
||||||
|
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
|
||||||
|
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
|
||||||
|
doNothing().when(mongoCollection).insertOne(any(Document.class));
|
||||||
|
assertDoesNotThrow(()-> {mongoDb.writeToDb(userAccount);});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void updateDb() {
|
||||||
|
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
|
||||||
|
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
|
||||||
|
assertDoesNotThrow(()-> {mongoDb.updateDb(userAccount);});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void upsertDb() {
|
||||||
|
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
|
||||||
|
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
|
||||||
|
assertDoesNotThrow(()-> {mongoDb.upsertDb(userAccount);});
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,13 +23,12 @@
|
|||||||
THE SOFTWARE.
|
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"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>callback</artifactId>
|
<artifactId>callback</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,13 +23,12 @@
|
|||||||
THE SOFTWARE.
|
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"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>chain-of-responsibility</artifactId>
|
<artifactId>chain-of-responsibility</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -23,11 +23,10 @@
|
|||||||
THE SOFTWARE.
|
THE SOFTWARE.
|
||||||
|
|
||||||
-->
|
-->
|
||||||
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
<!DOCTYPE suppressions PUBLIC "-//Puppy Crawl//DTD Suppressions 1.1//EN" "http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||||
"http://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
|
||||||
<suppressions>
|
<suppressions>
|
||||||
<suppress checks="AvoidStarImport" files="[\\/]src[\\/]test[\\/]java[\\/]"/>
|
<suppress checks="AvoidStarImport" files="[\\/]src[\\/]test[\\/]java[\\/]" />
|
||||||
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]test[\\/]resources[\\/]"/>
|
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]test[\\/]resources[\\/]" />
|
||||||
<suppress checks="[a-zA-Z0-9]*" files="[\\/]build[\\/]generated-sources[\\/]"/>
|
<suppress checks="[a-zA-Z0-9]*" files="[\\/]build[\\/]generated-sources[\\/]" />
|
||||||
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]main[\\/]resources[\\/]"/>
|
<suppress checks="[a-zA-Z0-9]*" files="[\\/]src[\\/]main[\\/]resources[\\/]" />
|
||||||
</suppressions>
|
</suppressions>
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,21 +24,21 @@
|
|||||||
|
|
||||||
-->
|
-->
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>circuit-breaker</artifactId>
|
<artifactId>circuit-breaker</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.junit.jupiter</groupId>
|
<groupId>org.junit.jupiter</groupId>
|
||||||
<artifactId>junit-jupiter-engine</artifactId>
|
<artifactId>junit-jupiter-engine</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
<plugin>
|
<plugin>
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
38
cloud-claim-check-pattern/.gitignore
vendored
Normal file
38
cloud-claim-check-pattern/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Build output
|
||||||
|
target/
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Azure Functions
|
||||||
|
local.settings.json
|
||||||
|
bin/
|
||||||
|
obj/
|
83
cloud-claim-check-pattern/README.md
Normal file
83
cloud-claim-check-pattern/README.md
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
---
|
||||||
|
layout: pattern
|
||||||
|
title: Claim Check Pattern
|
||||||
|
folder: cloud-claim-check-pattern
|
||||||
|
permalink: /patterns/cloud-claim-check-pattern/
|
||||||
|
categories: Cloud
|
||||||
|
language: en
|
||||||
|
tags:
|
||||||
|
- Cloud distributed
|
||||||
|
- Microservices
|
||||||
|
---
|
||||||
|
|
||||||
|
## Name
|
||||||
|
|
||||||
|
[Claim Check Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check)
|
||||||
|
|
||||||
|
## Also known as
|
||||||
|
|
||||||
|
[Reference-Based Messaging](https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html)
|
||||||
|
|
||||||
|
## Intent
|
||||||
|
|
||||||
|
- Reduce the load of data transfer through the Internet. Instead of sending actual data directly, just send the message reference.
|
||||||
|
- Improve data security. As only message reference is shared, no data is exposed to the Internet.
|
||||||
|
|
||||||
|
## Explanation
|
||||||
|
|
||||||
|
Real-World Example
|
||||||
|
|
||||||
|
> Suppose if you want to build a photo processing system. A photo processing system takes an image as input, processes it, and outputs a different set of images. Consider system has one persistent storage, one input component, ten processing components, messaging platform. Once a photo is given to the input component, it stores that image on persistent storage. It then creates ten different messages/events with the same image location and publishes them to the messaging platform. The messaging platform triggers ten different processing components. The ten processing components extract information about image location from the received event and then read an image from persistent storage. They generate ten different images from the original image and drop these images again to persistent storage.
|
||||||
|
|
||||||
|
In Plain words
|
||||||
|
|
||||||
|
> Split a large message into a claim check and a payload. Send the claim check to the messaging platform and store the payload to an external service. This pattern allows large messages to be processed while protecting the message bus and the client from being overwhelmed or slowed down. This pattern also helps to reduce costs, as storage is usually cheaper than resource units used by the messaging platform.([ref](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check))
|
||||||
|
|
||||||
|
## Architecture Diagram
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Applicability
|
||||||
|
|
||||||
|
Use the Claim Check Pattern when
|
||||||
|
|
||||||
|
- Huge processing data causes a lot of bandwidth consumption to transfer data through the Internet.
|
||||||
|
- To secure your data transfer by storing in common persistent storage.
|
||||||
|
- Using a cloud platform - Azure Functions or AWS Lambda, Azure EventGrid or AWS Event Bridge, Azure Blob Storage or AWS S3 Bucket.
|
||||||
|
- Each service must be independent and idempotent. Output data is dropped to persistent storage by the service.
|
||||||
|
- Publish-subscribe messaging pattern needs to be used.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
- This pattern is stateless. Any compute API will not store any data.
|
||||||
|
- You must have persistent storage and a reliable messaging platform.
|
||||||
|
|
||||||
|
## Tutorials
|
||||||
|
|
||||||
|
### Workflow
|
||||||
|
|
||||||
|
Suppose a telecom company wants to build call cost calculator system which generate the call cost daily. At the end of each day, details of the calls made by the consumers are stored somewhere. The call calculator system will read this data and generate call cost data for each user. Consumers will be billed using this generated data in case of postpaid service.
|
||||||
|
|
||||||
|
Producer class( `UsageDetailPublisherFunction` Azure Function) will generate call usage details (here we are generating call data in producer class itself. In real world scenario, it will read from storage). `UsageDetailPublisherFunction` creates a message. Message consists of message header and message body. Message header is basically an event grid event or claim or message reference. Message body contains actual data. `UsageDetailPublisherFunction` sends a message header to Event Grid topic `usage-detail` and drops an entire message to the blob storage. Event Grid then sent this message header to the `UsageCostProcessorFunction` Azure function. It will read the entire message from blob storage with the help of the header, will calculate call cost and drop the result to the blob storage.
|
||||||
|
|
||||||
|
### Class Diagrams
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Setup
|
||||||
|
|
||||||
|
- Any operating system can be used macOS, Windows, Linux as everything is deployed on Azure.
|
||||||
|
- Install Java JDK 11 and set up Java environmental variables.
|
||||||
|
- Install Git.
|
||||||
|
- Install Visual Studio Code.
|
||||||
|
- Install [ Azure Functions extension](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions) to be able to deploy using Visual studio.
|
||||||
|
|
||||||
|
### Storage Data
|
||||||
|
|
||||||
|
The data is stored in the Azure blob storage in the container `callusageapp`. For every trigger, one GUID is created. Under the `GUID folder`, 2 files will be created `input.json` and `output.json`.
|
||||||
|
`Input.json` is dropped `producer` azure function which contains call usage details.` Output.json` contains call cost details which are dropped by the `consumer` azure function.
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [Messaging Pattern - Claim Check](https://www.enterpriseintegrationpatterns.com/patterns/messaging/StoreInLibrary.html)
|
||||||
|
- [Azure Architecture Pattern - Claim Check Pattern](https://docs.microsoft.com/en-us/azure/architecture/patterns/claim-check)
|
38
cloud-claim-check-pattern/call-usage-app/.gitignore
vendored
Normal file
38
cloud-claim-check-pattern/call-usage-app/.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# Build output
|
||||||
|
target/
|
||||||
|
*.class
|
||||||
|
|
||||||
|
# Log file
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# BlueJ files
|
||||||
|
*.ctxt
|
||||||
|
|
||||||
|
# Mobile Tools for Java (J2ME)
|
||||||
|
.mtj.tmp/
|
||||||
|
|
||||||
|
# Package Files #
|
||||||
|
*.jar
|
||||||
|
*.war
|
||||||
|
*.ear
|
||||||
|
*.zip
|
||||||
|
*.tar.gz
|
||||||
|
*.rar
|
||||||
|
|
||||||
|
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
|
||||||
|
hs_err_pid*
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.idea/
|
||||||
|
*.iml
|
||||||
|
.settings/
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Azure Functions
|
||||||
|
local.settings.json
|
||||||
|
bin/
|
||||||
|
obj/
|
@ -0,0 +1,117 @@
|
|||||||
|
@startuml
|
||||||
|
package com.iluwatar.claimcheckpattern.producer.calldetails.functions {
|
||||||
|
class UsageDetailPublisherFunction {
|
||||||
|
- eventHandlerUtility : EventHandlerUtility<MessageHeader>
|
||||||
|
- messageHandlerUtility : MessageHandlerUtility<UsageDetail>
|
||||||
|
+ UsageDetailPublisherFunction()
|
||||||
|
+ UsageDetailPublisherFunction(messageHandlerUtility : MessageHandlerUtility<UsageDetail>, eventHandlerUtility : EventHandlerUtility<MessageHeader>)
|
||||||
|
+ run(request : HttpRequestMessage<Optional<String>>, context : ExecutionContext) : HttpResponseMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
package com.iluwatar.claimcheckpattern.domain {
|
||||||
|
class Message<T> {
|
||||||
|
- messageBody : MessageBody<T>
|
||||||
|
- messageHeader : MessageHeader
|
||||||
|
+ Message<T>()
|
||||||
|
+ getMessageBody() : MessageBody<T>
|
||||||
|
+ getMessageHeader() : MessageHeader
|
||||||
|
+ setMessageBody(messageBody : MessageBody<T>)
|
||||||
|
+ setMessageHeader(messageHeader : MessageHeader)
|
||||||
|
}
|
||||||
|
class MessageBody<T> {
|
||||||
|
- data : List<T>
|
||||||
|
+ MessageBody<T>()
|
||||||
|
+ getData() : List<T>
|
||||||
|
+ setData(data : List<T>)
|
||||||
|
}
|
||||||
|
class MessageHeader {
|
||||||
|
- data : Object
|
||||||
|
- dataVersion : String
|
||||||
|
- eventTime : String
|
||||||
|
- eventType : String
|
||||||
|
- id : String
|
||||||
|
- subject : String
|
||||||
|
- topic : String
|
||||||
|
+ MessageHeader()
|
||||||
|
+ getData() : Object
|
||||||
|
+ getDataVersion() : String
|
||||||
|
+ getEventTime() : String
|
||||||
|
+ getEventType() : String
|
||||||
|
+ getId() : String
|
||||||
|
+ getSubject() : String
|
||||||
|
+ getTopic() : String
|
||||||
|
+ setData(data : Object)
|
||||||
|
+ setDataVersion(dataVersion : String)
|
||||||
|
+ setEventTime(eventTime : String)
|
||||||
|
+ setEventType(eventType : String)
|
||||||
|
+ setId(id : String)
|
||||||
|
+ setSubject(subject : String)
|
||||||
|
+ setTopic(topic : String)
|
||||||
|
}
|
||||||
|
class MessageReference {
|
||||||
|
- dataFileName : String
|
||||||
|
- dataLocation : String
|
||||||
|
+ MessageReference()
|
||||||
|
+ MessageReference(dataLocation : String, dataFileName : String)
|
||||||
|
+ getDataFileName() : String
|
||||||
|
+ getDataLocation() : String
|
||||||
|
+ setDataFileName(dataFileName : String)
|
||||||
|
+ setDataLocation(dataLocation : String)
|
||||||
|
}
|
||||||
|
class UsageCostDetail {
|
||||||
|
- callCost : double
|
||||||
|
- dataCost : double
|
||||||
|
- userId : String
|
||||||
|
+ UsageCostDetail()
|
||||||
|
+ getCallCost() : double
|
||||||
|
+ getDataCost() : double
|
||||||
|
+ getUserId() : String
|
||||||
|
+ setCallCost(callCost : double)
|
||||||
|
+ setDataCost(dataCost : double)
|
||||||
|
+ setUserId(userId : String)
|
||||||
|
}
|
||||||
|
class UsageDetail {
|
||||||
|
- data : int
|
||||||
|
- duration : int
|
||||||
|
- userId : String
|
||||||
|
+ UsageDetail()
|
||||||
|
+ getData() : int
|
||||||
|
+ getDuration() : int
|
||||||
|
+ getUserId() : String
|
||||||
|
+ setData(data : int)
|
||||||
|
+ setDuration(duration : int)
|
||||||
|
+ setUserId(userId : String)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
package com.iluwatar.claimcheckpattern.utility {
|
||||||
|
class EventHandlerUtility<T> {
|
||||||
|
- customEventClient : EventGridPublisherClient<BinaryData>
|
||||||
|
+ EventHandlerUtility<T>()
|
||||||
|
+ EventHandlerUtility<T>(customEventClient : EventGridPublisherClient<BinaryData>)
|
||||||
|
+ publishEvent(customEvent : T, logger : Logger)
|
||||||
|
}
|
||||||
|
class MessageHandlerUtility<T> {
|
||||||
|
- blobServiceClient : BlobServiceClient
|
||||||
|
+ MessageHandlerUtility<T>()
|
||||||
|
+ MessageHandlerUtility<T>(blobServiceClient : BlobServiceClient)
|
||||||
|
+ dropToPersistantStorage(message : Message<T>, logger : Logger)
|
||||||
|
+ readFromPersistantStorage(messageReference : MessageReference, logger : Logger) : Message<T>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions {
|
||||||
|
class UsageCostProcessorFunction {
|
||||||
|
- messageHandlerUtilityForUsageCostDetail : MessageHandlerUtility<UsageCostDetail>
|
||||||
|
- messageHandlerUtilityForUsageDetail : MessageHandlerUtility<UsageDetail>
|
||||||
|
+ UsageCostProcessorFunction()
|
||||||
|
+ UsageCostProcessorFunction(messageHandlerUtilityForUsageDetail : MessageHandlerUtility<UsageDetail>, messageHandlerUtilityForUsageCostDetail : MessageHandlerUtility<UsageCostDetail>)
|
||||||
|
- calculateUsageCostDetails(usageDetailsList : List<UsageDetail>) : List<UsageCostDetail>
|
||||||
|
+ run(request : HttpRequestMessage<Optional<String>>, context : ExecutionContext) : HttpResponseMessage
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UsageCostProcessorFunction --> "-messageHandlerUtilityForUsageDetail" MessageHandlerUtility
|
||||||
|
Message --> "-messageBody" MessageBody
|
||||||
|
UsageDetailPublisherFunction --> "-eventHandlerUtility" EventHandlerUtility
|
||||||
|
Builder ..+ HttpResponseMessage
|
||||||
|
UsageDetailPublisherFunction --> "-messageHandlerUtility" MessageHandlerUtility
|
||||||
|
Message --> "-messageHeader" MessageHeader
|
||||||
|
@enduml
|
7
cloud-claim-check-pattern/call-usage-app/host.json
Normal file
7
cloud-claim-check-pattern/call-usage-app/host.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"version": "2.0",
|
||||||
|
"extensionBundle": {
|
||||||
|
"id": "Microsoft.Azure.Functions.ExtensionBundle",
|
||||||
|
"version": "[1.*, 2.0.0)"
|
||||||
|
}
|
||||||
|
}
|
150
cloud-claim-check-pattern/call-usage-app/pom.xml
Normal file
150
cloud-claim-check-pattern/call-usage-app/pom.xml
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<artifactId>claim-check-pattern</artifactId>
|
||||||
|
<version>1.25.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<artifactId>call-usage-app</artifactId>
|
||||||
|
<name>call-usage-app</name>
|
||||||
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
<azure.functions.maven.plugin.version>1.14.0</azure.functions.maven.plugin.version>
|
||||||
|
<azure.functions.java.library.version>1.4.2</azure.functions.java.library.version>
|
||||||
|
<functionAppName>CallUsageApp</functionAppName>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
<dependencyManagement>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.azure</groupId>
|
||||||
|
<artifactId>azure-sdk-bom</artifactId>
|
||||||
|
<version>1.0.4</version>
|
||||||
|
<type>pom</type>
|
||||||
|
<scope>import</scope>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
</dependencyManagement>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.microsoft.azure.functions</groupId>
|
||||||
|
<artifactId>azure-functions-java-library</artifactId>
|
||||||
|
<version>${azure.functions.java.library.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.azure</groupId>
|
||||||
|
<artifactId>azure-messaging-eventgrid</artifactId>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.azure</groupId>
|
||||||
|
<artifactId>azure-storage-blob</artifactId>
|
||||||
|
<version>12.13.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Test -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-core</artifactId>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>com.microsoft.azure</groupId>
|
||||||
|
<artifactId>azure-functions-maven-plugin</artifactId>
|
||||||
|
<version>${azure.functions.maven.plugin.version}</version>
|
||||||
|
<configuration>
|
||||||
|
<!-- function app name -->
|
||||||
|
<appName>${functionAppName}</appName>
|
||||||
|
<!-- function app resource group -->
|
||||||
|
<resourceGroup>java-functions-group</resourceGroup>
|
||||||
|
<!-- function app service plan name -->
|
||||||
|
<appServicePlanName>java-functions-app-service-plan</appServicePlanName>
|
||||||
|
<!-- function app region-->
|
||||||
|
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-regions for all valid values -->
|
||||||
|
<region>westus</region>
|
||||||
|
<!-- function pricingTier, default to be consumption if not specified -->
|
||||||
|
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details#supported-pricing-tiers for all valid values -->
|
||||||
|
<!-- <pricingTier></pricingTier> -->
|
||||||
|
<!-- Whether to disable application insights, default is false -->
|
||||||
|
<!-- refers https://github.com/microsoft/azure-maven-plugins/wiki/Azure-Functions:-Configuration-Details for all valid configurations for application insights-->
|
||||||
|
<!-- <disableAppInsights></disableAppInsights> -->
|
||||||
|
<runtime>
|
||||||
|
<!-- runtime os, could be windows, linux or docker-->
|
||||||
|
<os>windows</os>
|
||||||
|
<javaVersion>11</javaVersion>
|
||||||
|
</runtime>
|
||||||
|
<appSettings>
|
||||||
|
<property>
|
||||||
|
<name>FUNCTIONS_EXTENSION_VERSION</name>
|
||||||
|
<value>~3</value>
|
||||||
|
</property>
|
||||||
|
</appSettings>
|
||||||
|
</configuration>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>package-functions</id>
|
||||||
|
<goals>
|
||||||
|
<goal>package</goal>
|
||||||
|
</goals>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<!--Remove obj folder generated by .NET SDK in maven clean-->
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-clean-plugin</artifactId>
|
||||||
|
<version>3.1.0</version>
|
||||||
|
<configuration>
|
||||||
|
<filesets>
|
||||||
|
<fileset>
|
||||||
|
<directory>obj</directory>
|
||||||
|
</fileset>
|
||||||
|
</filesets>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</project>
|
@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions;
|
||||||
|
|
||||||
|
import com.azure.core.util.BinaryData;
|
||||||
|
import com.azure.core.util.serializer.TypeReference;
|
||||||
|
import com.azure.messaging.eventgrid.EventGridEvent;
|
||||||
|
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationEventData;
|
||||||
|
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationResponse;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.Message;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageBody;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageReference;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageCostDetail;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
|
||||||
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
|
import com.microsoft.azure.functions.HttpMethod;
|
||||||
|
import com.microsoft.azure.functions.HttpRequestMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpResponseMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpStatus;
|
||||||
|
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
|
||||||
|
import com.microsoft.azure.functions.annotation.FunctionName;
|
||||||
|
import com.microsoft.azure.functions.annotation.HttpTrigger;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure Functions with HTTP Trigger.
|
||||||
|
* This is Consumer class.
|
||||||
|
*/
|
||||||
|
public class UsageCostProcessorFunction {
|
||||||
|
|
||||||
|
private MessageHandlerUtility<UsageDetail> messageHandlerUtilityForUsageDetail;
|
||||||
|
private MessageHandlerUtility<UsageCostDetail> messageHandlerUtilityForUsageCostDetail;
|
||||||
|
|
||||||
|
public UsageCostProcessorFunction() {
|
||||||
|
this.messageHandlerUtilityForUsageDetail = new MessageHandlerUtility<>();
|
||||||
|
this.messageHandlerUtilityForUsageCostDetail = new MessageHandlerUtility<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsageCostProcessorFunction(
|
||||||
|
MessageHandlerUtility<UsageDetail> messageHandlerUtilityForUsageDetail,
|
||||||
|
MessageHandlerUtility<UsageCostDetail> messageHandlerUtilityForUsageCostDetail) {
|
||||||
|
this.messageHandlerUtilityForUsageDetail = messageHandlerUtilityForUsageDetail;
|
||||||
|
this.messageHandlerUtilityForUsageCostDetail = messageHandlerUtilityForUsageCostDetail;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure function which gets triggered when event grid event send event to it.
|
||||||
|
* After receiving event, it read input file from blob storage, calculate call cost details.
|
||||||
|
* It creates new message with cost details and drop message to blob storage.
|
||||||
|
* @param request represents HttpRequestMessage
|
||||||
|
* @param context represents ExecutionContext
|
||||||
|
* @return HttpResponseMessage
|
||||||
|
*/
|
||||||
|
@FunctionName("UsageCostProcessorFunction")
|
||||||
|
public HttpResponseMessage run(@HttpTrigger(name = "req", methods = { HttpMethod.GET,
|
||||||
|
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
|
||||||
|
HttpRequestMessage<Optional<String>> request,
|
||||||
|
final ExecutionContext context) {
|
||||||
|
try {
|
||||||
|
var eventGridEvents = EventGridEvent.fromString(request.getBody().get());
|
||||||
|
for (var eventGridEvent : eventGridEvents) {
|
||||||
|
// Handle system events
|
||||||
|
if (eventGridEvent.getEventType()
|
||||||
|
.equals("Microsoft.EventGrid.SubscriptionValidationEvent")) {
|
||||||
|
SubscriptionValidationEventData subscriptionValidationEventData = eventGridEvent.getData()
|
||||||
|
.toObject(SubscriptionValidationEventData.class);
|
||||||
|
// Handle the subscription validation event
|
||||||
|
var responseData = new SubscriptionValidationResponse();
|
||||||
|
responseData.setValidationResponse(subscriptionValidationEventData.getValidationCode());
|
||||||
|
return request.createResponseBuilder(HttpStatus.OK).body(responseData).build();
|
||||||
|
|
||||||
|
} else if (eventGridEvent.getEventType().equals("UsageDetail")) {
|
||||||
|
// Get message header and reference
|
||||||
|
var messageReference = eventGridEvent.getData()
|
||||||
|
.toObject(MessageReference.class);
|
||||||
|
|
||||||
|
// Read message from persistent storage
|
||||||
|
var message = this.messageHandlerUtilityForUsageDetail
|
||||||
|
.readFromPersistantStorage(messageReference, context.getLogger());
|
||||||
|
|
||||||
|
// Get Data and generate cost details
|
||||||
|
List<UsageDetail> usageDetailsList = BinaryData.fromObject(
|
||||||
|
message.getMessageBody().getData())
|
||||||
|
.toObject(new TypeReference<>() {
|
||||||
|
});
|
||||||
|
var usageCostDetailsList = calculateUsageCostDetails(usageDetailsList);
|
||||||
|
|
||||||
|
// Create message body
|
||||||
|
var newMessageBody = new MessageBody<UsageCostDetail>();
|
||||||
|
newMessageBody.setData(usageCostDetailsList);
|
||||||
|
|
||||||
|
// Create message header
|
||||||
|
var newMessageReference = new MessageReference("callusageapp",
|
||||||
|
eventGridEvent.getId() + "/output.json");
|
||||||
|
var newMessageHeader = new MessageHeader();
|
||||||
|
newMessageHeader.setId(eventGridEvent.getId());
|
||||||
|
newMessageHeader.setSubject("UsageCostProcessor");
|
||||||
|
newMessageHeader.setTopic("");
|
||||||
|
newMessageHeader.setEventType("UsageCostDetail");
|
||||||
|
newMessageHeader.setEventTime(OffsetDateTime.now().toString());
|
||||||
|
newMessageHeader.setData(newMessageReference);
|
||||||
|
newMessageHeader.setDataVersion("v1.0");
|
||||||
|
|
||||||
|
// Create entire message
|
||||||
|
var newMessage = new Message<UsageCostDetail>();
|
||||||
|
newMessage.setMessageHeader(newMessageHeader);
|
||||||
|
newMessage.setMessageBody(newMessageBody);
|
||||||
|
|
||||||
|
// Drop data to persistent storage
|
||||||
|
this.messageHandlerUtilityForUsageCostDetail.dropToPersistantStorage(newMessage,
|
||||||
|
context.getLogger());
|
||||||
|
|
||||||
|
context.getLogger().info("Message is dropped successfully");
|
||||||
|
return request.createResponseBuilder(HttpStatus.OK)
|
||||||
|
.body("Message is dropped successfully").build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
context.getLogger().warning(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.createResponseBuilder(HttpStatus.INTERNAL_SERVER_ERROR).body(null).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<UsageCostDetail> calculateUsageCostDetails(List<UsageDetail> usageDetailsList) {
|
||||||
|
if (usageDetailsList == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var usageCostDetailsList = new ArrayList<UsageCostDetail>();
|
||||||
|
|
||||||
|
usageDetailsList.forEach(usageDetail -> {
|
||||||
|
var usageCostDetail = new UsageCostDetail();
|
||||||
|
usageCostDetail.setUserId(usageDetail.getUserId());
|
||||||
|
usageCostDetail.setCallCost(usageDetail.getDuration() * 0.30); // 0.30₹ per minute
|
||||||
|
usageCostDetail.setDataCost(usageDetail.getData() * 0.20); // 0.20₹ per MB
|
||||||
|
|
||||||
|
usageCostDetailsList.add(usageCostDetail);
|
||||||
|
});
|
||||||
|
|
||||||
|
return usageCostDetailsList;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is the message which gets dropped or read by Producer or Consumer Azure functions.
|
||||||
|
* It is stored in the json format.
|
||||||
|
* @param <T> represents UsageDetail or UsageCostDetail
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Message<T> {
|
||||||
|
private MessageHeader messageHeader;
|
||||||
|
|
||||||
|
private MessageBody<T> messageBody;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.domain;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* It is message body of the message.
|
||||||
|
* It stores actual data in our case UsageCostDetail or UsageDetail.
|
||||||
|
* @param <T> represents UsageDetail or UsageCostDetail
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MessageBody<T> {
|
||||||
|
|
||||||
|
private List<T> data;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is message header or event which is sent to Event Grid.
|
||||||
|
* Its structure is same as Azure Event Grid Event Class.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MessageHeader {
|
||||||
|
|
||||||
|
private String id;
|
||||||
|
private String subject;
|
||||||
|
private String topic;
|
||||||
|
private String eventType;
|
||||||
|
private String eventTime;
|
||||||
|
private Object data;
|
||||||
|
private String dataVersion;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is claim/message reference class.
|
||||||
|
* It contains the information about data where it is stored in persistent storage
|
||||||
|
* and file name.
|
||||||
|
* dataLocation is blob storage container name.
|
||||||
|
* dataFileName is file name in above container.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class MessageReference {
|
||||||
|
|
||||||
|
private String dataLocation;
|
||||||
|
private String dataFileName;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is call cost details class.
|
||||||
|
* It stores userId of the caller, call duration cost and data cost.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UsageCostDetail {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
private double callCost;
|
||||||
|
private double dataCost;
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.domain;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is call usage detail calls.
|
||||||
|
* It stores userId of the caller, call duration and data used.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UsageDetail {
|
||||||
|
|
||||||
|
private String userId;
|
||||||
|
|
||||||
|
private int duration;
|
||||||
|
|
||||||
|
private int data;
|
||||||
|
}
|
@ -0,0 +1,146 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.producer.calldetails.functions;
|
||||||
|
|
||||||
|
import com.azure.messaging.eventgrid.EventGridEvent;
|
||||||
|
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationEventData;
|
||||||
|
import com.azure.messaging.eventgrid.systemevents.SubscriptionValidationResponse;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.Message;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageBody;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageReference;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.EventHandlerUtility;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
|
||||||
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
|
import com.microsoft.azure.functions.HttpMethod;
|
||||||
|
import com.microsoft.azure.functions.HttpRequestMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpResponseMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpStatus;
|
||||||
|
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
|
||||||
|
import com.microsoft.azure.functions.annotation.FunctionName;
|
||||||
|
import com.microsoft.azure.functions.annotation.HttpTrigger;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure Functions with HTTP Trigger.
|
||||||
|
* This is Producer class.
|
||||||
|
*/
|
||||||
|
public class UsageDetailPublisherFunction {
|
||||||
|
|
||||||
|
private MessageHandlerUtility<UsageDetail> messageHandlerUtility;
|
||||||
|
private EventHandlerUtility<MessageHeader> eventHandlerUtility;
|
||||||
|
|
||||||
|
public UsageDetailPublisherFunction() {
|
||||||
|
this.messageHandlerUtility = new MessageHandlerUtility<>();
|
||||||
|
this.eventHandlerUtility = new EventHandlerUtility<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UsageDetailPublisherFunction(MessageHandlerUtility<UsageDetail> messageHandlerUtility,
|
||||||
|
EventHandlerUtility<MessageHeader> eventHandlerUtility) {
|
||||||
|
this.messageHandlerUtility = messageHandlerUtility;
|
||||||
|
this.eventHandlerUtility = eventHandlerUtility;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Azure function which create message, drop it in persistent storage
|
||||||
|
* and publish the event to Event Grid topic.
|
||||||
|
* @param request represents HttpRequestMessage
|
||||||
|
* @param context represents ExecutionContext
|
||||||
|
* @return HttpResponseMessage
|
||||||
|
*/
|
||||||
|
@FunctionName("UsageDetailPublisherFunction")
|
||||||
|
public HttpResponseMessage run(@HttpTrigger(name = "req", methods = {
|
||||||
|
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)
|
||||||
|
HttpRequestMessage<Optional<String>> request,
|
||||||
|
final ExecutionContext context) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
var eventGridEvents = EventGridEvent.fromString(request.getBody().get());
|
||||||
|
|
||||||
|
for (EventGridEvent eventGridEvent : eventGridEvents) {
|
||||||
|
// Handle system events
|
||||||
|
if (eventGridEvent.getEventType()
|
||||||
|
.equals("Microsoft.EventGrid.SubscriptionValidationEvent")) {
|
||||||
|
SubscriptionValidationEventData subscriptionValidationEventData = eventGridEvent.getData()
|
||||||
|
.toObject(SubscriptionValidationEventData.class);
|
||||||
|
// Handle the subscription validation event
|
||||||
|
var responseData = new SubscriptionValidationResponse();
|
||||||
|
responseData.setValidationResponse(subscriptionValidationEventData.getValidationCode());
|
||||||
|
return request.createResponseBuilder(HttpStatus.OK).body(responseData).build();
|
||||||
|
|
||||||
|
} else if (eventGridEvent.getEventType().equals("UsageDetail")) {
|
||||||
|
// Create message body
|
||||||
|
var messageBody = new MessageBody<UsageDetail>();
|
||||||
|
var usageDetailsList = new ArrayList<UsageDetail>();
|
||||||
|
var random = new Random();
|
||||||
|
for (int i = 0; i < 51; i++) {
|
||||||
|
var usageDetail = new UsageDetail();
|
||||||
|
usageDetail.setUserId("userId" + i);
|
||||||
|
usageDetail.setData(random.nextInt(500));
|
||||||
|
usageDetail.setDuration(random.nextInt(500));
|
||||||
|
|
||||||
|
usageDetailsList.add(usageDetail);
|
||||||
|
}
|
||||||
|
messageBody.setData(usageDetailsList);
|
||||||
|
|
||||||
|
// Create message header
|
||||||
|
var messageHeader = new MessageHeader();
|
||||||
|
messageHeader.setId(UUID.randomUUID().toString());
|
||||||
|
messageHeader.setSubject("UsageDetailPublisher");
|
||||||
|
messageHeader.setTopic("usagecostprocessorfunction-topic");
|
||||||
|
messageHeader.setEventType("UsageDetail");
|
||||||
|
messageHeader.setEventTime(OffsetDateTime.now().toString());
|
||||||
|
var messageReference = new MessageReference("callusageapp",
|
||||||
|
messageHeader.getId() + "/input.json");
|
||||||
|
messageHeader.setData(messageReference);
|
||||||
|
messageHeader.setDataVersion("v1.0");
|
||||||
|
|
||||||
|
// Create entire message
|
||||||
|
var message = new Message<UsageDetail>();
|
||||||
|
message.setMessageHeader(messageHeader);
|
||||||
|
message.setMessageBody(messageBody);
|
||||||
|
|
||||||
|
// Drop data to persistent storage
|
||||||
|
this.messageHandlerUtility.dropToPersistantStorage(message, context.getLogger());
|
||||||
|
|
||||||
|
// Publish event to event grid topic
|
||||||
|
eventHandlerUtility.publishEvent(messageHeader, context.getLogger());
|
||||||
|
|
||||||
|
context.getLogger().info("Message is dropped and event is published successfully");
|
||||||
|
return request.createResponseBuilder(HttpStatus.OK).body(message).build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
context.getLogger().warning(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return request.createResponseBuilder(HttpStatus.OK).body(null).build();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.utility;
|
||||||
|
|
||||||
|
import com.azure.core.credential.AzureKeyCredential;
|
||||||
|
import com.azure.core.util.BinaryData;
|
||||||
|
import com.azure.messaging.eventgrid.EventGridPublisherClient;
|
||||||
|
import com.azure.messaging.eventgrid.EventGridPublisherClientBuilder;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class is event publisher utility which published message header to Event Grid topic.
|
||||||
|
* @param <T> represents UsageDetail or UsageCostDetail
|
||||||
|
*/
|
||||||
|
public class EventHandlerUtility<T> {
|
||||||
|
|
||||||
|
private EventGridPublisherClient<BinaryData> customEventClient;
|
||||||
|
|
||||||
|
/** Default constructor.
|
||||||
|
*/
|
||||||
|
public EventHandlerUtility() {
|
||||||
|
this.customEventClient = new EventGridPublisherClientBuilder()
|
||||||
|
.endpoint(System.getenv("EventGridURL"))
|
||||||
|
.credential(new AzureKeyCredential(System.getenv("EventGridKey")))
|
||||||
|
.buildCustomEventPublisherClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Parameterized constructor.
|
||||||
|
*/
|
||||||
|
public EventHandlerUtility(EventGridPublisherClient<BinaryData> customEventClient) {
|
||||||
|
this.customEventClient = customEventClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Method for publishing event to Event Grid Topic.
|
||||||
|
*/
|
||||||
|
public void publishEvent(T customEvent, Logger logger) {
|
||||||
|
try {
|
||||||
|
customEventClient.sendEvent(BinaryData.fromObject(customEvent));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,127 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.utility;
|
||||||
|
|
||||||
|
import com.azure.core.util.BinaryData;
|
||||||
|
import com.azure.core.util.serializer.TypeReference;
|
||||||
|
import com.azure.storage.blob.BlobClient;
|
||||||
|
import com.azure.storage.blob.BlobContainerClient;
|
||||||
|
import com.azure.storage.blob.BlobServiceClient;
|
||||||
|
import com.azure.storage.blob.BlobServiceClientBuilder;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.Message;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageReference;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class read and drop message from Azure blob storage.
|
||||||
|
* @param <T> represents UsageDetail or UsageCostDetail
|
||||||
|
*/
|
||||||
|
public class MessageHandlerUtility<T> {
|
||||||
|
|
||||||
|
private BlobServiceClient blobServiceClient;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parameterized constructor.
|
||||||
|
* @param blobServiceClient represents BlobServiceClient
|
||||||
|
*/
|
||||||
|
public MessageHandlerUtility(BlobServiceClient blobServiceClient) {
|
||||||
|
this.blobServiceClient = blobServiceClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default constructor.
|
||||||
|
*/
|
||||||
|
public MessageHandlerUtility() {
|
||||||
|
// Create a BlobServiceClient object which will be used to create a container
|
||||||
|
// client
|
||||||
|
this.blobServiceClient = new BlobServiceClientBuilder()
|
||||||
|
.connectionString(System.getenv("BlobStorageConnectionString")).buildClient();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read message from blob storage.
|
||||||
|
* @param messageReference represents MessageReference
|
||||||
|
* @param logger represents Logger
|
||||||
|
* @return Message
|
||||||
|
*/
|
||||||
|
public Message<T> readFromPersistantStorage(MessageReference messageReference, Logger logger) {
|
||||||
|
Message<T> message = null;
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Get container name from message reference
|
||||||
|
String containerName = messageReference.getDataLocation();
|
||||||
|
|
||||||
|
// Get blob name from message reference
|
||||||
|
String blobName = messageReference.getDataFileName();
|
||||||
|
|
||||||
|
// Get container client
|
||||||
|
BlobContainerClient containerClient = blobServiceClient.getBlobContainerClient(containerName);
|
||||||
|
|
||||||
|
// Get a reference to a blob
|
||||||
|
BlobClient blobClient = containerClient.getBlobClient(blobName);
|
||||||
|
|
||||||
|
// download the blob
|
||||||
|
message = blobClient.downloadContent().toObject(new TypeReference<Message<T>>() {
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Drop message to blob storage.
|
||||||
|
* @param message represents Message
|
||||||
|
* @param logger represents Logger
|
||||||
|
*/
|
||||||
|
public void dropToPersistantStorage(Message<T> message, Logger logger) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Get message reference
|
||||||
|
MessageReference messageReference = (MessageReference) message.getMessageHeader().getData();
|
||||||
|
|
||||||
|
// Create a unique name for the container
|
||||||
|
String containerName = messageReference.getDataLocation();
|
||||||
|
|
||||||
|
// Create the container and return a container client object
|
||||||
|
BlobContainerClient containerClient = this.blobServiceClient
|
||||||
|
.getBlobContainerClient(containerName);
|
||||||
|
if (!containerClient.exists()) {
|
||||||
|
containerClient.create();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a reference to a blob
|
||||||
|
BlobClient blobClient = containerClient.getBlobClient(messageReference.getDataFileName());
|
||||||
|
|
||||||
|
// Upload the blob
|
||||||
|
blobClient.upload(BinaryData.fromObject(message));
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.info(e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern;
|
||||||
|
|
||||||
|
import com.microsoft.azure.functions.HttpResponseMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpStatus;
|
||||||
|
import com.microsoft.azure.functions.HttpStatusType;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The mock for HttpResponseMessage, can be used in unit tests to verify if the
|
||||||
|
* returned response by HTTP trigger function is correct or not.
|
||||||
|
*/
|
||||||
|
public class HttpResponseMessageMock implements HttpResponseMessage {
|
||||||
|
private int httpStatusCode;
|
||||||
|
private HttpStatusType httpStatus;
|
||||||
|
private Object body;
|
||||||
|
private Map<String, String> headers;
|
||||||
|
|
||||||
|
public HttpResponseMessageMock(HttpStatusType status, Map<String, String> headers, Object body) {
|
||||||
|
this.httpStatus = status;
|
||||||
|
this.httpStatusCode = status.value();
|
||||||
|
this.headers = headers;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpStatusType getStatus() {
|
||||||
|
return this.httpStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getStatusCode() {
|
||||||
|
return httpStatusCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHeader(String key) {
|
||||||
|
return this.headers.get(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getBody() {
|
||||||
|
return this.body;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HttpResponseMessageBuilderMock implements HttpResponseMessage.Builder {
|
||||||
|
private Object body;
|
||||||
|
private int httpStatusCode;
|
||||||
|
private Map<String, String> headers = new HashMap<>();
|
||||||
|
private HttpStatusType httpStatus;
|
||||||
|
|
||||||
|
public Builder status(HttpStatus status) {
|
||||||
|
this.httpStatusCode = status.value();
|
||||||
|
this.httpStatus = status;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Builder status(HttpStatusType httpStatusType) {
|
||||||
|
this.httpStatusCode = httpStatusType.value();
|
||||||
|
this.httpStatus = httpStatusType;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage.Builder header(String key, String value) {
|
||||||
|
this.headers.put(key, value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage.Builder body(Object body) {
|
||||||
|
this.body = body;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage build() {
|
||||||
|
return new HttpResponseMessageMock(this.httpStatus, this.headers, this.body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,181 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.consumer.callcostprocessor.functions;
|
||||||
|
|
||||||
|
import com.iluwatar.claimcheckpattern.HttpResponseMessageMock;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.Message;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageBody;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageReference;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageCostDetail;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
|
||||||
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
|
import com.microsoft.azure.functions.HttpRequestMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpResponseMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpStatus;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for Function class.
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class UsageCostProcessorFunctionTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
MessageHandlerUtility<UsageDetail> mockMessageHandlerUtilityForUsageADetail;
|
||||||
|
@Mock
|
||||||
|
MessageHandlerUtility<UsageCostDetail> mockMessageHandlerUtilityForUsageCostDetail;
|
||||||
|
@Mock
|
||||||
|
ExecutionContext context;
|
||||||
|
|
||||||
|
Message<UsageCostDetail> messageToDrop;
|
||||||
|
Message<UsageDetail> messageToRead;
|
||||||
|
MessageReference messageReference;
|
||||||
|
@InjectMocks
|
||||||
|
UsageCostProcessorFunction usageCostProcessorFunction;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
var messageBodyUsageDetail = new MessageBody<UsageDetail>();
|
||||||
|
var usageDetailsList = new ArrayList<UsageDetail>();
|
||||||
|
|
||||||
|
var messageBodyUsageCostDetail = new MessageBody<UsageCostDetail>();
|
||||||
|
var usageCostDetailsList = new ArrayList<UsageCostDetail>();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
var usageDetail = new UsageDetail();
|
||||||
|
usageDetail.setUserId("userId" + i);
|
||||||
|
usageDetail.setData(i + 1);
|
||||||
|
usageDetail.setDuration(i + 1);
|
||||||
|
usageDetailsList.add(usageDetail);
|
||||||
|
|
||||||
|
var usageCostDetail = new UsageCostDetail();
|
||||||
|
usageCostDetail.setUserId(usageDetail.getUserId());
|
||||||
|
usageCostDetail.setDataCost(usageDetail.getData() * 0.20);
|
||||||
|
usageCostDetail.setCallCost(usageDetail.getDuration() * 0.30);
|
||||||
|
usageCostDetailsList.add(usageCostDetail);
|
||||||
|
}
|
||||||
|
messageBodyUsageDetail.setData(usageDetailsList);
|
||||||
|
messageBodyUsageCostDetail.setData(usageCostDetailsList);
|
||||||
|
|
||||||
|
// Create message header
|
||||||
|
var messageHeader = new MessageHeader();
|
||||||
|
messageHeader.setId(UUID.randomUUID().toString());
|
||||||
|
messageHeader.setSubject("UsageDetailPublisher");
|
||||||
|
messageHeader.setTopic("usagecostprocessorfunction-topic");
|
||||||
|
messageHeader.setEventType("UsageDetail");
|
||||||
|
messageHeader.setEventTime(OffsetDateTime.now().toString());
|
||||||
|
this.messageReference = new MessageReference("callusageapp", "d8284456-dfff-4bd4-9cef-ea99f70f4835/input.json");
|
||||||
|
messageHeader.setData(messageReference);
|
||||||
|
messageHeader.setDataVersion("v1.0");
|
||||||
|
|
||||||
|
// Create entire message
|
||||||
|
messageToRead = new Message<>();
|
||||||
|
messageToRead.setMessageHeader(messageHeader);
|
||||||
|
messageToRead.setMessageBody(messageBodyUsageDetail);
|
||||||
|
|
||||||
|
messageToDrop = new Message<>();
|
||||||
|
messageToDrop.setMessageHeader(messageHeader);
|
||||||
|
messageToDrop.setMessageBody(messageBodyUsageCostDetail);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for HttpTriggerJava method.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldTriggerHttpAzureFunctionJavaWithSubscriptionValidationEventType() throws Exception {
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
|
||||||
|
String fileAbsolutePath = getClass().getResource("/subscriptionValidationEvent.json").getPath()
|
||||||
|
.replaceAll("%20", " "), jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
|
||||||
|
doReturn(Optional.of(jsonBody)).when(req).getBody();
|
||||||
|
doAnswer(new Answer<HttpResponseMessage.Builder>() {
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
|
||||||
|
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
|
||||||
|
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
|
||||||
|
}
|
||||||
|
}).when(req).createResponseBuilder(any(HttpStatus.class));
|
||||||
|
|
||||||
|
final ExecutionContext context = mock(ExecutionContext.class);
|
||||||
|
|
||||||
|
// Invoke
|
||||||
|
final HttpResponseMessage ret = this.usageCostProcessorFunction.run(req, context);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(ret.getStatus(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldTriggerHttpAzureFunctionJavaWithUsageDetailEventType() throws Exception {
|
||||||
|
// Setup
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
|
||||||
|
String fileAbsolutePath = getClass().getResource("/usageDetailEvent.json").getPath().replaceAll("%20", " "),
|
||||||
|
jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
|
||||||
|
doReturn(Optional.of(jsonBody)).when(req).getBody();
|
||||||
|
doReturn(Logger.getGlobal()).when(context).getLogger();
|
||||||
|
|
||||||
|
when(this.mockMessageHandlerUtilityForUsageADetail.readFromPersistantStorage(any(MessageReference.class),
|
||||||
|
eq(Logger.getGlobal()))).thenReturn(messageToRead);
|
||||||
|
doAnswer(new Answer<HttpResponseMessage.Builder>() {
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
|
||||||
|
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
|
||||||
|
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
|
||||||
|
}
|
||||||
|
}).when(req).createResponseBuilder(any(HttpStatus.class));
|
||||||
|
|
||||||
|
assertNotNull(this.mockMessageHandlerUtilityForUsageADetail);
|
||||||
|
assertEquals(this.messageToRead, this.mockMessageHandlerUtilityForUsageADetail
|
||||||
|
.readFromPersistantStorage(this.messageReference, Logger.getGlobal()));
|
||||||
|
|
||||||
|
// Invoke
|
||||||
|
final HttpResponseMessage ret = this.usageCostProcessorFunction.run(req, context);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(HttpStatus.OK, ret.getStatus());
|
||||||
|
assertEquals("Message is dropped successfully", ret.getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.producer.calldetails.functions;
|
||||||
|
|
||||||
|
import com.iluwatar.claimcheckpattern.HttpResponseMessageMock;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.MessageHeader;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.EventHandlerUtility;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
|
||||||
|
import com.microsoft.azure.functions.ExecutionContext;
|
||||||
|
import com.microsoft.azure.functions.HttpRequestMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpResponseMessage;
|
||||||
|
import com.microsoft.azure.functions.HttpStatus;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import org.mockito.stubbing.Answer;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.*;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for Function class.
|
||||||
|
*/
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class UsageDetailPublisherFunctionTest {
|
||||||
|
@Mock
|
||||||
|
MessageHandlerUtility<UsageDetail> mockMessageHandlerUtility;
|
||||||
|
@Mock
|
||||||
|
EventHandlerUtility<MessageHeader> mockEventHandlerUtility;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
UsageDetailPublisherFunction usageDetailPublisherFunction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for HttpTriggerJava method.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void shouldTriggerHttpAzureFunctionJavaWithSubscriptionValidationEventType() throws Exception {
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
|
||||||
|
String fileAbsolutePath = getClass().getResource("/subscriptionValidationEvent.json").getPath()
|
||||||
|
.replaceAll("%20", " "), jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
|
||||||
|
doReturn(Optional.of(jsonBody)).when(req).getBody();
|
||||||
|
doAnswer(new Answer<HttpResponseMessage.Builder>() {
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
|
||||||
|
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
|
||||||
|
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
|
||||||
|
}
|
||||||
|
}).when(req).createResponseBuilder(any(HttpStatus.class));
|
||||||
|
|
||||||
|
final ExecutionContext context = mock(ExecutionContext.class);
|
||||||
|
|
||||||
|
// Invoke
|
||||||
|
final HttpResponseMessage ret = this.usageDetailPublisherFunction.run(req, context);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(ret.getStatus(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void shouldTriggerHttpAzureFunctionJavaWithUsageDetailEventType() throws Exception {
|
||||||
|
|
||||||
|
// Setup
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
|
||||||
|
String fileAbsolutePath = getClass().getResource("/usageDetailEvent.json").getPath().replaceAll("%20", " "),
|
||||||
|
jsonBody = Files.readString(Paths.get(fileAbsolutePath)).replaceAll("\n", " ");
|
||||||
|
doReturn(Optional.of(jsonBody)).when(req).getBody();
|
||||||
|
doAnswer(new Answer<HttpResponseMessage.Builder>() {
|
||||||
|
@Override
|
||||||
|
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
|
||||||
|
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
|
||||||
|
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
|
||||||
|
}
|
||||||
|
}).when(req).createResponseBuilder(any(HttpStatus.class));
|
||||||
|
|
||||||
|
final ExecutionContext context = mock(ExecutionContext.class);
|
||||||
|
doReturn(Logger.getGlobal()).when(context).getLogger();
|
||||||
|
|
||||||
|
// Invoke
|
||||||
|
final HttpResponseMessage ret = this.usageDetailPublisherFunction.run(req, context);
|
||||||
|
|
||||||
|
// Verify
|
||||||
|
assertEquals(ret.getStatus(), HttpStatus.OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.utility;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
import com.azure.core.util.BinaryData;
|
||||||
|
import com.azure.messaging.eventgrid.EventGridPublisherClient;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.Message;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.UsageDetail;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class EventHandlerUtilityTest {
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
EventGridPublisherClient<BinaryData> mockCustomEventClient;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
EventHandlerUtility<Message<UsageDetail>> eventHandlerUtility;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
|
||||||
|
System.setProperty("EventGridURL", "https://www.dummyEndpoint.com/api/events");
|
||||||
|
System.setProperty("EventGridKey", "EventGridURL");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPublishEvent() {
|
||||||
|
doNothing().when(mockCustomEventClient).sendEvent(any(BinaryData.class));
|
||||||
|
eventHandlerUtility.publishEvent(null, Logger.getLogger("logger"));
|
||||||
|
verify(mockCustomEventClient, times(1)).sendEvent(any(BinaryData.class));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldPublishEventWithNullLogger() {
|
||||||
|
eventHandlerUtility.publishEvent(null, null);
|
||||||
|
verify(mockCustomEventClient, times(1)).sendEvent(any(BinaryData.class));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* The MIT License
|
||||||
|
* Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in
|
||||||
|
* all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
* THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.iluwatar.claimcheckpattern.utility;
|
||||||
|
|
||||||
|
import com.azure.storage.blob.BlobClient;
|
||||||
|
import com.azure.storage.blob.BlobContainerClient;
|
||||||
|
import com.azure.storage.blob.BlobServiceClient;
|
||||||
|
import com.iluwatar.claimcheckpattern.domain.*;
|
||||||
|
import com.iluwatar.claimcheckpattern.utility.MessageHandlerUtility;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
|
import org.mockito.InjectMocks;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
import org.mockito.junit.jupiter.MockitoExtension;
|
||||||
|
import java.time.OffsetDateTime;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
|
import static org.mockito.Mockito.*;
|
||||||
|
|
||||||
|
@ExtendWith(MockitoExtension.class)
|
||||||
|
public class MessageHandlerUtilityTest {
|
||||||
|
@Mock
|
||||||
|
private BlobClient mockBlobClient;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BlobContainerClient mockContainerClient;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private BlobServiceClient mockBlobServiceClient;
|
||||||
|
|
||||||
|
@InjectMocks
|
||||||
|
private MessageHandlerUtility<UsageDetail> messageHandlerUtility;
|
||||||
|
|
||||||
|
private Message<UsageDetail> messageToPublish;
|
||||||
|
private MessageReference messageReference;
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setUp() {
|
||||||
|
System.setProperty("BlobStorageConnectionString", "https://www.dummyEndpoint.com/api/blobs");
|
||||||
|
|
||||||
|
var messageBody = new MessageBody<UsageDetail>();
|
||||||
|
var usageDetailsList = new ArrayList<UsageDetail>();
|
||||||
|
var random = new Random();
|
||||||
|
for (int i = 0; i < 51; i++) {
|
||||||
|
var usageDetail = new UsageDetail();
|
||||||
|
usageDetail.setUserId("userId" + i);
|
||||||
|
usageDetail.setData(random.nextInt(500));
|
||||||
|
usageDetail.setDuration(random.nextInt(500));
|
||||||
|
|
||||||
|
usageDetailsList.add(usageDetail);
|
||||||
|
}
|
||||||
|
messageBody.setData(usageDetailsList);
|
||||||
|
|
||||||
|
// Create message header
|
||||||
|
var messageHeader = new MessageHeader();
|
||||||
|
messageHeader.setId(UUID.randomUUID().toString());
|
||||||
|
messageHeader.setSubject("UsageDetailPublisher");
|
||||||
|
messageHeader.setTopic("usagecostprocessorfunction-topic");
|
||||||
|
messageHeader.setEventType("UsageDetail");
|
||||||
|
messageHeader.setEventTime(OffsetDateTime.now().toString());
|
||||||
|
this.messageReference = new MessageReference("callusageapp", messageHeader.getId() + "/input.json");
|
||||||
|
messageHeader.setData(messageReference);
|
||||||
|
messageHeader.setDataVersion("v1.0");
|
||||||
|
|
||||||
|
// Create entire message
|
||||||
|
this.messageToPublish = new Message<>();
|
||||||
|
this.messageToPublish.setMessageHeader(messageHeader);
|
||||||
|
this.messageToPublish.setMessageBody(messageBody);
|
||||||
|
|
||||||
|
when(mockContainerClient.getBlobClient(anyString())).thenReturn(mockBlobClient);
|
||||||
|
when(mockBlobServiceClient.getBlobContainerClient(anyString())).thenReturn(mockContainerClient);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldDropMessageToPersistantStorage() {
|
||||||
|
messageHandlerUtility.dropToPersistantStorage(messageToPublish, Logger.getLogger("logger"));
|
||||||
|
verify(mockBlobServiceClient, times(1)).getBlobContainerClient(anyString());
|
||||||
|
// verify(mockContainerClient, times(0)).exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void shouldReadMessageFromPersistantStorage() {
|
||||||
|
|
||||||
|
messageHandlerUtility.readFromPersistantStorage(messageReference, Logger.getLogger("logger"));
|
||||||
|
verify(mockBlobServiceClient, times(1)).getBlobContainerClient(anyString());
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
mock-maker-inline
|
@ -0,0 +1,15 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"validationCode": "C12F266E-79D9-4C0A-9922-5EF6201A34C2",
|
||||||
|
"validationUrl": "https://rp-centralindia.eventgrid.azure.net:553/eventsubscriptions/usagedetailpublisherfunction-subscription/validate?idu003dC12F266E-79D9-4C0A-9922-5EF6201A34C2u0026tu003d2021-10-26T08:10:52.4999377Zu0026apiVersionu003d2020-10-15-previewu0026tokenu003d30kEVoL8rAOWzQv0buurhrKnbP%2bGMtHObbA%2bax6wb4Y%3d"
|
||||||
|
},
|
||||||
|
"dataVersion": "2",
|
||||||
|
"eventTime": "2021-10-26T08:10:52.4999377Z",
|
||||||
|
"eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
|
||||||
|
"id": "e2a8466b-3dc0-46b7-bb7d-b999e51a2848",
|
||||||
|
"metadataVersion": "1",
|
||||||
|
"subject": "",
|
||||||
|
"topic": "/subscriptions/0fef643d-a6b1-48f9-a256-53fbd0d22f48/resourceGroups/resource-group-ccp/providers/Microsoft.EventGrid/domains/event-grid-domains-ccp/topics/usagedetailpublisherfunction-topic"
|
||||||
|
}
|
||||||
|
]
|
@ -0,0 +1,15 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"dataFileName": "d8284456-dfff-4bd4-9cef-ea99f70f4835/input.json",
|
||||||
|
"dataLocation": "callusageapp"
|
||||||
|
},
|
||||||
|
"dataVersion": "v1.0",
|
||||||
|
"eventTime": "2021-10-25T19:17:15.7468501Z",
|
||||||
|
"eventType": "UsageDetail",
|
||||||
|
"id": "d8284456-dfff-4bd4-9cef-ea99f70f4835",
|
||||||
|
"metadataVersion": "1",
|
||||||
|
"subject": "UsageDetailPublisher",
|
||||||
|
"topic": "/subscriptions/0fef643d-a6b1-48f9-a256-ea99f70f4835/resourceGroups/resource-group-ccp/providers/Microsoft.EventGrid/domains/event-grid-domains-ccp/topics/usagecostprocessorfunction-topic"
|
||||||
|
}
|
||||||
|
]
|
BIN
cloud-claim-check-pattern/etc/Claim-Check-Pattern.png
Normal file
BIN
cloud-claim-check-pattern/etc/Claim-Check-Pattern.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 52 KiB |
117
cloud-claim-check-pattern/etc/claim-check-pattern.urm.puml
Normal file
117
cloud-claim-check-pattern/etc/claim-check-pattern.urm.puml
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
@startuml
|
||||||
|
class UsageDetailPublisherFunction [[java:com.iluwatar.producer.calldetails.functions]] {
|
||||||
|
-messageHandlerUtility: MessageHandlerUtility<UsageDetail>
|
||||||
|
-eventHandlerUtility: EventHandlerUtility<MessageHeader>
|
||||||
|
+run(): HttpResponseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
class UsageCostProcessorFunction [[java:com.iluwatar.consumer.callcostprocessor.functions]] {
|
||||||
|
-messageHandlerUtilityForUsageDetail: MessageHandlerUtility<UsageDetail>
|
||||||
|
-messageHandlerUtilityForUsageCostDetail: MessageHandlerUtility<UsageCostDetail>
|
||||||
|
+run(): HttpResponseMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
class "MessageHandlerUtility<T>" as MessageHandlerUtility_T [[java:com.iluwatar.claimcheckpattern.utility]] {
|
||||||
|
+readFromPersistantStorage(messageReference: MessageReference, logger: Logger): Message
|
||||||
|
+dropToPersistantStorage(message: Message, logger: Logger): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class "EventHandlerUtility<T>" as EventHandlerUtility_T [[java:com.callusage.utility.PersistentLocalStorageUtility]] {
|
||||||
|
+publishEvent(customEvent: T, logger: Logger): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class "Message<T>" as Message_T [[java:com.iluwatar.claimcheckpattern.domain]] {
|
||||||
|
-messageHeader: MessageHeader
|
||||||
|
-messageData: MessageData<T>
|
||||||
|
+Message(messageHeader: MessageHeader, messageData: MessageData<T>)
|
||||||
|
+getMessageData(): MessageData<T>
|
||||||
|
+getMessageHeader(): MessageHeader
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class MessageHeader [[java:com.iluwatar.claimcheckpattern.domain]] {
|
||||||
|
-id: String
|
||||||
|
-subject: String
|
||||||
|
-topic: String
|
||||||
|
-eventType: String
|
||||||
|
-eventTime: String
|
||||||
|
-data: Object
|
||||||
|
-dataVersion: String
|
||||||
|
+getId(): String
|
||||||
|
+setId(id: String): void
|
||||||
|
+getSubject(): String
|
||||||
|
+setSubject(subject: String): void
|
||||||
|
+getTopic(): String
|
||||||
|
+setTopic(topic: String): void
|
||||||
|
+getEventType(): String
|
||||||
|
+setEventType(eventType: String): void
|
||||||
|
+getEventTime(): String
|
||||||
|
+setEventTime(eventTime: String): void
|
||||||
|
+getData(): Object
|
||||||
|
+setData(data: Object): void
|
||||||
|
+getDataVersion(): String
|
||||||
|
+setDataVersion(dataVersion:String): void
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class "MessageBody<T>" as MessageBody_T [[java:com.iluwatar.claimcheckpattern.domain]] {
|
||||||
|
-data: List[] T
|
||||||
|
+getData(): List[] T
|
||||||
|
+setData(data:List[] T): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class MessageReference [[java:com.iluwatar.claimcheckpattern.domain]] {
|
||||||
|
-dataLocation: String
|
||||||
|
-dataFileName: String
|
||||||
|
+getDataLocation(): String
|
||||||
|
+setDataLocation(dataLocation:String): void
|
||||||
|
+getDataFileName(): String
|
||||||
|
+setDataFileName(dataFileName:String): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class UsageDetail [[java:com.iluwatar.claimcheckpattern.domain]] {
|
||||||
|
-userId: String
|
||||||
|
-duration: int
|
||||||
|
-data: int
|
||||||
|
+getUserId(): String
|
||||||
|
+setUserId(userId: String): void
|
||||||
|
+getDuration(): long
|
||||||
|
+setDuration(duration: long): void
|
||||||
|
+getData(): long
|
||||||
|
+setData(data: long): void
|
||||||
|
}
|
||||||
|
|
||||||
|
class UsageCostDetail [[java:com.iluwatar.claimcheckpattern.domain]] {
|
||||||
|
-userId: String
|
||||||
|
-callCost: double
|
||||||
|
-dataCost: double
|
||||||
|
+getUserId(): String
|
||||||
|
+setUserId(userId: String): void
|
||||||
|
+getCallCost(): double
|
||||||
|
+setCallCost(callCost:double): void
|
||||||
|
+getDataCost(): double
|
||||||
|
+setDataCost(dataCost:double) : void
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Message_T "1" *-- "1" MessageHeader : has
|
||||||
|
Message_T "1" *-- "1" MessageBody_T : has
|
||||||
|
MessageHeader "1" *-- "1" MessageReference : has as data object
|
||||||
|
MessageBody_T "1" *-- "1" UsageDetail: has
|
||||||
|
MessageBody_T "1" *-- "1" UsageCostDetail: has
|
||||||
|
|
||||||
|
EventHandlerUtility_T "1" *-- "1" MessageHeader: has
|
||||||
|
MessageHandlerUtility_T "1" *-- "1" Message_T: has
|
||||||
|
|
||||||
|
UsageDetailPublisherFunction "1" *-- "1" MessageHandlerUtility_T : has
|
||||||
|
UsageDetailPublisherFunction "1" *-- "1" EventHandlerUtility_T : has
|
||||||
|
|
||||||
|
UsageCostProcessorFunction "1" *-- "1" MessageHandlerUtility_T : has
|
||||||
|
UsageCostProcessorFunction "1" *-- "1" MessageHandlerUtility_T : has
|
||||||
|
@enduml
|
BIN
cloud-claim-check-pattern/etc/class-diagram.png
Normal file
BIN
cloud-claim-check-pattern/etc/class-diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 108 KiB |
69
cloud-claim-check-pattern/pom.xml
Normal file
69
cloud-claim-check-pattern/pom.xml
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
The MIT License
|
||||||
|
Copyright © 2014-2021 Ilkka Seppälä
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||||
|
THE SOFTWARE.
|
||||||
|
|
||||||
|
-->
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
|
||||||
|
<parent>
|
||||||
|
<groupId>com.iluwatar</groupId>
|
||||||
|
<artifactId>java-design-patterns</artifactId>
|
||||||
|
<version>1.25.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<artifactId>claim-check-pattern</artifactId>
|
||||||
|
<packaging>pom</packaging>
|
||||||
|
|
||||||
|
<modules>
|
||||||
|
<module>call-usage-app</module>
|
||||||
|
</modules>
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.code.gson</groupId>
|
||||||
|
<artifactId>gson</artifactId>
|
||||||
|
<version>2.8.8</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.junit.jupiter</groupId>
|
||||||
|
<artifactId>junit-jupiter</artifactId>
|
||||||
|
<version>5.8.1</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-junit-jupiter</artifactId>
|
||||||
|
<version>4.0.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-inline</artifactId>
|
||||||
|
<version>4.0.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
</project>
|
@ -1,3 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -27,7 +28,7 @@
|
|||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
<artifactId>collection-pipeline</artifactId>
|
<artifactId>collection-pipeline</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!--
|
<!--
|
||||||
|
|
||||||
The MIT License
|
The MIT License
|
||||||
@ -23,15 +23,13 @@
|
|||||||
THE SOFTWARE.
|
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"
|
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<parent>
|
<parent>
|
||||||
<groupId>com.iluwatar</groupId>
|
<groupId>com.iluwatar</groupId>
|
||||||
<artifactId>java-design-patterns</artifactId>
|
<artifactId>java-design-patterns</artifactId>
|
||||||
<version>1.25.0-SNAPSHOT</version>
|
<version>1.26.0-SNAPSHOT</version>
|
||||||
</parent>
|
</parent>
|
||||||
|
|
||||||
<artifactId>combinator</artifactId>
|
<artifactId>combinator</artifactId>
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
@ -40,5 +38,4 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user