diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 14f16f365..0882fc165 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -1,26 +1,3 @@ -/* - * 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.database.DbManager; @@ -28,33 +5,42 @@ 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; - * write-through which writes data to the cache and DB in a single transaction, - * write-around which writes data immediately into the DB instead of the cache, - * write-behind which writes data into the cache initially whilst the data is only - * written into the DB when the cache is full, and cache-aside which pushes the - * responsibility of keeping the data synchronized in both data sources to the application itself. - * The read-through strategy is also included in the mentioned four strategies -- - * returns data from the cache to the caller if it exists else 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 maintain - * consistency 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 write-through which writes data to the cache and + * DB in a single transaction, write-around which writes data + * immediately into the DB instead of the cache, write-behind + * which writes data into the cache initially whilst the data is only + * written into the DB when the cache is full, and cache-aside + * which pushes the responsibility of keeping the data synchronized in both + * data sources to the application itself. The read-through + * strategy is also included in the mentioned four strategies -- + * returns data from the cache to the caller if it exists else + * 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. * - *

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). + *

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). *

- * {@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} + * {@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> + * DBManager} *

* *

@@ -68,10 +54,20 @@ import lombok.extern.slf4j.Slf4j; */ @Slf4j public class App { + /** + * Constant parameter name to use mongoDB. + */ private static final String USE_MONGO_DB = "--mongo"; - private AppManager appManager; + /** + * Application manager. + */ + private final AppManager appManager; - public App(boolean isMongo) { + /** + * Constructor of current App. + * @param isMongo boolean + */ + public App(final boolean isMongo) { DbManager dbManager = DbManagerFactory.initDb(isMongo); appManager = new AppManager(dbManager); appManager.initDb(); @@ -82,20 +78,20 @@ public class App { * * @param args command line args */ - public static void main(String[] args) { + public static void main(final String[] args) { // VirtualDB (instead of MongoDB) was used in running the JUnit tests // and the App class to avoid Maven compilation errors. Set flag to // true to run the tests with MongoDB (provided that MongoDB is // installed and socket connection is open). App app = new App(isDbMongo(args)); app.useReadAndWriteThroughStrategy(); - System.out.println("====================================================="); + System.out.println("=============================================="); app.useReadThroughAndWriteAroundStrategy(); - System.out.println("====================================================="); + System.out.println("=============================================="); app.useReadThroughAndWriteBehindStrategy(); - System.out.println("====================================================="); + System.out.println("=============================================="); app.useCacheAsideStategy(); - System.out.println("====================================================="); + System.out.println("=============================================="); } /** @@ -103,7 +99,7 @@ public class App { * @param args input params * @return true if there is "--mongo" parameter in arguments */ - private static boolean isDbMongo(String[] args) { + private static boolean isDbMongo(final String[] args) { for (String arg : args) { if (arg.equals(USE_MONGO_DB)) { return true; @@ -156,9 +152,15 @@ public class App { 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); @@ -166,7 +168,9 @@ 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"); @@ -181,9 +185,15 @@ public class App { 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); diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 880791034..cdf26b194 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -29,28 +29,41 @@ import com.iluwatar.caching.database.DbManager; 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. + */ private static CachingPolicy cachingPolicy; + /** + * Database Manager. + */ private final DbManager dbManager; + /** + * Cache Store. + */ private final CacheStore cacheStore; - public AppManager(DbManager dbManager) { - this.dbManager = dbManager; - this.cacheStore = new CacheStore(dbManager); + /** + * Constructor. + * @param newDbManager database manager + */ + public AppManager(final DbManager newDbManager) { + this.dbManager = newDbManager; + this.cacheStore = new CacheStore(newDbManager); } /** - * Developer/Tester is able to choose whether the application should use MongoDB as its underlying - * 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(); @@ -58,8 +71,9 @@ public class AppManager { /** * Initialize caching policy. + * @param policy is a {@link CachingPolicy} */ - public void initCachingPolicy(CachingPolicy policy) { + public void initCachingPolicy(final CachingPolicy policy) { cachingPolicy = policy; if (cachingPolicy == CachingPolicy.BEHIND) { Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache)); @@ -69,10 +83,13 @@ public class AppManager { /** * Find user account. + * @param userId String + * @return {@link UserAccount} */ - public UserAccount find(String userId) { + 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); @@ -84,8 +101,9 @@ public class AppManager { /** * Save user account. + * @param userAccount {@link UserAccount} */ - public void save(UserAccount userAccount) { + public void save(final UserAccount userAccount) { LOGGER.info("Save record!"); if (cachingPolicy == CachingPolicy.THROUGH) { cacheStore.writeThrough(userAccount); @@ -98,25 +116,33 @@ public class AppManager { } } + /** + * Returns String. + * @return String + */ public String printCacheContent() { return cacheStore.print(); } /** * Cache-Aside save user account helper. + * @param userAccount {@link UserAccount} */ - private void saveAside(UserAccount userAccount) { + private void saveAside(final UserAccount userAccount) { dbManager.updateDb(userAccount); cacheStore.invalidate(userAccount.getUserId()); } /** * Cache-Aside find user account helper. + * @param userId String + * @return {@link UserAccount} */ - private UserAccount findAside(String userId) { + private UserAccount findAside(final String userId) { return Optional.ofNullable(cacheStore.get(userId)) .or(() -> { - Optional userAccount = Optional.ofNullable(dbManager.readFromDb(userId)); + Optional userAccount = + Optional.ofNullable(dbManager.readFromDb(userId)); userAccount.ifPresent(account -> cacheStore.set(userId, account)); return userAccount; }) diff --git a/caching/src/main/java/com/iluwatar/caching/database/DbManager.java b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java index d1ec2dd3f..496384abc 100644 --- a/caching/src/main/java/com/iluwatar/caching/database/DbManager.java +++ b/caching/src/main/java/com/iluwatar/caching/database/DbManager.java @@ -3,14 +3,38 @@ package com.iluwatar.caching.database; import com.iluwatar.caching.UserAccount; /** - *

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.

+ *

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. + */ void connect(); + + /** + * 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); } diff --git a/caching/src/main/java/com/iluwatar/caching/database/package-info.java b/caching/src/main/java/com/iluwatar/caching/database/package-info.java new file mode 100644 index 000000000..f81386688 --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/database/package-info.java @@ -0,0 +1,4 @@ +/** + * Database classes + */ +package com.iluwatar.caching.database; diff --git a/caching/src/main/java/com/iluwatar/caching/package-info.java b/caching/src/main/java/com/iluwatar/caching/package-info.java new file mode 100644 index 000000000..d670c1bd0 --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/package-info.java @@ -0,0 +1,24 @@ +/* + * 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; \ No newline at end of file