From a073dbf13b58a5786591e493d62408d17d6b1df4 Mon Sep 17 00:00:00 2001 From: Steven Barclay Date: Sun, 19 Jan 2020 22:06:00 +0000 Subject: [PATCH] Fix potential resource leak in AvoidStandbyModeService Replace tail recursion of the play() method with an ordinary loop, to prevent a new open JAR resource InputStream + sound file OutputStream (which were created every 4 minute playback) from accumulating on the stack, closing them inside the loop instead. (This also prevents eventual stack overflow.) Also tidy up FileUtil.resourceToFile and put the JAR URL InputStream in a try-with-resources block, to ensure that it doesn't leak either. --- .../java/bisq/common/storage/FileUtil.java | 19 +++-- .../core/app/AvoidStandbyModeService.java | 73 ++++++++----------- 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/common/src/main/java/bisq/common/storage/FileUtil.java b/common/src/main/java/bisq/common/storage/FileUtil.java index 9ae139ca26..89289d8a39 100644 --- a/common/src/main/java/bisq/common/storage/FileUtil.java +++ b/common/src/main/java/bisq/common/storage/FileUtil.java @@ -22,6 +22,7 @@ import bisq.common.util.Utilities; import com.google.common.io.Files; import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; import java.nio.file.Path; import java.nio.file.Paths; @@ -32,6 +33,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.Arrays; +import java.util.Comparator; import java.util.Date; import java.util.List; @@ -80,7 +82,7 @@ public class FileUtil { if (files != null) { List filesList = Arrays.asList(files); if (filesList.size() > numMaxBackupFiles) { - filesList.sort((o1, o2) -> o1.getName().compareTo(o2.getName())); + filesList.sort(Comparator.comparing(File::getName)); File file = filesList.get(0); if (file.isFile()) { if (!file.delete()) @@ -155,15 +157,12 @@ public class FileUtil { } public static void resourceToFile(String resourcePath, File destinationFile) throws ResourceNotFoundException, IOException { - InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath); - if (inputStream == null) - throw new ResourceNotFoundException(resourcePath); - - try (FileOutputStream fileOutputStream = new FileOutputStream(destinationFile)) { - byte[] buffer = new byte[1024]; - int bytesRead; - while ((bytesRead = inputStream.read(buffer)) != -1) { - fileOutputStream.write(buffer, 0, bytesRead); + try (InputStream inputStream = ClassLoader.getSystemClassLoader().getResourceAsStream(resourcePath)) { + if (inputStream == null) { + throw new ResourceNotFoundException(resourcePath); + } + try (FileOutputStream fileOutputStream = new FileOutputStream(destinationFile)) { + IOUtils.copy(inputStream, fileOutputStream); } } } diff --git a/core/src/main/java/bisq/core/app/AvoidStandbyModeService.java b/core/src/main/java/bisq/core/app/AvoidStandbyModeService.java index 17d47ebb85..365e0f9a70 100644 --- a/core/src/main/java/bisq/core/app/AvoidStandbyModeService.java +++ b/core/src/main/java/bisq/core/app/AvoidStandbyModeService.java @@ -20,17 +20,14 @@ package bisq.core.app; import bisq.core.user.Preferences; import bisq.common.config.Config; +import bisq.common.storage.FileUtil; +import bisq.common.storage.ResourceNotFoundException; import javax.inject.Inject; import javax.inject.Singleton; -import org.apache.commons.io.IOUtils; - import java.io.File; -import java.io.FileOutputStream; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; import lombok.extern.slf4j.Slf4j; @@ -40,6 +37,7 @@ import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.DataLine; +import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; @Slf4j @@ -81,45 +79,38 @@ public class AvoidStandbyModeService { private void play() { - if (!isStopped) { - OutputStream outputStream = null; - InputStream inputStream = null; - try { - inputStream = getClass().getClassLoader().getResourceAsStream("prevent-app-nap-silent-sound.aiff"); - File soundFile = new File(config.appDataDir, "prevent-app-nap-silent-sound.aiff"); - if (!soundFile.exists()) { - outputStream = new FileOutputStream(soundFile); - IOUtils.copy(inputStream, outputStream); - } - - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(soundFile); - byte tempBuffer[] = new byte[10000]; - AudioFormat audioFormat = audioInputStream.getFormat(); - DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat); - SourceDataLine sourceDataLine = (SourceDataLine) AudioSystem.getLine(dataLineInfo); - sourceDataLine.open(audioFormat); - sourceDataLine.start(); - int cnt; - while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1 && !isStopped) { - if (cnt > 0) { - sourceDataLine.write(tempBuffer, 0, cnt); + try { + while (!isStopped) { + try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(getSoundFile()); + SourceDataLine sourceDataLine = getSourceDataLine(audioInputStream.getFormat())) { + byte[] tempBuffer = new byte[10000]; + sourceDataLine.open(audioInputStream.getFormat()); + sourceDataLine.start(); + int cnt; + while ((cnt = audioInputStream.read(tempBuffer, 0, tempBuffer.length)) != -1 && !isStopped) { + if (cnt > 0) { + sourceDataLine.write(tempBuffer, 0, cnt); + } } - } - sourceDataLine.drain(); - sourceDataLine.close(); - play(); - } catch (Exception e) { - log.error(e.toString()); - e.printStackTrace(); - } finally { - try { - if (inputStream != null) - inputStream.close(); - if (outputStream != null) - outputStream.close(); - } catch (IOException ignore) { + sourceDataLine.drain(); } } + } catch (Exception e) { + log.error(e.toString()); + e.printStackTrace(); } } + + private File getSoundFile() throws IOException, ResourceNotFoundException { + File soundFile = new File(config.appDataDir, "prevent-app-nap-silent-sound.aiff"); + if (!soundFile.exists()) { + FileUtil.resourceToFile("prevent-app-nap-silent-sound.aiff", soundFile); + } + return soundFile; + } + + private SourceDataLine getSourceDataLine(AudioFormat audioFormat) throws LineUnavailableException { + DataLine.Info dataLineInfo = new DataLine.Info(SourceDataLine.class, audioFormat); + return (SourceDataLine) AudioSystem.getLine(dataLineInfo); + } }