8024061: Exception thrown when drag and drop between two components is executed quickly
Reviewed-by: pchelko, serb
Contributed-by: alexey.ivanov@oracle.com
--- a/jdk/src/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java Mon Apr 21 14:35:14 2014 +0400
+++ b/jdk/src/share/classes/sun/awt/dnd/SunDropTargetContextPeer.java Mon Apr 21 16:32:41 2014 +0400
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2000, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 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
@@ -239,6 +239,13 @@
if (localTransferable != null) {
return localTransferable.getTransferData(df);
+ } else if (df.isMimeTypeEqual(DataFlavor.javaJVMLocalObjectMimeType)) {
+ // Workaround to JDK-8024061: Exception thrown when drag and drop
+ // between two components is executed quickly.
+ // It is expected localTransferable is not null if javaJVMLocalObjectMimeType
+ // is used. Executing further results in ClassCastException, so null is
+ // returned here as no transfer data is available in this case.
+ return null;
}
if (dropStatus != STATUS_ACCEPT || dropComplete) {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/awt/dnd/8024061/bug8024061.java Mon Apr 21 16:32:41 2014 +0400
@@ -0,0 +1,357 @@
+/*
+ * 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.
+ */
+
+/* @test
+ * @bug 8024061
+ * @summary Checks that no exception is thrown if dragGestureRecognized
+ * takes a while to complete.
+ */
+import sun.awt.OSInfo;
+import sun.awt.OSInfo.OSType;
+import sun.awt.SunToolkit;
+
+import java.awt.*;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.dnd.DnDConstants;
+import java.awt.dnd.DragGestureEvent;
+import java.awt.dnd.DragGestureListener;
+import java.awt.dnd.DragSource;
+import java.awt.dnd.DragSourceDragEvent;
+import java.awt.dnd.DragSourceDropEvent;
+import java.awt.dnd.DragSourceEvent;
+import java.awt.dnd.DragSourceListener;
+import java.awt.dnd.DropTarget;
+import java.awt.dnd.DropTargetDragEvent;
+import java.awt.dnd.DropTargetDropEvent;
+import java.awt.dnd.DropTargetEvent;
+import java.awt.dnd.DropTargetListener;
+import java.awt.event.InputEvent;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import javax.swing.*;
+
+/**
+ * If dragGestureRecognized() takes a while to complete and if user performs a drag quickly,
+ * an exception is thrown from DropTargetListener.dragEnter when it calls
+ * DropTargetDragEvent.getTransferable().
+ * <p>
+ * This class introduces a delay in dragGestureRecognized() to cause the exception.
+ */
+public class bug8024061 {
+ private static final DataFlavor DropObjectFlavor;
+ private static final int DELAY = 1000;
+
+ private final DnDPanel panel1 = new DnDPanel(Color.yellow);
+ private final DnDPanel panel2 = new DnDPanel(Color.pink);
+ private final JFrame frame;
+
+ private static final CountDownLatch lock = new CountDownLatch(1);
+ private static volatile Exception dragEnterException = null;
+
+ static {
+ DataFlavor flavor = null;
+ try {
+ flavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType);
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ }
+ DropObjectFlavor = flavor;
+ }
+
+ bug8024061() {
+ frame = new JFrame("DnDWithRobot");
+ frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
+
+ Dimension d = new Dimension(100, 100);
+
+ panel1.setPreferredSize(d);
+ panel2.setPreferredSize(d);
+
+ Container content = frame.getContentPane();
+ content.setLayout(new GridLayout(1, 2, 5, 5));
+ content.add(panel1);
+ content.add(panel2);
+
+ frame.pack();
+
+ DropObject drop = new DropObject();
+ drop.place(panel1, new Point(10, 10));
+ frame.setVisible(true);
+ }
+
+ public static void main(String[] args) throws AWTException, InvocationTargetException, InterruptedException {
+ OSType type = OSInfo.getOSType();
+ if (type != OSType.LINUX && type != OSType.SOLARIS) {
+ System.out.println("This test is for Linux and Solaris only... " +
+ "skipping!");
+ return;
+ }
+
+ final bug8024061[] dnd = {null};
+ SwingUtilities.invokeAndWait(new Runnable() {
+ @Override
+ public void run() {
+ dnd[0] = new bug8024061();
+ }
+ });
+ final Robot robot = new Robot();
+ robot.setAutoDelay(10);
+ SunToolkit toolkit = (SunToolkit) Toolkit.getDefaultToolkit();
+ toolkit.realSync();
+
+ JFrame frame = dnd[0].frame;
+ Point point = frame.getLocationOnScreen();
+ Point here = new Point(point.x + 35, point.y + 45);
+ Point there = new Point(point.x + 120, point.y + 45);
+ here.x += 25;
+ robot.mouseMove(here.x, here.y);
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ while (here.x < there.x) {
+ here.x += 20;
+ robot.mouseMove(here.x, here.y);
+ System.out.println("x = " + here.x);
+ }
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ toolkit.realSync();
+ robot.mousePress(InputEvent.BUTTON1_MASK);
+ robot.mouseRelease(InputEvent.BUTTON1_MASK);
+ System.out.println("finished");
+
+ try {
+ if (lock.await(5, TimeUnit.SECONDS)) {
+ if (dragEnterException == null) {
+ System.out.println("Test passed.");
+ } else {
+ System.out.println("Test failed.");
+ dragEnterException.printStackTrace();
+ throw new RuntimeException(dragEnterException);
+ }
+ } else {
+ System.out.println("Test failed. Timeout reached");
+ throw new RuntimeException("Timed out waiting for dragEnter()");
+ }
+ } finally {
+ frame.dispose();
+ }
+ }
+
+ class DropObject implements Transferable {
+ DnDPanel panel;
+ Color color = Color.CYAN;
+ int width = 50;
+ int height = 50;
+ int x;
+ int y;
+
+ void draw(Graphics2D g) {
+ Color savedColor = g.getColor();
+ g.setColor(color);
+ g.fillRect(x, y, width, height);
+ g.setColor(Color.lightGray);
+ g.drawRect(x, y, width, height);
+ g.setColor(savedColor);
+ }
+
+ boolean contains(int x, int y) {
+ return (x > this.x && x < this.x + width)
+ && (y > this.y && y < this.y + height);
+ }
+
+ @Override
+ public DataFlavor[] getTransferDataFlavors() {
+ return new DataFlavor[]{DropObjectFlavor};
+ }
+
+ void place(DnDPanel panel, Point location) {
+ if (panel != this.panel) {
+ x = location.x;
+ y = location.y;
+ if (this.panel != null) {
+ this.panel.setDropObject(null);
+ this.panel.repaint();
+ }
+ this.panel = panel;
+ this.panel.setDropObject(this);
+ this.panel.repaint();
+ }
+ }
+
+ @Override
+ public boolean isDataFlavorSupported(DataFlavor flavor) {
+ return DropObjectFlavor.equals(flavor);
+ }
+
+ @Override
+ public Object getTransferData(DataFlavor flavor)
+ throws UnsupportedFlavorException, IOException {
+ if (isDataFlavorSupported(flavor)) {
+ return this;
+ } else {
+ throw new UnsupportedFlavorException(flavor);
+ }
+ }
+ }
+
+ class DnDPanel extends JPanel {
+ DropObject dropObject;
+ final DragSource dragSource;
+ final DropTarget dropTarget;
+ final Color color;
+ final DragGestureListener dgListener;
+ final DragSourceListener dsListener;
+ final DropTargetListener dtListener;
+
+ DnDPanel(Color color) {
+ this.color = color;
+ this.dragSource = DragSource.getDefaultDragSource();
+ dgListener = new DragGestureListener() {
+ @Override
+ public void dragGestureRecognized(DragGestureEvent dge) {
+ Point location = dge.getDragOrigin();
+ if (dropObject != null && dropObject.contains(location.x, location.y)) {
+ dragSource.startDrag(dge, DragSource.DefaultCopyNoDrop, dropObject, dsListener);
+ try {
+ Thread.sleep(DELAY);
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ };
+
+ dsListener = new DragSourceListener() {
+ @Override
+ public void dragEnter(DragSourceDragEvent dsde) {
+ }
+
+ @Override
+ public void dragOver(DragSourceDragEvent dsde) {
+ }
+
+ @Override
+ public void dropActionChanged(DragSourceDragEvent dsde) {
+ }
+
+ @Override
+ public void dragExit(DragSourceEvent dse) {
+ }
+
+ @Override
+ public void dragDropEnd(DragSourceDropEvent dsde) {
+ }
+ };
+
+ dtListener = new DropTargetListener() {
+ @Override
+ public void dragEnter(DropTargetDragEvent dtde) {
+ if (dropObject != null) {
+ dtde.rejectDrag();
+ return;
+ }
+ dtde.acceptDrag(DnDConstants.ACTION_MOVE);
+ try {
+ Transferable t = dtde.getTransferable();
+ Object data = t.getTransferData(DropObjectFlavor);
+ if (data != null) {
+ throw new Exception("getTransferData returned non-null");
+ }
+ } catch (Exception e) {
+ dragEnterException = e;
+ e.printStackTrace();
+ } finally {
+ lock.countDown();
+ }
+ }
+
+ @Override
+ public void dragOver(DropTargetDragEvent dtde) {
+ if (dropObject != null) {
+ dtde.rejectDrag();
+ return;
+ }
+ dtde.acceptDrag(DnDConstants.ACTION_MOVE);
+ }
+
+ @Override
+ public void dropActionChanged(DropTargetDragEvent dtde) {
+ }
+
+ @Override
+ public void dragExit(DropTargetEvent dte) {
+ }
+
+ @Override
+ public void drop(DropTargetDropEvent dtde) {
+ if (dropObject != null) {
+ dtde.rejectDrop();
+ return;
+ }
+ try {
+ dtde.acceptDrop(DnDConstants.ACTION_MOVE);
+ Transferable t = dtde.getTransferable();
+ DropObject dropObject = (DropObject) t.getTransferData(DropObjectFlavor);
+ Point location = dtde.getLocation();
+ dropObject.place(DnDPanel.this, location);
+ dtde.dropComplete(true);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+
+ }
+ };
+
+ dragSource.createDefaultDragGestureRecognizer(this,
+ DnDConstants.ACTION_MOVE, dgListener);
+
+ dropTarget = new DropTarget(this, DnDConstants.ACTION_MOVE, dtListener, true);
+
+ }
+
+ public void paintComponent(Graphics g) {
+ super.paintComponent(g);
+ Color savedColor = g.getColor();
+ g.setColor(color);
+ g.fillRect(0, 0, getWidth(), getHeight());
+ g.setColor(savedColor);
+ if (dropObject != null) {
+ dropObject.draw((Graphics2D) g);
+ }
+ }
+
+ void setDropObject(DropObject dropObject) {
+ this.dropObject = dropObject;
+ }
+
+ DropObject findDropObject(int x, int y) {
+ if (dropObject != null && dropObject.contains(x, y)) {
+ return dropObject;
+ }
+ return null;
+ }
+ }
+}