JDK-8024061 : Exception thrown when drag and drop between two components is executed quickly
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 8
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: linux
  • CPU: generic
  • Submitted: 2013-08-30
  • Updated: 2014-12-09
  • Resolved: 2014-04-21
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 7 JDK 8 JDK 9
7u72Fixed 8u20Fixed 9 b15Fixed
Related Reports
Relates :  
Relates :  
Description
Dragging and dropping an object from one panel to another very quickly causes an exception. If the DnD operation is otherwise performed at a normal speed, the exception does not occur. It seems that the problem is a race condition between the XAWT and the EDT threads.

When the DnD operation is initiated, the SunDropTargetContextPeer.setCurrentJVMLocalSourceTransferable() is called to
set the Transferable involved in the DnD operation; that method stores it into a protected static variable. When the processEnterMessage() is called, the value of the static variable is copied into an instance variable called "local"; this instance variable is then set to null when the paired processExitMessage() is called. The static Transferable variable is finally set to null by the cleanup() method of the SunDragSourceContextPeer class.

What happens is that if the DnD operation is performed quickly relatively to the processing that the application has to do during the DnD session (grabbing an object from one view, dragging and dropping it on another view in the time frame of a second or less) the cleanup() method gets called before the processEnterMessage() relative to the target view is entered. Consequently, when the  processEnterMessage() executes, the static Transferable variable is null and so null becomes also the "local" variable. This makes the getTransferData() chain of calls follow a different path, which ends up with the exception:

java.lang.ClassCastException: DnD$DropObject cannot be cast to java.io.InputStream
        at sun.awt.datatransfer.DataTransferer.translateTransferable(DataTransferer.java:1186)
        at sun.awt.datatransfer.DataTransferer$6.run(DataTransferer.java:2145)
        at sun.awt.datatransfer.DataTransferer.processDataConversionRequests(DataTransferer.java:2202)
        at sun.awt.X11.XSelection.waitForSelectionNotify(XSelection.java:200)
        at sun.awt.X11.XSelection.getData(XSelection.java:340)
        at sun.awt.X11.XDnDDropTargetProtocol.getData(XDnDDropTargetProtocol.java:841)
        at sun.awt.X11.XDropTargetContextPeer.getNativeData(XDropTargetContextPeer.java:132)
        at sun.awt.dnd.SunDropTargetContextPeer.getTransferData(SunDropTargetContextPeer.java:245)
        at sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy.java:56)
        at java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTargetContext.java:359)
        at DnD$DnDPanel$3.dragEnter(DnD.java:232)
        at java.awt.dnd.DropTarget.dragEnter(DropTarget.java:341)
        at sun.awt.dnd.SunDropTargetContextPeer.processEnterMessage(SunDropTargetContextPeer.java:312)
        at sun.awt.X11.XDropTargetContextPeer.processEnterMessage(XDropTargetContextPeer.java:146)
        at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEnterEvent(SunDropTargetContextPeer.java:779)
        at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:747)
        at sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:30)
        at java.awt.Component.dispatchEventImpl(Component.java:4508)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4481)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577)
        at java.awt.LightweightDispatcher.trackMouseEnterExit(Container.java:4366)
        at java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:4304)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4163)
        at java.awt.Container.dispatchEventImpl(Container.java:2085)
        at java.awt.Window.dispatchEventImpl(Window.java:2478)
        at java.awt.Component.dispatchEvent(Component.java:4481)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:643)
        at java.awt.EventQueue.access$000(EventQueue.java:84)
        at java.awt.EventQueue$1.run(EventQueue.java:602)
        at java.awt.EventQueue$1.run(EventQueue.java:600)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:98)
        at java.awt.EventQueue$2.run(EventQueue.java:616)
        at java.awt.EventQueue$2.run(EventQueue.java:614)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:613)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

java.io.IOException: Owner failed to convert data
        at sun.awt.X11.XSelection.validateDataGetter(XSelection.java:486)
        at sun.awt.X11.XSelection.getData(XSelection.java:350)
        at sun.awt.X11.XDnDDropTargetProtocol.getData(XDnDDropTargetProtocol.java:841)
        at sun.awt.X11.XDropTargetContextPeer.getNativeData(XDropTargetContextPeer.java:132)
        at sun.awt.dnd.SunDropTargetContextPeer.getTransferData(SunDropTargetContextPeer.java:245)
        at sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy.java:56)
        at java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTargetContext.java:359)
        at DnD$DnDPanel$3.dragEnter(DnD.java:232)
        at java.awt.dnd.DropTarget.dragEnter(DropTarget.java:341)
        at sun.awt.dnd.SunDropTargetContextPeer.processEnterMessage(SunDropTargetContextPeer.java:312)
        at sun.awt.X11.XDropTargetContextPeer.processEnterMessage(XDropTargetContextPeer.java:146)
        at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEnterEvent(SunDropTargetContextPeer.java:779)
        at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:747)
        at sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:30)
        at java.awt.Component.dispatchEventImpl(Component.java:4508)
        at java.awt.Container.dispatchEventImpl(Container.java:2099)
        at java.awt.Component.dispatchEvent(Component.java:4481)
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4577)
        at java.awt.LightweightDispatcher.trackMouseEnterExit(Container.java:4366)
        at java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:4304)
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4163)
        at java.awt.Container.dispatchEventImpl(Container.java:2085)
        at java.awt.Window.dispatchEventImpl(Window.java:2478)
        at java.awt.Component.dispatchEvent(Component.java:4481)
        at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:643)
        at java.awt.EventQueue.access$000(EventQueue.java:84)
        at java.awt.EventQueue$1.run(EventQueue.java:602)
        at java.awt.EventQueue$1.run(EventQueue.java:600)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:98)
        at java.awt.EventQueue$2.run(EventQueue.java:616)
        at java.awt.EventQueue$2.run(EventQueue.java:614)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:613)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
        at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)


