8131974: AudioFileReader incorrectly handle EOFException
authorserb
Fri, 01 Jan 2016 18:33:53 +0300
changeset 35683 d8a9be6bd7db
parent 35682 5861cf47d8c4
child 35684 900e28dae561
8131974: AudioFileReader incorrectly handle EOFException Reviewed-by: amenkov
jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java
jdk/src/java.desktop/share/classes/com/sun/media/sound/SunFileReader.java
jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java
jdk/test/javax/sound/sampled/spi/AudioFileReader/ShortHeader.java
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java	Wed Jan 20 15:10:25 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftMidiAudioFileReader.java	Fri Jan 01 18:33:53 2016 +0300
@@ -25,6 +25,7 @@
 
 package com.sun.media.sound;
 
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 
@@ -131,7 +132,8 @@
         stream.mark(200);
         try {
             return getAudioInputStream(MidiSystem.getSequence(stream));
-        } catch (final InvalidMidiDataException ignored) {
+        } catch (InvalidMidiDataException | EOFException ignored) {
+            // stream is unsupported or the header is less than was expected
             stream.reset();
             throw new UnsupportedAudioFileException();
         }
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SunFileReader.java	Wed Jan 20 15:10:25 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SunFileReader.java	Fri Jan 01 18:33:53 2016 +0300
@@ -27,6 +27,7 @@
 
 import java.io.BufferedInputStream;
 import java.io.DataInputStream;
+import java.io.EOFException;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
@@ -51,6 +52,9 @@
         stream.mark(200); // The biggest value which was historically used
         try {
             return getAudioFileFormatImpl(stream);
+        } catch (final EOFException ignored) {
+            // the header is less than was expected
+            throw new UnsupportedAudioFileException();
         } finally {
             stream.reset();
         }
@@ -61,6 +65,9 @@
             throws UnsupportedAudioFileException, IOException {
         try (InputStream is = url.openStream()) {
             return getAudioFileFormatImpl(new BufferedInputStream(is));
+        } catch (final EOFException ignored) {
+            // the header is less than was expected
+            throw new UnsupportedAudioFileException();
         }
     }
 
@@ -69,6 +76,9 @@
             throws UnsupportedAudioFileException, IOException {
         try (InputStream is = new FileInputStream(file)) {
             return getAudioFileFormatImpl(new BufferedInputStream(is));
+        } catch (final EOFException ignored) {
+            // the header is less than was expected
+            throw new UnsupportedAudioFileException();
         }
     }
 
@@ -82,9 +92,10 @@
             // beginning of the audio data, so return an AudioInputStream
             return new AudioInputStream(stream, fileFormat.getFormat(),
                                         fileFormat.getFrameLength());
-        } catch (final UnsupportedAudioFileException e) {
+        } catch (UnsupportedAudioFileException | EOFException ignored) {
+            // stream is unsupported or the header is less than was expected
             stream.reset();
-            throw e;
+            throw new UnsupportedAudioFileException();
         }
     }
 
@@ -125,6 +136,9 @@
      * @throws UnsupportedAudioFileException if the stream does not point to
      *         valid audio file data recognized by the system
      * @throws IOException if an I/O exception occurs
+     * @throws EOFException is used incorrectly by our readers instead of
+     *         UnsupportedAudioFileException if the header is less than was
+     *         expected
      */
     abstract AudioFileFormat getAudioFileFormatImpl(InputStream stream)
             throws UnsupportedAudioFileException, IOException;
--- a/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java	Wed Jan 20 15:10:25 2016 +0300
+++ b/jdk/src/java.desktop/share/classes/javax/sound/sampled/AudioSystem.java	Fri Jan 01 18:33:53 2016 +0300
@@ -25,6 +25,7 @@
 
 package javax.sound.sampled;
 
+import java.io.EOFException;
 import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
@@ -919,28 +920,17 @@
      * @see InputStream#markSupported
      * @see InputStream#mark
      */
