JDK-8078607 : DataTransferer.DataFlavorComparator violates its general contract
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7u80,8u40,9
  • Priority: P3
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_7
  • CPU: x86_64
  • Submitted: 2015-04-24
  • Updated: 2015-09-14
  • Resolved: 2015-09-14
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Resolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b14)
Java HotSpot(TM) Client VM (build 25.45-b02, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.1.7601]

A DESCRIPTION OF THE PROBLEM :
When pasting an item into a Java application, SunClipboard eventually calls the method "DataTransferer.setToSortedDataFlavorArray()". This ultimately calls Array.sort on a DataFlavor set using a DataFlavor.DataFlavorComparator.

DataFlavor.DataFlavorComparator is passed to java.util.TimSort, which, depending on the data being sorted, sometimes throws the exception "java.lang.IllegalArgumentException: Comparison method violates its general contract!". TimSort does a check when sorting to verify if the comparator given to it is valid. With some DataFlavor sets, this check will fail.

It appears DataFlavor.DataFlavorComparator does not correctly honor the requirements that Comparator mandates for the .compare method.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Copy and paste data into a Java application. The bug is dependent on the order of the DataFlavor(s) (which is in a HashSet) and the type of DataFlavor(s), and there must be at least 32 DataFlavor(s) in order for TimSort to be used, so it's extremely difficult to reproduce in a normal application.


ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.lang.IllegalArgumentException: Comparison method violates its general contract!
            at java.util.TimSort.mergeHi(Unknown Source)
            at java.util.TimSort.mergeAt(Unknown Source)
            at java.util.TimSort.mergeForceCollapse(Unknown Source)
            at java.util.TimSort.sort(Unknown Source)
            at java.util.Arrays.sort(Unknown Source)
            at sun.awt.datatransfer.DataTransferer.setToSortedDataFlavorArray(Unknown Source)
            at sun.awt.datatransfer.ClipboardTransferable.<init>(Unknown Source)
            at sun.awt.datatransfer.SunClipboard.getContents(Unknown Source)
            at javax.swing.TransferHandler$TransferAction.actionPerformedImpl(Unknown Source)
            at javax.swing.TransferHandler$TransferAction.access$800(Unknown Source)
            at javax.swing.TransferHandler$TransferAction$1.run(Unknown Source)
            at javax.swing.TransferHandler$TransferAction$1.run(Unknown Source)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
            at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
            at javax.swing.TransferHandler$TransferAction$2.run(Unknown Source)
            at javax.swing.TransferHandler$TransferAction$2.run(Unknown Source)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
            at javax.swing.TransferHandler$TransferAction.actionPerformed(Unknown Source)
            at javax.swing.text.JTextComponent.invokeAction(Unknown Source)
            at javax.swing.text.JTextComponent.paste(Unknown Source)
            at javax.swing.text.DefaultEditorKit$PasteAction.actionPerformed(Unknown Source)
            at javax.swing.SwingUtilities.notifyAction(Unknown Source)
            at javax.swing.JComponent.processKeyBinding(Unknown Source)
            at javax.swing.JComponent.processKeyBindings(Unknown Source)
            at javax.swing.JComponent.processKeyEvent(Unknown Source)
            at java.awt.Component.processEvent(Unknown Source)
            at java.awt.Container.processEvent(Unknown Source)
            at java.awt.Component.dispatchEventImpl(Unknown Source)
            at java.awt.Container.dispatchEventImpl(Unknown Source)
            at java.awt.Component.dispatchEvent(Unknown Source)
            at java.awt.KeyboardFocusManager.redispatchEvent(Unknown Source)
            at java.awt.DefaultKeyboardFocusManager.dispatchKeyEvent(Unknown Source)
            at jSSS.jgui.KeyEventManager.dispatchKeyEvent(KeyEventManager.java:132)
            at java.awt.DefaultKeyboardFocusManager.preDispatchKeyEvent(Unknown Source)
            at java.awt.DefaultKeyboardFocusManager.typeAheadAssertions(Unknown Source)
            at java.awt.DefaultKeyboardFocusManager.dispatchEvent(Unknown Source)
            at jSSS.jgui.KeyEventManager.dispatchEvent(KeyEventManager.java:455)
            at java.awt.Component.dispatchEventImpl(Unknown Source)
            at java.awt.Container.dispatchEventImpl(Unknown Source)
            at java.awt.Window.dispatchEventImpl(Unknown Source)
            at java.awt.Component.dispatchEvent(Unknown Source)
            at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
            at java.awt.EventQueue.access$500(Unknown Source)
            at java.awt.EventQueue$3.run(Unknown Source)
            at java.awt.EventQueue$3.run(Unknown Source)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
            at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
            at java.awt.EventQueue$4.run(Unknown Source)
            at java.awt.EventQueue$4.run(Unknown Source)
            at java.security.AccessController.doPrivileged(Native Method)
            at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
            at java.awt.EventQueue.dispatchEvent(Unknown Source)
            at jSSS.jgui.sssworker.SSSMouseBlocker.dispatchEvent(SSSMouseBlocker.java:105)
            at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
            at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
            at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
            at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
            at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
            at java.awt.EventDispatchThread.run(Unknown Source)


