diff --git a/.travis.yml b/.travis.yml
index 613f737d7..ba226ff5c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -6,6 +6,7 @@ env:
global:
- GH_REF: github.com/iluwatar/java-design-patterns.git
- secure: LxTDuNS/rBWIvKkaEqr79ImZAe48mCdoYCF41coxNXgNoippo4GIBArknqtv+XvdkiuRZ1yGyj6pn8GU33c/yn+krddTUkVCwTbVatbalW5jhQjDbHYym/JcxaK9ZS/3JTeGcWrBgiPqHEEDhCf26vPZsXoMSeVCEORVKTp1BSg=
+ - secure: "eoWlW9GyTJY04P8K3pxayXwU9/hmptQg/LfirispQkV9YvmziCfSzXnatnBhNfud98sCzY8BScXnb+OWLTnjLKpId4rtEqb0aJ40Jc32cUKzgzFAUn7cNcDAbUIfyPAGVqyQqfj/11wYSADwWMMOPlW97ExUtoyiH2WenXuRHso="
before_install:
- export DISPLAY=:99.0
@@ -17,6 +18,7 @@ install:
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
# use latest java version available instead of travis default
diff --git a/caching/README.md b/caching/README.md
index 6432ffcea..b48d060f0 100644
--- a/caching/README.md
+++ b/caching/README.md
@@ -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)
* [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)
diff --git a/caching/etc/caching.png b/caching/etc/caching.png
index 6b3b2d055..b6ed703ab 100644
Binary files a/caching/etc/caching.png and b/caching/etc/caching.png differ
diff --git a/caching/etc/caching.ucls b/caching/etc/caching.ucls
index 815a62ec9..a058277ea 100644
--- a/caching/etc/caching.ucls
+++ b/caching/etc/caching.ucls
@@ -1,106 +1,90 @@
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java
index 8e5a84085..1b1b5e1ca 100644
--- a/caching/src/main/java/com/iluwatar/caching/App.java
+++ b/caching/src/main/java/com/iluwatar/caching/App.java
@@ -26,22 +26,23 @@ package com.iluwatar.caching;
*
* The Caching pattern describes how to avoid expensive re-acquisition of resources by not releasing
* the resources immediately after their use. The resources retain their identity, are kept in some
- * fast-access storage, and are re-used to avoid having to acquire them again. There are three main
- * caching strategies/techniques in this pattern; each with their own pros and cons. They are:
+ * fast-access storage, and are re-used to avoid having to acquire them again. There are four main
+ * caching strategies/techniques in this pattern; each with their own pros and cons. They are;
* write-through which writes data to the cache and DB in a single transaction,
- * write-around which writes data immediately into the DB instead of the cache, and
+ * write-around which writes data immediately into the DB instead of the cache,
* write-behind which writes data into the cache initially whilst the data is only
- * written into the DB when the cache is full. The read-through strategy is also
- * included in the mentioned three strategies -- returns data from the cache to the caller if
- * it exists else queries from DB and stores it into the cache for future use. These
- * strategies determine when the data in the cache should be written back to the backing store (i.e.
- * Database) and help keep both data sources synchronized/up-to-date. This pattern can improve
- * performance and also helps to maintain consistency between data held in the cache and the data in
- * the underlying data store.
+ * written into the DB when the cache is full, and cache-aside which pushes the
+ * responsibility of keeping the data synchronized in both data sources to the application itself.
+ * The read-through strategy is also included in the mentioned four strategies --
+ * returns data from the cache to the caller if it exists else queries from DB and
+ * stores it into the cache for future use. These strategies determine when the data in the cache
+ * should be written back to the backing store (i.e. Database) and help keep both data sources
+ * synchronized/up-to-date. This pattern can improve performance and also helps to maintain
+ * consistency between data held in the cache and the data in the underlying data store.
*
* In this example, the user account ({@link UserAccount}) entity is used as the underlying
* application data. The cache itself is implemented as an internal (Java) data structure. It adopts
- * a Least-Recently-Used (LRU) strategy for evicting data from itself when its full. The 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
* 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
@@ -74,6 +75,7 @@ public class App {
app.useReadAndWriteThroughStrategy();
app.useReadThroughAndWriteAroundStrategy();
app.useReadThroughAndWriteBehindStrategy();
+ app.useCacheAsideStategy();
}
/**
@@ -136,4 +138,26 @@ public class App {
AppManager.find("004");
System.out.println(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());
+ }
}
diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java
index 2967c759f..2d6a424a3 100644
--- a/caching/src/main/java/com/iluwatar/caching/AppManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java
@@ -64,12 +64,7 @@ public final class AppManager {
public static void initCachingPolicy(CachingPolicy policy) {
cachingPolicy = policy;
if (cachingPolicy == CachingPolicy.BEHIND) {
- Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
- @Override
- public void run() {
- CacheStore.flushCache();
- }
- }));
+ Runtime.getRuntime().addShutdownHook(new Thread(CacheStore::flushCache));
}
CacheStore.clearCache();
}
@@ -86,6 +81,8 @@ public final class AppManager {
return CacheStore.readThrough(userId);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
return CacheStore.readThroughWithWriteBackPolicy(userId);
+ } else if (cachingPolicy == CachingPolicy.ASIDE) {
+ return findAside(userId);
}
return null;
}
@@ -100,10 +97,37 @@ public final class AppManager {
CacheStore.writeAround(userAccount);
} else if (cachingPolicy == CachingPolicy.BEHIND) {
CacheStore.writeBehind(userAccount);
+ } else if (cachingPolicy == CachingPolicy.ASIDE) {
+ saveAside(userAccount);
}
}
public static String printCacheContent() {
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;
+ }
}
diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
index 5903f8219..c2b95a1bd 100644
--- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java
+++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java
@@ -40,7 +40,7 @@ public class CacheStore {
* Init cache capacity
*/
public static void initCapacity(int capacity) {
- if (null == cache) {
+ if (cache == null) {
cache = new LruCache(capacity);
} else {
cache.setCapacity(capacity);
@@ -121,7 +121,7 @@ public class CacheStore {
* Clears cache
*/
public static void clearCache() {
- if (null != cache) {
+ if (cache != null) {
cache.clear();
}
}
@@ -153,4 +153,25 @@ public class CacheStore {
sb.append("----\n");
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);
+ }
}
diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
index 490113baa..5ad32f699 100644
--- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
+++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java
@@ -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 {
- THROUGH("through"), AROUND("around"), BEHIND("behind");
+ THROUGH("through"), AROUND("around"), BEHIND("behind"), ASIDE("aside");
private String policy;
diff --git a/caching/src/main/java/com/iluwatar/caching/DbManager.java b/caching/src/main/java/com/iluwatar/caching/DbManager.java
index c12461d0c..455302106 100644
--- a/caching/src/main/java/com/iluwatar/caching/DbManager.java
+++ b/caching/src/main/java/com/iluwatar/caching/DbManager.java
@@ -82,7 +82,7 @@ public final class DbManager {
}
return null;
}
- if (null == db) {
+ if (db == null) {
try {
connect();
} catch (ParseException e) {
@@ -106,7 +106,7 @@ public final class DbManager {
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
- if (null == db) {
+ if (db == null) {
try {
connect();
} catch (ParseException e) {
@@ -126,7 +126,7 @@ public final class DbManager {
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
- if (null == db) {
+ if (db == null) {
try {
connect();
} catch (ParseException e) {
@@ -148,7 +148,7 @@ public final class DbManager {
virtualDB.put(userAccount.getUserId(), userAccount);
return;
}
- if (null == db) {
+ if (db == null) {
try {
connect();
} catch (ParseException e) {
diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java
index 5c5549afd..00e27ccf1 100644
--- a/caching/src/main/java/com/iluwatar/caching/LruCache.java
+++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java
@@ -73,7 +73,6 @@ public class LruCache {
}
/**
- *
* Remove node from linked list.
*/
public void remove(Node node) {
@@ -90,7 +89,6 @@ public class LruCache {
}
/**
- *
* Move node to the front of the list.
*/
public void setHead(Node node) {
@@ -136,10 +134,11 @@ public class LruCache {
* Invalidate cache for user
*/
public void invalidate(String userId) {
- System.out.println("# " + userId + " has been updated! Removing older version from cache...");
- Node toBeRemoved = cache.get(userId);
- remove(toBeRemoved);
- cache.remove(userId);
+ Node toBeRemoved = cache.remove(userId);
+ if (toBeRemoved != null) {
+ System.out.println("# " + userId + " has been updated! Removing older version from cache...");
+ remove(toBeRemoved);
+ }
}
public boolean isFull() {
@@ -160,7 +159,6 @@ public class LruCache {
}
/**
- *
* Returns cache data in list form.
*/
public List getCacheDataInListForm() {
diff --git a/caching/src/main/java/com/iluwatar/caching/UserAccount.java b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
index e2444ad72..657f12fa3 100644
--- a/caching/src/main/java/com/iluwatar/caching/UserAccount.java
+++ b/caching/src/main/java/com/iluwatar/caching/UserAccount.java
@@ -23,9 +23,7 @@
package com.iluwatar.caching;
/**
- *
* Entity class (stored in cache and DB) used in the application.
- *
*/
public class UserAccount {
private String userId;
diff --git a/caching/src/test/java/com/iluwatar/caching/CachingTest.java b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
index 19262a3b6..9d2299902 100644
--- a/caching/src/test/java/com/iluwatar/caching/CachingTest.java
+++ b/caching/src/test/java/com/iluwatar/caching/CachingTest.java
@@ -60,4 +60,9 @@ public class CachingTest {
public void testReadThroughAndWriteBehindStrategy() {
app.useReadThroughAndWriteBehindStrategy();
}
+
+ @Test
+ public void testCacheAsideStrategy() {
+ app.useCacheAsideStategy();
+ }
}
diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md
new file mode 100644
index 000000000..ef35d0b38
--- /dev/null
+++ b/event-asynchronous/README.md
@@ -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.
+
+
+
+## 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)
diff --git a/event-asynchronous/etc/event-asynchronous.png b/event-asynchronous/etc/event-asynchronous.png
new file mode 100644
index 000000000..a46ffe1c5
Binary files /dev/null and b/event-asynchronous/etc/event-asynchronous.png differ
diff --git a/event-asynchronous/etc/event-asynchronous.ucls b/event-asynchronous/etc/event-asynchronous.ucls
new file mode 100644
index 000000000..df09fc28d
--- /dev/null
+++ b/event-asynchronous/etc/event-asynchronous.ucls
@@ -0,0 +1,118 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/event-asynchronous/etc/event-asynchronous.urm.puml b/event-asynchronous/etc/event-asynchronous.urm.puml
new file mode 100644
index 000000000..c5b183187
--- /dev/null
+++ b/event-asynchronous/etc/event-asynchronous.urm.puml
@@ -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
+ - 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
+ + 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
\ No newline at end of file
diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml
new file mode 100644
index 000000000..1bc4549d4
--- /dev/null
+++ b/event-asynchronous/pom.xml
@@ -0,0 +1,42 @@
+
+
+
+ 4.0.0
+
+ com.iluwatar
+ java-design-patterns
+ 1.14.0-SNAPSHOT
+
+ event-asynchronous
+
+
+ junit
+ junit
+ test
+
+
+
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java
new file mode 100644
index 000000000..5a2565940
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java
@@ -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 Event-based Asynchronous 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: start, stop, getStatus. 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();
+ }
+
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
new file mode 100644
index 000000000..5e557fff2
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java
@@ -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);
+ }
+ }
+
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
new file mode 100644
index 000000000..77c1d479b
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java
@@ -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);
+ }
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
new file mode 100644
index 000000000..dae995e38
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java
@@ -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 eventPool;
+
+ /**
+ * EventManager constructor.
+ *
+ */
+ public EventManager() {
+ rand = new Random(1);
+ eventPool = new ConcurrentHashMap(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
+ * Integer.MAX_VALUE - 1.
+ */
+ 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 getEventPool() {
+ return eventPool;
+ }
+
+ /**
+ * Get number of currently running Synchronous events.
+ */
+ public int numOfCurrentlyRunningSyncEvent() {
+ return currentlyRunningSyncEvent;
+ }
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java
new file mode 100644
index 000000000..bcd78b6c0
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java
@@ -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();
+
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
new file mode 100644
index 000000000..4fd5b0eed
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java
@@ -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);
+ }
+
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
new file mode 100644
index 000000000..6817b1dd8
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java
@@ -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);
+ }
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
new file mode 100644
index 000000000..9f8f2891c
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java
@@ -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);
+ }
+}
diff --git a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
new file mode 100644
index 000000000..fd62a3e80
--- /dev/null
+++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java
@@ -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);
+}
diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties
new file mode 100644
index 000000000..d8bfa1f7e
--- /dev/null
+++ b/event-asynchronous/src/main/resources/config.properties
@@ -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
\ No newline at end of file
diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
new file mode 100644
index 000000000..8736fcf77
--- /dev/null
+++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java
@@ -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);
+ }
+}
diff --git a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java
new file mode 100644
index 000000000..213439203
--- /dev/null
+++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java
@@ -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());
+ }
+ }
+}
diff --git a/pom.xml b/pom.xml
index 7aec509d4..598386753 100644
--- a/pom.xml
+++ b/pom.xml
@@ -132,8 +132,13 @@
aggregator-microservices
promise
page-object
+<<<<<<< HEAD
object-mother
+=======
+ event-asynchronous
+
+>>>>>>> refs/remotes/iluwatar/master
diff --git a/service-locator/README.md b/service-locator/README.md
index 31d82b13f..479c9ed0f 100644
--- a/service-locator/README.md
+++ b/service-locator/README.md
@@ -33,6 +33,12 @@ improves the performance of application to great extent.
* lookups of services are done quite frequently
* 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
* [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2)
diff --git a/singleton/README.md b/singleton/README.md
index e1bb42a45..4032ffaed 100644
--- a/singleton/README.md
+++ b/singleton/README.md
@@ -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--)
+## 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
* [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612)