Merge pull request #483 from waisuan/master

Issue #469: Event-based Asynchronous pattern
This commit is contained in:
Ilkka Seppälä 2016-10-18 07:43:01 +03:00 committed by GitHub
commit 0f7b44c3b1
17 changed files with 1038 additions and 1 deletions

View File

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

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

View File

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

View File

@ -0,0 +1,42 @@
<?xml version="1.0"?>
<!--
The MIT License
Copyright (c) 2014 Ilkka Seppälä
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.iluwatar</groupId>
<artifactId>java-design-patterns</artifactId>
<version>1.14.0-SNAPSHOT</version>
</parent>
<artifactId>event-asynchronous</artifactId>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,206 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.Scanner;
/**
*
* This application demonstrates the <b>Event-based Asynchronous</b> pattern. Essentially, users (of the pattern) may
* choose to run events in an Asynchronous or Synchronous mode. There can be multiple Asynchronous events running at
* once but only one Synchronous event can run at a time. Asynchronous events are synonymous to multi-threads. The key
* point here is that the threads run in the background and the user is free to carry on with other processes. Once an
* event is complete, the appropriate listener/callback method will be called. The listener then proceeds to carry out
* further processing depending on the needs of the user.
*
* The {@link EventManager} manages the events/threads that the user creates. Currently, the supported event operations
* are: <code>start</code>, <code>stop</code>, <code>getStatus</code>. For Synchronous events, the user is unable to
* start another (Synchronous) event if one is already running at the time. The running event would have to either be
* stopped or completed before a new event can be started.
*
* The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many
* of the complex issues inherent in multithreaded design. Using a class that supports this pattern can allow you to:-
* (1) Perform time-consuming tasks, such as downloads and database operations, "in the background," without
* interrupting your application. (2) Execute multiple operations simultaneously, receiving notifications when each
* completes. (3) Wait for resources to become available without stopping ("hanging") your application. (4) Communicate
* with pending asynchronous operations using the familiar events-and-delegates model.
*
* @see EventManager
* @see Event
*
*/
public class App {
public static final String PROP_FILE_NAME = "config.properties";
boolean interactiveMode = false;
/**
* Program entry point.
*
* @param args command line args
*/
public static void main(String[] args) {
App app = new App();
app.setUp();
app.run();
}
/**
* App can run in interactive mode or not. Interactive mode == Allow user interaction with command line.
* Non-interactive is a quick sequential run through the available {@link EventManager} operations.
*/
public void setUp() {
Properties prop = new Properties();
InputStream inputStream = App.class.getClassLoader().getResourceAsStream(PROP_FILE_NAME);
if (inputStream != null) {
try {
prop.load(inputStream);
} catch (IOException e) {
System.out.println(PROP_FILE_NAME + " was not found. Defaulting to non-interactive mode.");
}
String property = prop.getProperty("INTERACTIVE_MODE");
if (property.equalsIgnoreCase("YES")) {
interactiveMode = true;
}
}
}
/**
* Run program in either interactive mode or not.
*/
public void run() {
if (interactiveMode) {
runInteractiveMode();
} else {
quickRun();
}
}
/**
* Run program in non-interactive mode.
*/
public void quickRun() {
EventManager eventManager = new EventManager();
try {
// Create an Asynchronous event.
int aEventId = eventManager.createAsync(60);
System.out.println("Async Event [" + aEventId + "] has been created.");
eventManager.start(aEventId);
System.out.println("Async Event [" + aEventId + "] has been started.");
// Create a Synchronous event.
int sEventId = eventManager.create(60);
System.out.println("Sync Event [" + sEventId + "] has been created.");
eventManager.start(sEventId);
System.out.println("Sync Event [" + sEventId + "] has been started.");
eventManager.status(aEventId);
eventManager.status(sEventId);
eventManager.cancel(aEventId);
System.out.println("Async Event [" + aEventId + "] has been stopped.");
eventManager.cancel(sEventId);
System.out.println("Sync Event [" + sEventId + "] has been stopped.");
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
| InvalidOperationException e) {
System.out.println(e.getMessage());
}
}
/**
* Run program in interactive mode.
*/
public void runInteractiveMode() {
EventManager eventManager = new EventManager();
Scanner s = new Scanner(System.in);
int option = -1;
while (option != 4) {
System.out.println("Hello. Would you like to boil some eggs?");
System.out.println("(1) BOIL AN EGG \n(2) STOP BOILING THIS EGG \n(3) HOW ARE MY EGGS? \n(4) EXIT");
System.out.print("Choose [1,2,3,4]: ");
option = s.nextInt();
if (option == 1) {
s.nextLine();
System.out.print("Boil multiple eggs at once (A) or boil them one-by-one (S)?: ");
String eventType = s.nextLine();
System.out.print("How long should this egg be boiled for (in seconds)?: ");
int eventTime = s.nextInt();
if (eventType.equalsIgnoreCase("A")) {
try {
int eventId = eventManager.createAsync(eventTime);
eventManager.start(eventId);
System.out.println("Egg [" + eventId + "] is being boiled.");
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else if (eventType.equalsIgnoreCase("S")) {
try {
int eventId = eventManager.create(eventTime);
eventManager.start(eventId);
System.out.println("Egg [" + eventId + "] is being boiled.");
} catch (MaxNumOfEventsAllowedException | InvalidOperationException | LongRunningEventException
| EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else {
System.out.println("Unknown event type.");
}
} else if (option == 2) {
System.out.print("Which egg?: ");
int eventId = s.nextInt();
try {
eventManager.cancel(eventId);
System.out.println("Egg [" + eventId + "] is removed from boiler.");
} catch (EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else if (option == 3) {
s.nextLine();
System.out.print("Just one egg (O) OR all of them (A) ?: ");
String eggChoice = s.nextLine();
if (eggChoice.equalsIgnoreCase("O")) {
System.out.print("Which egg?: ");
int eventId = s.nextInt();
try {
eventManager.status(eventId);
} catch (EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
} else if (eggChoice.equalsIgnoreCase("A")) {
eventManager.statusOfAllEvents();
}
} else if (option == 4) {
eventManager.shutdown();
}
}
s.close();
}
}

View File

@ -0,0 +1,101 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
/**
*
* Each Event runs as a separate/individual thread.
*
*/
public class Event implements IEvent, Runnable {
private int eventId;
private int eventTime;
private boolean isSynchronous;
private Thread thread;
private boolean isComplete = false;
private ThreadCompleteListener eventListener;
/**
*
* @param eventId event ID
* @param eventTime event time
* @param isSynchronous is of synchronous type
*/
public Event(final int eventId, final int eventTime, final boolean isSynchronous) {
this.eventId = eventId;
this.eventTime = eventTime;
this.isSynchronous = isSynchronous;
}
public boolean isSynchronous() {
return isSynchronous;
}
@Override
public void start() {
thread = new Thread(this);
thread.start();
}
@Override
public void stop() {
if (null == thread) {
return;
}
thread.interrupt();
}
@Override
public void status() {
if (!isComplete) {
System.out.println("[" + eventId + "] is not done.");
} else {
System.out.println("[" + eventId + "] is done.");
}
}
@Override
public void run() {
long currentTime = System.currentTimeMillis();
long endTime = currentTime + (eventTime * 1000);
while (System.currentTimeMillis() < endTime) {
try {
Thread.sleep(1000); // Sleep for 1 second.
} catch (InterruptedException e) {
return;
}
}
isComplete = true;
completed();
}
public final void addListener(final ThreadCompleteListener listener) {
this.eventListener = listener;
}
public final void removeListener(final ThreadCompleteListener listener) {
this.eventListener = null;
}
private final void completed() {
if (eventListener != null) {
eventListener.completedEventHandler(eventId);
}
}
}

View File

@ -0,0 +1,26 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
public class EventDoesNotExistException extends Exception {
private static final long serialVersionUID = -3398463738273811509L;
public EventDoesNotExistException(String message) {
super(message);
}
}

View File

@ -0,0 +1,218 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* EventManager handles and maintains a pool of event threads. {@link Event} threads are created upon user request. Thre
* are two types of events; Asynchronous and Synchronous. There can be multiple Asynchronous events running at once but
* only one Synchronous event running at a time. Currently supported event operations are: start, stop, and getStatus.
* Once an event is complete, it then notifies EventManager through a listener. The EventManager then takes the event
* out of the pool.
*
*/
public class EventManager implements ThreadCompleteListener {
public static final int MAX_RUNNING_EVENTS = 1000; // Just don't wanna have too many running events. :)
public static final int MIN_ID = 1;
public static final int MAX_ID = MAX_RUNNING_EVENTS;
public static final int MAX_EVENT_TIME = 1800; // in seconds / 30 minutes.
private int currentlyRunningSyncEvent = -1;
private Random rand;
private Map<Integer, Event> eventPool;
/**
* EventManager constructor.
*
*/
public EventManager() {
rand = new Random(1);
eventPool = new ConcurrentHashMap<Integer, Event>(MAX_RUNNING_EVENTS);
}
/**
* Create a Synchronous event.
*
* @param eventTime Time an event should run for.
* @return eventId
* @throws MaxNumOfEventsAllowedException When too many events are running at a time.
* @throws InvalidOperationException No new synchronous events can be created when one is already running.
* @throws LongRunningEventException Long running events are not allowed in the app.
*/
public int create(int eventTime)
throws MaxNumOfEventsAllowedException, InvalidOperationException, LongRunningEventException {
if (currentlyRunningSyncEvent != -1) {
throw new InvalidOperationException(
"Event [" + currentlyRunningSyncEvent + "] is still running. Please wait until it finishes and try again.");
}
int eventId = createEvent(eventTime, true);
currentlyRunningSyncEvent = eventId;
return eventId;
}
/**
* Create an Asynchronous event.
*
* @param eventTime Time an event should run for.
* @return eventId
* @throws MaxNumOfEventsAllowedException When too many events are running at a time.
* @throws LongRunningEventException Long running events are not allowed in the app.
*/
public int createAsync(int eventTime) throws MaxNumOfEventsAllowedException, LongRunningEventException {
return createEvent(eventTime, false);
}
private int createEvent(int eventTime, boolean isSynchronous)
throws MaxNumOfEventsAllowedException, LongRunningEventException {
if (eventPool.size() == MAX_RUNNING_EVENTS) {
throw new MaxNumOfEventsAllowedException("Too many events are running at the moment. Please try again later.");
}
if (eventTime >= MAX_EVENT_TIME) {
throw new LongRunningEventException(
"Maximum event time allowed is " + MAX_EVENT_TIME + " seconds. Please try again.");
}
int newEventId = generateId();
Event newEvent = new Event(newEventId, eventTime, isSynchronous);
newEvent.addListener(this);
eventPool.put(newEventId, newEvent);
return newEventId;
}
/**
* Starts event.
*
* @param eventId The event that needs to be started.
* @throws EventDoesNotExistException If event does not exist in our eventPool.
*/
public void start(int eventId) throws EventDoesNotExistException {
if (!eventPool.containsKey(eventId)) {
throw new EventDoesNotExistException(eventId + " does not exist.");
}
eventPool.get(eventId).start();
}
/**
* Stops event.
*
* @param eventId The event that needs to be stopped.
* @throws EventDoesNotExistException If event does not exist in our eventPool.
*/
public void cancel(int eventId) throws EventDoesNotExistException {
if (!eventPool.containsKey(eventId)) {
throw new EventDoesNotExistException(eventId + " does not exist.");
}
if (eventId == currentlyRunningSyncEvent) {
currentlyRunningSyncEvent = -1;
}
eventPool.get(eventId).stop();
eventPool.remove(eventId);
}
/**
* Get status of a running event.
*
* @param eventId The event to inquire status of.
* @throws EventDoesNotExistException If event does not exist in our eventPool.
*/
public void status(int eventId) throws EventDoesNotExistException {
if (!eventPool.containsKey(eventId)) {
throw new EventDoesNotExistException(eventId + " does not exist.");
}
eventPool.get(eventId).status();
}
/**
* Gets status of all running events.
*/
@SuppressWarnings("rawtypes")
public void statusOfAllEvents() {
Iterator it = eventPool.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
((Event) pair.getValue()).status();
}
}
/**
* Stop all running events.
*/
@SuppressWarnings("rawtypes")
public void shutdown() {
Iterator it = eventPool.entrySet().iterator();
while (it.hasNext()) {
Map.Entry pair = (Map.Entry) it.next();
((Event) pair.getValue()).stop();
}
}
/**
* Returns a pseudo-random number between min and max, inclusive. The difference between min and max can be at most
* <code>Integer.MAX_VALUE - 1</code>.
*/
private int generateId() {
// nextInt is normally exclusive of the top value,
// so add 1 to make it inclusive
int randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
while (eventPool.containsKey(randomNum)) {
randomNum = rand.nextInt((MAX_ID - MIN_ID) + 1) + MIN_ID;
}
return randomNum;
}
/**
* Callback from an {@link Event} (once it is complete). The Event is then removed from the pool.
*/
@Override
public void completedEventHandler(int eventId) {
eventPool.get(eventId).status();
if (eventPool.get(eventId).isSynchronous()) {
currentlyRunningSyncEvent = -1;
}
eventPool.remove(eventId);
}
/**
* Getter method for event pool.
*/
public Map<Integer, Event> getEventPool() {
return eventPool;
}
/**
* Get number of currently running Synchronous events.
*/
public int numOfCurrentlyRunningSyncEvent() {
return currentlyRunningSyncEvent;
}
}

View File

@ -0,0 +1,27 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
public interface IEvent {
void start();
void stop();
void status();
}

View File

@ -0,0 +1,27 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
public class InvalidOperationException extends Exception {
private static final long serialVersionUID = -6191545255213410803L;
public InvalidOperationException(String message) {
super(message);
}
}

View File

@ -0,0 +1,26 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
public class LongRunningEventException extends Exception {
private static final long serialVersionUID = -483423544320148809L;
public LongRunningEventException(String message) {
super(message);
}
}

View File

@ -0,0 +1,26 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
public class MaxNumOfEventsAllowedException extends Exception {
private static final long serialVersionUID = -8430876973516292695L;
public MaxNumOfEventsAllowedException(String message) {
super(message);
}
}

View File

@ -0,0 +1,21 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
public interface ThreadCompleteListener {
void completedEventHandler(final int eventId);
}

View File

@ -0,0 +1 @@
INTERACTIVE_MODE=NO

View File

@ -0,0 +1,32 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
import java.io.IOException;
import org.junit.Test;
/**
* Tests that EventAsynchronous example runs without errors.
*/
public class AppTest {
@Test
public void test() throws IOException {
String[] args = {};
App.main(args);
}
}

View File

@ -0,0 +1,135 @@
/**
* The MIT License Copyright (c) 2014 Ilkka Seppälä
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
* documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
* WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.iluwatar.event.asynchronous;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
/**
*
* Application test
*
*/
public class EventAsynchronousTest {
App app;
@Before
public void setUp() {
app = new App();
}
@Test
public void testAsynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int aEventId = eventManager.createAsync(60);
eventManager.start(aEventId);
assertTrue(eventManager.getEventPool().size() == 1);
assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() == -1);
eventManager.cancel(aEventId);
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
}
@Test
public void testSynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int sEventId = eventManager.create(60);
eventManager.start(sEventId);
assertTrue(eventManager.getEventPool().size() == 1);
assertTrue(eventManager.getEventPool().size() < EventManager.MAX_RUNNING_EVENTS);
assertTrue(eventManager.numOfCurrentlyRunningSyncEvent() != -1);
eventManager.cancel(sEventId);
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
| InvalidOperationException e) {
System.out.println(e.getMessage());
}
}
@Test(expected = InvalidOperationException.class)
public void testUnsuccessfulSynchronousEvent() throws InvalidOperationException {
EventManager eventManager = new EventManager();
try {
int sEventId = eventManager.create(60);
eventManager.start(sEventId);
sEventId = eventManager.create(60);
eventManager.start(sEventId);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
}
@Test
public void testFullSynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int eventTime = 1;
int sEventId = eventManager.create(eventTime);
assertTrue(eventManager.getEventPool().size() == 1);
eventManager.start(sEventId);
long currentTime = System.currentTimeMillis();
long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to
// complete
// properly.
while (System.currentTimeMillis() < endTime) {
}
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException
| InvalidOperationException e) {
System.out.println(e.getMessage());
}
}
@Test
public void testFullAsynchronousEvent() {
EventManager eventManager = new EventManager();
try {
int eventTime = 1;
int aEventId1 = eventManager.createAsync(eventTime);
int aEventId2 = eventManager.createAsync(eventTime);
int aEventId3 = eventManager.createAsync(eventTime);
assertTrue(eventManager.getEventPool().size() == 3);
eventManager.start(aEventId1);
eventManager.start(aEventId2);
eventManager.start(aEventId3);
long currentTime = System.currentTimeMillis();
long endTime = currentTime + (eventTime + 2 * 1000); // +2 to give a bit of buffer time for event to complete
// properly.
while (System.currentTimeMillis() < endTime) {
}
assertTrue(eventManager.getEventPool().size() == 0);
} catch (MaxNumOfEventsAllowedException | LongRunningEventException | EventDoesNotExistException e) {
System.out.println(e.getMessage());
}
}
}

View File

@ -132,6 +132,7 @@
<module>aggregator-microservices</module> <module>aggregator-microservices</module>
<module>promise</module> <module>promise</module>
<module>page-object</module> <module>page-object</module>
<module>event-asynchronous</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>