Skip to content

Commit 992e76a

Browse files
authored
Merge pull request iluwatar#566 from qpi/master
Event Queue pattern
2 parents 4b32fb6 + 167a43f commit 992e76a

File tree

14 files changed

+517
-18
lines changed

14 files changed

+517
-18
lines changed

event-queue/README.md

+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
layout: pattern
3+
title: Event Queue
4+
folder: event-queue
5+
permalink: /patterns/event-queue/
6+
categories: Concurrency
7+
tags:
8+
- Java
9+
- Difficulty Intermediate
10+
- Queue
11+
---
12+
13+
## Intent
14+
Event Queue is a good pattern if You have a limited accesibility resource (for example:
15+
Audio or Database), but You need to handle all the requests that want to use that.
16+
It puts all the requests in a queue and process them asynchronously.
17+
Gives the resource for the event when it is the next in the queue and in same time
18+
removes it from the queue.
19+
20+
![alt text](./etc/model.png "Event Queue")
21+
22+
## Applicability
23+
Use the Event Queue pattern when
24+
25+
* You have a limited accesibility resource and the asynchronous process is acceptable to reach that
26+
27+
## Credits
28+
29+
* [Mihaly Kuprivecz - Event Queue] (http://gameprogrammingpatterns.com/event-queue.html)

event-queue/etc/Bass-Drum-1.aif

216 KB
Binary file not shown.

event-queue/etc/Bass-Drum-1.wav

216 KB
Binary file not shown.

event-queue/etc/Closed-Hi-Hat-1.aif

12.7 KB
Binary file not shown.

event-queue/etc/Closed-Hi-Hat-1.wav

12.7 KB
Binary file not shown.

event-queue/etc/event-queue.urm.puml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
@startuml
2+
package com.iluwatar.event.queue {
3+
class App {
4+
+ App()
5+
+ getAudioStream(filePath : String) : AudioInputStream {static}
6+
+ main(args : String[]) {static}
7+
}
8+
class Audio {
9+
- MAX_PENDING : int {static}
10+
- headIndex : int {static}
11+
- pendingAudio : PlayMessage[] {static}
12+
- tailIndex : int {static}
13+
- updateThread : Thread {static}
14+
+ Audio()
15+
+ init() {static}
16+
+ playSound(stream : AudioInputStream, volume : float) {static}
17+
+ stopService() {static}
18+
+ update() {static}
19+
}
20+
class PlayMessage {
21+
~ stream : AudioInputStream
22+
~ volume : float
23+
+ PlayMessage()
24+
}
25+
}
26+
@enduml

event-queue/etc/model.png

16.8 KB
Loading

event-queue/etc/model.ucls

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<class-diagram version="1.2.0" icons="true" automaticImage="PNG" always-add-relationships="false" generalizations="true"
3+
realizations="true" associations="true" dependencies="false" nesting-relationships="true" router="FAN">
4+
<class id="1" language="java" name="com.iluwatar.event.queue.Audio" project="event-queue"
5+
file="/event-queue/src/main/java/com/iluwatar/event/queue/Audio.java" binary="false" corner="BOTTOM_RIGHT">
6+
<position height="-1" width="-1" x="285" y="179"/>
7+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
8+
sort-features="false" accessors="true" visibility="true">
9+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
10+
<operations public="true" package="true" protected="true" private="true" static="true"/>
11+
</display>
12+
</class>
13+
<class id="2" language="java" name="com.iluwatar.event.queue.PlayMessage" project="event-queue"
14+
file="/event-queue/src/main/java/com/iluwatar/event/queue/PlayMessage.java" binary="false" corner="BOTTOM_RIGHT">
15+
<position height="-1" width="-1" x="633" y="179"/>
16+
<display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
17+
sort-features="false" accessors="true" visibility="true">
18+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
19+
<operations public="true" package="true" protected="true" private="true" static="true"/>
20+
</display>
21+
</class>
22+
<association id="3">
23+
<end type="SOURCE" refId="1" navigable="false">
24+
<attribute id="4" name="pendingAudio"/>
25+
<multiplicity id="5" minimum="0" maximum="2147483647"/>
26+
</end>
27+
<end type="TARGET" refId="2" navigable="true"/>
28+
<display labels="true" multiplicity="true"/>
29+
</association>
30+
<classifier-display autosize="true" stereotype="true" package="true" initial-value="false" signature="true"
31+
sort-features="false" accessors="true" visibility="true">
32+
<attributes public="true" package="true" protected="true" private="true" static="true"/>
33+
<operations public="true" package="true" protected="true" private="true" static="true"/>
34+
</classifier-display>
35+
<association-display labels="true" multiplicity="true"/>
36+
</class-diagram>

event-queue/pom.xml

+43
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
4+
The MIT License
5+
Copyright (c) 2014-2016 Ilkka Seppälä
6+
7+
Permission is hereby granted, free of charge, to any person obtaining a copy
8+
of this software and associated documentation files (the "Software"), to deal
9+
in the Software without restriction, including without limitation the rights
10+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
copies of the Software, and to permit persons to whom the Software is
12+
furnished to do so, subject to the following conditions:
13+
14+
The above copyright notice and this permission notice shall be included in
15+
all copies or substantial portions of the Software.
16+
17+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
THE SOFTWARE.
24+
25+
-->
26+
<project xmlns="http://maven.apache.org/POM/4.0.0"
27+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
28+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
29+
<modelVersion>4.0.0</modelVersion>
30+
<parent>
31+
<artifactId>java-design-patterns</artifactId>
32+
<groupId>com.iluwatar</groupId>
33+
<version>1.16.0-SNAPSHOT</version>
34+
</parent>
35+
<artifactId>event-queue</artifactId>
36+
<dependencies>
37+
<dependency>
38+
<groupId>junit</groupId>
39+
<artifactId>junit</artifactId>
40+
<scope>test</scope>
41+
</dependency>
42+
</dependencies>
43+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/**
2+
* The MIT License
3+
* Copyright (c) 2014-2016 Ilkka Seppälä
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.event.queue;
25+
26+
import java.io.BufferedReader;
27+
import java.io.IOException;
28+
import java.io.InputStreamReader;
29+
30+
import javax.sound.sampled.UnsupportedAudioFileException;
31+
32+
/**
33+
* Event or message queues provide an asynchronous communications protocol, meaning that the sender
34+
* and receiver of the message do not need to interact with the message queue at the same time.
35+
* Events or messages placed onto the queue are stored until the recipient retrieves them. Event
36+
* or message queues have implicit or explicit limits on the size of data that may be transmitted
37+
* in a single message and the number of messages that may remain outstanding on the queue.
38+
* A queue stores a series of notifications or requests in first-in, first-out order.
39+
* Sending a notification enqueues the request and returns. The request processor then processes
40+
* items from the queue at a later time.
41+
*/
42+
public class App {
43+
/**
44+
* Program entry point.
45+
*
46+
* @param args command line args
47+
* @throws IOException when there is a problem with the audio file loading
48+
* @throws UnsupportedAudioFileException when the loaded audio file is unsupported
49+
*/
50+
public static void main(String[] args) throws UnsupportedAudioFileException, IOException {
51+
Audio.playSound(Audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f);
52+
Audio.playSound(Audio.getAudioStream("./etc/Closed-Hi-Hat-1.wav"), -8.0f);
53+
54+
System.out.println("Press Enter key to stop the program...");
55+
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
56+
br.read();
57+
Audio.stopService();
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/**
2+
* The MIT License
3+
* Copyright (c) 2014-2016 Ilkka Seppälä
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.iluwatar.event.queue;
25+
26+
import java.io.File;
27+
import java.io.IOException;
28+
29+
import javax.sound.sampled.AudioInputStream;
30+
import javax.sound.sampled.AudioSystem;
31+
import javax.sound.sampled.Clip;
32+
import javax.sound.sampled.LineUnavailableException;
33+
import javax.sound.sampled.UnsupportedAudioFileException;
34+
35+
/**
36+
* This class implements the Event Queue pattern.
37+
* @author mkuprivecz
38+
*
39+
*/
40+
public class Audio {
41+
42+
private static final int MAX_PENDING = 16;
43+
44+
private static int headIndex;
45+
46+
private static int tailIndex;
47+
48+
private static Thread updateThread = null;
49+
50+
private static PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
51+
52+
/**
53+
* This method stops the Update Method's thread.
54+
*/
55+
public static synchronized void stopService() {
56+
if (updateThread != null) {
57+
updateThread.interrupt();
58+
}
59+
}
60+
61+
/**
62+
* This method stops the Update Method's thread.
63+
* @return boolean
64+
*/
65+
public static synchronized boolean isServiceRunning() {
66+
if (updateThread != null && updateThread.isAlive() ) {
67+
return true;
68+
} else {
69+
return false;
70+
}
71+
}
72+
73+
/**
74+
* Starts the thread for the Update Method pattern if it was not started previously.
75+
* Also when the thread is is ready initializes the indexes of the queue
76+
*/
77+
public static void init() {
78+
if (updateThread == null) {
79+
updateThread = new Thread(new Runnable() {
80+
public void run() {
81+
while (!Thread.currentThread().isInterrupted()) {
82+
Audio.update();
83+
}
84+
}
85+
});
86+
}
87+
startThread();
88+
}
89+
90+
/**
91+
* This is a synchronized thread starter
92+
*/
93+
public static synchronized void startThread() {
94+
if (!updateThread.isAlive()) {
95+
updateThread.start();
96+
headIndex = 0;
97+
tailIndex = 0;
98+
}
99+
}
100+
101+
/**
102+
* This method adds a new audio into the queue.
103+
* @param stream is the AudioInputStream for the method
104+
* @param volume is the level of the audio's volume
105+
*/
106+
public static void playSound(AudioInputStream stream, float volume) {
107+
init();
108+
// Walk the pending requests.
109+
for (int i = headIndex; i != tailIndex; i = (i + 1) % MAX_PENDING) {
110+
if (getPendingAudio()[i].getStream() == stream) {
111+
// Use the larger of the two volumes.
112+
getPendingAudio()[i].setVolume(Math.max(volume, getPendingAudio()[i].getVolume()));
113+
114+
// Don't need to enqueue.
115+
return;
116+
}
117+
}
118+
getPendingAudio()[tailIndex] = new PlayMessage(stream, volume);
119+
tailIndex = (tailIndex + 1) % MAX_PENDING;
120+
}
121+
122+
/**
123+
* This method uses the Update Method pattern.
124+
* It takes the audio from the queue and plays it
125+
*/
126+
public static void update() {
127+
// If there are no pending requests, do nothing.
128+
if (headIndex == tailIndex) {
129+
return;
130+
}
131+
Clip clip = null;
132+
try {
133+
AudioInputStream audioStream = getPendingAudio()[headIndex].getStream();
134+
headIndex++;
135+
clip = AudioSystem.getClip();
136+
clip.open(audioStream);
137+
clip.start();
138+
} catch (LineUnavailableException e) {
139+
System.err.println("Error occoured while loading the audio: The line is unavailable");
140+
e.printStackTrace();
141+
} catch (IOException e) {
142+
System.err.println("Input/Output error while loading the audio");
143+
e.printStackTrace();
144+
} catch (IllegalArgumentException e) {
145+
System.err.println("The system doesn't support the sound: " + e.getMessage());
146+
}
147+
}
148+
149+
/**
150+
* Returns the AudioInputStream of a file
151+
* @param filePath is the path of the audio file
152+
* @return AudioInputStream
153+
* @throws UnsupportedAudioFileException when the audio file is not supported
154+
* @throws IOException when the file is not readable
155+
*/
156+
public static AudioInputStream getAudioStream(String filePath)
157+
throws UnsupportedAudioFileException, IOException {
158+
return AudioSystem.getAudioInputStream(new File(filePath).getAbsoluteFile());
159+
}
160+
161+
/**
162+
* Returns with the message array of the queue
163+
* @return PlayMessage[]
164+
*/
165+
public static PlayMessage[] getPendingAudio() {
166+
return pendingAudio;
167+
}
168+
169+
}

0 commit comments

Comments
 (0)