REPRODUCIBILITY :
This bug can be reproduced rarely.

---------- BEGIN SOURCE ----------
import sun.awt.datatransfer.DataTransferer;

import java.awt.datatransfer.DataFlavor;
import java.util.LinkedHashSet;
import java.util.Set;

public class Test {
	public static void main(String args[]) throws ClassNotFoundException {
		// If the set is smaller than TimSort.MIN_MERGE (32), TimSort will use a binary sort instead which won't
		// enter the code branch that we want.
		String[] mimes = new String[]{
				"text/plain; class=java.lang.Object; charset=unicode",
				"text/plain; class=java.io.InputStream; charset=unicode",
				"text/html; document=fragment; class=java.nio.ByteBuffer; charset=windows-1252",
				"text/html; document=all; class=java.nio.ByteBuffer; charset=ISO-8859-1",
				"text/html; document=all; class=\"[B\"; charset=UTF-16",
				"application/x-java-serialized-object; class=java.lang.String",
				"application/unknown; class=java.lang.Object",
				"text/html; document=selection; class=java.io.InputStream; charset=UTF-16BE",
				"text/html; document=selection; class=\"[B\"; charset=UTF-16BE",
				"text/plain; class=java.io.InputStream; charset=UTF-8",
				"text/html; document=fragment; class=java.io.InputStream; charset=US-ASCII",
				"text/plain; class=java.nio.ByteBuffer; charset=UTF-16LE",
				"text/plain; class=\"[B\"; charset=ISO-8859-1",
				"text/html; document=all; class=java.io.InputStream; charset=UTF-16BE",
				"text/html; document=all; class=java.nio.ByteBuffer; charset=UTF-16BE",
				"text/unknown; class=java.lang.String",
				"text/plain; class=java.lang.Object",
				"text/html; document=all; class=java.nio.ByteBuffer; charset=US-ASCII",
				"text/html; document=fragment; class=java.io.InputStream; charset=windows-1252",
				"text/html; class=java.lang.Object; charset=unicode",
				"text/html; document=all; class=java.nio.ByteBuffer; charset=UTF-8",
				"unknown/flavor; class=java.lang.Object",
				"text/html; document=fragment; class=java.io.InputStream; charset=UTF-8",
				"text/html; document=fragment; class=java.io.InputStream; charset=ISO-8859-1",
				"text/plain; class=java.io.Reader",
				"text/html; document=selection; class=java.io.InputStream; charset=UTF-16LE",
				"text/html; document=selection; class=java.nio.ByteBuffer; charset=windows-1252",
				"text/html; document=all; class=\"[B\"; charset=US-ASCII",
				"text/html; document=all; class=\"[B\"; charset=UTF-16LE",
				"text/html; document=fragment; class=java.lang.String; charset=Unicode",
				"text/plain; class=\"[C\"; charset=Unicode",
				"text/html; document=fragment; class=java.io.InputStream; charset=UTF-16LE"
		};

		// We have to use a LinkedHashSet as this bug is dependent on the order of the entries in the set.
		// Normally SunClipboard uses a HashSet which makes it difficult to replicate the test reliably.
		final Set<DataFlavor> flavorSet = new LinkedHashSet<>(32);
		for (String mime : mimes) {
			flavorSet.add(new DataFlavor(mime));
		}

		// This will throw an IllegalArgumentException.
		DataTransferer.setToSortedDataFlavorArray(flavorSet);
	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
-Djava.util.Arrays.useLegacyMergeSort=true as a JVM parameter will use the legacy sort and avoids the TimSort which performs this validation.


Comments
RULE closed/java/awt/Clipboard/NoOwnerNoTargetsTest/NoOwnerNoTargetsTest Exception java.lang.IllegalArgumentException: Comparison method violates its general contract!
29-07-2015

RULE closed/java/awt/Clipboard/AppletReloadClipboardTest/AppletReloadClipboardTest Exception java.lang.IllegalArgumentException: Comparison method violates its general contract! RULE closed/java/awt/Clipboard/HTMLTransferTest/HTMLTransferTest Exception java.lang.IllegalArgumentException: Comparison method violates its general contract! RULE closed/java/awt/Clipboard/ImageTransferTest/ImageTransferTest Exception java.lang.IllegalArgumentException: Comparison method violates its general contract! RULE java/awt/datatransfer/Clipboard/GetContentsInterruptedTest.java Exception java.lang.IllegalArgumentException: Comparison method violates its general contract! RULE java/awt/datatransfer/ClipboardInterVMTest/ClipboardInterVMTest.java Exception java.lang.IllegalArgumentException: Comparison method violates its general contract!
22-06-2015