From 9a90f2de1f50557e584d3a0ced4d134c8c702090 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Sun, 11 Sep 2016 18:45:51 +0100 Subject: [PATCH] Changes based on code review --- event-asynchronous/README.md | 2 + .../etc/event-asynchronous.ucls | 30 +++++---- .../com/iluwatar/event/asynchronous/App.java | 63 ++++++++++--------- .../iluwatar/event/asynchronous/Event.java | 14 ++--- .../event/asynchronous/EventManager.java | 59 ++++++++++++----- .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 37 ++++++----- 7 files changed, 125 insertions(+), 82 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index 59e6e8b33..dde434aba 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -5,6 +5,8 @@ folder: event-asynchronous permalink: /patterns/event-asynchronous/ categories: Other tags: + - difficulty-intermediate + - performance - Java --- diff --git a/event-asynchronous/etc/event-asynchronous.ucls b/event-asynchronous/etc/event-asynchronous.ucls index cc7241044..df09fc28d 100644 --- a/event-asynchronous/etc/event-asynchronous.ucls +++ b/event-asynchronous/etc/event-asynchronous.ucls @@ -65,12 +65,15 @@ - - - - + + + + + + + - + @@ -78,19 +81,24 @@ - + + + - - - - + + + + + + + - + 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 index fa6116b46..f951af07c 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -48,6 +48,8 @@ import java.util.Scanner; */ public class App { + public static final String PROP_FILE_NAME = "config.properties"; + boolean interactiveMode = false; /** @@ -68,15 +70,14 @@ public class App { */ public void setUp() { Properties prop = new Properties(); - String propFileName = "config.properties"; - InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME); if (inputStream != null) { try { prop.load(inputStream); } catch (IOException e) { - System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); + System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -104,23 +105,23 @@ public class App { try { // Create an Asynchronous event. - int aEventId = eventManager.createAsyncEvent(60); + int aEventId = eventManager.createAsync(60); System.out.println("Event [" + aEventId + "] has been created."); - eventManager.startEvent(aEventId); + eventManager.start(aEventId); System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventId = eventManager.createSyncEvent(60); + int sEventId = eventManager.create(60); System.out.println("Event [" + sEventId + "] has been created."); - eventManager.startEvent(sEventId); + eventManager.start(sEventId); System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventId); - eventManager.getStatus(sEventId); + eventManager.status(aEventId); + eventManager.status(sEventId); - eventManager.stopEvent(aEventId); + eventManager.cancel(aEventId); System.out.println("Event [" + aEventId + "] has been stopped."); - eventManager.stopEvent(sEventId); + eventManager.cancel(sEventId); System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException @@ -136,35 +137,33 @@ public class App { EventManager eventManager = new EventManager(); Scanner s = new Scanner(System.in); - int option = 0; - option = -1; + int option = -1; while (option != 5) { - System.out - .println("(1) START_EVENT \n(2) STOP_EVENT \n(3) STATUS_OF_EVENT \n(4) STATUS_OF_ALL_EVENTS \n(5) EXIT"); + 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 IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); System.out.print("Choose [1,2,3,4,5]: "); option = s.nextInt(); if (option == 1) { s.nextLine(); - System.out.print("(A)sync or (S)ync event?: "); + 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 event run for (in seconds)?: "); + 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.createAsyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + 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.createSyncEvent(eventTime); - System.out.println("Event [" + eventId + "] has been created."); - eventManager.startEvent(eventId); - System.out.println("Event [" + eventId + "] has been started."); + 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()); @@ -173,24 +172,26 @@ public class App { System.out.println("Unknown event type."); } } else if (option == 2) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.stopEvent(eventId); - System.out.println("Event [" + eventId + "] has been stopped."); + eventManager.cancel(eventId); + System.out.println("Egg [" + eventId + "] is removed from boiler."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Event ID: "); + System.out.print("Which egg?: "); int eventId = s.nextInt(); try { - eventManager.getStatus(eventId); + eventManager.status(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 4) { - eventManager.getStatusOfAllEvents(); + eventManager.statusOfAllEvents(); + } else if (option == 5) { + eventManager.shutdown(); } } 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 index 4b4fe1d94..1cb04acdc 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -26,11 +26,10 @@ public class Event implements IEvent, Runnable { private int eventId; private int eventTime; private Thread thread; - private long counter = 0; private boolean isComplete = false; private ThreadCompleteListener eventListener; - public Event(int eventId, int eventTime) { + public Event(final int eventId, final int eventTime) { this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +48,9 @@ public class Event implements IEvent, Runnable { @Override public void status() { if (!isComplete) { - System.out.println("[" + eventId + "] I am at not done. [" + counter + "%]"); + System.out.println("[" + eventId + "] is not done."); } else { - System.out.println("[" + eventId + "] I am done."); + System.out.println("[" + eventId + "] is done."); } } @@ -61,14 +60,13 @@ public class Event implements IEvent, Runnable { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - counter += 1; Thread.sleep(5000); // Sleep for 5 seconds. } catch (InterruptedException e) { return; } } isComplete = true; - notifyListener(); + completed(); } public final void addListener(final ThreadCompleteListener listener) { @@ -79,9 +77,9 @@ public class Event implements IEvent, Runnable { this.eventListener = null; } - private final void notifyListener() { + private final void completed() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventId); + eventListener.completedEventHandler(eventId); } } 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 index d3278594f..e65816cec 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -32,10 +32,10 @@ import java.util.concurrent.ConcurrentHashMap; */ public class EventManager implements ThreadCompleteListener { - private int minId = 1; - private int maxId = Integer.MAX_VALUE - 1; // Be cautious of overflows. - private int maxRunningEvents = 1000; // no particular reason. Just don't wanna have too many running events. :) - private int maxEventTime = 1800; // in seconds / 30 minutes. + 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; @@ -46,7 +46,7 @@ public class EventManager implements ThreadCompleteListener { */ public EventManager() { rand = new Random(1); - eventPool = new ConcurrentHashMap(maxRunningEvents); + eventPool = new ConcurrentHashMap(MAX_RUNNING_EVENTS); } @@ -59,7 +59,7 @@ public class EventManager implements ThreadCompleteListener { * @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 createSyncEvent(int eventTime) + public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { @@ -79,18 +79,18 @@ public class EventManager implements ThreadCompleteListener { * @throws MaxNumOfEventsAllowedException When too many events are running at a time. * @throws LongRunningEventException Long running events are not allowed in the app. */ - public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - if (eventPool.size() == maxRunningEvents) { + if (eventPool.size() == MAX_RUNNING_EVENTS) { throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); } - if (eventTime >= maxEventTime) { + if (eventTime >= MAX_EVENT_TIME) { throw new LongRunningEventException( - "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + "Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again."); } int newEventId = generateId(); @@ -108,7 +108,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event that needs to be started. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void startEvent(int eventId) throws EventDoesNotExistException { + public void start(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -122,7 +122,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event that needs to be stopped. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void stopEvent(int eventId) throws EventDoesNotExistException { + public void cancel(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -141,7 +141,7 @@ public class EventManager implements ThreadCompleteListener { * @param eventId The event to inquire status of. * @throws EventDoesNotExistException If event does not exist in our eventPool. */ - public void getStatus(int eventId) throws EventDoesNotExistException { + public void status(int eventId) throws EventDoesNotExistException { if (!eventPool.containsKey(eventId)) { throw new EventDoesNotExistException(eventId + " does not exist."); } @@ -153,7 +153,7 @@ public class EventManager implements ThreadCompleteListener { * Gets status of all running events. */ @SuppressWarnings("rawtypes") - public void getStatusOfAllEvents() { + public void statusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); while (it.hasNext()) { Map.Entry pair = (Map.Entry) it.next(); @@ -161,6 +161,18 @@ public class EventManager implements ThreadCompleteListener { } } + /** + * 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. @@ -168,9 +180,9 @@ public class EventManager implements ThreadCompleteListener { private int generateId() { // nextInt is normally exclusive of the top value, // so add 1 to make it inclusive - int randomNum = rand.nextInt((maxId - minId) + 1) + minId; + int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxId - minId) + 1) + minId; + randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID; } return randomNum; @@ -180,9 +192,22 @@ public class EventManager implements ThreadCompleteListener { * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. */ @Override - public void notifyOfThreadComplete(int eventId) { + public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); 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/ThreadCompleteListener.java b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java index 88f300634..fd62a3e80 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java @@ -17,5 +17,5 @@ package com.iluwatar.event.asynchronous; public interface ThreadCompleteListener { - void notifyOfThreadComplete(final int eventId); + void completedEventHandler(final int eventId); } 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 index 392c7fba6..6565d5bad 100644 --- a/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -16,6 +16,8 @@ */ package com.iluwatar.event.asynchronous; +import static org.junit.Assert.assertTrue; + import org.junit.Before; import org.junit.Test; @@ -36,9 +38,13 @@ public class EventAsynchronousTest { public void testAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int aEventId = eventManager.createAsyncEvent(60); - eventManager.startEvent(aEventId); - eventManager.stopEvent(aEventId); + 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()); } @@ -48,25 +54,28 @@ public class EventAsynchronousTest { public void testSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - eventManager.stopEvent(sEventId); + 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 - public void testUnsuccessfulSynchronousEvent() { + @Test(expected = InvalidOperationException.class) + public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException { EventManager eventManager = new EventManager(); try { - int sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - sEventId = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventId); - } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException - | InvalidOperationException e) { + 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()); } }