Skip to content

Commit 25ed7c0

Browse files
npathaiiluwatar
authored andcommitted
Refactored Event Queue (iluwatar#806)
* 1) Test cases were not stopping AudioService before ending test case 2) Changed Audio to be a good singleton, previously because of Audio being bad singleton, test cases which were using static methods could have caused intermittent failures. 3) Made some other refactorings as well * Removed sonar issue and converted Audio to eager singleton for simplicity * Updated class diagram PNG
1 parent 922fd62 commit 25ed7c0

File tree

4 files changed

+64
-43
lines changed

4 files changed

+64
-43
lines changed

event-queue/etc/model.png

5.86 KB
Loading

event-queue/src/main/java/com/iluwatar/event/queue/App.java

+8-6
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ public class App {
4747
* @throws IOException when there is a problem with the audio file loading
4848
* @throws UnsupportedAudioFileException when the loaded audio file is unsupported
4949
*/
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);
50+
public static void main(String[] args) throws UnsupportedAudioFileException, IOException, InterruptedException {
51+
Audio audio = Audio.getInstance();
52+
audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f);
53+
audio.playSound(audio.getAudioStream("./etc/Closed-Hi-Hat-1.wav"), -8.0f);
5354

5455
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();
56+
try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
57+
br.read();
58+
}
59+
audio.stopService();
5860
}
5961
}

event-queue/src/main/java/com/iluwatar/event/queue/Audio.java

+36-28
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@
2323

2424
package com.iluwatar.event.queue;
2525

26+
import org.slf4j.Logger;
27+
import org.slf4j.LoggerFactory;
28+
2629
import java.io.File;
2730
import java.io.IOException;
2831

