JDK-8146900 : [SwingNode, DND]: drag-and-drop from JTable not working, drag recognized twice
  • Type: Bug
  • Component: javafx
  • Sub-Component: swing
  • Affected Version: 8u60,9
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_8
  • CPU: x86
  • Submitted: 2016-01-08
  • Updated: 2018-09-06
  • Resolved: 2017-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.
Other
tbdResolved
Related Reports
Duplicate :  
Duplicate :  
Relates :  
Description
FULL PRODUCT VERSION :
java version "1.8.0_60"
Java(TM) SE Runtime Environment (build 1.8.0_60-b27)
Java HotSpot(TM) Client VM (build 25.60-b23, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Windows 8 64-bit with JDK8u60 32-bit

A DESCRIPTION OF THE PROBLEM :
We are creating a simple javafx Stage and Scene, placing a SwingNode within and embedding a Java Swing JTable. Then we attach DragGestureListener and DropTargetListener. 

When you start the example you see a window with that one table. Click on a row and drag and you get InvalidDndOperationException. The reason is that the DragGestureListener is invoked a second time with "dragGestureRecognized" while it is already in startDrag.

You can start the attached example also with the parameter "swing" and then the same JTable setup is started without JavaFX Stage but with a JFrame. In this case the code behaves as expected. while in startDrag no second dragGesture is recognized.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
see example code

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
no exception
ACTUAL -
InvalidDndOperationException

ERROR MESSAGES/STACK TRACES THAT OCCUR :
sun.awt.windows.WMouseDragGestureRecognizer@185ced1
java.awt.dnd.DropTarget@91d3f2
drag recognized...
drag recognized...
Exception in thread "AWT-EventQueue-0" java.awt.dnd.InvalidDnDOperationException: Drag and drop in progress
	at sun.awt.dnd.SunDragSourceContextPeer.setDragDropInProgress(SunDragSourceContextPeer.java:358)
	at java.awt.dnd.DragSource.startDrag(DragSource.java:301)
	at java.awt.dnd.DragSource.startDrag(DragSource.java:426)
	at java.awt.dnd.DragGestureEvent.startDrag(DragGestureEvent.java:238)
	at testfx.openjdk.bugreports.submit.SwingFXBug1a$3.dragGestureRecognized(SwingFXBug1a.java:105)
	at java.awt.dnd.DragGestureRecognizer.fireDragGestureRecognized(DragGestureRecognizer.java:361)
	at sun.awt.windows.WMouseDragGestureRecognizer.mouseDragged(WMouseDragGestureRecognizer.java:218)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:320)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:319)
	at java.awt.Component.processMouseMotionEvent(Component.java:6583)
	at javax.swing.JComponent.processMouseMotionEvent(JComponent.java:3342)
	at java.awt.Component.processEvent(Component.java:6304)
	at java.awt.Container.processEvent(Container.java:2236)
	at java.awt.Component.dispatchEventImpl(Component.java:4891)
	at java.awt.Container.dispatchEventImpl(Container.java:2294)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4542)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
	at java.awt.Container.dispatchEventImpl(Container.java:2280)
	at java.awt.Window.dispatchEventImpl(Window.java:2750)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.awt.EventQueue$4.run(EventQueue.java:729)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.WaitDispatchSupport$2.run(WaitDispatchSupport.java:182)
	at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:229)
	at java.awt.WaitDispatchSupport$4.run(WaitDispatchSupport.java:227)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.awt.WaitDispatchSupport.enter(WaitDispatchSupport.java:227)
	at javafx.embed.swing.FXDnD$FXDragSourceContextPeer.startDrag(FXDnD.java:343)
	at sun.awt.dnd.SunDragSourceContextPeer.startDrag(SunDragSourceContextPeer.java:135)
	at java.awt.dnd.DragSource.startDrag(DragSource.java:321)
	at java.awt.dnd.DragSource.startDrag(DragSource.java:426)
	at java.awt.dnd.DragGestureEvent.startDrag(DragGestureEvent.java:238)
	at testfx.openjdk.bugreports.submit.SwingFXBug1a$3.dragGestureRecognized(SwingFXBug1a.java:105)
	at java.awt.dnd.DragGestureRecognizer.fireDragGestureRecognized(DragGestureRecognizer.java:361)
	at sun.awt.windows.WMouseDragGestureRecognizer.mouseDragged(WMouseDragGestureRecognizer.java:218)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:320)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:319)
	at java.awt.Component.processMouseMotionEvent(Component.java:6583)
	at javax.swing.JComponent.processMouseMotionEvent(JComponent.java:3342)
	at java.awt.Component.processEvent(Component.java:6304)
	at java.awt.Container.processEvent(Container.java:2236)
	at java.awt.Component.dispatchEventImpl(Component.java:4891)
	at java.awt.Container.dispatchEventImpl(Container.java:2294)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4888)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:4542)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:4466)
	at java.awt.Container.dispatchEventImpl(Container.java:2280)
	at java.awt.Window.dispatchEventImpl(Window.java:2750)
	at java.awt.Component.dispatchEvent(Component.java:4713)
	at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:758)
	at java.awt.EventQueue.access$500(EventQueue.java:97)
	at java.awt.EventQueue$3.run(EventQueue.java:709)
	at java.awt.EventQueue$3.run(EventQueue.java:703)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:86)
	at java.awt.EventQueue$4.run(EventQueue.java:731)
	at java.awt.EventQueue$4.run(EventQueue.java:729)
	at java.security.AccessController.doPrivileged(Native Method)
	at java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:76)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:728)
	at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:116)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:105)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:93)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:82)


REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.datatransfer.StringSelection;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetAdapter;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetListener;

import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.embed.swing.SwingNode;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;

public class SwingFXBug1a extends Application {

	public static void main(String[] args) {
		if (args.length > 0) {
			if (args[0].equals("swing")) {
				new SwingFXBug1a().startAsSwingOnly();
			}
		} else {
			Application.launch(args);
		}
	}

	public void startAsSwingOnly() {
		JFrame frame = new JFrame();
		JPanel panel = new JPanel();
		panel.setPreferredSize(new Dimension(200, 200));
		createContent(panel);
		frame.getContentPane().add(panel, BorderLayout.CENTER);
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setTitle("Pure Swing");
		frame.pack();
		frame.setVisible(true);
	}

	@Override
	public void start(Stage stage) {

		final SwingNode swingNode = new SwingNode();
		JPanel panel = new JPanel();

		createContent(panel);
		swingNode.setContent(panel);
		BorderPane pane = new BorderPane();
		pane.setCenter(swingNode);

		stage.setTitle("Swing in JavaFX");
		stage.setScene(new Scene(pane, 250, 150));
		stage.onCloseRequestProperty().addListener(new InvalidationListener() {
			@Override
			public void invalidated(Observable observable) {
				System.exit(0);
			}
		});
		stage.show();
	}

	private void createContent(final JComponent component) {
		SwingUtilities.invokeLater(new Runnable() {

			@Override
			public void run() {

				DefaultTableModel tm = new DefaultTableModel();
				tm.addColumn("column-1");
				JTable table = new JTable(tm);
				tm.addRow(new String[] { "data-1" });
				tm.addRow(new String[] { "data-2" });
				tm.addRow(new String[] { "data-3" });
				tm.addRow(new String[] { "data-4" });

				// table.setTransferHandler(new TableRowTransferHandler(table));
				table.setDragEnabled(false);

				createDnDListener(table);

				component.add(table);
			}
		});
	}

	private void createDnDListener(JTable table) {
		DragGestureListener dgListener = new DragGestureListener() {
			@Override
			public void dragGestureRecognized(DragGestureEvent dge) {
				System.out.println("drag recognized...");
				dge.startDrag(java.awt.Cursor
						.getPredefinedCursor(java.awt.Cursor.HAND_CURSOR),
						new StringSelection("some string data"));
				System.out.println("drag finished");
			}
		};

		DropTargetListener dropListener = new DropTargetAdapter() {

			@Override
			public void drop(DropTargetDropEvent dtde) {
				// never gets here
				System.out.println("drop... " + dtde.getTransferable());
			}
		};

		DragSource dragSource = new DragSource();
		Object dragRecognizer = dragSource.createDefaultDragGestureRecognizer(
				table, DnDConstants.ACTION_COPY_OR_MOVE, dgListener);
		DropTarget dropTarget = new DropTarget(table, dropListener);
		System.out.println(dragRecognizer);
		System.out.println(dropTarget);

	}
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
as a workaround the application could track when startDrag is entered and prevent a second excution itself.


Comments
From: Yasumasa Suenaga We are developing HeapStats [1] with JavaFX 8. This tool uses SwingNode to use JGraphX [2]. JGraphX suports Drag & Drop. However, DnD in SwingNode occurs InvalidDnDOperationException. Callstack of it is same with JDK-8146900. I checked OpenJDK source, the flag of DnD is static field [3]. DnD event is trapped at FXDnD, and kick the same event via secondary event loop. Thus DnD event will be called twice, and second event will be thrown InvalidDnDOperationException. I think we can fix it as below. However I'm not sure it is correct. Could you help? ------------------- diff -r 20164b54e3d8 modules/swing/src/main/java/javafx/embed/swing/FXDnD.java --- a/modules/swing/src/main/java/javafx/embed/swing/FXDnD.java Wed Jun 08 13:34:10 2016 +1200 +++ b/modules/swing/src/main/java/javafx/embed/swing/FXDnD.java Wed Jun 08 22:02:10 2016 +0900 @@ -312,6 +312,10 @@ // and then start an AWT nested loop to wait until DnD finishes. loop = java.awt.Toolkit.getDefaultToolkit().getSystemEventQueue().createSecondaryLoop(); SwingFXUtils.leaveFXNestedLoop(FXDnD.this); + + // Avoid JDK-8146900 + setDragDropInProgress(false); + if (!loop.enter()) { // An error occured, but there's little we can do here... } ------------------- Thanks, Yasumasa [1] http://icedtea.classpath.org/wiki/HeapStats [2] https://github.com/jgraph/jgraphx [3] http://hg.openjdk.java.net/jdk9/dev/jdk/file/4f717d517d9e/src/java.desktop/share/classes/sun/awt/dnd/SunDragSourceContextPeer.java#l77
17-06-2016