This issue occurs when the processing of the various listeners' calls involved is not trivial. I found that the most affecting one is the
dragGestureRecognized() of the DragGestureListener. I have then created a test case that introduces a delay in that method. The test java class, called DnD is attached to this report.

To run without delay execute:
java DnD

To run with dealy execute:
java DnD <millis>
where <millis> is an integer number specifying the delay affecting  dragGestureRecognized(). A value of 200 should make the exception occurr.

When the app is running try to drag and drop the blue square from the yellow panel to the pink one very quickly.
Comments
SQE OK to take it in PSU14_04
30-06-2014

Stand-alone application to reproduce the issue. Start the app. Drag and drop the cyan rectangle quickly from one panel to another. You will see the exception in the console, and the square stays in the source panel. If you wait for 1 second before dropping, the operation will succeed. There's an artificial delay in dragGestureRecognized to reproduce the issue easily. The problem could also be reproduced with less delay, 200-300 ms which is quite close to real-time app timings if the app has to prepare the data to transfer.
11-04-2014

The proposed fix addresses the thrown exception only. If the locally dragged object is already cleared and if the data flavor is DataFlavor.javaJVMLocalObjectMimeType, SunDropTargetContextPeer.getTransferData returns null as other options do not apply. This is all that can be done. Trying to fix this drag-n-drop issue properly requires substantial changes in AWT / XServer communication and is too risky.
10-04-2014

It's not quite thread racing however it's similar. Here's how it goes: 1. User presses left mouse button and starts dragging. 2. Drag gesture is recognized and drag is started. DragGestureListener.dragGestureRecognized executes, which starts drag on the dragSource. At this point a delay is added, and EDT stops processing events for a while. 3. While EDT sleeps, user moves mouse to the second panel and releases the left mouse button. At this stage DnD-related events are posted to the EDT but are not processed yet. At the same time no mouse-move or mouse-enter events are generated. When user releases mouse button, i.e. drops the dragged object, DragSourceDropEvent is generated. 4. When delay completes, EDT starts processing events and quickly processes all DragSourceDragEvent (DISPATCH_MOUSE_MOVED, dragSourceContext.dragMouseMoved). When it finds a DragSourceDropEvent (DISPATCH_FINISH, dragSourceContext.dragDropEnd), it clears the state of SunDropTargetContextPeer as if drag-and-drop operation finished. Since all the posted events are processed, mouse events are generated again. And MOUSE_ENTERED event is generated; as the result SunDropTargetContextPeer posts dispatchEnterEvent, and DropTarget.dragEnter is called. Since the peer context is cleared, dragEnter can't get the data from the transferable. 5. After several more MOUSE_DRAGGED events, MOUSE_EXIT event is generated which results in DropTargetListener.dragExit being called, and another cleanup of SunDropTargetContextPeer. Thus drag-and-drop depends on MOUSE_ENTER event which is generated by EDT. It can't be generated while there are events to process. If handling dragGestureRecognized() takes a while, MOUSE_ENTER doesn't get generated until it's too late. The exception is thrown because of an issue in DataFlavor implementation. The sample uses DataFlavor.javaJVMLocalObjectMimeType. It means the object is not serialized, it's just transferred within JVM. At the same time, when DataFlavor object is initialized, its representationClass field is set to the default value of java.io.InputStream.class. Because of the wrong order of events, the local Transferable object is cleared, and DataTransferer.translateTransferable follows another execution path. The call to flavor.isRepresentationClassInputStream() returns true and the code tries to cast the object to InputStream but fails. That's why the exception is thrown. In the correctly working case, at the step 4 after MOUSE_ENTERED event is processed, DragSourceDragEvent (DISPATCH_MOTION, dragSourceContext.dragOver) are generated instead of DragSourceDragEvent (DISPATCH_MOUSE_MOVED, dragSourceContext.dragMouseMoved).
10-04-2014