Fix 40 errors from checkstyle plugin run. 139 left))

This commit is contained in:
Victor Z 2021-08-17 22:05:01 +03:00
parent a437f5b2eb
commit 3c213fcd69
5 changed files with 173 additions and 85 deletions

View File

@ -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; package com.iluwatar.caching;
import com.iluwatar.caching.database.DbManager; import com.iluwatar.caching.database.DbManager;
@ -28,33 +5,42 @@ import com.iluwatar.caching.database.DbManagerFactory;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing * The Caching pattern describes how to avoid expensive re-acquisition of
* the resources immediately after their use. The resources retain their identity, are kept in some * resources by not releasing the resources immediately after their use.
* fast-access storage, and are re-used to avoid having to acquire them again. There are four main * The resources retain their identity, are kept in some fast-access storage,
* caching strategies/techniques in this pattern; each with their own pros and cons. They are; * and are re-used to avoid having to acquire them again. There are four main
* <code>write-through</code> which writes data to the cache and DB in a single transaction, * caching strategies/techniques in this pattern; each with their own pros and
* <code>write-around</code> which writes data immediately into the DB instead of the cache, * cons. They are <code>write-through</code> which writes data to the cache and
* <code>write-behind</code> which writes data into the cache initially whilst the data is only * DB in a single transaction, <code>write-around</code> which writes data
* written into the DB when the cache is full, and <code>cache-aside</code> which pushes the * immediately into the DB instead of the cache, <code>write-behind</code>
* responsibility of keeping the data synchronized in both data sources to the application itself. * which writes data into the cache initially whilst the data is only
* The <code>read-through</code> strategy is also included in the mentioned four strategies -- * written into the DB when the cache is full, and <code>cache-aside</code>
* returns data from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and * which pushes the responsibility of keeping the data synchronized in both
* stores it into the cache for future use. These strategies determine when the data in the cache * data sources to the application itself. The <code>read-through</code>
* should be written back to the backing store (i.e. Database) and help keep both data sources * strategy is also included in the mentioned four strategies --
* synchronized/up-to-date. This pattern can improve performance and also helps to maintain * returns data from the cache to the caller <b>if</b> it exists <b>else</b>
* consistency between data held in the cache and the data in the underlying data store. * queries from DB and stores it into the cache for future use. These strategies
* determine when the data in the cache should be written back to the backing
* store (i.e. Database) and help keep both data sources
* synchronized/up-to-date. This pattern can improve performance and also helps
* to maintainconsistency between data held in the cache and the data in
* the underlying data store.
* *
* <p>In this example, the user account ({@link UserAccount}) entity is used as the underlying * <p>In this example, the user account ({@link UserAccount}) entity is used
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts * as the underlying application data. The cache itself is implemented as an
* a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The four * internal (Java) data structure. It adopts a Least-Recently-Used (LRU)
* strategies are individually tested. The testing of the cache is restricted towards saving and * strategy for evicting data from itself when its full. The four
* querying of user accounts from the underlying data store ( {@link DbManager}). The main class ( * strategies are individually tested. The testing of the cache is restricted
* {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and * towards saving and querying of user accounts from the
* whether the data is coming from the cache or the DB (i.e. separation of concern). The AppManager * underlying data store( {@link DbManager}). The main class ( {@link App}
* ({@link AppManager}) handles the transaction of data to-and-from the underlying data store * is not aware of the underlying mechanics of the application
* (depending on the preferred caching policy/strategy). * (i.e. save and query) and whether the data is coming from the cache or the
* DB (i.e. separation of concern). The AppManager ({@link AppManager}) handles
* the transaction of data to-and-from the underlying data store (depending on
* the preferred caching policy/strategy).
* <p> * <p>
* <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy --> DBManager} </i> * <i>{@literal App --> AppManager --> CacheStore/LRUCache/CachingPolicy -->
* DBManager} </i>
* </p> * </p>
* *
* <p> * <p>
@ -68,10 +54,20 @@ 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"; 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); DbManager dbManager = DbManagerFactory.initDb(isMongo);
appManager = new AppManager(dbManager); appManager = new AppManager(dbManager);
appManager.initDb(); appManager.initDb();
@ -82,20 +78,20 @@ public class App {
* *
* @param args command line args * @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 // 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).
App app = new App(isDbMongo(args)); App app = new App(isDbMongo(args));
app.useReadAndWriteThroughStrategy(); app.useReadAndWriteThroughStrategy();
System.out.println("====================================================="); System.out.println("==============================================");
app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteAroundStrategy();
System.out.println("====================================================="); System.out.println("==============================================");
app.useReadThroughAndWriteBehindStrategy(); app.useReadThroughAndWriteBehindStrategy();
System.out.println("====================================================="); System.out.println("==============================================");
app.useCacheAsideStategy(); app.useCacheAsideStategy();
System.out.println("====================================================="); System.out.println("==============================================");
} }
/** /**
@ -103,7 +99,7 @@ public class App {
* @param args input params * @param args input params
* @return true if there is "--mongo" parameter in arguments * @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) { for (String arg : args) {
if (arg.equals(USE_MONGO_DB)) { if (arg.equals(USE_MONGO_DB)) {
return true; return true;
@ -156,9 +152,15 @@ public class App {
LOGGER.info("# CachingPolicy.BEHIND"); LOGGER.info("# CachingPolicy.BEHIND");
appManager.initCachingPolicy(CachingPolicy.BEHIND); appManager.initCachingPolicy(CachingPolicy.BEHIND);
var userAccount3 = new UserAccount("003", "Adam", "He likes food."); var userAccount3 = new UserAccount("003",
var userAccount4 = new UserAccount("004", "Rita", "She hates cats."); "Adam",
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); "He likes food.");
var userAccount4 = new UserAccount("004",
"Rita",
"She hates cats.");
var userAccount5 = new UserAccount("005",
"Isaac",
"He is allergic to mustard.");
appManager.save(userAccount3); appManager.save(userAccount3);
appManager.save(userAccount4); appManager.save(userAccount4);
@ -166,7 +168,9 @@ public class App {
LOGGER.info(appManager.printCacheContent()); LOGGER.info(appManager.printCacheContent());
appManager.find("003"); appManager.find("003");
LOGGER.info(appManager.printCacheContent()); LOGGER.info(appManager.printCacheContent());
UserAccount userAccount6 = new UserAccount("006", "Yasha", "She is an only child."); UserAccount userAccount6 = new UserAccount("006",
"Yasha",
"She is an only child.");
appManager.save(userAccount6); appManager.save(userAccount6);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(appManager.printCacheContent());
appManager.find("004"); appManager.find("004");
@ -181,9 +185,15 @@ public class App {
appManager.initCachingPolicy(CachingPolicy.ASIDE); appManager.initCachingPolicy(CachingPolicy.ASIDE);
LOGGER.info(appManager.printCacheContent()); LOGGER.info(appManager.printCacheContent());
var userAccount3 = new UserAccount("003", "Adam", "He likes food."); var userAccount3 = new UserAccount("003",
var userAccount4 = new UserAccount("004", "Rita", "She hates cats."); "Adam",
var userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard."); "He likes food.");
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);

View File

@ -29,28 +29,41 @@ import com.iluwatar.caching.database.DbManager;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
/** /**
* AppManager helps to bridge the gap in communication between the main class and the application's * AppManager helps to bridge the gap in communication between the main class
* back-end. DB connection is initialized through this class. The chosen caching strategy/policy is * and the application's back-end. DB connection is initialized through this
* also initialized here. Before the cache can be used, the size of the cache has to be set. * class. The chosen caching strategy/policy is also initialized here.
* Depending on the chosen caching policy, AppManager will call the appropriate function in the * Before the cache can be used, the size of the cache has to be set.
* CacheStore class. * Depending on the chosen caching policy, AppManager will call the
* appropriate function in the CacheStore class.
*/ */
@Slf4j @Slf4j
public class AppManager { public class AppManager {
/**
* Caching Policy.
*/
private static CachingPolicy cachingPolicy; private static CachingPolicy cachingPolicy;
/**
* Database Manager.
*/
private final DbManager dbManager; private final DbManager dbManager;
/**
* Cache Store.
*/
private final CacheStore cacheStore; private final CacheStore cacheStore;
public AppManager(DbManager dbManager) { /**
this.dbManager = dbManager; * Constructor.
this.cacheStore = new CacheStore(dbManager); * @param newDbManager database manager
*/
public AppManager(final DbManager newDbManager) {
this.dbManager = newDbManager;
this.cacheStore = new CacheStore(newDbManager);
} }
/** /**
* Developer/Tester is able to choose whether the application should use MongoDB as its underlying * Developer/Tester is able to choose whether the application should use
* data storage or a simple Java data structure to (temporarily) store the data/objects during * MongoDB as its underlying data storage or a simple Java data structure
* runtime. * to (temporarily) store the data/objects during runtime.
*/ */
public void initDb() { public void initDb() {
dbManager.connect(); dbManager.connect();
@ -58,8 +71,9 @@ public class AppManager {
/** /**
* Initialize caching policy. * Initialize caching policy.
* @param policy is a {@link CachingPolicy}
*/ */
public void initCachingPolicy(CachingPolicy policy) { public void initCachingPolicy(final CachingPolicy policy) {
cachingPolicy = policy; cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) { if (cachingPolicy == CachingPolicy.BEHIND) {
Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache)); Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache));
@ -69,10 +83,13 @@ public class AppManager {
/** /**
* Find user account. * 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); 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); return cacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) { } else if (cachingPolicy == CachingPolicy.BEHIND) {
return cacheStore.readThroughWithWriteBackPolicy(userId); return cacheStore.readThroughWithWriteBackPolicy(userId);
@ -84,8 +101,9 @@ public class AppManager {
/** /**
* Save user account. * Save user account.
* @param userAccount {@link UserAccount}
*/ */
public void save(UserAccount userAccount) { public void save(final UserAccount userAccount) {
LOGGER.info("Save record!"); LOGGER.info("Save record!");
if (cachingPolicy == CachingPolicy.THROUGH) { if (cachingPolicy == CachingPolicy.THROUGH) {
cacheStore.writeThrough(userAccount); cacheStore.writeThrough(userAccount);
@ -98,25 +116,33 @@ public class AppManager {
} }
} }
/**
* Returns String.
* @return String
*/
public String printCacheContent() { public String printCacheContent() {
return cacheStore.print(); return cacheStore.print();
} }
/** /**
* Cache-Aside save user account helper. * Cache-Aside save user account helper.
* @param userAccount {@link UserAccount}
*/ */
private void saveAside(UserAccount userAccount) { private void saveAside(final UserAccount userAccount) {
dbManager.updateDb(userAccount); dbManager.updateDb(userAccount);
cacheStore.invalidate(userAccount.getUserId()); cacheStore.invalidate(userAccount.getUserId());
} }
/** /**
* Cache-Aside find user account helper. * Cache-Aside find user account helper.
* @param userId String
* @return {@link UserAccount}
*/ */
private UserAccount findAside(String userId) { private UserAccount findAside(final String userId) {
return Optional.ofNullable(cacheStore.get(userId)) return Optional.ofNullable(cacheStore.get(userId))
.or(() -> { .or(() -> {
Optional<UserAccount> userAccount = Optional.ofNullable(dbManager.readFromDb(userId)); Optional<UserAccount> userAccount =
Optional.ofNullable(dbManager.readFromDb(userId));
userAccount.ifPresent(account -> cacheStore.set(userId, account)); userAccount.ifPresent(account -> cacheStore.set(userId, account));
return userAccount; return userAccount;
}) })

View File

@ -3,14 +3,38 @@ package com.iluwatar.caching.database;
import com.iluwatar.caching.UserAccount; import com.iluwatar.caching.UserAccount;
/** /**
* <p>DBManager handles the communication with the underlying data store i.e. Database. It contains * <p>DBManager handles the communication with the underlying data store i.e.
* the implemented methods for querying, inserting, and updating data. MongoDB was used as the * Database. It contains the implemented methods for querying, inserting,
* database for the application.</p> * and updating data. MongoDB was used as the database for the application.</p>
*/ */
public interface DbManager { public interface DbManager {
/**
* Connect to DB.
*/
void connect(); void connect();
/**
* Read from DB.
* @param userId {@link String}
* @return {@link UserAccount}
*/
UserAccount readFromDb(String userId); UserAccount readFromDb(String userId);
/**
* Write to DB.
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount writeToDb(UserAccount userAccount); UserAccount writeToDb(UserAccount userAccount);
/**
* Update record.
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount updateDb(UserAccount userAccount); UserAccount updateDb(UserAccount userAccount);
/**
* Update record or Insert if not exists.
* @param userAccount {@link UserAccount}
* @return {@link UserAccount}
*/
UserAccount upsertDb(UserAccount userAccount); UserAccount upsertDb(UserAccount userAccount);
} }

View File

@ -0,0 +1,4 @@
/**
* Database classes
*/
package com.iluwatar.caching.database;

View File

@ -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;