* 'master' of https://github.com/iluwatar/java-design-patterns: (27 commits)
  Remove use of coveralls-maven-plugin (sonarqube.com covers this)
  Add SonarQube.com badge
  Fix environment variable
  Add Travis instructions for SonarQube.com analysis
  Adds more criticism to Singleton pattern.
  Event Based Asynchronous pattern: Add missing license header and puml diagram
  Changed config to non-interactive
  Moved config into a separate dir
  Unused import removed.
  End process logic clause has been corrected.
  Caching pattern: Documentation and diagram
  Fixes #437. Adds criticism to Singleton pattern.
  Alter JUnit tests to run in lesser time.
  Updated version snapshot to 1.14.0
  Changes based on review feedback.
  Closes #436. Adds criticism to service locator pattern.
  Caching pattern: Implementation of Cache-Aside pattern
  Caching pattern: Style fix for null check
  Caching pattern: Refactor LRU cache to avoid NPE and unnecessary cache lookup
  Caching pattern: Refactor shutdown hook to use method reference
  ...
This commit is contained in:
daniel-bryla 2016-11-04 11:51:45 +01:00
commit 0a427710bb
35 changed files with 1336 additions and 185 deletions

View File

@ -6,6 +6,7 @@ env:
global: global:
- GH_REF: github.com/iluwatar/java-design-patterns.git - GH_REF: github.com/iluwatar/java-design-patterns.git
- secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg= - secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg=
- secure: "eoWlW9GyTJY04P8K3pxayXwU9/hmptQg/LfirispQkV9YvmziCfSzXnatnBhNfud98sCzY8BScXnb+OWLTnjLKpId4rtEqb0aJ40Jc32cUKzgzFAUn7cNcDAbUIfyPAGVqyQqfj/11wYSADwWMMOPlW97ExUtoyiH2WenXuRHso="
before_install: before_install:
- export DISPLAY=:99.0 - export DISPLAY=:99.0
@ -16,7 +17,7 @@ install:
- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e
after_success: after_success:
- mvn clean test jacoco:report coveralls:report - mvn clean org.jacoco:jacoco-maven-plugin:prepare-agent package sonar:sonar -Dsonar.host.url=https://sonarqube.com -Dsonar.login=$SONAR_TOKEN
- bash update-ghpages.sh - bash update-ghpages.sh
# use latest java version available instead of travis default # use latest java version available instead of travis default

View File

