From 399ad53b09fec5aa6a23ecf04bf40da56c0d72e9 Mon Sep 17 00:00:00 2001
From: Victor Zalevskii
+ * To run the application with MongoDb, just start it with parameter --mongo + * Example: java -jar app.jar --mongo + *
+ * * @see CacheStore * @see LruCache * @see CachingPolicy */ @Slf4j public class App { + private static final String USE_MONGO_DB = "--mongo"; + private DbManager dbManager; + private AppManager appManager; - /** - * Program entry point. - * - * @param args command line args - */ - public static void main(String[] args) { - 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 - // true to run the tests with MongoDB (provided that MongoDB is - // installed and socket connection is open). - AppManager.initCacheCapacity(3); - var app = new App(); - app.useReadAndWriteThroughStrategy(); - app.useReadThroughAndWriteAroundStrategy(); - app.useReadThroughAndWriteBehindStrategy(); - app.useCacheAsideStategy(); - } + public App(boolean isMongo) { + dbManager = DbManagerFactory.initDb(isMongo); + appManager = new AppManager(dbManager); + appManager.initDb(); + } - /** - * Read-through and write-through. - */ - public void useReadAndWriteThroughStrategy() { - LOGGER.info("# CachingPolicy.THROUGH"); - AppManager.initCachingPolicy(CachingPolicy.THROUGH); + /** + * Program entry point. + * + * @param args command line args + */ + public static void main(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(); + app.useReadThroughAndWriteAroundStrategy(); + app.useReadThroughAndWriteBehindStrategy(); + app.useCacheAsideStategy(); + } - var userAccount1 = new UserAccount("001", "John", "He is a boy."); + /** + * Check the input parameters. if + * @param args input params + * @return true if there is "--mongo" parameter in arguments + */ + private static boolean isDbMongo(String[] args) { + for (String arg : args) { + if (arg.equals(USE_MONGO_DB)) { + return true; + } + } + return false; + } - AppManager.save(userAccount1); - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("001"); - AppManager.find("001"); - } + /** + * Read-through and write-through. + */ + public void useReadAndWriteThroughStrategy() { + LOGGER.info("# CachingPolicy.THROUGH"); + appManager.initCachingPolicy(CachingPolicy.THROUGH); - /** - * Read-through and write-around. - */ - public void useReadThroughAndWriteAroundStrategy() { - LOGGER.info("# CachingPolicy.AROUND"); - AppManager.initCachingPolicy(CachingPolicy.AROUND); + var userAccount1 = new UserAccount("001", "John", "He is a boy."); - var userAccount2 = new UserAccount("002", "Jane", "She is a girl."); + appManager.save(userAccount1); + LOGGER.info(appManager.printCacheContent()); + appManager.find("001"); + appManager.find("001"); + } - AppManager.save(userAccount2); - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("002"); - LOGGER.info(AppManager.printCacheContent()); - userAccount2 = AppManager.find("002"); - userAccount2.setUserName("Jane G."); - AppManager.save(userAccount2); - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("002"); - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("002"); - } + /** + * Read-through and write-around. + */ + public void useReadThroughAndWriteAroundStrategy() { + LOGGER.info("# CachingPolicy.AROUND"); + appManager.initCachingPolicy(CachingPolicy.AROUND); - /** - * Read-through and write-behind. - */ - public void useReadThroughAndWriteBehindStrategy() { - LOGGER.info("# CachingPolicy.BEHIND"); - AppManager.initCachingPolicy(CachingPolicy.BEHIND); + var userAccount2 = new UserAccount("002", "Jane", "She is a girl."); - 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(userAccount2); + LOGGER.info(appManager.printCacheContent()); + appManager.find("002"); + LOGGER.info(appManager.printCacheContent()); + userAccount2 = appManager.find("002"); + userAccount2.setUserName("Jane G."); + appManager.save(userAccount2); + LOGGER.info(appManager.printCacheContent()); + appManager.find("002"); + LOGGER.info(appManager.printCacheContent()); + appManager.find("002"); + } - AppManager.save(userAccount3); - AppManager.save(userAccount4); - AppManager.save(userAccount5); - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("003"); - LOGGER.info(AppManager.printCacheContent()); - 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()); - } + /** + * Read-through and write-behind. + */ + public void useReadThroughAndWriteBehindStrategy() { + LOGGER.info("# CachingPolicy.BEHIND"); + appManager.initCachingPolicy(CachingPolicy.BEHIND); - /** - * Cache-Aside. - */ - public void useCacheAsideStategy() { - 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); + appManager.save(userAccount3); + appManager.save(userAccount4); + appManager.save(userAccount5); + LOGGER.info(appManager.printCacheContent()); + appManager.find("003"); + LOGGER.info(appManager.printCacheContent()); + 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()); + } - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("003"); - LOGGER.info(AppManager.printCacheContent()); - AppManager.find("004"); - LOGGER.info(AppManager.printCacheContent()); - } + /** + * Cache-Aside. + */ + public void useCacheAsideStategy() { + 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."); + appManager.save(userAccount3); + appManager.save(userAccount4); + appManager.save(userAccount5); + + LOGGER.info(appManager.printCacheContent()); + appManager.find("003"); + LOGGER.info(appManager.printCacheContent()); + appManager.find("004"); + LOGGER.info(appManager.printCacheContent()); + } } diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 68c8a0d36..cca24014a 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -23,8 +23,9 @@ package com.iluwatar.caching; -import java.text.ParseException; import java.util.Optional; + +import com.iluwatar.caching.database.DbManager; import lombok.extern.slf4j.Slf4j; /** @@ -35,11 +36,15 @@ import lombok.extern.slf4j.Slf4j; * CacheStore class. */ @Slf4j -public final class AppManager { +public class AppManager { private static CachingPolicy cachingPolicy; + private final DbManager dbManager; + private final CacheStore cacheStore; - private AppManager() { + public AppManager(DbManager dbManager) { + this.dbManager = dbManager; + this.cacheStore = new CacheStore(dbManager); } /** @@ -47,41 +52,29 @@ public final class AppManager { * data storage or a simple Java data structure to (temporarily) store the data/objects during * runtime. */ - public static void initDb(boolean useMongoDb) { - if (useMongoDb) { - try { - DbManager.connect(); - } catch (ParseException e) { - LOGGER.error("Error connecting to MongoDB", e); - } - } else { - DbManager.createVirtualDb(); - } + public void initDb() { + dbManager.connect(); } /** * Initialize caching policy. */ - public static void initCachingPolicy(CachingPolicy policy) { + public void initCachingPolicy(CachingPolicy policy) { cachingPolicy = policy; if (cachingPolicy == CachingPolicy.BEHIND) { - Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache)); + Runtime.getRuntime().addShutdownHook(new Thread(cacheStore::flushCache)); } - CacheStore.clearCache(); - } - - public static void initCacheCapacity(int capacity) { - CacheStore.initCapacity(capacity); + cacheStore.clearCache(); } /** * Find user account. */ - public static UserAccount find(String userId) { + public UserAccount find(String userId) { if (cachingPolicy == CachingPolicy.THROUGH || cachingPolicy == CachingPolicy.AROUND) { - return CacheStore.readThrough(userId); + return cacheStore.readThrough(userId); } else if (cachingPolicy == CachingPolicy.BEHIND) { - return CacheStore.readThroughWithWriteBackPolicy(userId); + return cacheStore.readThroughWithWriteBackPolicy(userId); } else if (cachingPolicy == CachingPolicy.ASIDE) { return findAside(userId); } @@ -91,38 +84,38 @@ public final class AppManager { /** * Save user account. */ - public static void save(UserAccount userAccount) { + public void save(UserAccount userAccount) { if (cachingPolicy == CachingPolicy.THROUGH) { - CacheStore.writeThrough(userAccount); + cacheStore.writeThrough(userAccount); } else if (cachingPolicy == CachingPolicy.AROUND) { - CacheStore.writeAround(userAccount); + cacheStore.writeAround(userAccount); } else if (cachingPolicy == CachingPolicy.BEHIND) { - CacheStore.writeBehind(userAccount); + cacheStore.writeBehind(userAccount); } else if (cachingPolicy == CachingPolicy.ASIDE) { saveAside(userAccount); } } - public static String printCacheContent() { - return CacheStore.print(); + public String printCacheContent() { + return cacheStore.print(); } /** * Cache-Aside save user account helper. */ - private static void saveAside(UserAccount userAccount) { - DbManager.updateDb(userAccount); - CacheStore.invalidate(userAccount.getUserId()); + private void saveAside(UserAccount userAccount) { + dbManager.updateDb(userAccount); + cacheStore.invalidate(userAccount.getUserId()); } /** * Cache-Aside find user account helper. */ - private static UserAccount findAside(String userId) { - return Optional.ofNullable(CacheStore.get(userId)) + private UserAccount findAside(String userId) { + return Optional.ofNullable(cacheStore.get(userId)) .or(() -> { - OptionalDBManager 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.
- * - *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()).
- */ -@Slf4j -public final class DbManager { - - private static MongoClient mongoClient; - private static MongoDatabase db; - private static boolean useMongoDB; - - private static MapDBManager 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 { + void connect(); + UserAccount readFromDb(String userId); + UserAccount writeToDb(UserAccount userAccount); + UserAccount updateDb(UserAccount userAccount); + UserAccount upsertDb(UserAccount userAccount); +} diff --git a/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java new file mode 100644 index 000000000..0e6ed9fe1 --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/database/DbManagerFactory.java @@ -0,0 +1,10 @@ +package com.iluwatar.caching.database; + +public class DbManagerFactory { + public static DbManager initDb(boolean isMongo){ + if(isMongo){ + return new MongoDb(); + } + return new VirtualDb(); + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java new file mode 100644 index 000000000..50a5d805d --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/database/MongoDb.java @@ -0,0 +1,81 @@ +package com.iluwatar.caching.database; + +import com.iluwatar.caching.UserAccount; +import com.iluwatar.caching.constants.CachingConstants; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.UpdateOptions; +import org.bson.Document; + +/** + * Implementation of DatabaseManager. + * implements base methods to work with MongoDb. + */ +public class MongoDb implements DbManager { + private MongoDatabase db; + + @Override + public void connect() { + MongoClient mongoClient = new MongoClient(); + db = mongoClient.getDatabase("test"); + } + + @Override + public UserAccount readFromDb(String userId) { + if (db == null) { + connect(); + } + 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); + } + + @Override + public UserAccount writeToDb(UserAccount userAccount) { + if (db == null) { + connect(); + } + 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()) + ); + return userAccount; + } + + @Override + public UserAccount updateDb(UserAccount userAccount) { + if (db == null) { + connect(); + } + 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()))); + return userAccount; + } + + @Override + public UserAccount upsertDb(UserAccount userAccount) { + if (db == null) { + connect(); + } + 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) + ); + return userAccount; + } +} diff --git a/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java new file mode 100644 index 000000000..af67225ad --- /dev/null +++ b/caching/src/main/java/com/iluwatar/caching/database/VirtualDb.java @@ -0,0 +1,44 @@ +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 { + private Map