From 2d9906190221ff46ed796b6526e0ce1631623828 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:31:41 +0100 Subject: [PATCH 01/27] Issue #469: Implementation of Event-based Asynchronous pattern --- event-asynchronous/README.md | 28 +++ event-asynchronous/etc/event-asynchronous.png | Bin 0 -> 31413 bytes .../etc/event-asynchronous.ucls | 110 +++++++++++ event-asynchronous/pom.xml | 42 ++++ .../com/iluwatar/event/asynchronous/App.java | 185 ++++++++++++++++++ .../iluwatar/event/asynchronous/Event.java | 88 +++++++++ .../EventDoesNotExistException.java | 26 +++ .../event/asynchronous/EventManager.java | 148 ++++++++++++++ .../iluwatar/event/asynchronous/IEvent.java | 27 +++ .../InvalidOperationException.java | 27 +++ .../LongRunningEventException.java | 26 +++ .../MaxNumOfEventsAllowedException.java | 26 +++ .../asynchronous/ThreadCompleteListener.java | 21 ++ .../src/main/java/config.properties | 1 + .../iluwatar/event/asynchronous/AppTest.java | 32 +++ .../asynchronous/EventAsynchronousTest.java | 73 +++++++ 16 files changed, 860 insertions(+) create mode 100644 event-asynchronous/README.md create mode 100644 event-asynchronous/etc/event-asynchronous.png create mode 100644 event-asynchronous/etc/event-asynchronous.ucls create mode 100644 event-asynchronous/pom.xml create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventDoesNotExistException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/InvalidOperationException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/LongRunningEventException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/MaxNumOfEventsAllowedException.java create mode 100644 event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/ThreadCompleteListener.java create mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/AppTest.java create mode 100644 event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md new file mode 100644 index 000000000..59e6e8b33 --- /dev/null +++ b/event-asynchronous/README.md @@ -0,0 +1,28 @@ +--- +layout: pattern +title: Event-based Asynchronous +folder: event-asynchronous +permalink: /patterns/event-asynchronous/ +categories: Other +tags: + - 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) diff --git a/event-asynchronous/etc/event-asynchronous.png b/event-asynchronous/etc/event-asynchronous.png new file mode 100644 index 0000000000000000000000000000000000000000..a46ffe1c5d17773d38dac792349cc547adc06955 GIT binary patch literal 31413 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 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml new file mode 100644 index 000000000..60ab8f0aa --- /dev/null +++ b/event-asynchronous/pom.xml @@ -0,0 +1,42 @@ + + + + 4.0.0 + + com.iluwatar + java-design-patterns + 1.13.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..3375fd4ec --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/App.java @@ -0,0 +1,185 @@ +/** + * 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 { + + boolean interactiveMode = false; + + 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(); + String propFileName = "config.properties"; + + InputStream inputStream = App.class.getClassLoader().getResourceAsStream(propFileName); + + if (inputStream != null) { + try { + prop.load(inputStream); + } catch (IOException e) { + } + String property = prop.getProperty("INTERACTIVE_MODE"); + if (property.equalsIgnoreCase("YES")) { + interactiveMode = true; + } + } + } + + public void run() { + if (interactiveMode) { + runInteractiveMode(); + } else { + quickRun(); + } + } + + public void quickRun() { + EventManager eventManager = new EventManager(); + + try { + // Create an Asynchronous event. + int aEventID = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventID + "] has been created."); + eventManager.startEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been started."); + + // Create a Synchronous event. + int sEventID = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventID + "] has been created."); + eventManager.startEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been started."); + + eventManager.getStatus(aEventID); + eventManager.getStatus(sEventID); + + eventManager.stopEvent(aEventID); + System.out.println("Event [" + aEventID + "] has been stopped."); + eventManager.stopEvent(sEventID); + System.out.println("Event [" + sEventID + "] has been stopped."); + + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + public void runInteractiveMode() { + EventManager eventManager = new EventManager(); + + Scanner s = new Scanner(System.in); + int option = 0; + 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.print("Choose [1,2,3,4,5]: "); + option = s.nextInt(); + + if (option == 1) { + s.nextLine(); + System.out.print("(A)sync or (S)ync event?: "); + String eventType = s.nextLine(); + System.out.print("How long should this event run 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."); + } 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."); + } 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("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.stopEvent(eventID); + System.out.println("Event [" + eventID + "] has been stopped."); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 3) { + System.out.print("Event ID: "); + int eventID = s.nextInt(); + try { + eventManager.getStatus(eventID); + } catch (EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } else if (option == 4) { + eventManager.getStatusOfAllEvents(); + } + } + + 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..48dc37236 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/Event.java @@ -0,0 +1,88 @@ +/** + * 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 Thread thread; + private long counter = 0; + private boolean isComplete = false; + private ThreadCompleteListener eventListener; + + public Event(int eventID, int eventTime) { + this.eventID = eventID; + this.eventTime = eventTime; + } + + @Override + public void start() { + thread = new Thread(this); + thread.start(); + } + + @Override + public void stop() { + thread.interrupt(); + } + + @Override + public void status() { + if (!isComplete) { + System.out.println("[" + eventID + "] I am at not done. [" + counter + "%]"); + } else { + System.out.println("[" + eventID + "] I am done."); + } + } + + @Override + public void run() { + long currentTime = System.currentTimeMillis(); + 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(); + } + + public final void addListener(final ThreadCompleteListener listener) { + this.eventListener = listener; + } + + public final void removeListener(final ThreadCompleteListener listener) { + this.eventListener = null; + } + + private final void notifyListener() { + if (eventListener != null) { + eventListener.notifyOfThreadComplete(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..305548111 --- /dev/null +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/EventManager.java @@ -0,0 +1,148 @@ +/** + * 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 { + + 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. + private int currentlyRunningSyncEvent = -1; + private Random rand; + private Map eventPool; + + public EventManager() { + rand = new Random(1); + eventPool = new ConcurrentHashMap(maxRunningEvents); + + } + + // Create a Synchronous event. + public int createSyncEvent(int eventTime) + throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { + int eventID = createEvent(eventTime); + if (currentlyRunningSyncEvent != -1) { + throw new InvalidOperationException( + "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); + } + currentlyRunningSyncEvent = eventID; + + return eventID; + } + + // Create an Asynchronous event. + public int createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + return createEvent(eventTime); + } + + private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + if (eventPool.size() == maxRunningEvents) { + throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later."); + } + + if (eventTime >= maxEventTime) { + throw new LongRunningEventException( + "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); + } + + int newEventID = generateID(); + + Event newEvent = new Event(newEventID, eventTime); + newEvent.addListener(this); + eventPool.put(newEventID, newEvent); + + return newEventID; + } + + public void startEvent(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).start(); + } + + public void stopEvent(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); + } + + public void getStatus(int eventID) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventID)) { + throw new EventDoesNotExistException(eventID + " does not exist."); + } + + eventPool.get(eventID).status(); + } + + @SuppressWarnings("rawtypes") + public void getStatusOfAllEvents() { + Iterator it = eventPool.entrySet().iterator(); + while (it.hasNext()) { + Map.Entry pair = (Map.Entry) it.next(); + ((Event) pair.getValue()).status(); + } + } + + /** + * 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((maxID - minID) + 1) + minID; + while (eventPool.containsKey(randomNum)) { + randomNum = rand.nextInt((maxID - minID) + 1) + minID; + } + + return randomNum; + } + + /** + * Callback from an {@link Event} (once it is complete). The Event is then removed from the pool. + */ + @Override + public void notifyOfThreadComplete(int eventID) { + eventPool.get(eventID).status(); + eventPool.remove(eventID); + } + +} 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..448c02e84 --- /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 { + + public void start(); + + public void stop(); + + public 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..e5c910289 --- /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 notifyOfThreadComplete(final int eventID); +} diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties new file mode 100644 index 000000000..edbe90e05 --- /dev/null +++ b/event-asynchronous/src/main/java/config.properties @@ -0,0 +1 @@ +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..0ab901106 --- /dev/null +++ b/event-asynchronous/src/test/java/com/iluwatar/event/asynchronous/EventAsynchronousTest.java @@ -0,0 +1,73 @@ +/** + * 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 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.createAsyncEvent(60); + eventManager.startEvent(aEventID); + eventManager.stopEvent(aEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int sEventID = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventID); + eventManager.stopEvent(sEventID); + } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException + | InvalidOperationException e) { + System.out.println(e.getMessage()); + } + } + + @Test + public void testUnsuccessfulSynchronousEvent() { + 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) { + System.out.println(e.getMessage()); + } + } +} From e1836fee2f3fe7944faf89ff1b66b137294492ed Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 8 Aug 2016 23:44:39 +0100 Subject: [PATCH 02/27] Updated parent POM to include new pattern (Event-asynchronous) --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index 302346993..058dee5a1 100644 --- a/pom.xml +++ b/pom.xml @@ -131,6 +131,7 @@ aggregator-microservices promise page-object + event-asynchronous From f11597136f8bebdcb208a141f8e0fd47ee4dbd1e Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 00:32:05 +0100 Subject: [PATCH 03/27] Fixed Checkstyle errors. --- .../com/iluwatar/event/asynchronous/App.java | 69 +++++++----- .../iluwatar/event/asynchronous/Event.java | 12 +-- .../event/asynchronous/EventManager.java | 102 ++++++++++++------ .../asynchronous/ThreadCompleteListener.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 20 ++-- 5 files changed, 130 insertions(+), 75 deletions(-) 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 3375fd4ec..fa6116b46 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 @@ -50,6 +50,11 @@ public class App { boolean interactiveMode = false; + /** + * Program entry point. + * + * @param args command line args + */ public static void main(String[] args) { App app = new App(); @@ -71,6 +76,7 @@ public class App { try { prop.load(inputStream); } catch (IOException e) { + System.out.println(propFileName + " was not found. Defaulting to non-interactive mode."); } String property = prop.getProperty("INTERACTIVE_MODE"); if (property.equalsIgnoreCase("YES")) { @@ -79,6 +85,9 @@ public class App { } } + /** + * Run program in either interactive mode or not. + */ public void run() { if (interactiveMode) { runInteractiveMode(); @@ -87,29 +96,32 @@ public class App { } } + /** + * Run program in non-interactive mode. + */ public void quickRun() { EventManager eventManager = new EventManager(); try { // Create an Asynchronous event. - int aEventID = eventManager.createAsyncEvent(60); - System.out.println("Event [" + aEventID + "] has been created."); - eventManager.startEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been started."); + int aEventId = eventManager.createAsyncEvent(60); + System.out.println("Event [" + aEventId + "] has been created."); + eventManager.startEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been started."); // Create a Synchronous event. - int sEventID = eventManager.createSyncEvent(60); - System.out.println("Event [" + sEventID + "] has been created."); - eventManager.startEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been started."); + int sEventId = eventManager.createSyncEvent(60); + System.out.println("Event [" + sEventId + "] has been created."); + eventManager.startEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been started."); - eventManager.getStatus(aEventID); - eventManager.getStatus(sEventID); + eventManager.getStatus(aEventId); + eventManager.getStatus(sEventId); - eventManager.stopEvent(aEventID); - System.out.println("Event [" + aEventID + "] has been stopped."); - eventManager.stopEvent(sEventID); - System.out.println("Event [" + sEventID + "] has been stopped."); + eventManager.stopEvent(aEventId); + System.out.println("Event [" + aEventId + "] has been stopped."); + eventManager.stopEvent(sEventId); + System.out.println("Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { @@ -117,6 +129,9 @@ public class App { } } + /** + * Run program in interactive mode. + */ public void runInteractiveMode() { EventManager eventManager = new EventManager(); @@ -137,19 +152,19 @@ public class App { 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.createAsyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } 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.createSyncEvent(eventTime); + System.out.println("Event [" + eventId + "] has been created."); + eventManager.startEvent(eventId); + System.out.println("Event [" + eventId + "] has been started."); } catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); @@ -159,18 +174,18 @@ public class App { } } else if (option == 2) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.stopEvent(eventID); - System.out.println("Event [" + eventID + "] has been stopped."); + eventManager.stopEvent(eventId); + System.out.println("Event [" + eventId + "] has been stopped."); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } } else if (option == 3) { System.out.print("Event ID: "); - int eventID = s.nextInt(); + int eventId = s.nextInt(); try { - eventManager.getStatus(eventID); + eventManager.getStatus(eventId); } catch (EventDoesNotExistException e) { System.out.println(e.getMessage()); } 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 48dc37236..4b4fe1d94 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 @@ -23,15 +23,15 @@ package com.iluwatar.event.asynchronous; */ public class Event implements IEvent, Runnable { - private int eventID; + 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) { - this.eventID = eventID; + public Event(int eventId, int eventTime) { + this.eventId = eventId; this.eventTime = eventTime; } @@ -49,9 +49,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 + "] I am at not done. [" + counter + "%]"); } else { - System.out.println("[" + eventID + "] I am done."); + System.out.println("[" + eventId + "] I am done."); } } @@ -81,7 +81,7 @@ public class Event implements IEvent, Runnable { private final void notifyListener() { if (eventListener != null) { - eventListener.notifyOfThreadComplete(eventID); + eventListener.notifyOfThreadComplete(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 305548111..d3278594f 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,34 +32,53 @@ 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 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. private int currentlyRunningSyncEvent = -1; private Random rand; private Map eventPool; + /** + * EventManager constructor. + * + */ public EventManager() { rand = new Random(1); eventPool = new ConcurrentHashMap(maxRunningEvents); } - // Create a Synchronous event. + /** + * 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 createSyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventID = createEvent(eventTime); + int eventId = createEvent(eventTime); if (currentlyRunningSyncEvent != -1) { throw new InvalidOperationException( "Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again."); } - currentlyRunningSyncEvent = eventID; + currentlyRunningSyncEvent = eventId; - return eventID; + return eventId; } - // Create an Asynchronous event. + /** + * 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 createAsyncEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { return createEvent(eventTime); } @@ -74,44 +93,65 @@ public class EventManager implements ThreadCompleteListener { "Maximum event time allowed is " + maxEventTime + " seconds. Please try again."); } - int newEventID = generateID(); + int newEventId = generateId(); - Event newEvent = new Event(newEventID, eventTime); + Event newEvent = new Event(newEventId, eventTime); newEvent.addListener(this); - eventPool.put(newEventID, newEvent); + eventPool.put(newEventId, newEvent); - return newEventID; + return newEventId; } - public void startEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Starts event. + * + * @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 { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).start(); + eventPool.get(eventId).start(); } - public void stopEvent(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * Stops event. + * + * @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 { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - if (eventID == currentlyRunningSyncEvent) { + if (eventId == currentlyRunningSyncEvent) { currentlyRunningSyncEvent = -1; } - eventPool.get(eventID).stop(); - eventPool.remove(eventID); + eventPool.get(eventId).stop(); + eventPool.remove(eventId); } - public void getStatus(int eventID) throws EventDoesNotExistException { - if (!eventPool.containsKey(eventID)) { - throw new EventDoesNotExistException(eventID + " does not exist."); + /** + * 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 getStatus(int eventId) throws EventDoesNotExistException { + if (!eventPool.containsKey(eventId)) { + throw new EventDoesNotExistException(eventId + " does not exist."); } - eventPool.get(eventID).status(); + eventPool.get(eventId).status(); } + /** + * Gets status of all running events. + */ @SuppressWarnings("rawtypes") public void getStatusOfAllEvents() { Iterator it = eventPool.entrySet().iterator(); @@ -125,12 +165,12 @@ public class EventManager implements ThreadCompleteListener { * 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() { + 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((maxId - minId) + 1) + minId; while (eventPool.containsKey(randomNum)) { - randomNum = rand.nextInt((maxID - minID) + 1) + minID; + randomNum = rand.nextInt((maxId - minId) + 1) + minId; } return randomNum; @@ -140,9 +180,9 @@ 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) { - eventPool.get(eventID).status(); - eventPool.remove(eventID); + public void notifyOfThreadComplete(int eventId) { + eventPool.get(eventId).status(); + eventPool.remove(eventId); } } 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 e5c910289..88f300634 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 notifyOfThreadComplete(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 0ab901106..392c7fba6 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 @@ -36,9 +36,9 @@ 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.createAsyncEvent(60); + eventManager.startEvent(aEventId); + eventManager.stopEvent(aEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) { System.out.println(e.getMessage()); } @@ -48,9 +48,9 @@ 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.createSyncEvent(60); + eventManager.startEvent(sEventId); + eventManager.stopEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); @@ -61,10 +61,10 @@ public class EventAsynchronousTest { public void testUnsuccessfulSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); - sEventID = eventManager.createSyncEvent(60); - eventManager.startEvent(sEventID); + int sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); + sEventId = eventManager.createSyncEvent(60); + eventManager.startEvent(sEventId); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { System.out.println(e.getMessage()); From 233f1e69f9401611967c8be6cc70f16d2ac18e1b Mon Sep 17 00:00:00 2001 From: WSSIA Date: Tue, 9 Aug 2016 22:00:19 +0100 Subject: [PATCH 04/27] Removed PUBLIC modifiers from IEvent --- .../main/java/com/iluwatar/event/asynchronous/IEvent.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 index 448c02e84..bcd78b6c0 100644 --- a/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java +++ b/event-asynchronous/src/main/java/com/iluwatar/event/asynchronous/IEvent.java @@ -18,10 +18,10 @@ package com.iluwatar.event.asynchronous; public interface IEvent { - public void start(); + void start(); - public void stop(); + void stop(); - public void status(); + void status(); } From 9a90f2de1f50557e584d3a0ced4d134c8c702090 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Sun, 11 Sep 2016 18:45:51 +0100 Subject: [PATCH 05/27] 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()); } } From dbd605e3786eb0d90ded55436976784865328d53 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:04 +0100 Subject: [PATCH 06/27] Changes based on latest code review --- .../com/iluwatar/event/asynchronous/App.java | 29 +++++++++++-------- .../iluwatar/event/asynchronous/Event.java | 17 ++++++++++- .../event/asynchronous/EventManager.java | 13 ++++++--- .../src/main/java/config.properties | 2 +- 4 files changed, 43 insertions(+), 18 deletions(-) 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 f951af07c..65ae02e56 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 @@ -138,11 +138,10 @@ public class App { Scanner s = new Scanner(System.in); int option = -1; - while (option != 5) { + 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 IS MY EGG? \n(4) HOW ARE MY EGGS? \n(5) EXIT"); - System.out.print("Choose [1,2,3,4,5]: "); + 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) { @@ -181,16 +180,22 @@ public class App { System.out.println(e.getMessage()); } } else if (option == 3) { - System.out.print("Which egg?: "); - int eventId = s.nextInt(); - try { - eventManager.status(eventId); - } catch (EventDoesNotExistException e) { - System.out.println(e.getMessage()); + 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.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 1cb04acdc..c2e14ad68 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 @@ -25,13 +25,25 @@ 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; - public Event(final int eventId, final int eventTime) { + /** + * + * @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 @@ -42,6 +54,9 @@ public class Event implements IEvent, Runnable { @Override public void stop() { + if (null == thread) { + return; + } thread.interrupt(); } 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 e65816cec..dae995e38 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 @@ -61,11 +61,12 @@ public class EventManager implements ThreadCompleteListener { */ public int create(int eventTime) throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException { - int eventId = createEvent(eventTime); 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; @@ -80,10 +81,11 @@ public class EventManager implements ThreadCompleteListener { * @throws LongRunningEventException Long running events are not allowed in the app. */ public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { - return createEvent(eventTime); + return createEvent(eventTime, false); } - private int createEvent(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException { + 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."); } @@ -95,7 +97,7 @@ public class EventManager implements ThreadCompleteListener { int newEventId = generateId(); - Event newEvent = new Event(newEventId, eventTime); + Event newEvent = new Event(newEventId, eventTime, isSynchronous); newEvent.addListener(this); eventPool.put(newEventId, newEvent); @@ -194,6 +196,9 @@ public class EventManager implements ThreadCompleteListener { @Override public void completedEventHandler(int eventId) { eventPool.get(eventId).status(); + if (eventPool.get(eventId).isSynchronous()) { + currentlyRunningSyncEvent = -1; + } eventPool.remove(eventId); } diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index edbe90e05..7216f665d 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=NO \ No newline at end of file +INTERACTIVE_MODE=YES \ No newline at end of file From 371b262a5135484d83efb0666269af0a55c6f185 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 19 Sep 2016 21:50:23 +0100 Subject: [PATCH 07/27] Changes based on latest code review --- event-asynchronous/src/main/java/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties index 7216f665d..edbe90e05 100644 --- a/event-asynchronous/src/main/java/config.properties +++ b/event-asynchronous/src/main/java/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 6ed842e58bab97bc7eebe47575838a0b1f0910d7 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 09:35:17 +0200 Subject: [PATCH 08/27] Caching pattern: Refactor shutdown hook to use method reference --- caching/src/main/java/com/iluwatar/caching/AppManager.java | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/AppManager.java b/caching/src/main/java/com/iluwatar/caching/AppManager.java index 2967c759f..13ef4d4dd 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(); } From 865f788612b4c5fd7fd978e346c47174750f8af4 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 09:38:23 +0200 Subject: [PATCH 09/27] Caching pattern: Refactor LRU cache to avoid NPE and unnecessary cache lookup --- caching/src/main/java/com/iluwatar/caching/LruCache.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 5c5549afd..4527b5548 100644 --- a/caching/src/main/java/com/iluwatar/caching/LruCache.java +++ b/caching/src/main/java/com/iluwatar/caching/LruCache.java @@ -136,10 +136,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() { From e3355d76d1930f935d902f54d4cf7fae4f94bd24 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 20:33:24 +0200 Subject: [PATCH 10/27] Caching pattern: Style fix for null check --- .../src/main/java/com/iluwatar/caching/CacheStore.java | 4 ++-- caching/src/main/java/com/iluwatar/caching/DbManager.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/caching/src/main/java/com/iluwatar/caching/CacheStore.java b/caching/src/main/java/com/iluwatar/caching/CacheStore.java index 5903f8219..dd425645e 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(); } } 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) { From b31edda3cf9b376423e1a6cec0b7803a9597e522 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Thu, 22 Sep 2016 20:41:26 +0200 Subject: [PATCH 11/27] Caching pattern: Implementation of Cache-Aside pattern --- .../main/java/com/iluwatar/caching/App.java | 23 +++++++++++++++ .../java/com/iluwatar/caching/AppManager.java | 29 +++++++++++++++++++ .../java/com/iluwatar/caching/CacheStore.java | 21 ++++++++++++++ .../com/iluwatar/caching/CachingPolicy.java | 2 +- .../com/iluwatar/caching/CachingTest.java | 5 ++++ 5 files changed, 79 insertions(+), 1 deletion(-) diff --git a/caching/src/main/java/com/iluwatar/caching/App.java b/caching/src/main/java/com/iluwatar/caching/App.java index 8e5a84085..4a3a9ab42 100644 --- a/caching/src/main/java/com/iluwatar/caching/App.java +++ b/caching/src/main/java/com/iluwatar/caching/App.java @@ -74,6 +74,7 @@ public class App { app.useReadAndWriteThroughStrategy(); app.useReadThroughAndWriteAroundStrategy(); app.useReadThroughAndWriteBehindStrategy(); + app.useCacheAsideStategy(); } /** @@ -136,4 +137,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 13ef4d4dd..2d6a424a3 100644 --- a/caching/src/main/java/com/iluwatar/caching/AppManager.java +++ b/caching/src/main/java/com/iluwatar/caching/AppManager.java @@ -81,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; } @@ -95,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 dd425645e..c2b95a1bd 100644 --- a/caching/src/main/java/com/iluwatar/caching/CacheStore.java +++ b/caching/src/main/java/com/iluwatar/caching/CacheStore.java @@ -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..0c907fe88 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -28,7 +28,7 @@ package com.iluwatar.caching; * */ 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/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(); + } } From 9512f3ec701a8236ee2bf07bedd9ff9cb15aa154 Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Mon, 3 Oct 2016 21:59:36 +0200 Subject: [PATCH 12/27] Closes #436. Adds criticism to service locator pattern. --- service-locator/README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/service-locator/README.md b/service-locator/README.md index 31d82b13f..75a00ca57 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -33,6 +33,13 @@ 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. +* Limits object composability by stopping the clients to specify needed dependencies for different objects instantiation. + ## Credits * [J2EE Design Patterns](http://www.amazon.com/J2EE-Design-Patterns-William-Crawford/dp/0596004273/ref=sr_1_2) From 12544caa759fa1ac0fec7bd5bde5dae9d7a4a082 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:05:11 +0100 Subject: [PATCH 13/27] Changes based on review feedback. --- event-asynchronous/README.md | 2 +- .../com/iluwatar/event/asynchronous/App.java | 12 ++--- .../iluwatar/event/asynchronous/Event.java | 2 +- .../asynchronous/EventAsynchronousTest.java | 52 +++++++++++++++++++ 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/event-asynchronous/README.md b/event-asynchronous/README.md index dde434aba..ef35d0b38 100644 --- a/event-asynchronous/README.md +++ b/event-asynchronous/README.md @@ -3,7 +3,7 @@ layout: pattern title: Event-based Asynchronous folder: event-asynchronous permalink: /patterns/event-asynchronous/ -categories: Other +categories: Concurrency tags: - difficulty-intermediate - performance 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 65ae02e56..5a2565940 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 @@ -106,23 +106,23 @@ public class App { try { // Create an Asynchronous event. int aEventId = eventManager.createAsync(60); - System.out.println("Event [" + aEventId + "] has been created."); + System.out.println("Async Event [" + aEventId + "] has been created."); eventManager.start(aEventId); - System.out.println("Event [" + aEventId + "] has been started."); + System.out.println("Async Event [" + aEventId + "] has been started."); // Create a Synchronous event. int sEventId = eventManager.create(60); - System.out.println("Event [" + sEventId + "] has been created."); + System.out.println("Sync Event [" + sEventId + "] has been created."); eventManager.start(sEventId); - System.out.println("Event [" + sEventId + "] has been started."); + System.out.println("Sync Event [" + sEventId + "] has been started."); eventManager.status(aEventId); eventManager.status(sEventId); eventManager.cancel(aEventId); - System.out.println("Event [" + aEventId + "] has been stopped."); + System.out.println("Async Event [" + aEventId + "] has been stopped."); eventManager.cancel(sEventId); - System.out.println("Event [" + sEventId + "] has been stopped."); + System.out.println("Sync Event [" + sEventId + "] has been stopped."); } catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException | InvalidOperationException e) { 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 c2e14ad68..5e557fff2 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 @@ -75,7 +75,7 @@ public class Event implements IEvent, Runnable { long endTime = currentTime + (eventTime * 1000); while (System.currentTimeMillis() < endTime) { try { - Thread.sleep(5000); // Sleep for 5 seconds. + Thread.sleep(1000); // Sleep for 1 second. } catch (InterruptedException e) { return; } 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 6565d5bad..92bacda99 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 @@ -79,4 +79,56 @@ public class EventAsynchronousTest { System.out.println(e.getMessage()); } } + + @Test + public void testFullSynchronousEvent() { + EventManager eventManager = new EventManager(); + try { + int eventTime = 5; + + int sEventId = eventManager.create(eventTime); + assertTrue(eventManager.getEventPool().size() == 1); + eventManager.start(sEventId); + + long currentTime = System.currentTimeMillis(); + long endTime = currentTime + (eventTime + 5 * 1000); // +5 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 = 5; + + 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 + 5 * 1000); // +5 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()); + } + } } From 622376e0fa3b1723b86853c4c8a882b6bd378cb2 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:16:23 +0100 Subject: [PATCH 14/27] Updated version snapshot to 1.14.0 --- event-asynchronous/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/pom.xml b/event-asynchronous/pom.xml index 60ab8f0aa..1bc4549d4 100644 --- a/event-asynchronous/pom.xml +++ b/event-asynchronous/pom.xml @@ -29,7 +29,7 @@ com.iluwatar java-design-patterns - 1.13.0-SNAPSHOT + 1.14.0-SNAPSHOT event-asynchronous From 8f1758c28f663f73ea79be8d2a1575fcc4d9fffe Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 3 Oct 2016 21:46:16 +0100 Subject: [PATCH 15/27] Alter JUnit tests to run in lesser time. --- .../event/asynchronous/EventAsynchronousTest.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) 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 92bacda99..213439203 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 @@ -84,15 +84,16 @@ public class EventAsynchronousTest { public void testFullSynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int sEventId = eventManager.create(eventTime); assertTrue(eventManager.getEventPool().size() == 1); eventManager.start(sEventId); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete - // properly. + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to + // complete + // properly. while (System.currentTimeMillis() < endTime) { } @@ -108,7 +109,7 @@ public class EventAsynchronousTest { public void testFullAsynchronousEvent() { EventManager eventManager = new EventManager(); try { - int eventTime = 5; + int eventTime = 1; int aEventId1 = eventManager.createAsync(eventTime); int aEventId2 = eventManager.createAsync(eventTime); @@ -120,7 +121,7 @@ public class EventAsynchronousTest { eventManager.start(aEventId3); long currentTime = System.currentTimeMillis(); - long endTime = currentTime + (eventTime + 5 * 1000); // +5 to give a bit of buffer time for event to complete + long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete // properly. while (System.currentTimeMillis() < endTime) { } From eea8785a22527899da48c56f64b63aa0db1df1b5 Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Tue, 4 Oct 2016 14:34:01 +0200 Subject: [PATCH 16/27] Fixes #437. Adds criticism to Singleton pattern. --- singleton/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/singleton/README.md b/singleton/README.md index e1bb42a45..38a05349b 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -36,6 +36,11 @@ 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. + ## Credits * [Design Patterns: Elements of Reusable Object-Oriented Software](http://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) From f114b5b957c8ef432a504b24a56efa36599a9017 Mon Sep 17 00:00:00 2001 From: Christoffer Hamberg Date: Sun, 9 Oct 2016 12:43:43 +0200 Subject: [PATCH 17/27] Caching pattern: Documentation and diagram --- caching/README.md | 1 + caching/etc/caching.png | Bin 56234 -> 114847 bytes caching/etc/caching.ucls | 190 ++++++++---------- .../main/java/com/iluwatar/caching/App.java | 23 ++- .../com/iluwatar/caching/CachingPolicy.java | 2 +- .../java/com/iluwatar/caching/LruCache.java | 3 - .../com/iluwatar/caching/UserAccount.java | 2 - 7 files changed, 101 insertions(+), 120 deletions(-) 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 6b3b2d0556c51b7c593028b2c9120a00c47716c1..b6ed703ab8b7dfce5e5d82f097dac534828fc93a 100644 GIT binary patch literal 114847 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

