Compare commits
2 Commits
vuepress
...
all-contri
Author | SHA1 | Date | |
---|---|---|---|
7f5354cbc4 | |||
9d2e0c6d7c |
@ -1577,60 +1577,6 @@
|
|||||||
"contributions": [
|
"contributions": [
|
||||||
"code"
|
"code"
|
||||||
]
|
]
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "mortezaadi",
|
|
||||||
"name": "Morteza Adigozalpour",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/1329687?v=4",
|
|
||||||
"profile": "https://github.com/mortezaadi",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "tan31989",
|
|
||||||
"name": "Nagaraj Tantri",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/3784194?v=4",
|
|
||||||
"profile": "https://stackoverflow.com/users/308565/nagaraj-tantri",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "frascu",
|
|
||||||
"name": "Francesco Scuccimarri",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/7107651?v=4",
|
|
||||||
"profile": "http://scuccimarri.it",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "Conhan93",
|
|
||||||
"name": "Conny Hansson",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/71334757?v=4",
|
|
||||||
"profile": "https://github.com/Conhan93",
|
|
||||||
"contributions": [
|
|
||||||
"doc"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "muklasr",
|
|
||||||
"name": "Muklas Rahmanto",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/43443753?v=4",
|
|
||||||
"profile": "http://muklasr.medium.com",
|
|
||||||
"contributions": [
|
|
||||||
"translation"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "VxDxK",
|
|
||||||
"name": "Vadim",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/38704817?v=4",
|
|
||||||
"profile": "https://github.com/VxDxK",
|
|
||||||
"contributions": [
|
|
||||||
"translation"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"contributorsPerLine": 4,
|
"contributorsPerLine": 4,
|
||||||
|
12
README.md
12
README.md
@ -10,12 +10,12 @@
|
|||||||
[](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), [**id**](localization/id/README.md), [**ru**](localization/ru/README.md)
|
Read in different language : [**zh**](/localization/zh/README.md), [**ko**](/localization/ko/README.md), [**fr**](/localization/fr/README.md), [**tr**](/localization/tr/README.md), [**ar**](/localization/ar/README.md), [**es**](/localization/es/README.md), [**pt**](/localization/pt/README.md)
|
||||||
|
|
||||||
<br/>
|
<br/>
|
||||||
|
|
||||||
@ -335,14 +335,6 @@ This project is licensed under the terms of the MIT license.
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td align="center"><a href="https://github.com/karthikbhat13"><img src="https://avatars.githubusercontent.com/u/22431014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>karthikbhat13</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=karthikbhat13" title="Code">💻</a></td>
|
<td align="center"><a href="https://github.com/karthikbhat13"><img src="https://avatars.githubusercontent.com/u/22431014?v=4?s=100" width="100px;" alt=""/><br /><sub><b>karthikbhat13</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=karthikbhat13" title="Code">💻</a></td>
|
||||||
<td align="center"><a href="https://github.com/mortezaadi"><img src="https://avatars.githubusercontent.com/u/1329687?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Morteza Adigozalpour</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=mortezaadi" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://stackoverflow.com/users/308565/nagaraj-tantri"><img src="https://avatars.githubusercontent.com/u/3784194?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nagaraj Tantri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=tan31989" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://scuccimarri.it"><img src="https://avatars.githubusercontent.com/u/7107651?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Francesco Scuccimarri</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=frascu" title="Code">💻</a></td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/Conhan93"><img src="https://avatars.githubusercontent.com/u/71334757?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Conny Hansson</b></sub></a><br /><a href="https://github.com/iluwatar/java-design-patterns/commits?author=Conhan93" title="Documentation">📖</a></td>
|
|
||||||
<td align="center"><a href="http://muklasr.medium.com"><img src="https://avatars.githubusercontent.com/u/43443753?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Muklas Rahmanto</b></sub></a><br /><a href="#translation-muklasr" title="Translation">🌍</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/VxDxK"><img src="https://avatars.githubusercontent.com/u/38704817?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Vadim</b></sub></a><br /><a href="#translation-VxDxK" title="Translation">🌍</a></td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Abstract Document
|
title: Abstract Document
|
||||||
category: Structural
|
folder: abstract-document
|
||||||
|
permalink: /patterns/abstract-document/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Abstract Factory
|
title: Abstract Factory
|
||||||
category: Creational
|
folder: abstract-factory
|
||||||
|
permalink: /patterns/abstract-factory/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Active Object
|
title: Active Object
|
||||||
category: Concurrency
|
folder: active-object
|
||||||
|
permalink: /patterns/active-object/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
@ -8,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
|
||||||
|
|
||||||
@ -67,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 the wastelands.",name());
|
logger.info("{} has started to roam and the wastelands.",name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -79,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 invoke and execute methods.
|
We can see that any class that will extend the ActiveCreature class will have its own thread of control to execute and invocate methods.
|
||||||
|
|
||||||
For example, the Orc class:
|
For example, the Orc class:
|
||||||
|
|
||||||
@ -93,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) {
|
||||||
@ -120,4 +123,4 @@ Now, we can create multiple creatures such as Orcs, tell them to eat and roam, a
|
|||||||
|
|
||||||
## Class diagram
|
## Class diagram
|
||||||
|
|
||||||

|

|
||||||
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 19 KiB |
@ -82,7 +82,7 @@ public abstract class ActiveCreature {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Roam the wastelands.
|
* Roam in 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 {
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Acyclic Visitor
|
title: Acyclic Visitor
|
||||||
category: Behavioral
|
folder: acyclic-visitor
|
||||||
|
permalink: /patterns/acyclic-visitor/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Adapter
|
title: Adapter
|
||||||
category: Structural
|
folder: adapter
|
||||||
|
permalink: /patterns/adapter/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Aggregator Microservices
|
title: Aggregator Microservices
|
||||||
category: Architectural
|
folder: aggregator-microservices
|
||||||
|
permalink: /patterns/aggregator-microservices/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
|
@ -48,7 +48,7 @@ class AggregatorTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Ambassador
|
title: Ambassador
|
||||||
category: Structural
|
folder: ambassador
|
||||||
|
permalink: /patterns/ambassador/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: API Gateway
|
title: API Gateway
|
||||||
category: Architectural
|
folder: api-gateway
|
||||||
|
permalink: /patterns/api-gateway/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
|
@ -48,7 +48,7 @@ class ApiGatewayTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void setup() {
|
public void setup() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Arrange/Act/Assert
|
title: Arrange/Act/Assert
|
||||||
category: Idiom
|
folder: arrange-act-assert
|
||||||
|
permalink: /patterns/arrange-act-assert/
|
||||||
|
categories: Idiom
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Testing
|
- Testing
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Async Method Invocation
|
title: Async Method Invocation
|
||||||
category: Concurrency
|
folder: async-method-invocation
|
||||||
|
permalink: /patterns/async-method-invocation/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -68,7 +68,7 @@ class ThreadAsyncExecutorTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Balking
|
title: Balking
|
||||||
category: Concurrency
|
folder: balking
|
||||||
|
permalink: /patterns/balking/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Bridge
|
title: Bridge
|
||||||
category: Structural
|
folder: bridge
|
||||||
|
permalink: /patterns/bridge/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Builder
|
title: Builder
|
||||||
category: Creational
|
folder: builder
|
||||||
|
permalink: /patterns/builder/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Business Delegate
|
title: Business Delegate
|
||||||
category: Structural
|
folder: business-delegate
|
||||||
|
permalink: /patterns/business-delegate/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Bytecode
|
title: Bytecode
|
||||||
category: Behavioral
|
folder: bytecode
|
||||||
|
permalink: /patterns/bytecode/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Game programming
|
- Game programming
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Caching
|
title: Caching
|
||||||
category: Behavioral
|
folder: caching
|
||||||
|
permalink: /patterns/caching/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
@ -40,29 +43,39 @@ 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` interface which handles
|
which is a simple Java object containing the user account details, and `DbManager` which handles
|
||||||
reading and writing of these objects to/from database.
|
reading and writing of these objects to/from MongoDB database.
|
||||||
|
|
||||||
```java
|
```java
|
||||||
@Data
|
@Setter
|
||||||
|
@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;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface DbManager {
|
@Slf4j
|
||||||
|
public final class DbManager {
|
||||||
|
|
||||||
void connect();
|
private static MongoClient mongoClient;
|
||||||
void disconnect();
|
private static MongoDatabase db;
|
||||||
|
|
||||||
UserAccount readFromDb(String userId);
|
private DbManager() { /*...*/ }
|
||||||
UserAccount writeToDb(UserAccount userAccount);
|
|
||||||
UserAccount updateDb(UserAccount userAccount);
|
public static void createVirtualDb() { /*...*/ }
|
||||||
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) { /*...*/ }
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -158,43 +171,30 @@ 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 UserAccount readThrough(final String userId) {
|
public static UserAccount readThrough(String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Found in Cache!");
|
LOGGER.info("# Cache Hit!");
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
LOGGER.info("# Not found in cache! Go to DB!!");
|
LOGGER.info("# Cache Miss!");
|
||||||
UserAccount userAccount = dbManager.readFromDb(userId);
|
UserAccount userAccount = DbManager.readFromDb(userId);
|
||||||
cache.set(userId, userAccount);
|
cache.set(userId, userAccount);
|
||||||
return userAccount;
|
return userAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeThrough(final UserAccount userAccount) {
|
public static void writeThrough(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,39 +225,34 @@ 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 void initDb() { /* ... */ }
|
public static void initDb(boolean useMongoDb) { /* ... */ }
|
||||||
|
|
||||||
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 UserAccount find(final String userId) {
|
public static UserAccount find(String userId) {
|
||||||
LOGGER.info("Trying to find {} in cache", userId);
|
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH
|
return CacheStore.readThrough(userId);
|
||||||
|| 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 void save(final UserAccount userAccount) {
|
public static void save(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);
|
||||||
}
|
}
|
||||||
@ -277,35 +272,24 @@ Here is what we do in the main class of the application.
|
|||||||
@Slf4j
|
@Slf4j
|
||||||
public class App {
|
public class App {
|
||||||
|
|
||||||
public static void main(final String[] args) {
|
public static void main(String[] args) {
|
||||||
boolean isDbMongo = isDbMongo(args);
|
AppManager.initDb(false);
|
||||||
if(isDbMongo){
|
AppManager.initCacheCapacity(3);
|
||||||
LOGGER.info("Using the Mongo database engine to run the application.");
|
var app = new App();
|
||||||
} 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);
|
||||||
appManager.save(userAccount1);
|
LOGGER.info(AppManager.printCacheContent());
|
||||||
LOGGER.info(appManager.printCacheContent());
|
AppManager.find("001");
|
||||||
appManager.find("001");
|
AppManager.find("001");
|
||||||
appManager.find("001");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
public void useReadThroughAndWriteAroundStrategy() { /* ... */ }
|
||||||
@ -316,6 +300,16 @@ 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
|
||||||
|
|
||||||

|

|
||||||
|
@ -1,11 +0,0 @@
|
|||||||
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
|
|
@ -39,21 +39,19 @@
|
|||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mockito</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
<artifactId>mockito-junit-jupiter</artifactId>
|
<artifactId>mongodb-driver</artifactId>
|
||||||
<version>3.12.4</version>
|
<version>3.12.1</version>
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.mockito</groupId>
|
|
||||||
<artifactId>mockito-all</artifactId>
|
|
||||||
<version>1.10.19</version>
|
|
||||||
<scope>test</scope>
|
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.mongodb</groupId>
|
<groupId>org.mongodb</groupId>
|
||||||
<artifactId>mongo-java-driver</artifactId>
|
<artifactId>mongodb-driver-core</artifactId>
|
||||||
<version>3.4.1</version>
|
<version>3.0.4</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mongodb</groupId>
|
||||||
|
<artifactId>bson</artifactId>
|
||||||
|
<version>3.0.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<!--
|
<!--
|
||||||
|
@ -1,62 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
|
||||||
* resources by not releasing the resources immediately after their use.
|
* the resources immediately after their use. The resources retain their identity, are kept in some
|
||||||
* The resources retain their identity, are kept in some fast-access storage,
|
* fast-access storage, and are re-used to avoid having to acquire them again. There are four main
|
||||||
* and are re-used to avoid having to acquire them again. There are four main
|
* caching strategies/techniques in this pattern; each with their own pros and cons. They are;
|
||||||
* caching strategies/techniques in this pattern; each with their own pros and
|
* <code>write-through</code> which writes data to the cache and DB in a single transaction,
|
||||||
* cons. They are <code>write-through</code> which writes data to the cache and
|
* <code>write-around</code> which writes data immediately into the DB instead of the cache,
|
||||||
* DB in a single transaction, <code>write-around</code> which writes data
|
* <code>write-behind</code> which writes data into the cache initially whilst the data is only
|
||||||
* immediately into the DB instead of the cache, <code>write-behind</code>
|
* written into the DB when the cache is full, and <code>cache-aside</code> which pushes the
|
||||||
* which writes data into the cache initially whilst the data is only
|
* responsibility of keeping the data synchronized in both data sources to the application itself.
|
||||||
* written into the DB when the cache is full, and <code>cache-aside</code>
|
* The <code>read-through</code> strategy is also included in the mentioned four strategies --
|
||||||
* which pushes the responsibility of keeping the data synchronized in both
|
* returns data from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and
|
||||||
* data sources to the application itself. The <code>read-through</code>
|
* stores it into the cache for future use. These strategies determine when the data in the cache
|
||||||
* strategy is also included in the mentioned four strategies --
|
* should be written back to the backing store (i.e. Database) and help keep both data sources
|
||||||
* returns data from the cache to the caller <b>if</b> it exists <b>else</b>
|
* synchronized/up-to-date. This pattern can improve performance and also helps to maintain
|
||||||
* queries from DB and stores it into the cache for future use. These strategies
|
* consistency between data held in the cache and the data in the underlying data store.
|
||||||
* 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
|
* <p>In this example, the user account ({@link UserAccount}) entity is used as the underlying
|
||||||
* as the underlying application data. The cache itself is implemented as an
|
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
|
||||||
* internal (Java) data structure. It adopts a Least-Recently-Used (LRU)
|
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four
|
||||||
* strategy for evicting data from itself when its full. The four
|
* strategies are individually tested. The testing of the cache is restricted towards saving and
|
||||||
* strategies are individually tested. The testing of the cache is restricted
|
* querying of user accounts from the underlying data store ( {@link DbManager}). The main class (
|
||||||
* towards saving and querying of user accounts from the
|
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
|
||||||
* underlying data store( {@link DbManager}). The main class ( {@link App}
|
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
|
||||||
* is not aware of the underlying mechanics of the application
|
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
|
||||||
* (i.e. save and query) and whether the data is coming from the cache or the
|
* (depending on the preferred caching policy/strategy).
|
||||||
* 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 -->
|
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i>
|
||||||
* 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
|
||||||
@ -65,67 +61,23 @@ 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(final String[] args) {
|
public static void main(String[] args) {
|
||||||
// VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
AppManager.initDb(false); // 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).
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -133,14 +85,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -148,21 +100,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");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -170,31 +122,23 @@ 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",
|
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
||||||
"Adam",
|
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
||||||
"He likes food.");
|
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
||||||
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",
|
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
|
||||||
"Yasha",
|
AppManager.save(userAccount6);
|
||||||
"She is an only child.");
|
LOGGER.info(AppManager.printCacheContent());
|
||||||
appManager.save(userAccount6);
|
AppManager.find("004");
|
||||||
LOGGER.info(appManager.printCacheContent());
|
LOGGER.info(AppManager.printCacheContent());
|
||||||
appManager.find("004");
|
|
||||||
LOGGER.info(appManager.printCacheContent());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -202,26 +146,20 @@ 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",
|
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
||||||
"Adam",
|
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
||||||
"He likes food.");
|
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
||||||
var userAccount4 = new UserAccount("004",
|
AppManager.save(userAccount3);
|
||||||
"Rita",
|
AppManager.save(userAccount4);
|
||||||
"She hates cats.");
|
AppManager.save(userAccount5);
|
||||||
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,80 +23,65 @@
|
|||||||
|
|
||||||
package com.iluwatar.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
import com.iluwatar.caching.database.DbManager;
|
import java.text.ParseException;
|
||||||
|
|
||||||
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
|
* AppManager helps to bridge the gap in communication between the main class and the application's
|
||||||
* and the application's back-end. DB connection is initialized through this
|
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
|
||||||
* class. The chosen caching strategy/policy is also initialized here.
|
* also initialized here. Before the cache can be used, the size of the cache has to be set.
|
||||||
* Before the cache can be used, the size of the cache has to be set.
|
* Depending on the chosen caching policy, AppManager will call the appropriate function in the
|
||||||
* Depending on the chosen caching policy, AppManager will call the
|
* CacheStore class.
|
||||||
* appropriate function in the CacheStore class.
|
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class AppManager {
|
public final 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
|
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
|
||||||
* MongoDB as its underlying data storage or a simple Java data structure
|
* data storage or a simple Java data structure to (temporarily) store the data/objects during
|
||||||
* to (temporarily) store the data/objects during runtime.
|
* runtime.
|
||||||
*/
|
*/
|
||||||
public void initDb() {
|
public static void initDb(boolean useMongoDb) {
|
||||||
dbManager.connect();
|
if (useMongoDb) {
|
||||||
|
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 void initCachingPolicy(final CachingPolicy policy) {
|
public static void initCachingPolicy(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 UserAccount find(final String userId) {
|
public static UserAccount find(String userId) {
|
||||||
LOGGER.info("Trying to find {} in cache", userId);
|
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
||||||
if (cachingPolicy == CachingPolicy.THROUGH
|
return CacheStore.readThrough(userId);
|
||||||
|| 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);
|
||||||
}
|
}
|
||||||
@ -105,55 +90,41 @@ public class AppManager {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Save user account.
|
* Save user account.
|
||||||
*
|
|
||||||
* @param userAccount {@link UserAccount}
|
|
||||||
*/
|
*/
|
||||||
public void save(final UserAccount userAccount) {
|
public static void save(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() {
|
||||||
* Returns String.
|
return CacheStore.print();
|
||||||
*
|
|
||||||
* @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 void saveAside(final UserAccount userAccount) {
|
private static void saveAside(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 UserAccount findAside(final String userId) {
|
private static UserAccount findAside(String userId) {
|
||||||
return Optional.ofNullable(cacheStore.get(userId))
|
return Optional.ofNullable(CacheStore.get(userId))
|
||||||
.or(() -> {
|
.or(() -> {
|
||||||
Optional<UserAccount> userAccount =
|
Optional<UserAccount> userAccount = Optional.ofNullable(DbManager.readFromDb(userId));
|
||||||
Optional.ofNullable(dbManager.readFromDb(userId));
|
userAccount.ifPresent(account -> CacheStore.set(userId, account));
|
||||||
userAccount.ifPresent(account -> cacheStore.set(userId, account));
|
return userAccount;
|
||||||
return userAccount;
|
})
|
||||||
})
|
.orElse(null);
|
||||||
.orElse(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -23,11 +23,9 @@
|
|||||||
|
|
||||||
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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -35,34 +33,16 @@ 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 void initCapacity(final int capacity) {
|
public static void initCapacity(int capacity) {
|
||||||
if (cache == null) {
|
if (cache == null) {
|
||||||
cache = new LruCache(capacity);
|
cache = new LruCache(capacity);
|
||||||
} else {
|
} else {
|
||||||
@ -72,64 +52,57 @@ 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 UserAccount readThrough(final String userId) {
|
public static UserAccount readThrough(String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Found in Cache!");
|
LOGGER.info("# Cache Hit!");
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
LOGGER.info("# Not found in cache! Go to DB!!");
|
LOGGER.info("# Cache Miss!");
|
||||||
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 void writeThrough(final UserAccount userAccount) {
|
public static void writeThrough(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 void writeAround(final UserAccount userAccount) {
|
public static void writeAround(UserAccount userAccount) {
|
||||||
if (cache.contains(userAccount.getUserId())) {
|
if (cache.contains(userAccount.getUserId())) {
|
||||||
dbManager.updateDb(userAccount);
|
DbManager.updateDb(userAccount);
|
||||||
// Cache data has been updated -- remove older
|
cache.invalidate(userAccount.getUserId()); // 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 UserAccount readThroughWithWriteBackPolicy(final String userId) {
|
public static UserAccount readThroughWithWriteBackPolicy(String userId) {
|
||||||
if (cache.contains(userId)) {
|
if (cache.contains(userId)) {
|
||||||
LOGGER.info("# Found in cache!");
|
LOGGER.info("# Cache Hit!");
|
||||||
return cache.get(userId);
|
return cache.get(userId);
|
||||||
}
|
}
|
||||||
LOGGER.info("# Not found in Cache!");
|
LOGGER.info("# Cache Miss!");
|
||||||
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;
|
||||||
@ -137,13 +110,12 @@ public class CacheStore {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set user account.
|
* Set user account.
|
||||||
* @param userAccount {@link UserAccount}
|
|
||||||
*/
|
*/
|
||||||
public void writeBehind(final UserAccount userAccount) {
|
public static void writeBehind(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);
|
||||||
}
|
}
|
||||||
@ -151,7 +123,7 @@ public class CacheStore {
|
|||||||
/**
|
/**
|
||||||
* Clears cache.
|
* Clears cache.
|
||||||
*/
|
*/
|
||||||
public void clearCache() {
|
public static void clearCache() {
|
||||||
if (cache != null) {
|
if (cache != null) {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
}
|
}
|
||||||
@ -160,51 +132,44 @@ public class CacheStore {
|
|||||||
/**
|
/**
|
||||||
* Writes remaining content in the cache into the DB.
|
* Writes remaining content in the cache into the DB.
|
||||||
*/
|
*/
|
||||||
public void flushCache() {
|
public static 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 String print() {
|
public static 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", "----"));
|
.collect(Collectors.joining("", "\n--CACHE CONTENT--\n", "----\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delegate to backing cache store.
|
* Delegate to backing cache store.
|
||||||
* @param userId {@link String}
|
|
||||||
* @return {@link UserAccount}
|
|
||||||
*/
|
*/
|
||||||
public UserAccount get(final String userId) {
|
public static UserAccount get(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 void set(final String userId, final UserAccount userAccount) {
|
public static void set(String userId, 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 void invalidate(final String userId) {
|
public static void invalidate(String userId) {
|
||||||
cache.invalidate(userId);
|
cache.invalidate(userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -32,25 +32,10 @@ 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;
|
||||||
}
|
}
|
||||||
|
171
caching/src/main/java/com/iluwatar/caching/DbManager.java
Normal file
171
caching/src/main/java/com/iluwatar/caching/DbManager.java
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
/*
|
||||||
|
* 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,83 +29,41 @@ 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
|
* Data structure/implementation of the application's cache. The data structure consists of a hash
|
||||||
* consists of a hash table attached with a doubly linked-list. The linked-list
|
* table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
|
||||||
* helps in capturing and maintaining the LRU data in the cache. When a data is
|
* LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
|
||||||
* queried (from the cache), added (to the cache), or updated, the data is
|
* the data is moved to the front of the list to depict itself as the most-recently-used data. The
|
||||||
* moved to the front of the list to depict itself as the most-recently-used
|
* LRU data is always at the end of the list.
|
||||||
* 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 {
|
|
||||||
/**
|
|
||||||
* user id.
|
|
||||||
*/
|
|
||||||
private final String userId;
|
|
||||||
/**
|
|
||||||
* User Account.
|
|
||||||
*/
|
|
||||||
private UserAccount userAccount;
|
|
||||||
/**
|
|
||||||
* previous.
|
|
||||||
*/
|
|
||||||
private Node previous;
|
|
||||||
/**
|
|
||||||
* next.
|
|
||||||
*/
|
|
||||||
private Node next;
|
|
||||||
|
|
||||||
/**
|
static class Node {
|
||||||
* Node definition.
|
String userId;
|
||||||
*
|
UserAccount userAccount;
|
||||||
* @param id String
|
Node previous;
|
||||||
* @param account {@link UserAccount}
|
Node next;
|
||||||
*/
|
|
||||||
Node(final String id, final UserAccount account) {
|
public Node(String userId, UserAccount userAccount) {
|
||||||
this.userId = id;
|
this.userId = userId;
|
||||||
this.userAccount = account;
|
this.userAccount = userAccount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
int capacity;
|
||||||
* Capacity of Cache.
|
Map<String, Node> cache = new HashMap<>();
|
||||||
*/
|
Node head;
|
||||||
private int capacity;
|
Node end;
|
||||||
/**
|
|
||||||
* Cache {@link HashMap}.
|
|
||||||
*/
|
|
||||||
private Map<String, Node> cache = new HashMap<>();
|
|
||||||
/**
|
|
||||||
* Head.
|
|
||||||
*/
|
|
||||||
private Node head;
|
|
||||||
/**
|
|
||||||
* End.
|
|
||||||
*/
|
|
||||||
private Node end;
|
|
||||||
|
|
||||||
/**
|
public LruCache(int capacity) {
|
||||||
* Constructor.
|
this.capacity = capacity;
|
||||||
*
|
|
||||||
* @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(final String userId) {
|
public UserAccount get(String userId) {
|
||||||
if (cache.containsKey(userId)) {
|
if (cache.containsKey(userId)) {
|
||||||
var node = cache.get(userId);
|
var node = cache.get(userId);
|
||||||
remove(node);
|
remove(node);
|
||||||
@ -117,10 +75,8 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove node from linked list.
|
* Remove node from linked list.
|
||||||
*
|
|
||||||
* @param node {@link Node}
|
|
||||||
*/
|
*/
|
||||||
public void remove(final Node node) {
|
public void remove(Node node) {
|
||||||
if (node.previous != null) {
|
if (node.previous != null) {
|
||||||
node.previous.next = node.next;
|
node.previous.next = node.next;
|
||||||
} else {
|
} else {
|
||||||
@ -135,10 +91,8 @@ 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(final Node node) {
|
public void setHead(Node node) {
|
||||||
node.next = head;
|
node.next = head;
|
||||||
node.previous = null;
|
node.previous = null;
|
||||||
if (head != null) {
|
if (head != null) {
|
||||||
@ -152,11 +106,8 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set user account.
|
* Set user account.
|
||||||
*
|
|
||||||
* @param userAccount {@link UserAccount}
|
|
||||||
* @param userId {@link String}
|
|
||||||
*/
|
*/
|
||||||
public void set(final String userId, final UserAccount userAccount) {
|
public void set(String userId, 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;
|
||||||
@ -176,43 +127,25 @@ 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(final String userId) {
|
public void invalidate(String userId) {
|
||||||
var toBeRemoved = cache.remove(userId);
|
var toBeRemoved = cache.remove(userId);
|
||||||
if (toBeRemoved != null) {
|
if (toBeRemoved != null) {
|
||||||
LOGGER.info("# {} has been updated! "
|
LOGGER.info("# {} has been updated! Removing older version from cache...", userId);
|
||||||
+ "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;
|
||||||
}
|
}
|
||||||
@ -228,8 +161,6 @@ 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>();
|
||||||
@ -243,14 +174,10 @@ public class LruCache {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Set cache capacity.
|
* Set cache capacity.
|
||||||
*
|
|
||||||
* @param newCapacity int
|
|
||||||
*/
|
*/
|
||||||
public void setCapacity(final int newCapacity) {
|
public void setCapacity(int newCapacity) {
|
||||||
if (capacity > newCapacity) {
|
if (capacity > newCapacity) {
|
||||||
// Behavior can be modified to accommodate
|
clear(); // Behavior can be modified to accommodate for decrease in cache size. For now, we'll
|
||||||
// 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,28 +24,19 @@
|
|||||||
package com.iluwatar.caching;
|
package com.iluwatar.caching;
|
||||||
|
|
||||||
import lombok.AllArgsConstructor;
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Getter;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.Setter;
|
||||||
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.
|
||||||
*/
|
*/
|
||||||
@Data
|
@Setter
|
||||||
|
@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,27 +26,11 @@ package com.iluwatar.caching.constants;
|
|||||||
/**
|
/**
|
||||||
* Constant class for defining constants.
|
* Constant class for defining constants.
|
||||||
*/
|
*/
|
||||||
public final class CachingConstants {
|
public 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() {
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +0,0 @@
|
|||||||
/**
|
|
||||||
* Constants.
|
|
||||||
*/
|
|
||||||
package com.iluwatar.caching.constants;
|
|
@ -1,52 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,78 +0,0 @@
|
|||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,4 +0,0 @@
|
|||||||
/**
|
|
||||||
* Database classes.
|
|
||||||
*/
|
|
||||||
package com.iluwatar.caching.database;
|
|
@ -1,20 +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;
|
|
@ -25,21 +25,25 @@ 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,30 +43,32 @@ 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).
|
||||||
app = new App(false);
|
AppManager.initDb(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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,84 +0,0 @@
|
|||||||
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.ArgumentMatchers.any;
|
|
||||||
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,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Callback
|
title: Callback
|
||||||
category: Idiom
|
folder: callback
|
||||||
|
permalink: /patterns/callback/
|
||||||
|
categories: Idiom
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Chain of responsibility
|
title: Chain of responsibility
|
||||||
category: Behavioral
|
folder: chain-of-responsibility
|
||||||
|
permalink: /patterns/chain-of-responsibility/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Circuit Breaker
|
title: Circuit Breaker
|
||||||
category: Behavioral
|
folder: circuit-breaker
|
||||||
|
permalink: /patterns/circuit-breaker/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Static Content Hosting
|
title: Static Content Hosting
|
||||||
category: Cloud
|
folder: cloud-static-content-hosting
|
||||||
|
permalink: /patterns/cloud-static-content-hosting/
|
||||||
|
categories: Cloud
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Collection Pipeline
|
title: Collection Pipeline
|
||||||
category: Functional
|
folder: collection-pipeline
|
||||||
|
permalink: /patterns/collection-pipeline/
|
||||||
|
categories: Functional
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Combinator
|
title: Combinator
|
||||||
category: Idiom
|
folder: combinator
|
||||||
|
permalink: /patterns/combinator/
|
||||||
|
categories: Idiom
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Command
|
title: Command
|
||||||
category: Behavioral
|
folder: command
|
||||||
|
permalink: /patterns/command/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Commander
|
title: Commander
|
||||||
category: Concurrency
|
folder: commander
|
||||||
|
permalink: /patterns/commander/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Cloud distributed
|
- Cloud distributed
|
||||||
@ -22,4 +25,4 @@ We need a mechanism in place which can handle these kinds of situations. We have
|
|||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
* [Distributed Transactions: The Icebergs of Microservices](https://www.grahamlea.com/2016/08/distributed-transactions-microservices-icebergs/)
|
* [https://www.grahamlea.com/2016/08/distributed-transactions-microservices-icebergs/]
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Composite Entity
|
title: Composite Entity
|
||||||
category: Structural
|
folder: composite-entity
|
||||||
|
permalink: /patterns/composite-entity/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Enterprise Integration Pattern
|
- Enterprise Integration Pattern
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Composite
|
title: Composite
|
||||||
category: Structural
|
folder: composite
|
||||||
|
permalink: /patterns/composite/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Converter
|
title: Converter
|
||||||
category: Creational
|
folder: converter
|
||||||
|
permalink: /patterns/converter/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: CQRS
|
title: CQRS
|
||||||
category: Architectural
|
folder: cqrs
|
||||||
|
permalink: /patterns/cqrs/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Data Access Object
|
title: Data Access Object
|
||||||
category: Architectural
|
folder: dao
|
||||||
|
permalink: /patterns/dao/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Data access
|
- Data access
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Data Bus
|
title: Data Bus
|
||||||
category: Architectural
|
folder: data-bus
|
||||||
|
permalink: /patterns/data-bus/
|
||||||
|
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -46,7 +46,7 @@ class DataBusTest {
|
|||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
MockitoAnnotations.openMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Data Locality
|
title: Data Locality
|
||||||
category: Behavioral
|
folder: data-locality
|
||||||
|
permalink: /patterns/data-locality/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Game programming
|
- Game programming
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Data Mapper
|
title: Data Mapper
|
||||||
category: Architectural
|
folder: data-mapper
|
||||||
|
permalink: /patterns/data-mapper/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Data Transfer Object
|
title: Data Transfer Object
|
||||||
category: Architectural
|
folder: data-transfer-object
|
||||||
|
permalink: /patterns/data-transfer-object/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Decorator
|
title: Decorator
|
||||||
category: Structural
|
folder: decorator
|
||||||
|
permalink: /patterns/decorator/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Delegation
|
title: Delegation
|
||||||
category: Structural
|
folder: delegation
|
||||||
|
permalink: /patterns/delegation/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Dependency Injection
|
title: Dependency Injection
|
||||||
category: Creational
|
folder: dependency-injection
|
||||||
|
permalink: /patterns/dependency-injection/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
@ -100,4 +103,4 @@ Use the Dependency Injection pattern when:
|
|||||||
* [Dependency Injection Principles, Practices, and Patterns](https://www.amazon.com/gp/product/161729473X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=161729473X&linkId=57079257a5c7d33755493802f3b884bd)
|
* [Dependency Injection Principles, Practices, and Patterns](https://www.amazon.com/gp/product/161729473X/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=161729473X&linkId=57079257a5c7d33755493802f3b884bd)
|
||||||
* [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.com/gp/product/0132350882/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0132350882&linkCode=as2&tag=javadesignpat-20&linkId=2c390d89cc9e61c01b9e7005c7842871)
|
* [Clean Code: A Handbook of Agile Software Craftsmanship](https://www.amazon.com/gp/product/0132350882/ref=as_li_tl?ie=UTF8&camp=1789&creative=9325&creativeASIN=0132350882&linkCode=as2&tag=javadesignpat-20&linkId=2c390d89cc9e61c01b9e7005c7842871)
|
||||||
* [Java 9 Dependency Injection: Write loosely coupled code with Spring 5 and Guice](https://www.amazon.com/gp/product/1788296257/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=1788296257&linkId=4e9137a3bf722a8b5b156cce1eec0fc1)
|
* [Java 9 Dependency Injection: Write loosely coupled code with Spring 5 and Guice](https://www.amazon.com/gp/product/1788296257/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=1788296257&linkId=4e9137a3bf722a8b5b156cce1eec0fc1)
|
||||||
* [Google Guice: Agile Lightweight Dependency Injection Framework](https://www.amazon.com/gp/product/1590599977/ref=as_li_qf_asin_il_tl?ie=UTF8&tag=javadesignpat-20&creative=9325&linkCode=as2&creativeASIN=1590599977&linkId=3b10c90b7ba480a1b7777ff38000f956)
|
* [Google Guice Tutorial: Open source Java based dependency injection framework](https://www.amazon.com/gp/product/B083P7DZ8M/ref=as_li_tl?ie=UTF8&tag=javadesignpat-20&camp=1789&creative=9325&linkCode=as2&creativeASIN=B083P7DZ8M&linkId=04f0f902c877921e45215b624a124bfe)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Dirty Flag
|
title: Dirty Flag
|
||||||
category: Behavioral
|
folder: dirty-flag
|
||||||
|
permalink: /patterns/dirty-flag/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Game programming
|
- Game programming
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Domain Model
|
title: Domain Model
|
||||||
category: Architectural
|
folder: domain-model
|
||||||
|
permalink: /patterns/domain-model/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Domain
|
- Domain
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Double Buffer
|
title: Double Buffer
|
||||||
category: Behavioral
|
folder: double-buffer
|
||||||
|
permalink: /patterns/double-buffer/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Double Checked Locking
|
title: Double Checked Locking
|
||||||
category: Idiom
|
folder: double-checked-locking
|
||||||
|
permalink: /patterns/double-checked-locking/
|
||||||
|
categories: Idiom
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Double Dispatch
|
title: Double Dispatch
|
||||||
category: Idiom
|
folder: double-dispatch
|
||||||
|
permalink: /patterns/double-dispatch/
|
||||||
|
categories: Idiom
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: EIP Aggregator
|
title: EIP Aggregator
|
||||||
category: Integration
|
folder: eip-aggregator
|
||||||
|
permalink: /patterns/eip-aggregator/
|
||||||
|
categories: Integration
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Enterprise Integration Pattern
|
- Enterprise Integration Pattern
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: EIP Message Channel
|
title: EIP Message Channel
|
||||||
category: Integration
|
folder: eip-message-channel
|
||||||
|
permalink: /patterns/eip-message-channel/
|
||||||
|
categories: Integration
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Enterprise Integration Pattern
|
- Enterprise Integration Pattern
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: EIP Publish Subscribe
|
title: EIP Publish Subscribe
|
||||||
category: Integration
|
folder: eip-publish-subscribe
|
||||||
|
permalink: /patterns/eip-publish-subscribe/
|
||||||
|
categories: Integration
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Enterprise Integration Pattern
|
- Enterprise Integration Pattern
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: EIP Splitter
|
title: EIP Splitter
|
||||||
category: Integration
|
folder: eip-splitter
|
||||||
|
permalink: /patterns/eip-splitter/
|
||||||
|
categories: Integration
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Enterprise Integration Pattern
|
- Enterprise Integration Pattern
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: EIP Wire Tap
|
title: EIP Wire Tap
|
||||||
category: Integration
|
folder: eip-wire-tap
|
||||||
|
permalink: /patterns/eip-wire-tap/
|
||||||
|
categories: Integration
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Enterprise Integration Pattern
|
- Enterprise Integration Pattern
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Event Aggregator
|
title: Event Aggregator
|
||||||
category: Structural
|
folder: event-aggregator
|
||||||
|
permalink: /patterns/event-aggregator/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Event-based Asynchronous
|
title: Event-based Asynchronous
|
||||||
category: Concurrency
|
folder: event-asynchronous
|
||||||
|
permalink: /patterns/event-asynchronous/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Event Driven Architecture
|
title: Event Driven Architecture
|
||||||
category: Architectural
|
folder: event-driven-architecture
|
||||||
|
permalink: /patterns/event-driven-architecture/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
@ -21,6 +24,7 @@ Use an Event-driven architecture when
|
|||||||
|
|
||||||
## Real world examples
|
## Real world examples
|
||||||
|
|
||||||
|
* SendGrid, an email API, sends events whenever an email is processed, delivered, opened etc... (https://sendgrid.com/docs/API_Reference/Webhooks/event.html)
|
||||||
* Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events)
|
* Chargify, a billing API, exposes payment activity through various events (https://docs.chargify.com/api-events)
|
||||||
* Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda)
|
* Amazon's AWS Lambda, lets you execute code in response to events such as changes to Amazon S3 buckets, updates to an Amazon DynamoDB table, or custom events generated by your applications or devices. (https://aws.amazon.com/lambda)
|
||||||
* MySQL runs triggers based on events such as inserts and update events happening on database tables.
|
* MySQL runs triggers based on events such as inserts and update events happening on database tables.
|
||||||
@ -28,6 +32,6 @@ Use an Event-driven architecture when
|
|||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
* [Event-driven architecture - Wikipedia](https://en.wikipedia.org/wiki/Event-driven_architecture)
|
* [Event-driven architecture - Wikipedia](https://en.wikipedia.org/wiki/Event-driven_architecture)
|
||||||
* [What is an Event-Driven Architecture](https://aws.amazon.com/event-driven-architecture/)
|
* [Fundamental Components of an Event-Driven Architecture](http://giocc.com/fundamental-components-of-an-event-driven-architecture.html)
|
||||||
* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications)
|
* [Real World Applications/Event Driven Applications](https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications)
|
||||||
* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture)
|
* [Event-driven architecture definition](http://searchsoa.techtarget.com/definition/event-driven-architecture)
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Event Queue
|
title: Event Queue
|
||||||
category: Concurrency
|
folder: event-queue
|
||||||
|
permalink: /patterns/event-queue/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Game programming
|
- Game programming
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Event Sourcing
|
title: Event Sourcing
|
||||||
category: Architectural
|
folder: event-sourcing
|
||||||
|
permalink: /patterns/event-sourcing/
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Execute Around
|
title: Execute Around
|
||||||
category: Idiom
|
folder: execute-around
|
||||||
|
permalink: /patterns/execute-around/
|
||||||
|
categories: Idiom
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Extension objects
|
title: Extension objects
|
||||||
category: Behavioral
|
folder: extension-objects
|
||||||
|
permalink: /patterns/extension-objects/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Facade
|
title: Facade
|
||||||
category: Structural
|
folder: facade
|
||||||
|
permalink: /patterns/facade/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang Of Four
|
- Gang Of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Factory Kit
|
title: Factory Kit
|
||||||
category: Creational
|
folder: factory-kit
|
||||||
|
permalink: /patterns/factory-kit/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Factory Method
|
title: Factory Method
|
||||||
category: Creational
|
folder: factory-method
|
||||||
|
permalink: /patterns/factory-method/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Factory
|
title: Factory
|
||||||
category: Creational
|
folder: factory
|
||||||
|
permalink: /patterns/factory/
|
||||||
|
categories: Creational
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Fan-Out/Fan-In
|
title: Fan-Out/Fan-In
|
||||||
category: Integration
|
folder: fanout-fanin
|
||||||
|
permalink: /patterns/fanout-fanin/
|
||||||
|
categories: Integration
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Microservices
|
- Microservices
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Feature Toggle
|
title: Feature Toggle
|
||||||
category: Behavioral
|
folder: feature-toggle
|
||||||
|
permalink: /patterns/feature-toggle/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Filterer
|
title: Filterer
|
||||||
|
folder: filterer
|
||||||
|
permalink: /patterns/filterer/
|
||||||
description: Design pattern that helps container-like objects to return filtered version of themselves.# short meta description that shows in Google search results
|
description: Design pattern that helps container-like objects to return filtered version of themselves.# short meta description that shows in Google search results
|
||||||
language: en
|
language: en
|
||||||
category: Functional
|
categories:
|
||||||
|
- Functional
|
||||||
tags:
|
tags:
|
||||||
- Extensibility
|
- Extensibility
|
||||||
---
|
---
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Fluent Interface
|
title: Fluent Interface
|
||||||
category: Functional
|
folder: fluentinterface
|
||||||
|
permalink: /patterns/fluentinterface/
|
||||||
|
categories: Functional
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Reactive
|
- Reactive
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Flux
|
title: Flux
|
||||||
category: Structural
|
folder: flux
|
||||||
|
permalink: /patterns/flux/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Flyweight
|
title: Flyweight
|
||||||
category: Structural
|
folder: flyweight
|
||||||
|
permalink: /patterns/flyweight/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Gang of Four
|
- Gang of Four
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Front Controller
|
title: Front Controller
|
||||||
category: Structural
|
folder: front-controller
|
||||||
|
permalink: /patterns/front-controller/
|
||||||
|
categories: Structural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Game Loop
|
title: Game Loop
|
||||||
category: Behavioral
|
folder: game-loop
|
||||||
|
permalink: /patterns/game-loop/
|
||||||
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Game programming
|
- Game programming
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Guarded Suspension
|
title: Guarded Suspension
|
||||||
category: Concurrency
|
folder: guarded-suspension
|
||||||
|
permalink: /patterns/guarded-suspension/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Half-Sync/Half-Async
|
title: Half-Sync/Half-Async
|
||||||
category: Concurrency
|
folder: half-sync-half-async
|
||||||
|
permalink: /patterns/half-sync-half-async/
|
||||||
|
categories: Concurrency
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Performance
|
- Performance
|
||||||
@ -27,7 +30,7 @@ Use Half-Sync/Half-Async pattern when
|
|||||||
|
|
||||||
* [BSD Unix networking subsystem](https://www.dre.vanderbilt.edu/~schmidt/PDF/PLoP-95.pdf)
|
* [BSD Unix networking subsystem](https://www.dre.vanderbilt.edu/~schmidt/PDF/PLoP-95.pdf)
|
||||||
* [Real Time CORBA](http://www.omg.org/news/meetings/workshops/presentations/realtime2001/4-3_Pyarali_thread-pool.pdf)
|
* [Real Time CORBA](http://www.omg.org/news/meetings/workshops/presentations/realtime2001/4-3_Pyarali_thread-pool.pdf)
|
||||||
* [Android AsyncTask framework](https://developer.android.com/reference/android/os/AsyncTask)
|
* [Android AsyncTask framework](http://developer.android.com/reference/android/os/AsyncTask.html)
|
||||||
|
|
||||||
## Credits
|
## Credits
|
||||||
|
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Hexagonal Architecture
|
title: Hexagonal Architecture
|
||||||
category: Architectural
|
folder: hexagonal
|
||||||
|
permalink: /patterns/hexagonal/
|
||||||
|
pumlformat: svg
|
||||||
|
categories: Architectural
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
- Decoupling
|
- Decoupling
|
||||||
|
147
index.md
147
index.md
@ -1,147 +0,0 @@
|
|||||||
| Pattern | Category | Tags |
|
|
||||||
| :--- | :--- | :--- |
|
|
||||||
| [Abstract Document](abstract-document) | Structural | Extensibility |
|
|
||||||
| [Abstract Factory](abstract-factory) | Creational | Gang of Four |
|
|
||||||
| [Active Object](active-object) | Concurrency | Performance |
|
|
||||||
| [Acyclic Visitor](acyclic-visitor) | Behavioral | Extensibility |
|
|
||||||
| [Adapter](adapter) | Structural | Gang of Four |
|
|
||||||
| [Aggregator Microservices](aggregator-microservices) | Architectural | Cloud distributed |
|
|
||||||
| [Ambassador](ambassador) | Structural | Decoupling |
|
|
||||||
| [API Gateway](api-gateway) | Architectural | Cloud distributed, Decoupling |
|
|
||||||
| [Arrange/Act/Assert](arrange-act-assert) | Idiom | Testing |
|
|
||||||
| [Async Method Invocation](async-method-invocation) | Concurrency | Reactive |
|
|
||||||
| [Balking](balking) | Concurrency | Decoupling |
|
|
||||||
| [Bridge](bridge) | Structural | Gang of Four |
|
|
||||||
| [Builder](builder) | Creational | Gang of Four |
|
|
||||||
| [Business Delegate](business-delegate) | Structural | Decoupling |
|
|
||||||
| [Bytecode](bytecode) | Behavioral | Game programming |
|
|
||||||
| [Caching](caching) | Behavioral | Performance |
|
|
||||||
| [Callback](callback) | Idiom | Reactive |
|
|
||||||
| [Chain of Responsibility](chain-of-responsibility) | Behavioral | Gang of Four |
|
|
||||||
| [Circuit Breaker](circuit-breaker) | Behavioral | Performance, Decoupling |
|
|
||||||
| [Cloud Static Content Hosting](cloud-static-content-hosting) | Cloud | Cloud distributed |
|
|
||||||
| [Collection Pipeline](collection-pipeline) | Functional | Reactive |
|
|
||||||
| [Combinator](combinator) | Idiom | Reactive |
|
|
||||||
| [Command](command) | Behavioral | Gang of Four |
|
|
||||||
| [Commander](commander) | Concurrency | Cloud distributed |
|
|
||||||
| [Composite](composite) | Structural | Gang of Four |
|
|
||||||
| [Composite Entity](composite-entity) | Structural | Enterprise Integration Pattern |
|
|
||||||
| [Converter](converter) | Creational | Decoupling |
|
|
||||||
| [Command Query Responsibility Segregation](command-query-responsibility-segregation) | Architectural | Performance, Cloud distributed |
|
|
||||||
| [Data Access Object](data-access-object) | Architectural | Data access |
|
|
||||||
| [Data Bus](data-bus) | Architectural | Decoupling |
|
|
||||||
| [Data Locality](data-locality) | Behavioral | Performance, Game programming |
|
|
||||||
| [Data Mapper](data-mapper) | Architectural | Decoupling |
|
|
||||||
| [Data Transfer Object](data-transfer-object) | Architectural | Performance |
|
|
||||||
| [Decorator](decorator) | Structural | Gang of Four, Extensibility |
|
|
||||||
| [Delegation](delegation) | Structural | Decoupling |
|
|
||||||
| [Dependency Injection](dependency-injection) | Creational | Decoupling |
|
|
||||||
| [Dirty Flag](dirty-flag) | Behavioral | Performance, Game programming |
|
|
||||||
| [Double Buffer](double-buffer) | Behavioral | Performance, Game programming |
|
|
||||||
| [Double Checked Locking](double-checked-locking) | Idiom | Performance |
|
|
||||||
| [Double Dispatch](double-dispatch) | Idiom | Extensibility |
|
|
||||||
| [EIP Aggregator](eip-aggregator) | Integration | Enterprise Integration Pattern |
|
|
||||||
| [EIP Message Channel](eip-message-channel) | Integration | Enterprise Integration Pattern |
|
|
||||||
| [EIP Publish and Subscribe](eip-publish-and-subscribe) | Integration | Enterprise Integration Pattern |
|
|
||||||
| [EIP Splitter](eip-splitter) | Integration | Enterprise Integration Pattern |
|
|
||||||
| [EIP Wire Tap](eip-wire-tap) | Integration | Enterprise Integration Pattern |
|
|
||||||
| [Event Aggregator](event-aggregator) | Structural | Reactive |
|
|
||||||
| [Event Based Asynchronous](event-based-asynchronous) | Concurrency | Reactive |
|
|
||||||
| [Event Driven Architecture](event-driven-architecture) | Architectural | Reactive |
|
|
||||||
| [Event Queue](event-queue) | Concurrency | Game programming |
|
|
||||||
| [Event Sourcing](event-sourcing) | Architectural | Performance |
|
|
||||||
| [Execute Around](execute-around) | Idiom | Extensibility |
|
|
||||||
| [Extension Objects](extension-objects) | Behavioral | Extensibility |
|
|
||||||
| [Facade](facade) | Structural | Gang of Four, Decoupling |
|
|
||||||
| [Factory](factory) | Creational | Gang of Four |
|
|
||||||
| [Factory kit](factory-kit) | Creational | Extensibility |
|
|
||||||
| [Factory Method](factory-method) | Creational | Gang of Four, Extensibility |
|
|
||||||
| [Fan-Out/Fan-In](fanout-fanin) | Integration | Microservices |
|
|
||||||
| [Feature Toggle](feature-toggle) | Behavioral | Extensibility |
|
|
||||||
| [Filterer](filterer) | Functional | Extensibility |
|
|
||||||
| [Fluent Interface](fluent-interface) | Functional | Reactive |
|
|
||||||
| [Flux](flux) | Structural | Decoupling |
|
|
||||||
| [Flyweight](flyweight) | Structural | Gang of Four, Performance |
|
|
||||||
| [Front Controller](front-controller) | Structural | Decoupling |
|
|
||||||
| [Game Loop](game-loop) | Behavioral | Game programming |
|
|
||||||
| [Guarded Suspension](guarded-suspension) | Concurrency | Decoupling |
|
|
||||||
| [Half-Sync Half-Async](half-sync-half-async) | Concurrency | Performance |
|
|
||||||
| [Hexagonal](hexagonal) | Architectural | Decoupling |
|
|
||||||
| [Intercepting Filter](intercepting-filter) | Behavioral | Decoupling |
|
|
||||||
| [Interpreter](interpreter) | Behavioral | Gang of Four |
|
|
||||||
| [Iterator](iterator) | Behavioral | Gang of Four |
|
|
||||||
| [Layers](layers) | Architectural | Decoupling |
|
|
||||||
| [Lazy Loading](lazy-loading) | Idiom | Performance |
|
|
||||||
| [Leader Election](leader-election) | Behavioral | Cloud distributed |
|
|
||||||
| [Leader Followers](leader-followers) | Concurrency | Performance |
|
|
||||||
| [Lockable Object](lockable-object) | Concurrency | Performance |
|
|
||||||
| [Marker Interface](marker-interface) | Structural | Decoupling |
|
|
||||||
| [Master Worker](master-worker) | Concurrency | Performance |
|
|
||||||
| [Mediator](mediator) | Behavioral | Gang of Four, Decoupling |
|
|
||||||
| [Memento](memento) | Behavioral | Gang of Four |
|
|
||||||
| [Model View Controller](model-view-controller) | Architectural | Decoupling |
|
|
||||||
| [Model View Presenter](model-view-presenter) | Architectural | Decoupling |
|
|
||||||
| [Model View ViewModel](model-view-viewmodel) | Architectural | Decoupling |
|
|
||||||
| [Module](module) | Structural | Decoupling |
|
|
||||||
| [Monad](monad) | Functional | Reactive |
|
|
||||||
| [Monostate](monostate) | Creational | Instantiation |
|
|
||||||
| [Multiton](multiton) | Creational | Instantiation |
|
|
||||||
| [Mute Idiom](mute-idiom) | Idiom | Decoupling |
|
|
||||||
| [Naked Objects](naked-objects) | Architectural | Decoupling |
|
|
||||||
| [Null Object](null-object) | Behavioral | Extensibility |
|
|
||||||
| [Object Mother](object-mother) | Creational | Instantiation |
|
|
||||||
| [Object Pool](object-pool) | Creational | Performance, Game programming |
|
|
||||||
| [Observer](observer) | Behavioral | Gang of Four, Reactive |
|
|
||||||
| [Page Object](page-object) | Structural | Decoupling |
|
|
||||||
| [Parameter Object](parameter-object) | Behavioral | Extensibility |
|
|
||||||
| [Partial Response](partial-response) | Behavioral | Decoupling |
|
|
||||||
| [Pipeline](pipeline) | Behavioral | Decoupling |
|
|
||||||
| [Poison Pill](poison-pill) | Behavioral | Cloud distributed, Reactive |
|
|
||||||
| [Presentation Model](presentation-model) | Behavioral | Decoupling |
|
|
||||||
| [Priority Queue](priority-queue) | Behavioral | Decoupling |
|
|
||||||
| [Private Class Data](private-class-data) | Idiom | Data access |
|
|
||||||
| [Producer Consumer](producer-consumer) | Concurrency | Reactive |
|
|
||||||
| [Promise](promise) | Concurrency | Reactive |
|
|
||||||
| [Property](property) | Creational | Instantiation |
|
|
||||||
| [Prototype](prototype) | Creational | Gang of Four, Instantiation |
|
|
||||||
| [Proxy](proxy) | Structural | Gang of Four, Decoupling |
|
|
||||||
| [Queue Based Load Leveling](queue-based-load-leveling) | Concurrency | Performance, Decoupling |
|
|
||||||
| [Reactor](reactor) | Concurrency | Performance, Reactive |
|
|
||||||
| [Reader Writer Lock](reader-writer-lock) | Concurrency | Performance |
|
|
||||||
| [Registry](registry) | Creational | Instantiation |
|
|
||||||
| [Repository](repository) | Architectural | Data access |
|
|
||||||
| [Resource Acquisition Is Initialization](resource-acquisition-is-initialization) | Idiom | Data access |
|
|
||||||
| [Retry](retry) | Behavioral | Performance |
|
|
||||||
| [Role Object](role-object) | Structural | Extensibility |
|
|
||||||
| [Saga](saga) | Concurrency | Cloud distributed |
|
|
||||||
| [Semaphore](semaphore) | Concurrency | Performance |
|
|
||||||
| [Separated Interface](separated-interface) | Structural | Decoupling |
|
|
||||||
| [Servant](servant) | Behavioral | Decoupling |
|
|
||||||
| [Serverless](serverless) | Architectural | Cloud distributed |
|
|
||||||
| [Service Layer](service-layer) | Architectural | Data access |
|
|
||||||
| [Service Locator](service-locator) | Architectural | Performance, Game programming |
|
|
||||||
| [Sharding](sharding) | Behavioral | Performance, Cloud distributed |
|
|
||||||
| [Singleton](singleton) | Creational | Gang of Four |
|
|
||||||
| [Spatial Partition](spatial-partition) | Behavioral | Performance, Game programming |
|
|
||||||
| [Special Case](special-case) | Behavioral | Extensibility |
|
|
||||||
| [Specification](specification) | Behavioral | Data access |
|
|
||||||
| [State](state) | Behavioral | Gang of Four |
|
|
||||||
| [Step Builder](step-builder) | Creational | Instantiation |
|
|
||||||
| [Strangler](strangler) | Structural | Cloud distributed, Extensibility |
|
|
||||||
| [Strategy](strategy) | Behavioral | Gang of Four |
|
|
||||||
| [Subclass Sandbox](subclass-sandbox) | Behavioral | Game programming |
|
|
||||||
| [Table Module](table-module) | Structural | Data access |
|
|
||||||
| [Template Method](table-module) | Behavioral | Gang of Four |
|
|
||||||
| [Thread Pool](thread-pool) | Concurrency | Performance |
|
|
||||||
| [Throttling](throttling) | Behavioral | Performance |
|
|
||||||
| [Thread Local Storage](thread-local-storage) | Idiom | Performance |
|
|
||||||
| [Tolerant Reader](tolerant-reader) | Integration | Decoupling |
|
|
||||||
| [Trampoline](trampoline) | Behavioral | Performance |
|
|
||||||
| [Transaction Script](transaction-script) | Behavioral | Data access |
|
|
||||||
| [Twin](twin) | Structural | Extensibility |
|
|
||||||
| [Type Object](type-object) | Behavioral | Game programming, Extensibility |
|
|
||||||
| [Unit of Work](unit-of-work) | Architectural | Data access |
|
|
||||||
| [Update Method](update-method) | Behavioral | Game programming |
|
|
||||||
| [Value Object](value-object) | Creational | Instantiation |
|
|
||||||
| [Version Number](version-number) | Concurrency | Data access, Microservices |
|
|
||||||
| [Visitor](visitor) | Behavioral | Gang of Four |
|
|
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Intercepting Filter
|
title: Intercepting Filter
|
||||||
|
folder: intercepting-filter
|
||||||
|
permalink: /patterns/intercepting-filter/
|
||||||
categories: Behavioral
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Interpreter
|
title: Interpreter
|
||||||
|
folder: interpreter
|
||||||
|
permalink: /patterns/interpreter/
|
||||||
categories: Behavioral
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
layout: pattern
|
||||||
title: Iterator
|
title: Iterator
|
||||||
|
folder: iterator
|
||||||
|
permalink: /patterns/iterator/
|
||||||
categories: Behavioral
|
categories: Behavioral
|
||||||
language: en
|
language: en
|
||||||
tags:
|
tags:
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user