mirror of
https://github.com/tiennm99/java-design-patterns.git
synced 2026-06-03 08:15:41 +00:00
deps: Refactor dependencies (#3224)
* remove spring dep move junit, logging, mockito under dep mgmt * upgrade anti-corruption-layer deps * async method invocation * balking, bloc * bridge to bytecode * caching * callback - cqrs * component - health check * hexagonal - metadata mapping * rest of the patterns * remove checkstyle, take spotless into use
This commit is contained in:
@@ -29,59 +29,44 @@ import com.iluwatar.caching.database.DbManagerFactory;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* The Caching pattern describes how to avoid expensive re-acquisition of
|
||||
* resources by not releasing the resources immediately after their use.
|
||||
* The resources retain their identity, are kept in some fast-access storage,
|
||||
* 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 <code>write-through</code> which writes data to the cache and
|
||||
* DB in a single transaction, <code>write-around</code> which writes data
|
||||
* immediately into the DB instead of the cache, <code>write-behind</code>
|
||||
* which writes data into the cache initially whilst the data is only
|
||||
* written into the DB when the cache is full, and <code>cache-aside</code>
|
||||
* which pushes the responsibility of keeping the data synchronized in both
|
||||
* data sources to the application itself. The <code>read-through</code>
|
||||
* strategy is also included in the mentioned four strategies --
|
||||
* returns data from the cache to the caller <b>if</b> it exists <b>else</b>
|
||||
* queries from DB and stores it into the cache for future use. These strategies
|
||||
* determine when the data in the cache should be written back to the backing
|
||||
* store (i.e. Database) and help keep both data sources
|
||||
* synchronized/up-to-date. This pattern can improve performance and also helps
|
||||
* to maintainconsistency between data held in the cache and the data in
|
||||
* the underlying data store.
|
||||
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
|
||||
* the resources immediately after their use. The resources retain their identity, are kept in some
|
||||
* fast-access storage, 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 <code>
|
||||
* write-through</code> which writes data to the cache and DB in a single transaction, <code>
|
||||
* write-around</code> which writes data immediately into the DB instead of the cache, <code>
|
||||
* write-behind</code> which writes data into the cache initially whilst the data is only written
|
||||
* into the DB when the cache is full, and <code>cache-aside</code> which pushes the responsibility
|
||||
* of keeping the data synchronized in both data sources to the application itself. The <code>
|
||||
* read-through</code> strategy is also included in the mentioned four strategies -- returns data
|
||||
* from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and stores it into
|
||||
* the cache for future use. These strategies determine when the data in the cache should be written
|
||||
* back to the backing store (i.e. Database) and help keep both data sources
|
||||
* synchronized/up-to-date. This pattern can improve performance and also helps to
|
||||
* maintainconsistency between data held in the cache and the data in the underlying data store.
|
||||
*
|
||||
* <p>In this example, the user account ({@link UserAccount}) entity is used
|
||||
* as the underlying application data. The cache itself is implemented as an
|
||||
* internal (Java) data structure. It adopts a Least-Recently-Used (LRU)
|
||||
* 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 querying of user accounts from the
|
||||
* underlying data store( {@link DbManager}). The main class ( {@link App}
|
||||
* is not aware of the underlying mechanics of the application
|
||||
* (i.e. save and query) and whether the data is coming from the cache or the
|
||||
* DB (i.e. separation of concern). The AppManager ({@link AppManager}) handles
|
||||
* the transaction of data to-and-from the underlying data store (depending on
|
||||
* the preferred caching policy/strategy).
|
||||
* <p>
|
||||
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy -->
|
||||
* DBManager} </i>
|
||||
* </p>
|
||||
* <p>In this example, the user account ({@link UserAccount}) entity is used as the underlying
|
||||
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
|
||||
* a Least-Recently-Used (LRU) 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
|
||||
* querying of user accounts from the underlying data store( {@link DbManager}). The main class (
|
||||
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
|
||||
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager
|
||||
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store
|
||||
* (depending on the preferred caching policy/strategy).
|
||||
*
|
||||
* <p>
|
||||
* There are 2 ways to launch the application.
|
||||
* - to use "in Memory" database.
|
||||
* - to use the MongoDb as a database
|
||||
* <p><i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i>
|
||||
*
|
||||
* To run the application with "in Memory" database, just launch it without parameters
|
||||
* Example: 'java -jar app.jar'
|
||||
* <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 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>To run the application with "in Memory" database, just launch it without parameters Example:
|
||||
* 'java -jar app.jar'
|
||||
*
|
||||
* <p>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'
|
||||
*
|
||||
* @see CacheStore
|
||||
* @see LruCache
|
||||
@@ -89,13 +74,10 @@ import lombok.extern.slf4j.Slf4j;
|
||||
*/
|
||||
@Slf4j
|
||||
public class App {
|
||||
/**
|
||||
* Constant parameter name to use mongoDB.
|
||||
*/
|
||||
/** Constant parameter name to use mongoDB. */
|
||||
private static final String USE_MONGO_DB = "--mongo";
|
||||
/**
|
||||
* Application manager.
|
||||
*/
|
||||
|
||||
/** Application manager. */
|
||||
private final AppManager appManager;
|
||||
|
||||
/**
|
||||
@@ -152,9 +134,7 @@ public class App {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-through and write-through.
|
||||
*/
|
||||
/** Read-through and write-through. */
|
||||
public void useReadAndWriteThroughStrategy() {
|
||||
LOGGER.info("# CachingPolicy.THROUGH");
|
||||
appManager.initCachingPolicy(CachingPolicy.THROUGH);
|
||||
@@ -167,9 +147,7 @@ public class App {
|
||||
appManager.find("001");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-through and write-around.
|
||||
*/
|
||||
/** Read-through and write-around. */
|
||||
public void useReadThroughAndWriteAroundStrategy() {
|
||||
LOGGER.info("# CachingPolicy.AROUND");
|
||||
appManager.initCachingPolicy(CachingPolicy.AROUND);
|
||||
@@ -189,22 +167,14 @@ public class App {
|
||||
appManager.find("002");
|
||||
}
|
||||
|
||||
/**
|
||||
* Read-through and write-behind.
|
||||
*/
|
||||
/** Read-through and write-behind. */
|
||||
public void useReadThroughAndWriteBehindStrategy() {
|
||||
LOGGER.info("# CachingPolicy.BEHIND");
|
||||
appManager.initCachingPolicy(CachingPolicy.BEHIND);
|
||||
|
||||
var userAccount3 = new UserAccount("003",
|
||||
"Adam",
|
||||
"He likes food.");
|
||||
var userAccount4 = new UserAccount("004",
|
||||
"Rita",
|
||||
"She hates cats.");
|
||||
var userAccount5 = new UserAccount("005",
|
||||
"Isaac",
|
||||
"He is allergic to mustard.");
|
||||
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
||||
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
||||
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
||||
|
||||
appManager.save(userAccount3);
|
||||
appManager.save(userAccount4);
|
||||
@@ -212,32 +182,22 @@ public class App {
|
||||
LOGGER.info(appManager.printCacheContent());
|
||||
appManager.find("003");
|
||||
LOGGER.info(appManager.printCacheContent());
|
||||
UserAccount userAccount6 = new UserAccount("006",
|
||||
"Yasha",
|
||||
"She is an only child.");
|
||||
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child.");
|
||||
appManager.save(userAccount6);
|
||||
LOGGER.info(appManager.printCacheContent());
|
||||
appManager.find("004");
|
||||
LOGGER.info(appManager.printCacheContent());
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache-Aside.
|
||||
*/
|
||||
/** Cache-Aside. */
|
||||
public void useCacheAsideStrategy() {
|
||||
LOGGER.info("# CachingPolicy.ASIDE");
|
||||
appManager.initCachingPolicy(CachingPolicy.ASIDE);
|
||||
LOGGER.info(appManager.printCacheContent());
|
||||
|
||||
var userAccount3 = new UserAccount("003",
|
||||
"Adam",
|
||||
"He likes food.");
|
||||
var userAccount4 = new UserAccount("004",
|
||||
"Rita",
|
||||
"She hates cats.");
|
||||
var userAccount5 = new UserAccount("005",
|
||||
"Isaac",
|
||||
"He is allergic to mustard.");
|
||||
var userAccount3 = new UserAccount("003", "Adam", "He likes food.");
|
||||
var userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
|
||||
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
|
||||
appManager.save(userAccount3);
|
||||
appManager.save(userAccount4);
|
||||
appManager.save(userAccount5);
|
||||
|
||||
@@ -29,26 +29,21 @@ import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* AppManager helps to bridge the gap in communication between the main class
|
||||
* and the application's back-end. DB connection is initialized through this
|
||||
* class. The chosen caching strategy/policy is also initialized here.
|
||||
* 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 CacheStore class.
|
||||
* AppManager helps to bridge the gap in communication between the main class and the application's
|
||||
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is
|
||||
* also initialized here. 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
|
||||
* CacheStore class.
|
||||
*/
|
||||
@Slf4j
|
||||
public class AppManager {
|
||||
/**
|
||||
* Caching Policy.
|
||||
*/
|
||||
/** Caching Policy. */
|
||||
private CachingPolicy cachingPolicy;
|
||||
/**
|
||||
* Database Manager.
|
||||
*/
|
||||
|
||||
/** Database Manager. */
|
||||
private final DbManager dbManager;
|
||||
/**
|
||||
* Cache Store.
|
||||
*/
|
||||
|
||||
/** Cache Store. */
|
||||
private final CacheStore cacheStore;
|
||||
|
||||
/**
|
||||
@@ -62,9 +57,9 @@ public class AppManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Developer/Tester is able to choose whether the application should use
|
||||
* MongoDB as its underlying data storage or a simple Java data structure
|
||||
* to (temporarily) store the data/objects during runtime.
|
||||
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying
|
||||
* data storage or a simple Java data structure to (temporarily) store the data/objects during
|
||||
* runtime.
|
||||
*/
|
||||
public void initDb() {
|
||||
dbManager.connect();
|
||||
@@ -91,8 +86,7 @@ public class AppManager {
|
||||
*/
|
||||
public UserAccount find(final String userId) {
|
||||
LOGGER.info("Trying to find {} in cache", userId);
|
||||
if (cachingPolicy == CachingPolicy.THROUGH
|
||||
|| cachingPolicy == CachingPolicy.AROUND) {
|
||||
if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) {
|
||||
return cacheStore.readThrough(userId);
|
||||
} else if (cachingPolicy == CachingPolicy.BEHIND) {
|
||||
return cacheStore.readThroughWithWriteBackPolicy(userId);
|
||||
@@ -147,12 +141,12 @@ public class AppManager {
|
||||
*/
|
||||
private UserAccount findAside(final String userId) {
|
||||
return Optional.ofNullable(cacheStore.get(userId))
|
||||
.or(() -> {
|
||||
Optional<UserAccount> userAccount =
|
||||
Optional.ofNullable(dbManager.readFromDb(userId));
|
||||
.or(
|
||||
() -> {
|
||||
Optional<UserAccount> userAccount = Optional.ofNullable(dbManager.readFromDb(userId));
|
||||
userAccount.ifPresent(account -> cacheStore.set(userId, account));
|
||||
return userAccount;
|
||||
})
|
||||
.orElse(null);
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,27 +30,21 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* The caching strategies are implemented in this class.
|
||||
*/
|
||||
/** The caching strategies are implemented in this class. */
|
||||
@Slf4j
|
||||
public class CacheStore {
|
||||
/**
|
||||
* Cache capacity.
|
||||
*/
|
||||
/** Cache capacity. */
|
||||
private static final int CAPACITY = 3;
|
||||
|
||||
/**
|
||||
* Lru cache see {@link LruCache}.
|
||||
*/
|
||||
/** Lru cache see {@link LruCache}. */
|
||||
private LruCache cache;
|
||||
/**
|
||||
* DbManager.
|
||||
*/
|
||||
|
||||
/** DbManager. */
|
||||
private final DbManager dbManager;
|
||||
|
||||
/**
|
||||
* Cache Store.
|
||||
*
|
||||
* @param dataBaseManager {@link DbManager}
|
||||
*/
|
||||
public CacheStore(final DbManager dataBaseManager) {
|
||||
@@ -60,6 +54,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Init cache capacity.
|
||||
*
|
||||
* @param capacity int
|
||||
*/
|
||||
public void initCapacity(final int capacity) {
|
||||
@@ -72,6 +67,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Get user account using read-through cache.
|
||||
*
|
||||
* @param userId {@link String}
|
||||
* @return {@link UserAccount}
|
||||
*/
|
||||
@@ -88,6 +84,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Get user account using write-through cache.
|
||||
*
|
||||
* @param userAccount {@link UserAccount}
|
||||
*/
|
||||
public void writeThrough(final UserAccount userAccount) {
|
||||
@@ -101,6 +98,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Get user account using write-around cache.
|
||||
*
|
||||
* @param userAccount {@link UserAccount}
|
||||
*/
|
||||
public void writeAround(final UserAccount userAccount) {
|
||||
@@ -116,6 +114,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Get user account using read-through cache with write-back policy.
|
||||
*
|
||||
* @param userId {@link String}
|
||||
* @return {@link UserAccount}
|
||||
*/
|
||||
@@ -137,6 +136,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Set user account.
|
||||
*
|
||||
* @param userAccount {@link UserAccount}
|
||||
*/
|
||||
public void writeBehind(final UserAccount userAccount) {
|
||||
@@ -148,18 +148,14 @@ public class CacheStore {
|
||||
cache.set(userAccount.getUserId(), userAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears cache.
|
||||
*/
|
||||
/** Clears cache. */
|
||||
public void clearCache() {
|
||||
if (cache != null) {
|
||||
cache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes remaining content in the cache into the DB.
|
||||
*/
|
||||
/** Writes remaining content in the cache into the DB. */
|
||||
public void flushCache() {
|
||||
LOGGER.info("# flushCache...");
|
||||
Optional.ofNullable(cache)
|
||||
@@ -171,6 +167,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Print user accounts.
|
||||
*
|
||||
* @return {@link String}
|
||||
*/
|
||||
public String print() {
|
||||
@@ -184,6 +181,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Delegate to backing cache store.
|
||||
*
|
||||
* @param userId {@link String}
|
||||
* @return {@link UserAccount}
|
||||
*/
|
||||
@@ -193,6 +191,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Delegate to backing cache store.
|
||||
*
|
||||
* @param userId {@link String}
|
||||
* @param userAccount {@link UserAccount}
|
||||
*/
|
||||
@@ -202,6 +201,7 @@ public class CacheStore {
|
||||
|
||||
/**
|
||||
* Delegate to backing cache store.
|
||||
*
|
||||
* @param userId {@link String}
|
||||
*/
|
||||
public void invalidate(final String userId) {
|
||||
|
||||
@@ -27,31 +27,19 @@ package com.iluwatar.caching;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* Enum class containing the four caching strategies implemented in the pattern.
|
||||
*/
|
||||
/** Enum class containing the four caching strategies implemented in the pattern. */
|
||||
@AllArgsConstructor
|
||||
@Getter
|
||||
public enum CachingPolicy {
|
||||
/**
|
||||
* Through.
|
||||
*/
|
||||
/** Through. */
|
||||
THROUGH("through"),
|
||||
/**
|
||||
* AROUND.
|
||||
*/
|
||||
/** AROUND. */
|
||||
AROUND("around"),
|
||||
/**
|
||||
* BEHIND.
|
||||
*/
|
||||
/** BEHIND. */
|
||||
BEHIND("behind"),
|
||||
/**
|
||||
* ASIDE.
|
||||
*/
|
||||
/** ASIDE. */
|
||||
ASIDE("aside");
|
||||
|
||||
/**
|
||||
* Policy value.
|
||||
*/
|
||||
/** Policy value. */
|
||||
private final String policy;
|
||||
}
|
||||
|
||||
@@ -30,42 +30,33 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
/**
|
||||
* Data structure/implementation of the application's cache. The data structure
|
||||
* consists of a hash table attached with a doubly linked-list. The linked-list
|
||||
* helps in capturing and maintaining the LRU data in the cache. When a data is
|
||||
* queried (from the cache), added (to the cache), or updated, the data is
|
||||
* moved to the front of the list to depict itself as the most-recently-used
|
||||
* data. The LRU data is always at the end of the list.
|
||||
* Data structure/implementation of the application's cache. The data structure consists of a hash
|
||||
* table attached with a doubly linked-list. The linked-list helps in capturing and maintaining the
|
||||
* LRU data in the cache. When a data is queried (from the cache), added (to the cache), or updated,
|
||||
* the data is moved to the front of the list to depict itself as the most-recently-used data. The
|
||||
* LRU data is always at the end of the list.
|
||||
*/
|
||||
@Slf4j
|
||||
public class LruCache {
|
||||
/**
|
||||
* Static class Node.
|
||||
*/
|
||||
/** Static class Node. */
|
||||
static class Node {
|
||||
/**
|
||||
* user id.
|
||||
*/
|
||||
/** user id. */
|
||||
private final String userId;
|
||||
/**
|
||||
* User Account.
|
||||
*/
|
||||
|
||||
/** User Account. */
|
||||
private UserAccount userAccount;
|
||||
/**
|
||||
* previous.
|
||||
*/
|
||||
|
||||
/** previous. */
|
||||
private Node previous;
|
||||
/**
|
||||
* next.
|
||||
*/
|
||||
|
||||
/** next. */
|
||||
private Node next;
|
||||
|
||||
/**
|
||||
* Node definition.
|
||||
*
|
||||
* @param id String
|
||||
* @param id String
|
||||
* @param account {@link UserAccount}
|
||||
*/
|
||||
Node(final String id, final UserAccount account) {
|
||||
@@ -74,21 +65,16 @@ public class LruCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Capacity of Cache.
|
||||
*/
|
||||
/** Capacity of Cache. */
|
||||
private int capacity;
|
||||
/**
|
||||
* Cache {@link HashMap}.
|
||||
*/
|
||||
|
||||
/** Cache {@link HashMap}. */
|
||||
private Map<String, Node> cache = new HashMap<>();
|
||||
/**
|
||||
* Head.
|
||||
*/
|
||||
|
||||
/** Head. */
|
||||
private Node head;
|
||||
/**
|
||||
* End.
|
||||
*/
|
||||
|
||||
/** End. */
|
||||
private Node end;
|
||||
|
||||
/**
|
||||
@@ -155,7 +141,7 @@ public class LruCache {
|
||||
* Set user account.
|
||||
*
|
||||
* @param userAccount {@link UserAccount}
|
||||
* @param userId {@link String}
|
||||
* @param userId {@link String}
|
||||
*/
|
||||
public void set(final String userId, final UserAccount userAccount) {
|
||||
if (cache.containsKey(userId)) {
|
||||
@@ -195,14 +181,14 @@ public class LruCache {
|
||||
public void invalidate(final String userId) {
|
||||
var toBeRemoved = cache.remove(userId);
|
||||
if (toBeRemoved != null) {
|
||||
LOGGER.info("# {} has been updated! "
|
||||
+ "Removing older version from cache...", userId);
|
||||
LOGGER.info("# {} has been updated! " + "Removing older version from cache...", userId);
|
||||
remove(toBeRemoved);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the cache is full.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isFull() {
|
||||
@@ -218,9 +204,7 @@ public class LruCache {
|
||||
return end.userAccount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache.
|
||||
*/
|
||||
/** Clear cache. */
|
||||
public void clear() {
|
||||
head = null;
|
||||
end = null;
|
||||
|
||||
@@ -29,24 +29,18 @@ import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
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
|
||||
@AllArgsConstructor
|
||||
@ToString
|
||||
@EqualsAndHashCode
|
||||
public class UserAccount {
|
||||
/**
|
||||
* User Id.
|
||||
*/
|
||||
/** User Id. */
|
||||
private String userId;
|
||||
/**
|
||||
* User Name.
|
||||
*/
|
||||
|
||||
/** User Name. */
|
||||
private String userName;
|
||||
/**
|
||||
* Additional Info.
|
||||
*/
|
||||
|
||||
/** Additional Info. */
|
||||
private String additionalInfo;
|
||||
}
|
||||
|
||||
@@ -24,30 +24,20 @@
|
||||
*/
|
||||
package com.iluwatar.caching.constants;
|
||||
|
||||
/**
|
||||
* Constant class for defining constants.
|
||||
*/
|
||||
/** Constant class for defining constants. */
|
||||
public final class CachingConstants {
|
||||
/**
|
||||
* User Account.
|
||||
*/
|
||||
/** User Account. */
|
||||
public static final String USER_ACCOUNT = "user_accounts";
|
||||
/**
|
||||
* User ID.
|
||||
*/
|
||||
|
||||
/** User ID. */
|
||||
public static final String USER_ID = "userID";
|
||||
/**
|
||||
* User Name.
|
||||
*/
|
||||
|
||||
/** User Name. */
|
||||
public static final String USER_NAME = "userName";
|
||||
/**
|
||||
* Additional Info.
|
||||
*/
|
||||
|
||||
/** Additional Info. */
|
||||
public static final String ADD_INFO = "additionalInfo";
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
private CachingConstants() {
|
||||
}
|
||||
/** Constructor. */
|
||||
private CachingConstants() {}
|
||||
}
|
||||
|
||||
@@ -22,7 +22,5 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
* Constants.
|
||||
*/
|
||||
/** Constants. */
|
||||
package com.iluwatar.caching.constants;
|
||||
|
||||
@@ -27,19 +27,15 @@ 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>
|
||||
* 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.
|
||||
*/
|
||||
public interface DbManager {
|
||||
/**
|
||||
* Connect to DB.
|
||||
*/
|
||||
/** Connect to DB. */
|
||||
void connect();
|
||||
|
||||
/**
|
||||
* Disconnect from DB.
|
||||
*/
|
||||
/** Disconnect from DB. */
|
||||
void disconnect();
|
||||
|
||||
/**
|
||||
|
||||
@@ -24,15 +24,10 @@
|
||||
*/
|
||||
package com.iluwatar.caching.database;
|
||||
|
||||
/**
|
||||
* Creates the database connection according the input parameter.
|
||||
*/
|
||||
/** Creates the database connection according the input parameter. */
|
||||
public final class DbManagerFactory {
|
||||
/**
|
||||
* Private constructor.
|
||||
*/
|
||||
private DbManagerFactory() {
|
||||
}
|
||||
/** Private constructor. */
|
||||
private DbManagerFactory() {}
|
||||
|
||||
/**
|
||||
* Init database.
|
||||
|
||||
@@ -40,10 +40,7 @@ import com.mongodb.client.model.UpdateOptions;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.bson.Document;
|
||||
|
||||
/**
|
||||
* Implementation of DatabaseManager.
|
||||
* implements base methods to work with MongoDb.
|
||||
*/
|
||||
/** Implementation of DatabaseManager. implements base methods to work with MongoDb. */
|
||||
@Slf4j
|
||||
public class MongoDb implements DbManager {
|
||||
private static final String DATABASE_NAME = "admin";
|
||||
@@ -56,14 +53,11 @@ public class MongoDb implements DbManager {
|
||||
this.db = db;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to Db. Check th connection
|
||||
*/
|
||||
/** Connect to Db. Check th connection */
|
||||
@Override
|
||||
public void connect() {
|
||||
MongoCredential mongoCredential = MongoCredential.createCredential(MONGO_USER,
|
||||
DATABASE_NAME,
|
||||
MONGO_PASSWORD.toCharArray());
|
||||
MongoCredential mongoCredential =
|
||||
MongoCredential.createCredential(MONGO_USER, DATABASE_NAME, MONGO_PASSWORD.toCharArray());
|
||||
MongoClientOptions options = MongoClientOptions.builder().build();
|
||||
client = new MongoClient(new ServerAddress(), mongoCredential, options);
|
||||
db = client.getDatabase(DATABASE_NAME);
|
||||
@@ -82,9 +76,8 @@ public class MongoDb implements DbManager {
|
||||
*/
|
||||
@Override
|
||||
public UserAccount readFromDb(final String userId) {
|
||||
var iterable = db
|
||||
.getCollection(CachingConstants.USER_ACCOUNT)
|
||||
.find(new Document(USER_ID, userId));
|
||||
var iterable =
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT).find(new Document(USER_ID, userId));
|
||||
if (iterable.first() == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -106,11 +99,11 @@ public class MongoDb implements DbManager {
|
||||
*/
|
||||
@Override
|
||||
public UserAccount writeToDb(final UserAccount userAccount) {
|
||||
db.getCollection(USER_ACCOUNT).insertOne(
|
||||
db.getCollection(USER_ACCOUNT)
|
||||
.insertOne(
|
||||
new Document(USER_ID, userAccount.getUserId())
|
||||
.append(USER_NAME, userAccount.getUserName())
|
||||
.append(ADD_INFO, userAccount.getAdditionalInfo())
|
||||
);
|
||||
.append(USER_NAME, userAccount.getUserName())
|
||||
.append(ADD_INFO, userAccount.getAdditionalInfo()));
|
||||
return userAccount;
|
||||
}
|
||||
|
||||
@@ -123,10 +116,10 @@ public class MongoDb implements DbManager {
|
||||
@Override
|
||||
public UserAccount updateDb(final UserAccount userAccount) {
|
||||
Document id = new Document(USER_ID, userAccount.getUserId());
|
||||
Document dataSet = new Document(USER_NAME, userAccount.getUserName())
|
||||
Document dataSet =
|
||||
new Document(USER_NAME, userAccount.getUserName())
|
||||
.append(ADD_INFO, userAccount.getAdditionalInfo());
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT)
|
||||
.updateOne(id, new Document("$set", dataSet));
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(id, new Document("$set", dataSet));
|
||||
return userAccount;
|
||||
}
|
||||
|
||||
@@ -141,15 +134,15 @@ public class MongoDb implements DbManager {
|
||||
String userId = userAccount.getUserId();
|
||||
String userName = userAccount.getUserName();
|
||||
String additionalInfo = userAccount.getAdditionalInfo();
|
||||
db.getCollection(CachingConstants.USER_ACCOUNT).updateOne(
|
||||
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)
|
||||
);
|
||||
new Document(
|
||||
"$set",
|
||||
new Document(USER_ID, userId)
|
||||
.append(USER_NAME, userName)
|
||||
.append(ADD_INFO, additionalInfo)),
|
||||
new UpdateOptions().upsert(true));
|
||||
return userAccount;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,19 +28,12 @@ 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.
|
||||
*/
|
||||
/** Implementation of DatabaseManager. implements base methods to work with hashMap as database. */
|
||||
public class VirtualDb implements DbManager {
|
||||
/**
|
||||
* Virtual DataBase.
|
||||
*/
|
||||
/** Virtual DataBase. */
|
||||
private Map<String, UserAccount> db;
|
||||
|
||||
/**
|
||||
* Creates new HashMap.
|
||||
*/
|
||||
/** Creates new HashMap. */
|
||||
@Override
|
||||
public void connect() {
|
||||
db = new HashMap<>();
|
||||
|
||||
@@ -22,7 +22,5 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
/**
|
||||
* Database classes.
|
||||
*/
|
||||
/** Database classes. */
|
||||
package com.iluwatar.caching.database;
|
||||
|
||||
@@ -24,23 +24,20 @@
|
||||
*/
|
||||
package com.iluwatar.caching;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
|
||||
/**
|
||||
* Tests that Caching example runs without errors.
|
||||
*/
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
/** Tests that Caching example runs without errors. */
|
||||
class AppTest {
|
||||
/**
|
||||
* 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}
|
||||
* throws an exception.
|
||||
*
|
||||
* <p>Solution: Inserted assertion to check whether the execution of the main method in {@link
|
||||
* App} throws an exception.
|
||||
*/
|
||||
|
||||
@Test
|
||||
void shouldExecuteApplicationWithoutException() {
|
||||
assertDoesNotThrow(() -> App.main(new String[]{}));
|
||||
assertDoesNotThrow(() -> App.main(new String[] {}));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,20 +24,16 @@
|
||||
*/
|
||||
package com.iluwatar.caching;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
||||
/**
|
||||
* Application test
|
||||
*/
|
||||
/** Application test */
|
||||
class CachingTest {
|
||||
private App app;
|
||||
|
||||
/**
|
||||
* Setup of application test includes: initializing DB connection and cache size/capacity.
|
||||
*/
|
||||
/** Setup of application test includes: initializing DB connection and cache size/capacity. */
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
// VirtualDB (instead of MongoDB) was used in running the JUnit tests
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
*/
|
||||
package com.iluwatar.caching.database;
|
||||
|
||||
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
|
||||
import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
|
||||
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import com.iluwatar.caching.UserAccount;
|
||||
import com.iluwatar.caching.constants.CachingConstants;
|
||||
import com.mongodb.client.FindIterable;
|
||||
@@ -34,20 +41,12 @@ import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mock;
|
||||
|
||||
import static com.iluwatar.caching.constants.CachingConstants.ADD_INFO;
|
||||
import static com.iluwatar.caching.constants.CachingConstants.USER_ID;
|
||||
import static com.iluwatar.caching.constants.CachingConstants.USER_NAME;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
class MongoDbTest {
|
||||
private static final String ID = "123";
|
||||
private static final String NAME = "Some user";
|
||||
private static final String ADDITIONAL_INFO = "Some app Info";
|
||||
|
||||
@Mock
|
||||
MongoDatabase db;
|
||||
@Mock MongoDatabase db;
|
||||
private MongoDb mongoDb = new MongoDb();
|
||||
|
||||
private UserAccount userAccount;
|
||||
@@ -66,9 +65,8 @@ class MongoDbTest {
|
||||
|
||||
@Test
|
||||
void readFromDb() {
|
||||
Document document = new Document(USER_ID, ID)
|
||||
.append(USER_NAME, NAME)
|
||||
.append(ADD_INFO, ADDITIONAL_INFO);
|
||||
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);
|
||||
|
||||
@@ -77,27 +75,36 @@ class MongoDbTest {
|
||||
|
||||
when(findIterable.first()).thenReturn(document);
|
||||
|
||||
assertEquals(mongoDb.readFromDb(ID),userAccount);
|
||||
assertEquals(mongoDb.readFromDb(ID), userAccount);
|
||||
}
|
||||
|
||||
@Test
|
||||
void writeToDb() {
|
||||
MongoCollection<Document> mongoCollection = mock(MongoCollection.class);
|
||||
when(db.getCollection(CachingConstants.USER_ACCOUNT)).thenReturn(mongoCollection);
|
||||
assertDoesNotThrow(()-> {mongoDb.writeToDb(userAccount);});
|
||||
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);});
|
||||
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);});
|
||||
assertDoesNotThrow(
|
||||
() -> {
|
||||
mongoDb.upsertDb(userAccount);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user