$0ITcj(KRIbdLxc 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 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 4a3a9ab42..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 diff --git a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java index 0c907fe88..5ad32f699 100644 --- a/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java +++ b/caching/src/main/java/com/iluwatar/caching/CachingPolicy.java @@ -24,7 +24,7 @@ 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 { diff --git a/caching/src/main/java/com/iluwatar/caching/LruCache.java b/caching/src/main/java/com/iluwatar/caching/LruCache.java index 4527b5548..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) { @@ -161,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; From 85060784a7758af064aa883d66f029dd292ea9e8 Mon Sep 17 00:00:00 2001 From: dzmitryh Date: Sat, 15 Oct 2016 14:27:15 +0300 Subject: [PATCH 18/27] End process logic clause has been corrected. --- .../async/method/invocation/ThreadAsyncExecutor.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java index 7f96d9ab7..5a10232f0 100644 --- a/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java +++ b/async-method-invocation/src/main/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutor.java @@ -57,12 +57,10 @@ public class ThreadAsyncExecutor implements AsyncExecutor { @Override public T endProcess(AsyncResult asyncResult) throws ExecutionException, InterruptedException { - if (asyncResult.isCompleted()) { - return asyncResult.getValue(); - } else { + if (!asyncResult.isCompleted()) { asyncResult.await(); - return asyncResult.getValue(); } + return asyncResult.getValue(); } /** From 37b930c3b75ebc17ac46f6ce9f93ef1ed8435aeb Mon Sep 17 00:00:00 2001 From: dzmitryh Date: Sat, 15 Oct 2016 14:29:32 +0300 Subject: [PATCH 19/27] Unused import removed. --- .../async/method/invocation/ThreadAsyncExecutorTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java index b4a23222a..09126e99f 100644 --- a/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java +++ b/async-method-invocation/src/test/java/com/iluwatar/async/method/invocation/ThreadAsyncExecutorTest.java @@ -31,7 +31,6 @@ import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import static org.junit.Assert.*; -import static org.mockito.Matchers.eq; import static org.mockito.Mockito.*; import static org.mockito.internal.verification.VerificationModeFactory.times; From 986c529eb67e5eccd999e4bc13d268a8c5997fc7 Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 21:29:03 +0100 Subject: [PATCH 20/27] Moved config into a separate dir --- event-asynchronous/src/main/java/config.properties | 1 - event-asynchronous/src/main/resources/config.properties | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 event-asynchronous/src/main/java/config.properties create mode 100644 event-asynchronous/src/main/resources/config.properties diff --git a/event-asynchronous/src/main/java/config.properties b/event-asynchronous/src/main/java/config.properties deleted file mode 100644 index edbe90e05..000000000 --- a/event-asynchronous/src/main/java/config.properties +++ /dev/null @@ -1 +0,0 @@ -INTERACTIVE_MODE=NO \ No newline at end of file diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties new file mode 100644 index 000000000..7216f665d --- /dev/null +++ b/event-asynchronous/src/main/resources/config.properties @@ -0,0 +1 @@ +INTERACTIVE_MODE=YES \ No newline at end of file From 70318123fe762d919eda221e08b0f7bf2db5b8dd Mon Sep 17 00:00:00 2001 From: WSSIA Date: Mon, 17 Oct 2016 22:22:06 +0100 Subject: [PATCH 21/27] Changed config to non-interactive --- event-asynchronous/src/main/resources/config.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/event-asynchronous/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index 7216f665d..edbe90e05 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +1 @@ -INTERACTIVE_MODE=YES \ No newline at end of file +INTERACTIVE_MODE=NO \ No newline at end of file From 99677867c66a318f4f41e714a7ed867131cfcdd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 18 Oct 2016 07:51:37 +0300 Subject: [PATCH 22/27] Event Based Asynchronous pattern: Add missing license header and puml diagram --- .../etc/event-asynchronous.urm.puml | 65 +++++++++++++++++++ .../src/main/resources/config.properties | 23 +++++++ 2 files changed, 88 insertions(+) create mode 100644 event-asynchronous/etc/event-asynchronous.urm.puml 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/src/main/resources/config.properties b/event-asynchronous/src/main/resources/config.properties index edbe90e05..d8bfa1f7e 100644 --- a/event-asynchronous/src/main/resources/config.properties +++ b/event-asynchronous/src/main/resources/config.properties @@ -1 +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 From b66e8ecef970858e7823e6319ac378ba7fadfaea Mon Sep 17 00:00:00 2001 From: Dmitry Avershin Date: Tue, 18 Oct 2016 14:18:47 +0200 Subject: [PATCH 23/27] Adds more criticism to Singleton pattern. --- service-locator/README.md | 1 - singleton/README.md | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/service-locator/README.md b/service-locator/README.md index 75a00ca57..479c9ed0f 100644 --- a/service-locator/README.md +++ b/service-locator/README.md @@ -38,7 +38,6 @@ improves the performance of application to great extent. * 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. -* Limits object composability by stopping the clients to specify needed dependencies for different objects instantiation. ## Credits diff --git a/singleton/README.md b/singleton/README.md index 38a05349b..4032ffaed 100644 --- a/singleton/README.md +++ b/singleton/README.md @@ -40,6 +40,8 @@ Use the Singleton pattern when * 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 From ffdaf2ec4717164f466294bd7c37427fe3cddcdd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 19 Oct 2016 22:25:37 +0300 Subject: [PATCH 24/27] Add Travis instructions for SonarQube.com analysis --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 613f737d7..4cfffe701 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 From 19cb715d20f269eb00aa6569244bf090459eb6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Wed, 19 Oct 2016 23:08:51 +0300 Subject: [PATCH 25/27] Fix environment variable --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4cfffe701..ba226ff5c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,7 +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 +- 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 From 1c0278592703a20720bb00b7490d435819afffb2 Mon Sep 17 00:00:00 2001 From: Fabrice Bellingard Date: Sun, 23 Oct 2016 14:35:45 +0200 Subject: [PATCH 26/27] Add SonarQube.com badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 1789398ec..214fa91ff 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ [![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) [![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 From 124fd33da07de5ea5e10a69a237cdce157cc5bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ilkka=20Sepp=C3=A4l=C3=A4?= Date: Tue, 1 Nov 2016 21:31:54 +0200 Subject: [PATCH 27/27] Remove use of coveralls-maven-plugin (sonarqube.com covers this) --- .travis.yml | 1 - README.md | 1 - pom.xml | 40 ---------------------------------------- 3 files changed, 42 deletions(-) diff --git a/.travis.yml b/.travis.yml index ba226ff5c..817b6635f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -17,7 +17,6 @@ install: - mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V -e 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 diff --git a/README.md b/README.md index 214fa91ff..6eed9561b 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,6 @@ # 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) -[![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) [![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) diff --git a/pom.xml b/pom.xml index 538058a81..b8440f664 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,6 @@ 1.4.190 4.12 3.0 - 4.0.0 0.7.2.201409121644 1.4 2.16.1 @@ -320,29 +319,10 @@ 1.8 - - org.eluder.coveralls - coveralls-maven-plugin - ${coveralls.version} - - jb6wYzxkVvjolD6qOWpzWdcWBzYk2fAmF - - org.jacoco jacoco-maven-plugin ${jacoco.version} - - - - - domainapp/dom/modules/simple/QSimpleObject.class - **com.steadystate* - - prepare-agent @@ -378,26 +358,6 @@ - - - org.jacoco - jacoco-maven-plugin - 0.7.5.201505241946 - - - - prepare-agent - - - - report - prepare-package - - report - - - - org.apache.maven.plugins maven-surefire-plugin