@ -5,9 +5,9 @@
# Design patterns implemented in Java # Design patterns implemented in Java
[![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns) [![Build status](https://travis-ci.org/iluwatar/java-design-patterns.svg?branch=master)](https://travis-ci.org/iluwatar/java-design-patterns)
[![Coverage Status](https://coveralls.io/repos/iluwatar/java-design-patterns/badge.svg?branch=master)](https://coveralls.io/r/iluwatar/java-design-patterns?branch=master)
[![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md) [![License MIT](https://img.shields.io/badge/license-MIT-blue.svg)](https://raw.githubusercontent.com/iluwatar/java-design-patterns/master/LICENSE.md)
[![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the chat at https://gitter.im/iluwatar/java-design-patterns](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/iluwatar/java-design-patterns?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[![Quality Gate](https://sonarqube.com/api/badges/gate?key=com.iluwatar%3Ajava-design-patterns)](https://sonarqube.com/dashboard/index/com.iluwatar%3Ajava-design-patterns)
# Introduction # Introduction

View File

@ -57,12 +57,10 @@ public class ThreadAsyncExecutor implements AsyncExecutor {
@Override @Override
public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException { public <T> T endProcess(AsyncResult<T> asyncResult) throws ExecutionException, InterruptedException {
if (asyncResult.isCompleted()) { if (!asyncResult.isCompleted()) {
return asyncResult.getValue();
} else {
asyncResult.await(); asyncResult.await();
return asyncResult.getValue();
} }
return asyncResult.getValue();
} }
/** /**

View File

@ -31,7 +31,6 @@ import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.mockito.internal.verification.VerificationModeFactory.times; import static org.mockito.internal.verification.VerificationModeFactory.times;

View File

@ -27,3 +27,4 @@ Use the Caching pattern(s) when
* [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained) * [Write-through, write-around, write-back: Cache explained](http://www.computerweekly.com/feature/Write-through-write-around-write-back-Cache-explained)
* [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177) * [Read-Through, Write-Through, Write-Behind, and Refresh-Ahead Caching](https://docs.oracle.com/cd/E15357_01/coh.360/e15723/cache_rtwtwbra.htm#COHDG5177)
* [Cache-Aside](https://msdn.microsoft.com/en-us/library/dn589799.aspx)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View File

@ -1,106 +1,90 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.8" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true" <class-diagram version="1.1.11" icons="true" automaticImage="PNG" always-add-relationships="false"
realizations="true" associations="true" dependencies="false" nesting-relationships="true"> generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"
<class id="1" language="java" name="main.java.com.wssia.caching.App" project="CachingPatterns" router="FAN">
file="/CachingPatterns/src/main/java/com/wssia/caching/App.java" binary="false" corner="BOTTOM_RIGHT"> <class id="1" language="java" name="com.iluwatar.caching.CacheStore" project="caching"
<position height="-1" width="-1" x="249" y="150"/> file="/caching/src/main/java/com/iluwatar/caching/CacheStore.java" binary="false" corner="BOTTOM_RIGHT">
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <position height="-1" width="-1" x="322" y="444"/>
sort-features="false" accessors="true" visibility="true"> <display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
<attributes public="true" package="true" protected="true" private="true" static="true"/> sort-features="false" accessors="true" visibility="true">
<operations public="true" package="true" protected="true" private="true" static="true"/> <attributes public="true" package="true" protected="true" private="true" static="true"/>
</display> <operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.caching.LruCache" project="caching"
file="/caching/src/main/java/com/iluwatar/caching/LruCache.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="700" y="446"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<enumeration id="3" language="java" name="com.iluwatar.caching.CachingPolicy" project="caching"
file="/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="943" y="122"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</enumeration>
<class id="4" language="java" name="com.iluwatar.caching.DbManager" project="caching"
file="/caching/src/main/java/com/iluwatar/caching/DbManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1011" y="426"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="5" language="java" name="com.iluwatar.caching.App" project="caching"
file="/caching/src/main/java/com/iluwatar/caching/App.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="304" y="95"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="6" language="java" name="com.iluwatar.caching.AppManager" project="caching"
file="/caching/src/main/java/com/iluwatar/caching/AppManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="604" y="122"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="7" language="java" name="com.iluwatar.caching.UserAccount" project="caching"
file="/caching/src/main/java/com/iluwatar/caching/UserAccount.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1161" y="139"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class> </class>
<class id="2" language="java" name="main.java.com.wssia.caching.AppManager" project="CachingPatterns" <association id="8">
file="/CachingPatterns/src/main/java/com/wssia/caching/AppManager.java" binary="false" corner="BOTTOM_RIGHT"> <end type="SOURCE" refId="1" navigable="false">
<position height="-1" width="-1" x="502" y="163"/> <attribute id="9" name="cache"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" <multiplicity id="10" minimum="0" maximum="1"/>
sort-features="false" accessors="true" visibility="true"> </end>
<attributes public="true" package="true" protected="true" private="true" static="true"/> <end type="TARGET" refId="2" navigable="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> <display labels="true" multiplicity="true"/>
</display> </association>
</class> <association id="11">
<class id="3" language="java" name="main.java.com.wssia.caching.CacheStore" project="CachingPatterns" <end type="SOURCE" refId="6" navigable="false">
file="/CachingPatterns/src/main/java/com/wssia/caching/CacheStore.java" binary="false" corner="BOTTOM_RIGHT"> <attribute id="12" name="cachingPolicy"/>
<position height="-1" width="-1" x="537" y="436"/> <multiplicity id="13" minimum="0" maximum="1"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true" </end>
sort-features="false" accessors="true" visibility="true"> <end type="TARGET" refId="3" navigable="true"/>
<attributes public="true" package="true" protected="true" private="true" static="true"/> <display labels="true" multiplicity="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/> </association>
</display> <classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
</class> sort-features="false" accessors="true" visibility="true">
<enumeration id="4" language="java" name="main.java.com.wssia.caching.CachingPolicy" project="CachingPatterns" <attributes public="true" package="true" protected="true" private="true" static="true"/>
file="/CachingPatterns/src/main/java/com/wssia/caching/CachingPolicy.java" binary="false" corner="BOTTOM_RIGHT"> <operations public="true" package="true" protected="true" private="true" static="true"/>
<position height="-1" width="-1" x="789" y="162"/> </classifier-display>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</enumeration>
<class id="5" language="java" name="main.java.com.wssia.caching.DBManager" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/DBManager.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1137" y="134"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="6" language="java" name="main.java.com.wssia.caching.LRUCache" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/LRUCache.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="884" y="435"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="7" language="java" name="main.java.com.wssia.caching.UserAccount" project="CachingPatterns"
file="/CachingPatterns/src/main/java/com/wssia/caching/UserAccount.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="1140" y="405"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="8" language="java" name="test.java.com.wssia.caching.AppTest" project="CachingPatterns"
file="/CachingPatterns/src/test/java/com/wssia/caching/AppTest.java" binary="false" corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="251" y="374"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="9">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="10" name="cachingPolicy"/>
<multiplicity id="11" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="4" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="12">
<end type="SOURCE" refId="8" navigable="false">
<attribute id="13" name="app"/>
<multiplicity id="14" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="15">
<end type="SOURCE" refId="3" navigable="false">
<attribute id="16" name="cache"/>
<multiplicity id="17" minimum="0" maximum="1"/>
</end>
<end type="TARGET" refId="6" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/> <association-display labels="true" multiplicity="true"/>
</class-diagram> </class-diagram>

View File

@ -29,22 +29,23 @@ import org.slf4j.LoggerFactory;
* *
* 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 resources by not releasing
* the resources immediately after their use. The resources retain their identity, are kept in some * 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 three main * 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: * 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-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, and * <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 * <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. The <code>read-through</code> strategy is also * written into the DB when the cache is full, and <code>cache-aside</code> which pushes the
* included in the mentioned three strategies -- returns data from the cache to the caller <b>if</b> * responsibility of keeping the data synchronized in both data sources to the application itself.
* it exists <b>else</b> queries from DB and stores it into the cache for future use. These * The <code>read-through</code> strategy is also included in the mentioned four strategies --
* strategies determine when the data in the cache should be written back to the backing store (i.e. * returns data from the cache to the caller <b>if</b> it exists <b>else</b> queries from DB and
* Database) and help keep both data sources synchronized/up-to-date. This pattern can improve * stores it into the cache for future use. These strategies determine when the data in the cache
* performance and also helps to maintain consistency between data held in the cache and the data in * should be written back to the backing store (i.e. Database) and help keep both data sources
* the underlying data store. * 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.
* <p> * <p>
* In this example, the user account ({@link UserAccount}) entity is used as the underlying * 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 * 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 three * 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 * 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 ( * 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 * {@link App} is not aware of the underlying mechanics of the application (i.e. save and query) and
@ -80,6 +81,7 @@ public class App {
app.useReadAndWriteThroughStrategy(); app.useReadAndWriteThroughStrategy();
app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteAroundStrategy();
app.useReadThroughAndWriteBehindStrategy(); app.useReadThroughAndWriteBehindStrategy();
app.useCacheAsideStategy();
} }
/** /**
@ -142,4 +144,26 @@ public class App {
AppManager.find("004"); AppManager.find("004");
LOGGER.info(AppManager.printCacheContent()); LOGGER.info(AppManager.printCacheContent());
} }
/**
* Cache-Aside
*/
public void useCacheAsideStategy() {
System.out.println("# CachingPolicy.ASIDE");
AppManager.initCachingPolicy(CachingPolicy.ASIDE);
System.out.println(AppManager.printCacheContent());
UserAccount userAccount3 = new UserAccount("003", "Adam", "He likes food.");
UserAccount userAccount4 = new UserAccount("004", "Rita", "She hates cats.");
UserAccount userAccount5 = new UserAccount("005", "Isaac", "He is allergic to mustard.");
AppManager.save(userAccount3);
AppManager.save(userAccount4);
AppManager.save(userAccount5);
System.out.println(AppManager.printCacheContent());
AppManager.find("003");
System.out.println(AppManager.printCacheContent());
AppManager.find("004");
System.out.println(AppManager.printCacheContent());
}
} }

View File

@ -64,12 +64,7 @@ public final class AppManager {
public static void initCachingPolicy(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(new Runnable() { Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache));
@Override
public void run() {
CacheStore.flushCache();
}
}));
} }
CacheStore.clearCache(); CacheStore.clearCache();
} }
@ -86,6 +81,8 @@ public final class AppManager {
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);
} else if (cachingPolicy == CachingPolicy.ASIDE) {
return findAside(userId);
} }
return null; return null;
} }
@ -100,10 +97,37 @@ public final class AppManager {
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) {
saveAside(userAccount);
} }
} }
public static String printCacheContent() { public static String printCacheContent() {
return CacheStore.print(); return CacheStore.print();
} }
/**
* Cache-Aside save user account helper
*/
private static void saveAside(UserAccount userAccount) {
DbManager.updateDb(userAccount);
CacheStore.invalidate(userAccount.getUserId());
}
/**
* Cache-Aside find user account helper
*/
private static UserAccount findAside(String userId) {
UserAccount userAccount = CacheStore.get(userId);
if (userAccount != null) {
return userAccount;
}
userAccount = DbManager.readFromDb(userId);
if (userAccount != null) {
CacheStore.set(userId, userAccount);
}
return userAccount;
}
} }