-    public static AudioFileFormat getAudioFileFormat(InputStream stream)
+    public static AudioFileFormat getAudioFileFormat(final InputStream stream)
             throws UnsupportedAudioFileException, IOException {
         Objects.requireNonNull(stream);
 
-        List<AudioFileReader> providers = getAudioFileReaders();
-        AudioFileFormat format = null;
-
-        for(int i = 0; i < providers.size(); i++ ) {
-            AudioFileReader reader = providers.get(i);
+        for (final AudioFileReader reader : getAudioFileReaders()) {
             try {
-                format = reader.getAudioFileFormat( stream ); // throws IOException
-                break;
-            } catch (UnsupportedAudioFileException e) {
-                continue;
+                return reader.getAudioFileFormat(stream);
+            } catch (final UnsupportedAudioFileException ignored) {
             }
         }
-
-        if( format==null ) {
-            throw new UnsupportedAudioFileException("file is not a supported file type");
-        } else {
-            return format;
-        }
+        throw new UnsupportedAudioFileException("Stream of unsupported format");
     }
 
     /**
@@ -956,28 +946,17 @@
      * @throws IOException if an input/output exception occurs
      * @throws NullPointerException if {@code url} is {@code null}
      */
-    public static AudioFileFormat getAudioFileFormat(URL url)
+    public static AudioFileFormat getAudioFileFormat(final URL url)
             throws UnsupportedAudioFileException, IOException {
         Objects.requireNonNull(url);
 
-        List<AudioFileReader> providers = getAudioFileReaders();
-        AudioFileFormat format = null;
-
-        for(int i = 0; i < providers.size(); i++ ) {
-            AudioFileReader reader = providers.get(i);
+        for (final AudioFileReader reader : getAudioFileReaders()) {
             try {
-                format = reader.getAudioFileFormat( url ); // throws IOException
-                break;
-            } catch (UnsupportedAudioFileException e) {
-                continue;
+                return reader.getAudioFileFormat(url);
+            } catch (final UnsupportedAudioFileException ignored) {
             }
         }
-
-        if( format==null ) {
-            throw new UnsupportedAudioFileException("file is not a supported file type");
-        } else {
-            return format;
-        }
+        throw new UnsupportedAudioFileException("URL of unsupported format");
     }
 
     /**
@@ -993,28 +972,17 @@
      * @throws IOException if an I/O exception occurs
      * @throws NullPointerException if {@code file} is {@code null}
      */
-    public static AudioFileFormat getAudioFileFormat(File file)
+    public static AudioFileFormat getAudioFileFormat(final File file)
             throws UnsupportedAudioFileException, IOException {
         Objects.requireNonNull(file);
 
-        List<AudioFileReader> providers = getAudioFileReaders();
-        AudioFileFormat format = null;
-
-        for(int i = 0; i < providers.size(); i++ ) {
-            AudioFileReader reader = providers.get(i);
+        for (final AudioFileReader reader : getAudioFileReaders()) {
             try {
-                format = reader.getAudioFileFormat( file ); // throws IOException
-                break;
-            } catch (UnsupportedAudioFileException e) {
-                continue;
+                return reader.getAudioFileFormat(file);
+            } catch (final UnsupportedAudioFileException ignored) {
             }
         }
-
-        if( format==null ) {
-            throw new UnsupportedAudioFileException("file is not a supported file type");
-        } else {
-            return format;
-        }
+        throw new UnsupportedAudioFileException("File of unsupported format");
     }
 
     /**
@@ -1038,28 +1006,17 @@
      * @see InputStream#markSupported
      * @see InputStream#mark
      */
-    public static AudioInputStream getAudioInputStream(InputStream stream)
+    public static AudioInputStream getAudioInputStream(final InputStream stream)
             throws UnsupportedAudioFileException, IOException {
         Objects.requireNonNull(stream);
 
-        List<AudioFileReader> providers = getAudioFileReaders();
-        AudioInputStream audioStream = null;
-
-        for(int i = 0; i < providers.size(); i++ ) {
-            AudioFileReader reader = providers.get(i);
+        for (final AudioFileReader reader : getAudioFileReaders()) {
             try {
-                audioStream = reader.getAudioInputStream( stream ); // throws IOException
-                break;
-            } catch (UnsupportedAudioFileException e) {
-                continue;
+                return reader.getAudioInputStream(stream);
+            } catch (final UnsupportedAudioFileException ignored) {
             }
         }
-
-        if( audioStream==null ) {
-            throw new UnsupportedAudioFileException("could not get audio input stream from input stream");
-        } else {
-            return audioStream;
-        }
+        throw new UnsupportedAudioFileException("Stream of unsupported format");
     }
 
     /**
@@ -1075,28 +1032,17 @@
      * @throws IOException if an I/O exception occurs
      * @throws NullPointerException if {@code url} is {@code null}
      */
-    public static AudioInputStream getAudioInputStream(URL url)
+    public static AudioInputStream getAudioInputStream(final URL url)
             throws UnsupportedAudioFileException, IOException {
         Objects.requireNonNull(url);
 
-        List<AudioFileReader> providers = getAudioFileReaders();
-        AudioInputStream audioStream = null;
-
-        for(int i = 0; i < providers.size(); i++ ) {
-            AudioFileReader reader = providers.get(i);
+        for (final AudioFileReader reader : getAudioFileReaders()) {
             try {
-                audioStream = reader.getAudioInputStream( url ); // throws IOException
-                break;
-            } catch (UnsupportedAudioFileException e) {
-                continue;
+                return reader.getAudioInputStream(url);
+            } catch (final UnsupportedAudioFileException ignored) {
             }
         }
-
-        if( audioStream==null ) {
-            throw new UnsupportedAudioFileException("could not get audio input stream from input URL");
-        } else {
-            return audioStream;
-        }
+        throw new UnsupportedAudioFileException("URL of unsupported format");
     }
 
     /**
@@ -1112,28 +1058,17 @@
      * @throws IOException if an I/O exception occurs
      * @throws NullPointerException if {@code file} is {@code null}
      */
-    public static AudioInputStream getAudioInputStream(File file)
+    public static AudioInputStream getAudioInputStream(final File file)
             throws UnsupportedAudioFileException, IOException {
         Objects.requireNonNull(file);
 
-        List<AudioFileReader> providers = getAudioFileReaders();
-        AudioInputStream audioStream = null;
-
-        for(int i = 0; i < providers.size(); i++ ) {
-            AudioFileReader reader = providers.get(i);
+        for (final AudioFileReader reader : getAudioFileReaders()) {
             try {
-                audioStream = reader.getAudioInputStream( file ); // throws IOException
-                break;
-            } catch (UnsupportedAudioFileException e) {
-                continue;
+                return reader.getAudioInputStream(file);
+            } catch (final UnsupportedAudioFileException ignored) {
             }
         }
-
-        if( audioStream==null ) {
-            throw new UnsupportedAudioFileException("could not get audio input stream from input file");
-        } else {
-            return audioStream;
-        }
+        throw new UnsupportedAudioFileException("File of unsupported format");
     }
 
     /**
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/sampled/spi/AudioFileReader/ShortHeader.java	Fri Jan 01 18:33:53 2016 +0300
@@ -0,0 +1,179 @@
+/*
+ * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URL;
+import java.nio.file.Files;
+import java.util.Arrays;
+
+import javax.sound.sampled.AudioSystem;
+import javax.sound.sampled.UnsupportedAudioFileException;
+import javax.sound.sampled.spi.AudioFileReader;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8131974
+ * @summary Short files should be reported as unsupported
+ */
+public final class ShortHeader {
+
+    private static byte[] W = {-12, 3, 45};
+
+    private static byte[] R = new byte[3];
+
+    public static void main(final String[] args) throws Exception {
+        final File file = Files.createTempFile("audio", "test").toFile();
+        file.deleteOnExit();
+        try (final OutputStream fos = new FileOutputStream(file)) {
+            fos.write(W);
+        }
+
+        testAS(file);
+        for (final AudioFileReader afr : load(AudioFileReader.class)) {
+            testAFR(afr, file);
+        }
+    }
+
+    /**
+     * Tests the part of AudioSystem API, which implemented via
+     * AudioFileReader.
+     *
+     * @see AudioSystem#getAudioFileFormat(InputStream)
+     * @see AudioSystem#getAudioFileFormat(File)
+     * @see AudioSystem#getAudioFileFormat(URL)
+     * @see AudioSystem#getAudioInputStream(InputStream)
+     * @see AudioSystem#getAudioInputStream(File)
+     * @see AudioSystem#getAudioInputStream(URL)
+     */
+    private static void testAS(final File file) throws IOException {
+        try {
+            AudioSystem.getAudioFileFormat(file);
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+        try {
+            AudioSystem.getAudioFileFormat(file.toURL());
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+        try {
+            AudioSystem.getAudioInputStream(file);
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+        try {
+            AudioSystem.getAudioInputStream(file.toURL());
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+
+        //  AudioSystem.getAudioXXX(stream) should properly reset the stream
+
+        try (FileInputStream fis = new FileInputStream(file);
+             InputStream stream = new BufferedInputStream(fis)) {
+
+            try {
+                AudioSystem.getAudioFileFormat(stream);
+                throw new RuntimeException();
+            } catch (final UnsupportedAudioFileException ignored) {
+            }
+            try {
+                AudioSystem.getAudioInputStream(stream);
+                throw new RuntimeException();
+            } catch (final UnsupportedAudioFileException ignored) {
+            }
+            stream.read(R, 0, R.length);
+        }
+
+        if (!Arrays.equals(R, W)) {
+            System.err.println("Expected = " + Arrays.toString(W));
+            System.err.println("Actual = " + Arrays.toString(R));
+            throw new RuntimeException();
+        }
+    }
+    /**
+     * Tests the AudioFileReader API directly.
+     *
+     * @see AudioFileReader#getAudioFileFormat(InputStream)
+     * @see AudioFileReader#getAudioFileFormat(File)
+     * @see AudioFileReader#getAudioFileFormat(URL)
+     * @see AudioFileReader#getAudioInputStream(InputStream)
+     * @see AudioFileReader#getAudioInputStream(File)
+     * @see AudioFileReader#getAudioInputStream(URL)
+     */
+    private static void testAFR(final AudioFileReader fcp, final File file)
+            throws Exception {
+        try {
+            fcp.getAudioFileFormat(file);
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+        try {
+            fcp.getAudioFileFormat(file.toURL());
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+        try {
+            fcp.getAudioInputStream(file);
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+        try {
+            fcp.getAudioInputStream(file.toURL());
+            throw new RuntimeException();
+        } catch (final UnsupportedAudioFileException ignored) {
+        }
+
+        // AudioFileReader should properly reset the stream
+
+        try (FileInputStream fis = new FileInputStream(file);
+             InputStream stream = new BufferedInputStream(fis)) {
+
+            try {
+                fcp.getAudioFileFormat(stream);
+                throw new RuntimeException();
+            } catch (final UnsupportedAudioFileException ignored) {
+            }
+            try {
+                fcp.getAudioInputStream(stream);
+                throw new RuntimeException();
+            } catch (final UnsupportedAudioFileException ignored) {
+            }
+            stream.read(R, 0, R.length);
+        }
+
+        if (!Arrays.equals(R, W)) {
+            System.err.println("Expected = " + Arrays.toString(W));
+            System.err.println("Actual = " + Arrays.toString(R));
+            throw new RuntimeException();
+        }
+    }
+}