@@ -38,49 +41,56 @@
3841
*
3942
*/
4043
public class Audio {
44+
private static final Logger LOGGER = LoggerFactory.getLogger(Audio.class);
45+
private static final Audio INSTANCE = new Audio();
4146

4247
private static final int MAX_PENDING = 16;
4348

44-
private static int headIndex;
49+
private int headIndex;
50+
51+
private int tailIndex;
4552

46-
private static int tailIndex;
53+
private volatile Thread updateThread = null;
4754

48-
private static Thread updateThread = null;
55+
private PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
4956

50-
private static PlayMessage[] pendingAudio = new PlayMessage[MAX_PENDING];
57+
// Visible only for testing purposes
58+
Audio() {
59+
60+
}
61+
62+
public static Audio getInstance() {
63+
return INSTANCE;
64+
}
5165

5266
/**
53-
* This method stops the Update Method's thread.
67+
* This method stops the Update Method's thread and waits till service stops.
5468
*/
55-
public static synchronized void stopService() {
69+
public synchronized void stopService() throws InterruptedException {
5670
if (updateThread != null) {
5771
updateThread.interrupt();
5872
}
73+
updateThread.join();
74+
updateThread = null;
5975
}
6076

6177
/**
6278
* This method check the Update Method's thread is started.
6379
* @return boolean
6480
*/
65-
public static synchronized boolean isServiceRunning() {
66-
if (updateThread != null && updateThread.isAlive() ) {
67-
return true;
68-
} else {
69-
return false;
70-
}
81+
public synchronized boolean isServiceRunning() {
82+
return updateThread != null && updateThread.isAlive();
7183
}
7284

7385
/**
7486
* Starts the thread for the Update Method pattern if it was not started previously.
7587
* Also when the thread is is ready initializes the indexes of the queue
7688
*/
77-
public static void init() {
89+
public void init() {
7890
if (updateThread == null) {
79-
updateThread = new Thread(new Runnable() {
80-
public void run() {
81-
while (!Thread.currentThread().isInterrupted()) {
82-
Audio.update();
83-
}
91+
updateThread = new Thread(() -> {
92+
while (!Thread.currentThread().isInterrupted()) {
93+
update();
8494
}
8595
});
8696
}
@@ -90,7 +100,7 @@ public void run() {
90100
/**
91101
* This is a synchronized thread starter
92102
*/
93-
public static synchronized void startThread() {
103+
private synchronized void startThread() {
94104
if (!updateThread.isAlive()) {
95105
updateThread.start();
96106
headIndex = 0;
@@ -103,7 +113,7 @@ public static synchronized void startThread() {
103113
* @param stream is the AudioInputStream for the method
104114
* @param volume is the level of the audio's volume
105115
*/
106-
public static void playSound(AudioInputStream stream, float volume) {
116+
public void playSound(AudioInputStream stream, float volume) {
107117
init();
108118
// Walk the pending requests.
109119
for (int i = headIndex; i != tailIndex; i = (i + 1) % MAX_PENDING) {
@@ -123,7 +133,7 @@ public static void playSound(AudioInputStream stream, float volume) {
123133
* This method uses the Update Method pattern.
124134
* It takes the audio from the queue and plays it
125135
*/
126-
public static void update() {
136+
private void update() {
127137
// If there are no pending requests, do nothing.
128138
if (headIndex == tailIndex) {
129139
return;
@@ -136,13 +146,11 @@ public static void update() {
136146
clip.open(audioStream);
137147
clip.start();
138148
} catch (LineUnavailableException e) {
139-
System.err.println("Error occoured while loading the audio: The line is unavailable");
140-
e.printStackTrace();
149+
LOGGER.trace("Error occoured while loading the audio: The line is unavailable", e);
141150
} catch (IOException e) {
142-
System.err.println("Input/Output error while loading the audio");
143-
e.printStackTrace();
151+
LOGGER.trace("Input/Output error while loading the audio", e);
144152
} catch (IllegalArgumentException e) {
145-
System.err.println("The system doesn't support the sound: " + e.getMessage());
153+
LOGGER.trace("The system doesn't support the sound: " + e.getMessage(), e);
146154
}
147155
}
148156

@@ -153,7 +161,7 @@ public static void update() {
153161
* @throws UnsupportedAudioFileException when the audio file is not supported
154162
* @throws IOException when the file is not readable
155163
*/
156-
public static AudioInputStream getAudioStream(String filePath)
164+
public AudioInputStream getAudioStream(String filePath)
157165
throws UnsupportedAudioFileException, IOException {
158166
return AudioSystem.getAudioInputStream(new File(filePath).getAbsoluteFile());
159167
}
@@ -162,7 +170,7 @@ public static AudioInputStream getAudioStream(String filePath)
162170
* Returns with the message array of the queue
163171
* @return PlayMessage[]
164172
*/
165-
public static PlayMessage[] getPendingAudio() {
173+
public PlayMessage[] getPendingAudio() {
166174
return pendingAudio;
167175
}
168176

event-queue/src/test/java/com/iluwatar/event/queue/AudioTest.java

+20-9
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
package com.iluwatar.event.queue;
2525

26+
import org.junit.jupiter.api.BeforeEach;
2627
import org.junit.jupiter.api.Test;
2728

2829
import javax.sound.sampled.UnsupportedAudioFileException;
@@ -39,6 +40,12 @@
3940
*/
4041
public class AudioTest {
4142

43+
private Audio audio;
44+
45+
@BeforeEach
46+
void createAudioInstance() {
47+
audio = new Audio();
48+
}
4249
/**
4350
* Test here that the playSound method works correctly
4451
* @throws UnsupportedAudioFileException when the audio file is not supported
@@ -47,13 +54,15 @@ public class AudioTest {
4754
*/
4855
@Test
4956
public void testPlaySound() throws UnsupportedAudioFileException, IOException, InterruptedException {
50-
Audio.playSound(Audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f);
57+
audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.wav"), -10.0f);
5158
// test that service is started
52-
assertTrue(Audio.isServiceRunning());
59+
assertTrue(audio.isServiceRunning());
5360
// adding a small pause to be sure that the sound is ended
5461
Thread.sleep(5000);
62+
63+
audio.stopService();
5564
// test that service is finished
56-
assertFalse(!Audio.isServiceRunning());
65+
assertFalse(audio.isServiceRunning());
5766
}
5867

5968
/**
@@ -64,16 +73,18 @@ public void testPlaySound() throws UnsupportedAudioFileException, IOException, I
6473
*/
6574
@Test
6675
public void testQueue() throws UnsupportedAudioFileException, IOException, InterruptedException {
67-
Audio.playSound(Audio.getAudioStream("./etc/Bass-Drum-1.aif"), -10.0f);
68-
Audio.playSound(Audio.getAudioStream("./etc/Bass-Drum-1.aif"), -10.0f);
69-
Audio.playSound(Audio.getAudioStream("./etc/Bass-Drum-1.aif"), -10.0f);
70-
assertTrue(Audio.getPendingAudio().length > 0);
76+
audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.aif"), -10.0f);
77+
audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.aif"), -10.0f);
78+
audio.playSound(audio.getAudioStream("./etc/Bass-Drum-1.aif"), -10.0f);
79+
assertTrue(audio.getPendingAudio().length > 0);
7180
// test that service is started
72-
assertTrue(Audio.isServiceRunning());
81+
assertTrue(audio.isServiceRunning());
7382
// adding a small pause to be sure that the sound is ended
7483
Thread.sleep(10000);
84+
85+
audio.stopService();
7586
// test that service is finished
76-
assertFalse(!Audio.isServiceRunning());
87+
assertFalse(audio.isServiceRunning());
7788
}
7889

7990
}

0 commit comments

Comments
 (0)