8058115: Some of MidiDeviceProviders do not follow the specification
Reviewed-by: prr, azvegint
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/AbstractMidiDeviceProvider.java Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2002, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2002, 2014, 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
@@ -117,7 +117,7 @@
}
}
-
+ @Override
public final MidiDevice.Info[] getDeviceInfo() {
readDeviceInfos();
Info[] infos = getInfoCache();
@@ -126,7 +126,7 @@
return localArray;
}
-
+ @Override
public final MidiDevice getDevice(MidiDevice.Info info) {
if (info instanceof Info) {
readDeviceInfos();
@@ -143,9 +143,7 @@
}
}
}
-
- throw new IllegalArgumentException("MidiDevice " + info.toString()
- + " not supported by this provider.");
+ throw MidiUtils.unsupportedDevice(info);
}
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/MidiUtils.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/MidiUtils.java Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -25,9 +25,15 @@
package com.sun.media.sound;
-import javax.sound.midi.*;
import java.util.ArrayList;
+import javax.sound.midi.MetaMessage;
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiEvent;
+import javax.sound.midi.MidiMessage;
+import javax.sound.midi.Sequence;
+import javax.sound.midi.Track;
+
// TODO:
// - define and use a global symbolic constant for 60000000 (see convertTempo)
@@ -48,6 +54,17 @@
private MidiUtils() {
}
+ /**
+ * Returns an exception which should be thrown if MidiDevice is unsupported.
+ *
+ * @param info an info object that describes the desired device
+ * @return an exception instance
+ */
+ static RuntimeException unsupportedDevice(final MidiDevice.Info info) {
+ return new IllegalArgumentException(String.format(
+ "MidiDevice %s not supported by this provider", info));
+ }
+
/** return true if the passed message is Meta End Of Track */
public static boolean isMetaEndOfTrack(MidiMessage midiMsg) {
// first check if it is a META message at all
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencer.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencer.java Tue Sep 30 17:39:04 2014 +0400
@@ -64,7 +64,7 @@
/**
* All RealTimeSequencers share this info object.
*/
- static final RealTimeSequencerInfo info = new RealTimeSequencerInfo();
+ static final MidiDevice.Info info = new RealTimeSequencerInfo();
private static final Sequencer.SyncMode[] masterSyncModes = { Sequencer.SyncMode.INTERNAL_CLOCK };
@@ -154,7 +154,7 @@
/* ****************************** CONSTRUCTOR ****************************** */
- RealTimeSequencer() throws MidiUnavailableException {
+ RealTimeSequencer(){
super(info);
if (Printer.trace) Printer.trace(">> RealTimeSequencer CONSTRUCTOR");
@@ -1088,7 +1088,7 @@
private static final String description = "Software sequencer";
private static final String version = "Version 1.0";
- private RealTimeSequencerInfo() {
+ RealTimeSequencerInfo() {
super(name, vendor, description, version);
}
} // class Info
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/RealTimeSequencerProvider.java Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2003, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -26,7 +26,6 @@
package com.sun.media.sound;
import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.spi.MidiDeviceProvider;
/**
@@ -36,23 +35,16 @@
*/
public final class RealTimeSequencerProvider extends MidiDeviceProvider {
-
+ @Override
public MidiDevice.Info[] getDeviceInfo() {
-
- MidiDevice.Info[] localArray = { RealTimeSequencer.info };
- return localArray;
+ return new MidiDevice.Info[]{RealTimeSequencer.info};
}
-
- public MidiDevice getDevice(MidiDevice.Info info) {
- if ((info != null) && (!info.equals(RealTimeSequencer.info))) {
- return null;
+ @Override
+ public MidiDevice getDevice(final MidiDevice.Info info) {
+ if (RealTimeSequencer.info.equals(info)) {
+ return new RealTimeSequencer();
}
-
- try {
- return new RealTimeSequencer();
- } catch (MidiUnavailableException e) {
- return null;
- }
+ throw MidiUtils.unsupportedDevice(info);
}
}
--- a/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/com/sun/media/sound/SoftProvider.java Tue Sep 30 17:39:04 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2007, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2007, 2014, 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
@@ -22,11 +22,10 @@
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
+
package com.sun.media.sound;
-import java.util.Arrays;
import javax.sound.midi.MidiDevice;
-import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.spi.MidiDeviceProvider;
/**
@@ -36,17 +35,16 @@
*/
public final class SoftProvider extends MidiDeviceProvider {
- static final Info softinfo = SoftSynthesizer.info;
- private static final Info[] softinfos = {softinfo};
-
+ @Override
public MidiDevice.Info[] getDeviceInfo() {
- return Arrays.copyOf(softinfos, softinfos.length);
+ return new MidiDevice.Info[]{SoftSynthesizer.info};
}
- public MidiDevice getDevice(MidiDevice.Info info) {
- if (info == softinfo) {
+ @Override
+ public MidiDevice getDevice(final MidiDevice.Info info) {
+ if (SoftSynthesizer.info.equals(info)) {
return new SoftSynthesizer();
}
- return null;
+ throw MidiUtils.unsupportedDevice(info);
}
}
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/MidiSystem.java Tue Sep 30 17:39:04 2014 +0400
@@ -31,6 +31,7 @@
import java.io.OutputStream;
import java.net.URL;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
@@ -162,18 +163,11 @@
* of length 0 is returned.
*/
public static MidiDevice.Info[] getMidiDeviceInfo() {
- List<MidiDevice.Info> allInfos = new ArrayList<>();
- List<MidiDeviceProvider> providers = getMidiDeviceProviders();
-
- for(int i = 0; i < providers.size(); i++) {
- MidiDeviceProvider provider = providers.get(i);
- MidiDevice.Info[] tmpinfo = provider.getDeviceInfo();
- for (int j = 0; j < tmpinfo.length; j++) {
- allInfos.add( tmpinfo[j] );
- }
+ final List<MidiDevice.Info> allInfos = new ArrayList<>();
+ for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
+ Collections.addAll(allInfos, provider.getDeviceInfo());
}
- MidiDevice.Info[] infosArray = allInfos.toArray(new MidiDevice.Info[0]);
- return infosArray;
+ return allInfos.toArray(new MidiDevice.Info[allInfos.size()]);
}
/**
@@ -187,17 +181,15 @@
* MIDI device installed on the system
* @see #getMidiDeviceInfo
*/
- public static MidiDevice getMidiDevice(MidiDevice.Info info) throws MidiUnavailableException {
- List<MidiDeviceProvider> providers = getMidiDeviceProviders();
-
- for(int i = 0; i < providers.size(); i++) {
- MidiDeviceProvider provider = providers.get(i);
+ public static MidiDevice getMidiDevice(final MidiDevice.Info info)
+ throws MidiUnavailableException {
+ for (final MidiDeviceProvider provider : getMidiDeviceProviders()) {
if (provider.isDeviceSupported(info)) {
- MidiDevice device = provider.getDevice(info);
- return device;
+ return provider.getDevice(info);
}
}
- throw new IllegalArgumentException("Requested device not installed: " + info);
+ throw new IllegalArgumentException(String.format(
+ "Requested device not installed: %s", info));
}
/**
--- a/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java Tue Sep 30 14:51:33 2014 +0400
+++ b/jdk/src/java.desktop/share/classes/javax/sound/midi/spi/MidiDeviceProvider.java Tue Sep 30 17:39:04 2014 +0400
@@ -25,6 +25,8 @@
package javax.sound.midi.spi;
+import java.util.Arrays;
+
import javax.sound.midi.MidiDevice;
/**
@@ -45,16 +47,8 @@
* @return {@code true} if the specified device is supported, otherwise
* {@code false}
*/
- public boolean isDeviceSupported(MidiDevice.Info info) {
-
- MidiDevice.Info infos[] = getDeviceInfo();
-
- for(int i=0; i<infos.length; i++) {
- if( info.equals( infos[i] ) ) {
- return true;
- }
- }
- return false;
+ public boolean isDeviceSupported(final MidiDevice.Info info) {
+ return Arrays.asList(getDeviceInfo()).contains(info);
}
/**
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/MidiDeviceProvider/NullInfo.java Tue Sep 30 17:39:04 2014 +0400
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2014, 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.util.Collection;
+import java.util.HashSet;
+
+import javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.MidiUnavailableException;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8058115
+ * @summary MidiDeviceProvider shouldn't returns incorrect results or throw NPE
+ * in case of null MidiDevice.Info
+ * @author Sergey Bylokhov
+ */
+public final class NullInfo {
+
+ public static void main(final String[] args) {
+ // MidiSystem API
+ try {
+ MidiSystem.getMidiDevice(null);
+ throw new RuntimeException("IllegalArgumentException expected");
+ } catch (final MidiUnavailableException e) {
+ throw new RuntimeException("IllegalArgumentException expected", e);
+ } catch (final IllegalArgumentException ignored) {
+ // expected
+ }
+ // MidiDeviceProvider API
+ final Collection<String> errors = new HashSet<>();
+ for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+ try {
+ if (mdp.isDeviceSupported(null)) {
+ throw new RuntimeException("null is supported");
+ }
+ final MidiDevice device = mdp.getDevice(null);
+ System.err.println("MidiDevice: " + device);
+ throw new RuntimeException("IllegalArgumentException expected");
+ } catch (final IllegalArgumentException e) {
+ errors.add(e.getMessage());
+ }
+ }
+ if (errors.size() != 1) {
+ throw new RuntimeException("Wrong number of messages:" + errors);
+ }
+ }
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/javax/sound/midi/MidiDeviceProvider/UnsupportedInfo.java Tue Sep 30 17:39:04 2014 +0400
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2014, 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 javax.sound.midi.MidiDevice;
+import javax.sound.midi.MidiSystem;
+import javax.sound.midi.spi.MidiDeviceProvider;
+
+import static java.util.ServiceLoader.load;
+
+/**
+ * @test
+ * @bug 8058115
+ * @summary MidiDeviceProvider shouldn't returns incorrect results in case of
+ * unsupported MidiDevice.Info
+ * @author Sergey Bylokhov
+ */
+public final class UnsupportedInfo {
+
+ public static void main(final String[] args) {
+ final MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
+ for (final MidiDeviceProvider mdp : load(MidiDeviceProvider.class)) {
+ for (final MidiDevice.Info info : infos) {
+ if (mdp.isDeviceSupported(info)) {
+ if (mdp.getDevice(info) == null) {
+ throw new RuntimeException("MidiDevice is null");
+ }
+ } else {
+ try {
+ mdp.getDevice(info);
+ throw new RuntimeException(
+ "IllegalArgumentException expected");
+ } catch (final IllegalArgumentException ignored) {
+ // expected
+ }
+ }
+ }
+ }
+ }
+}