View File

@ -45,7 +45,7 @@ public class CacheStore {
* Init cache capacity * Init cache capacity
*/ */
public static void initCapacity(int capacity) { public static void initCapacity(int capacity) {
if (null == cache) { if (cache == null) {
cache = new LruCache(capacity); cache = new LruCache(capacity);
} else { } else {
cache.setCapacity(capacity); cache.setCapacity(capacity);
@ -126,7 +126,7 @@ public class CacheStore {
* Clears cache * Clears cache
*/ */
public static void clearCache() { public static void clearCache() {
if (null != cache) { if (cache != null) {
cache.clear(); cache.clear();
} }
} }
@ -158,4 +158,25 @@ public class CacheStore {
sb.append("----\n"); sb.append("----\n");
return sb.toString(); return sb.toString();
} }
/**
* Delegate to backing cache store
*/
public static UserAccount get(String userId) {
return cache.get(userId);
}
/**
* Delegate to backing cache store
*/
public static void set(String userId, UserAccount userAccount) {
cache.set(userId, userAccount);
}
/**
* Delegate to backing cache store
*/
public static void invalidate(String userId) {
cache.invalidate(userId);
}
} }

View File

@ -24,11 +24,11 @@ package com.iluwatar.caching;
/** /**
* *
* Enum class containing the three caching strategies implemented in the pattern. * Enum class containing the four caching strategies implemented in the pattern.
* *
*/ */
public enum CachingPolicy { public enum CachingPolicy {
THROUGH("through"), AROUND("around"), BEHIND("behind"); THROUGH("through"), AROUND("around"), BEHIND("behind"), ASIDE("aside");
private String policy; private String policy;

View File

@ -82,7 +82,7 @@ public final class DbManager {
} }
return null; return null;
} }
if (null == db) { if (db == null) {
try { try {
connect(); connect();
} catch (ParseException e) { } catch (ParseException e) {
@ -106,7 +106,7 @@ public final class DbManager {
virtualDB.put(userAccount.getUserId(), userAccount); virtualDB.put(userAccount.getUserId(), userAccount);
return; return;
} }
if (null == db) { if (db == null) {
try { try {
connect(); connect();
} catch (ParseException e) { } catch (ParseException e) {
@ -126,7 +126,7 @@ public final class DbManager {
virtualDB.put(userAccount.getUserId(), userAccount); virtualDB.put(userAccount.getUserId(), userAccount);
return; return;
} }
if (null == db) { if (db == null) {
try { try {
connect(); connect();
} catch (ParseException e) { } catch (ParseException e) {
@ -148,7 +148,7 @@ public final class DbManager {
virtualDB.put(userAccount.getUserId(), userAccount); virtualDB.put(userAccount.getUserId(), userAccount);
return; return;
} }
if (null == db) { if (db == null) {
try { try {
connect(); connect();
} catch (ParseException e) { } catch (ParseException e) {

View File

@ -78,7 +78,6 @@ public class LruCache {
} }
/** /**
*
* Remove node from linked list. * Remove node from linked list.
*/ */
public void remove(Node node) { public void remove(Node node) {
@ -95,7 +94,6 @@ public class LruCache {
} }
/** /**
*
* Move node to the front of the list. * Move node to the front of the list.
*/ */
public void setHead(Node node) { public void setHead(Node node) {
@ -141,10 +139,11 @@ public class LruCache {
* Invalidate cache for user * Invalidate cache for user
*/ */
public void invalidate(String userId) { public void invalidate(String userId) {
LOGGER.info("# {} has been updated! Removing older version from cache...", userId); Node toBeRemoved = cache.remove(userId);
Node toBeRemoved = cache.get(userId); if (toBeRemoved != null) {
remove(toBeRemoved); LOGGER.info("# {} has been updated! Removing older version from cache...", userId);
cache.remove(userId); remove(toBeRemoved);
}
} }
public boolean isFull() { public boolean isFull() {
@ -165,7 +164,6 @@ public class LruCache {
} }
/** /**
*
* Returns cache data in list form. * Returns cache data in list form.
*/ */
public List<UserAccount> getCacheDataInListForm() { public List<UserAccount> getCacheDataInListForm() {

View File

@ -23,9 +23,7 @@
package com.iluwatar.caching; package com.iluwatar.caching;
/** /**
*
* Entity class (stored in cache and DB) used in the application. * Entity class (stored in cache and DB) used in the application.
*
*/ */
public class UserAccount { public class UserAccount {
private String userId; private String userId;

View File

@ -60,4 +60,9 @@ public class CachingTest {
public void testReadThroughAndWriteBehindStrategy() { public void testReadThroughAndWriteBehindStrategy() {
app.useReadThroughAndWriteBehindStrategy(); app.useReadThroughAndWriteBehindStrategy();
} }
@Test
public void testCacheAsideStrategy() {
app.useCacheAsideStategy();
}
} }

View File

@ -0,0 +1,30 @@
---
layout: pattern
title: Event-based Asynchronous
folder: event-asynchronous
permalink: /patterns/event-asynchronous/
categories: Concurrency
tags:
- difficulty-intermediate
- performance
- Java
---
## Intent
The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many
of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:-
(1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without interrupting your application.
(2) Execute multiple operations simultaneously, receiving notifications when each completes.
(3) Wait for resources to become available without stopping ("hanging") your application.
(4) Communicate with pending asynchronous operations using the familiar events-and-delegates model.
![alt text](./etc/event-asynchronous.png "Event-based Asynchronous")
## Applicability
Use the Event-based Asynchronous pattern(s) when
* Time-consuming tasks are needed to run in the background without disrupting the current application.
## Credits
* [Event-based Asynchronous Pattern Overview](https://msdn.microsoft.com/en-us/library/wewwczdw%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396)

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -0,0 +1,118 @@
<?xml version="1.0" encoding="UTF-8"?>
<class-diagram version="1.1.10" icons="true" automaticImage="PNG" always-add-relationships="false"
generalizations="true" realizations="true" associations="true" dependencies="false" nesting-relationships="true"
router="FAN">
<class id="1" language="java" name="com.iluwatar.event.asynchronous.App" project="event-asynchronous"
file="/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="629" y="221"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="2" language="java" name="com.iluwatar.event.asynchronous.Event" project="event-asynchronous"
file="/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="195" y="475"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<class id="3" language="java" name="com.iluwatar.event.asynchronous.EventManager" project="event-asynchronous"
file="/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="575" y="475"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="false" package="false" protected="false" private="false" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<interface id="4" language="java" name="com.iluwatar.event.asynchronous.IEvent" project="event-asynchronous"
file="/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="196" y="197"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<interface id="5" language="java" name="com.iluwatar.event.asynchronous.ThreadCompleteListener"
project="event-asynchronous"
file="/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="396" y="229"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</interface>
<class id="6" language="java" name="com.iluwatar.event.asynchronous.EventAsynchronousTest"
project="event-asynchronous"
file="/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java" binary="false"
corner="BOTTOM_RIGHT">
<position height="-1" width="-1" x="924" y="220"/>
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</display>
</class>
<association id="7">
<end type="SOURCE" refId="6" navigable="false">
<attribute id="8" name="app">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="9" minimum="0" maximum="1">
<position height="16" width="23" x="714" y="226"/>
</multiplicity>
</end>
<end type="TARGET" refId="1" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="10">
<end type="SOURCE" refId="2" navigable="false">
<attribute id="11" name="eventListener">
<position height="18" width="74" x="250" y="287"/>
</attribute>
<multiplicity id="12" minimum="0" maximum="1">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="5" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<association id="13">
<bendpoint x="433" y="475"/>
<end type="SOURCE" refId="3" navigable="false">
<attribute id="14" name="eventPool">
<position height="0" width="0" x="0" y="0"/>
</attribute>
<multiplicity id="15" minimum="0" maximum="2147483647">
<position height="0" width="0" x="0" y="0"/>
</multiplicity>
</end>
<end type="TARGET" refId="2" navigable="true"/>
<display labels="true" multiplicity="true"/>
</association>
<realization id="16">
<end type="SOURCE" refId="2"/>
<end type="TARGET" refId="4"/>
</realization>
<realization id="17">
<end type="SOURCE" refId="3"/>
<end type="TARGET" refId="5"/>
</realization>
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
sort-features="false" accessors="true" visibility="true">
<attributes public="true" package="true" protected="true" private="true" static="true"/>
<operations public="true" package="true" protected="true" private="true" static="true"/>
</classifier-display>
<association-display labels="true" multiplicity="true"/>
</class-diagram>

View File

@ -0,0 +1,65 @@
@startuml
package com.iluwatar.event.asynchronous {
class Event {
- eventId : int
- eventListener : ThreadCompleteListener
- eventTime : int
- isComplete : boolean
- isSynchronous : boolean
- thread : Thread
+ Event(eventId : int, eventTime : int, isSynchronous : boolean)
+ addListener(listener : ThreadCompleteListener)
- completed()
+ isSynchronous() : boolean
+ removeListener(listener : ThreadCompleteListener)
+ run()
+ start()
+ status()
+ stop()
}
interface ThreadCompleteListener {
+ completedEventHandler(int) {abstract}
}
class EventManager {
+ MAX_EVENT_TIME : int {static}
+ MAX_ID : int {static}
+ MAX_RUNNING_EVENTS : int {static}
+ MIN_ID : int {static}
- currentlyRunningSyncEvent : int
- eventPool : Map<Integer, Event>
- rand : Random
+ EventManager()
+ cancel(eventId : int)
+ completedEventHandler(eventId : int)
+ create(eventTime : int) : int
+ createAsync(eventTime : int) : int
- createEvent(eventTime : int, isSynchronous : boolean) : int
- generateId() : int
+ getEventPool() : Map<Integer, Event>
+ numOfCurrentlyRunningSyncEvent() : int
+ shutdown()
+ start(eventId : int)
+ status(eventId : int)
+ statusOfAllEvents()
}
class App {
+ PROP_FILE_NAME : String {static}
~ interactiveMode : boolean
+ App()
+ main(args : String[]) {static}
+ quickRun()
+ run()
+ runInteractiveMode()
+ setUp()
}
interface IEvent {
+ start() {abstract}
+ status() {abstract}
+ stop() {abstract}
}
}
Event --> "-eventListener" ThreadCompleteListener
EventManager --+ Map
Event ..|> IEvent
EventManager ..|> ThreadCompleteListener
@enduml

View File

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<!--
The MIT License
Copyright (c) 2014 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.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>event-asynchronous</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,206 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Scanner;
/**
*
* This application demonstrates the <b>Event-based Asynchronous</b> pattern. Essentially, users (of the pattern) may
* choose to run events in an Asynchronous or Synchronous mode. There can be multiple Asynchronous events running at
* once but only one Synchronous event can run at a time. Asynchronous events are synonymous to multi-threads. The key
* point here is that the threads run in the background and the user is free to carry on with other processes. Once an
* event is complete, the appropriate listener/callback method will be called. The listener then proceeds to carry out
* further processing depending on the needs of the user.
*
* The {@link EventManager} manages the events/threads that the user creates. Currently, the supported event operations
* are: <code>start</code>, <code>stop</code>, <code>getStatus</code>. For Synchronous events, the user is unable to
* start another (Synchronous) event if one is already running at the time. The running event would have to either be
* stopped or completed before a new event can be started.
*
* The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many
* of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:-
* (1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without
* interrupting your application. (2) Execute multiple operations simultaneously, receiving notifications when each
* completes. (3) Wait for resources to become available without stopping ("hanging") your application. (4) Communicate
* with pending asynchronous operations using the familiar events-and-delegates model.
*
* @see EventManager
* @see Event
*
*/
public class App {
public static final String PROP_FILE_NAME = "config.properties";
boolean interactiveMode = false;
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
App app = new App();
app.setUp();
app.run();
}
/**
* App can run in interactive mode or not. Interactive mode == Allow user interaction with command line.
* Non-interactive is a quick sequential run through the available {@link EventManager} operations.
*/
public void setUp() {
Properties prop = new Properties();
InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME);
if (inputStream != null) {
try {
prop.load(inputStream);
} catch (IOException e) {
System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode.");
}
String property = prop.getProperty("INTERACTIVE_MODE");
if (property.equalsIgnoreCase("YES")) {
interactiveMode = true;
}
}
}
/**
* Run program in either interactive mode or not.
*/
public void run() {
if (interactiveMode) {
runInteractiveMode();
} else {
quickRun();
}
}
/**
* Run program in non-interactive mode.
*/
public void quickRun() {
EventManager eventManager = new EventManager();
try {
// Create an Asynchronous event.
int aEventId = eventManager.createAsync(60);
System.out.println("Async Event [" + aEventId + "] has been created.");
eventManager.start(aEventId);
System.out.println("Async Event [" + aEventId + "] has been started.");
// Create a Synchronous event.
int sEventId = eventManager.create(60);
System.out.println("Sync Event [" + sEventId + "] has been created.");
eventManager.start(sEventId);
System.out.println("Sync Event [" + sEventId + "] has been started.");
eventManager.status(aEventId);
eventManager.status(sEventId);
eventManager.cancel(aEventId);
System.out.println("Async Event [" + aEventId + "] has been stopped.");
eventManager.cancel(sEventId);
System.out.println("Sync Event [" + sEventId + "] has been stopped.");
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
| InvalidOperationException e) {
System.out.println(e.getMessage());
}
}
/**
* Run program in interactive mode.
*/
public void runInteractiveMode() {
EventManager eventManager = new EventManager();
Scanner s = new Scanner(System.in);
int option = -1;
while (option != 4) {
System.out.println("Hello. Would you like to boil some eggs?");
System.out.println("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT");
System.out.print("Choose [1,2,3,4]: ");
option = s.nextInt();
if (option == 1) {
s.nextLine();
System.out.print("Boil multiple eggs at once (A) or boil them one-by-one (S)?: ");
String eventType = s.nextLine();
System.out.print("How long should this egg be boiled for (in seconds)?: ");
int eventTime = s.nextInt();
if (eventType.equalsIgnoreCase("A")) {
try {
int eventId = eventManager.createAsync(eventTime);
eventManager.start(eventId);
System.out.println("Egg [" + eventId + "] is being boiled.");
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else if (eventType.equalsIgnoreCase("S")) {
try {
int eventId = eventManager.create(eventTime);
eventManager.start(eventId);
System.out.println("Egg [" + eventId + "] is being boiled.");
} catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException
| EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Unknown event type.");
}
} else if (option == 2) {
System.out.print("Which egg?: ");
int eventId = s.nextInt();
try {
eventManager.cancel(eventId);
System.out.println("Egg [" + eventId + "] is removed from boiler.");
} catch (EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else if (option == 3) {
s.nextLine();
System.out.print("Just one egg (O) OR all of them (A) ?: ");
String eggChoice = s.nextLine();
if (eggChoice.equalsIgnoreCase("O")) {
System.out.print("Which egg?: ");
int eventId = s.nextInt();
try {
eventManager.status(eventId);
} catch (EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else if (eggChoice.equalsIgnoreCase("A")) {
eventManager.statusOfAllEvents();
}
} else if (option == 4) {
eventManager.shutdown();
}
}
s.close();
}
}

View File

@ -0,0 +1,101 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
/**
*
* Each Event runs as a separate/individual thread.
*
*/
public class Event implements IEvent, Runnable {
private int eventId;
private int eventTime;
private boolean isSynchronous;
private Thread thread;
private boolean isComplete = false;
private ThreadCompleteListener eventListener;
/**
*
* @param eventId event ID
* @param eventTime event time
* @param isSynchronous is of synchronous type
*/
public Event(final int eventId, final int eventTime, final boolean isSynchronous) {
this.eventId = eventId;
this.eventTime = eventTime;
this.isSynchronous = isSynchronous;
}
public boolean isSynchronous() {
return isSynchronous;
}
@Override
public void start() {
thread = new Thread(this);
thread.start();
}
@Override
public void stop() {
if (null == thread) {
return;
}
thread.interrupt();
}
@Override
public void status() {
if (!isComplete) {
System.out.println("[" + eventId + "] is not done.");
} else {
System.out.println("[" + eventId + "] is done.");
}
}
@Override
public void run() {
long currentTime = System.currentTimeMillis();
long endTime = currentTime + (eventTime * 1000);
while (System.currentTimeMillis() < endTime) {
try {
Thread.sleep(1000); // Sleep for 1 second.
} catch (InterruptedException e) {
return;
}
}
isComplete = true;
completed();
}
public final void addListener(final ThreadCompleteListener listener) {
this.eventListener = listener;
}
public final void removeListener(final ThreadCompleteListener listener) {
this.eventListener = null;
}
private final void completed() {
if (eventListener != null) {
eventListener.completedEventHandler(eventId);
}
}
}

View File

@ -0,0 +1,26 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
public class EventDoesNotExistException extends Exception {
private static final long serialVersionUID = -3398463738273811509L;
public EventDoesNotExistException(String message) {
super(message);
}
}

View File

@ -0,0 +1,218 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* EventManager handles and maintains a pool of event threads. {@link Event} threads are created upon user request. Thre
* are two types of events; Asynchronous and Synchronous. There can be multiple Asynchronous events running at once but
* only one Synchronous event running at a time. Currently supported event operations are: start, stop, and getStatus.
* Once an event is complete, it then notifies EventManager through a listener. The EventManager then takes the event
* out of the pool.
*
*/
public class EventManager implements ThreadCompleteListener {
public static final int MAX_RUNNING_EVENTS = 1000; // Just don't wanna have too many running events. :)
public static final int MIN_ID = 1;
public static final int MAX_ID = MAX_RUNNING_EVENTS;
public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes.
private int currentlyRunningSyncEvent = -1;
private Random rand;
private Map<Integer, Event> eventPool;
/**
* EventManager constructor.
*
*/
public EventManager() {
rand = new Random(1);
eventPool = new ConcurrentHashMap<Integer, Event>(MAX_RUNNING_EVENTS);
}
/**
* Create a Synchronous event.
*
* @param eventTime Time an event should run for.
* @return eventId
* @throws MaxNumOfEventsAllowedException When too many events are running at a time.
* @throws InvalidOperationException No new synchronous events can be created when one is already running.
* @throws LongRunningEventException Long running events are not allowed in the app.
*/
public int create(int eventTime)
throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException {
if (currentlyRunningSyncEvent != -1) {
throw new InvalidOperationException(
"Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again.");
}
int eventId = createEvent(eventTime, true);
currentlyRunningSyncEvent = eventId;
return eventId;
}
/**
* Create an Asynchronous event.
*
* @param eventTime Time an event should run for.
* @return eventId
* @throws MaxNumOfEventsAllowedException When too many events are running at a time.
* @throws LongRunningEventException Long running events are not allowed in the app.
*/
public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException {
return createEvent(eventTime, false);
}
private int createEvent(int eventTime, boolean isSynchronous)
throws MaxNumOfEventsAllowedException, LongRunningEventException {
if (eventPool.size() == MAX_RUNNING_EVENTS) {
throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later.");
}
if (eventTime >= MAX_EVENT_TIME) {
throw new LongRunningEventException(
"Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again.");
}
int newEventId = generateId();
Event newEvent = new Event(newEventId, eventTime, isSynchronous);
newEvent.addListener(this);
eventPool.put(newEventId, newEvent);
return newEventId;
}
/**
* Starts event.
*
* @param eventId The event that needs to be started.
* @throws EventDoesNotExistException If event does not exist in our eventPool.
*/
public void start(int eventId) throws EventDoesNotExistException {
if (!eventPool.containsKey(eventId)) {
throw new EventDoesNotExistException(eventId + " does not exist.");
}
eventPool.get(eventId).start();
}
/**
* Stops event.
*
* @param eventId The event that needs to be stopped.
* @throws EventDoesNotExistException If event does not exist in our eventPool.
*/
public void cancel(int eventId) throws EventDoesNotExistException {
if (!eventPool.containsKey(eventId)) {
throw new EventDoesNotExistException(eventId + " does not exist.");
}
if (eventId == currentlyRunningSyncEvent) {
currentlyRunningSyncEvent = -1;
}
eventPool.get(eventId).stop();
eventPool.remove(eventId);
}
/**
* Get status of a running event.
*
* @param eventId The event to inquire status of.
* @throws EventDoesNotExistException If event does not exist in our eventPool.
*/
public void status(int eventId) throws EventDoesNotExistException {
if (!eventPool.containsKey(eventId)) {
throw new EventDoesNotExistException(eventId + " does not exist.");
}
eventPool.get(eventId).status();
}
/**
* Gets status of all running events.
*/
@SuppressWarnings("rawtypes")
public void statusOfAllEvents() {
Iterator it = eventPool.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
((Event) pair.getValue()).status();
}
}
/**
* Stop all running events.
*/
@SuppressWarnings("rawtypes")
public void shutdown() {
Iterator it = eventPool.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
((Event) pair.getValue()).stop();
}
}
/**
* Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most
* <code>Integer.MAX_VALUE - 1</code>.
*/
private int generateId() {
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
while (eventPool.containsKey(randomNum)) {
randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
}
return randomNum;
}
/**
* Callback from an {@link Event} (once it is complete). The Event is then removed from the pool.
*/
@Override
public void completedEventHandler(int eventId) {
eventPool.get(eventId).status();
if (eventPool.get(eventId).isSynchronous()) {
currentlyRunningSyncEvent = -1;
}
eventPool.remove(eventId);
}
/**
* Getter method for event pool.
*/
public Map<Integer, Event> getEventPool() {
return eventPool;
}
/**
* Get number of currently running Synchronous events.
*/
public int numOfCurrentlyRunningSyncEvent() {
return currentlyRunningSyncEvent;
}
}

View File

@ -0,0 +1,27 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
public interface IEvent {
void start();
void stop();
void status();
}

View File

@ -0,0 +1,27 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
public class InvalidOperationException extends Exception {
private static final long serialVersionUID = -6191545255213410803L;
public InvalidOperationException(String message) {
super(message);
}
}

View File

@ -0,0 +1,26 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
public class LongRunningEventException extends Exception {
private static final long serialVersionUID = -483423544320148809L;
public LongRunningEventException(String message) {
super(message);
}
}

View File

@ -0,0 +1,26 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
public class MaxNumOfEventsAllowedException extends Exception {
private static final long serialVersionUID = -8430876973516292695L;
public MaxNumOfEventsAllowedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,21 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
public interface ThreadCompleteListener {
void completedEventHandler(final int eventId);
}

View File

@ -0,0 +1,24 @@
#
# The MIT License
# Copyright (c) 2014 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.
#
INTERACTIVE_MODE=NO

View File

@ -0,0 +1,32 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
import java.io.IOException;
import org.junit.Test;
/**
* Tests that EventAsynchronous example runs without errors.
*/
public class AppTest {
@Test
public void test() throws IOException {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,135 @@
/**
* The MIT License Copyright (c) 2014 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.event.asynchronous;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
/**
*
* Application test
*
*/
public class EventAsynchronousTest {
App app;
@Before
public void setUp() {
app = new App();
}
@Test
public void testAsynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int aEventId = eventManager.createAsync(60);
eventManager.start(aEventId);
assertTrue(eventManager.getEventPool().size() == 1);
assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() == -1);
eventManager.cancel(aEventId);
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
}
@Test
public void testSynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int sEventId = eventManager.create(60);
eventManager.start(sEventId);
assertTrue(eventManager.getEventPool().size() == 1);
assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() != -1);
eventManager.cancel(sEventId);
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
| InvalidOperationException e) {
System.out.println(e.getMessage());
}
}
@Test(expected = InvalidOperationException.class)
public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException {
EventManager eventManager = new EventManager();
try {
int sEventId = eventManager.create(60);
eventManager.start(sEventId);
sEventId = eventManager.create(60);
eventManager.start(sEventId);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
}
@Test
public void testFullSynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int eventTime = 1;
int sEventId = eventManager.create(eventTime);
assertTrue(eventManager.getEventPool().size() == 1);
eventManager.start(sEventId);
long currentTime = System.currentTimeMillis();
long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to
// complete
// properly.
while (System.currentTimeMillis() < endTime) {
}
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
| InvalidOperationException e) {
System.out.println(e.getMessage());
}
}
@Test
public void testFullAsynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int eventTime = 1;
int aEventId1 = eventManager.createAsync(eventTime);
int aEventId2 = eventManager.createAsync(eventTime);
int aEventId3 = eventManager.createAsync(eventTime);
assertTrue(eventManager.getEventPool().size() == 3);
eventManager.start(aEventId1);
eventManager.start(aEventId2);
eventManager.start(aEventId3);
long currentTime = System.currentTimeMillis();
long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete
// properly.
while (System.currentTimeMillis() < endTime) {
}
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
}
}

43
pom.xml
View File

@ -35,7 +35,6 @@
<h2.version>1.4.190</h2.version> <h2.version>1.4.190</h2.version>
<junit.version>4.12</junit.version> <junit.version>4.12</junit.version>
<compiler.version>3.0</compiler.version> <compiler.version>3.0</compiler.version>
<coveralls.version>4.0.0</coveralls.version>
<jacoco.version>0.7.2.201409121644</jacoco.version> <jacoco.version>0.7.2.201409121644</jacoco.version>
<commons-dbcp.version>1.4</commons-dbcp.version> <commons-dbcp.version>1.4</commons-dbcp.version>
<camel.version>2.16.1</camel.version> <camel.version>2.16.1</camel.version>
@ -133,7 +132,8 @@
<module>aggregator-microservices</module> <module>aggregator-microservices</module>
<module>promise</module> <module>promise</module>
<module>page-object</module> <module>page-object</module>
</modules> <module>event-asynchronous</module>
</modules>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
@ -332,29 +332,10 @@
<target>1.8</target> <target>1.8</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<groupId>org.eluder.coveralls</groupId>
<artifactId>coveralls-maven-plugin</artifactId>
<version>${coveralls.version}</version>
<configuration>
<repoToken>jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF</repoToken>
</configuration>
</plugin>
<plugin> <plugin>
<groupId>org.jacoco</groupId> <groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId> <artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version> <version>${jacoco.version}</version>
<!-- The following exclude configuration was added because error occurred
when executing "mvn clean test jacoco:report coveralls:report" -->
<!-- [ERROR] Failed to execute goal org.eluder.coveralls:coveralls-maven-plugin:3.1.0:report
(default-cli) on project java-design-patterns: I/O operation failed: No source
found for domainapp/dom/modules/simple/QSimpleObject.java -> [Help 1] -->
<configuration>
<excludes>
<exclude>domainapp/dom/modules/simple/QSimpleObject.class</exclude>
<exclude>**com.steadystate*</exclude>
</excludes>
</configuration>
<executions> <executions>
<execution> <execution>
<id>prepare-agent</id> <id>prepare-agent</id>
@ -390,26 +371,6 @@
</execution> </execution>
</executions> </executions>
</plugin> </plugin>
<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>0.7.5.201505241946</version>
<executions>
<execution>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>prepare-package</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId> <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId> <artifactId>maven-surefire-plugin</artifactId>

View File

@ -33,6 +33,12 @@ improves the performance of application to great extent.
* lookups of services are done quite frequently * lookups of services are done quite frequently
* large number of services are being used * large number of services are being used
## Consequences
* Violates Interface Segregation Principle (ISP) by providing pattern consumers with an access
to a number of services that they don't potentially need.
* Creates hidden dependencies that can break the clients at runtime.
## Credits ## Credits
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)

View File

@ -36,6 +36,13 @@ Use the Singleton pattern when
* [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--) * [java.lang.System#getSecurityManager()](http://docs.oracle.com/javase/8/docs/api/java/lang/System.html#getSecurityManager--)
## Consequences
* Violates Single Responsibility Principle (SRP) by controlling their own creation and lifecycle.
* Encourages using a global shared instance which prevents an object and resources used by this object from being deallocated.
* Creates tightly coupled code that is difficult to test.
* Makes it almost impossible to subclass a Singleton.
## Credits ## Credits
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)