JDK-4352221 : DND still deadlocks in 1.3 when displaying JOptionPane
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.3.0,1.4.0
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • OS: generic,windows_nt
  • CPU: generic,x86
  • Submitted: 2000-07-11
  • Updated: 2003-09-23
  • Resolved: 2003-09-23
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
5.0 tigerFixed
Related Reports
Relates :  
Relates :  
Relates :  
Description

Name: jk109818			Date: 07/11/2000


java version "1.3.0"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.0-C)
Java HotSpot(TM) Client VM (build 1.3.0-C, mixed mode)

Application still dead locks when displaying a JOptionPane whilst performing
drag and drop.  Interstingly when run from the command line control-c no longer
kills the application - it now enters a tight loop consuming all of the CPU!
(On 1.2.2_05a at least control-c ends the app!).

Run the following app:

    java DNDLock lock - will kill it
    java DNDLock - works round it

////////////////////////////
// code starts
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
import java.awt.dnd.*;
import javax.swing.*;
import javax.swing.border.*;

/** Deadlock within 1.3.  JOptionPane within DND method = death.<p>
 ** Workround use SwingUtilities.invokeLater.
 **/
public class DNDLock extends JFrame
{
    private static boolean m_showingDialog = false;
    private static boolean m_performLockup = false;
    public static void main(String[] args)
    {
        DNDLock application = new DNDLock("SERVICEPower DND Lock-up
Application: Displaying Dialogs");
	
	if(args.length == 1)
        {
	    if(args[0].equals("lock"))
            {
		m_performLockup = true;
            }
        }
        application.show();
        return;
    }

    /** Ctor sets up the basic dialog **/
    public DNDLock(final String title)
    {
        super(title);
        getContentPane().add(m_mainPanel);
        m_mainPanel.add(m_centrePanel, BorderLayout.CENTER);

        // handle the frame's close button being pressed
        java.awt.event.WindowListener l = new java.awt.event.WindowAdapter()
	{
            public void windowClosing(java.awt.event.WindowEvent event)
            {
                System.err.println("DNDLock ended");
                System.exit(0);
            }
        };

        addWindowListener(l);
        setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
        setSize(500, 250);

        m_centrePanel.add(m_fifthObject);
    }

    private JPanel m_mainPanel = new JPanel(new BorderLayout());
    private DropPanel m_centrePanel = new DropPanel(Color.black, "Centre");

    private DraggableObject m_fifthObject = new DraggableObject("DRAG OBJECT
5", m_centrePanel);

    /**************************************************************************
     ** DropPanel <p>
     ** Instances of this are available as drop targets, in this application
     ** they simply display some text to state where they are up to.
     **************************************************************************/
    public class DropPanel extends JPanel implements DropTargetListener
    {

        public DropPanel(final Color color, final String name)
        {
            m_name = name;
            setBorder(BorderFactory.createLineBorder(color));
            m_dropTarget = new DropTarget(this, this);
        }
        ///////////////////////////////////////////////////////////////////////
        // DropTargetListener implementation
        public void dragEnter(DropTargetDragEvent dtde)
        {
            System.err.println(m_name + " - dragEnter");
            m_draggingHere = true;
            setCurrentDragBounds(dtde.getLocation());
            dtde.acceptDrag(dtde.getSourceActions());
        }
	/** This processing as invokeLater works with 1.3, and 1.2.2_05a but
not with 1.2.2-W **/
        public void dragOver(DropTargetDragEvent dtde)
        {
            System.err.println(m_name + " - dragOver");
            setCurrentDragBounds(dtde.getLocation());

            dtde.acceptDrag(dtde.getSourceActions());

	    if(m_performLockup)
            {
	    	JOptionPane.showMessageDialog(null, "Has this locked the
application?", "Are we locked", JOptionPane.QUESTION_MESSAGE);
	    }
	    else
	    {
	        if(!DNDLock.m_showingDialog)
	        {
		    DNDLock.m_showingDialog = true;
	            SwingUtilities.invokeLater(new Runnable()
  	            {
	                public void run()
	                {
	                    JOptionPane.showMessageDialog(null, "Has this
locked the application?", "Are we locked", JOptionPane.QUESTION_MESSAGE);
		            DNDLock.m_showingDialog = false;
	                }
	            });
	        }
 	    }
            repaint();
        }

        public void dropActionChanged(DropTargetDragEvent dtde)
        {
            System.err.println(m_name + " - dropActionChanged");
            setCurrentDragBounds(dtde.getLocation());
        }

        public void dragExit(DropTargetEvent dte)
        {
            System.err.println(m_name + " - dragExit");
            m_draggingHere = false;
        }

        public void drop(DropTargetDropEvent dtde)
        {
            System.err.println(m_name + " - drop");
            setCurrentDragBounds(dtde.getLocation());
            dtde.rejectDrop();
	    dtde.dropComplete(true);
            m_draggingHere = false;
        }

        /** It is not necessary to include any of your own drawing code to get
the application to lock.
         ** This code is included because it is similar in spirit to our core
application. **/
        public void paintComponent(Graphics g)
        {
            super.paintComponent(g);
            paintDrag(g, m_draggingHere);
        }

        /** As for paintComponent, this is not needed to hang the application.
**/
        public void paintDrag(Graphics g, boolean dragging)
        {
            Rectangle rect = (Rectangle)m_repaintRect.clone();
            Graphics2D g2 = (Graphics2D) g;
            Color oldColor = g.getColor();
            if(dragging)
            {
                g2.setColor(Color.yellow);
            }
            else
            {
                g2.setColor(Color.lightGray);
            }
            Stroke s = g2.getStroke();
            g2.setStroke(m_rectStroke);
            g2.draw(rect);
            g2.setColor(oldColor);
            g2.setStroke(s);
        }

        private void setCurrentDragBounds(final Point location)
        {
            // repaint rectangle
            m_repaintRect.x = location.x;
            m_repaintRect.y = location.y;
            m_repaintRect.width = 100;
            m_repaintRect.height = 50;
        }

        private Rectangle m_repaintRect = new Rectangle();

        private boolean m_draggingHere = false;
        private final String m_name;
        private DropTarget m_dropTarget;
        private final float[] dash1 = {10.0f};
        private BasicStroke m_rectStroke = new BasicStroke(3.0f,
BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, dash1, 0.0f);

    }

    /**************************************************************************
     ** DraggableObject <p>
     ** Instances of this JComponent derivations are used as draggable objects
     ** within the core application.  Here I use a JLabel for simplicity.
     **************************************************************************/
    public class DraggableObject extends JLabel
                                 implements DragSourceListener,
DragGestureListener, Transferable
    {
        public DraggableObject(final String name, JPanel owner)
        {
            setText(name);
            setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED));
            m_owner = owner;
            m_name = name;
            m_dragSource = DragSource.getDefaultDragSource();
            m_dragComponent = this;

            m_gestureRecogniser =
m_dragSource.createDefaultDragGestureRecognizer(m_dragComponent,
DnDConstants.ACTION_COPY_OR_MOVE, this);
        }
        ///////////////////////////////////////////////////////////////////////
        // DragSourceListener implementation
    	public void dragDropEnd(DragSourceDropEvent dsde)
    	{
            System.err.println(m_name + " - dragDropEnd: " + dsde.getDropSuccess
());
            
            m_owner.repaint();
            m_owner.revalidate();
    	}

     	public void dragEnter(DragSourceDragEvent dsde)
     	{
            System.err.println(m_name + " - dragEnter");
     	}

    	public void dragExit(DragSourceEvent dse)
    	{
            System.err.println(m_name + " - dragExit");
    	}

    	public void dragOver(DragSourceDragEvent dsde)
    	{
            System.err.println(m_name + " - dragOver");
    	}

        /** It is not necessary to include any of your own drawing code to get
the application to lock.
         ** This code is included because it is similar in spirit to our core
application. **/
        public void paintComponent1(Graphics g)
        {
            super.paintComponent(g);
            m_owner.repaint();
        }

    	public void dropActionChanged(DragSourceDragEvent dsde)
    	{
            System.err.println(m_name + " - dropActionChanged");
    	}
        ///////////////////////////////////////////////////////////////////////
        // DragGestureListener implementation
        public void dragGestureRecognized(DragGestureEvent dge)
    	{
            System.err.println(m_name + " - dragGestureRecognized");
            // we're in a valid drag situation
            dge.startDrag(DragSource.DefaultMoveDrop, this, this);
    	}
        ///////////////////////////////////////////////////////////////////////
        // Transferable implementation
        public DataFlavor[] getTransferDataFlavors()
        {
            DataFlavor[] flavours = { DRAG_FLAVOUR };
            return flavours;
        }
        public boolean isDataFlavorSupported(DataFlavor flavour)
        {
            return flavour.equals(DRAG_FLAVOUR);
        }
        public Object getTransferData(DataFlavor flavor) throws
UnsupportedFlavorException, IOException
        {
            return new String(m_name + " transferData");
        }

        private JPanel m_owner;
        private String m_name;
        private DragSource m_dragSource;
        private DragGestureRecognizer m_gestureRecogniser;
        private JComponent m_dragComponent;
    }

    ///////////////////////////////////////////////////////////////////////////
    // DataFlavor stuff for DragGesture implementation
	public static DataFlavor DRAG_FLAVOUR = new DataFlavor
(DragFlavour.class, "DragFlavor");
    public class DragFlavour
    {
        public DragFlavour()
        {
        }
    }

}

// code ends
////////////////////////////
(Review ID: 107069) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger FIXED IN: tiger INTEGRATED IN: tiger tiger-b21
14-06-2004

WORK AROUND Name: jk109818 Date: 07/11/2000 As mentioned on other reports use SwingUtilities.invokeLater to work round the problem... public void dragOver(DropTargetDragEvent dtde) { ... ... SwingUtilities.invokeLater(new Runnable() { public void run() { JOptionPane.showMessageDialog(null, "Has not locked the application!", "Are we locked?", JOptionPane.QUESTION_MESSAGE); } }); ... ... } ======================================================================
11-06-2004

EVALUATION Reproducible on Solaris with kestrel and merlin-25. I am not seeing this problem on Windows kestrel, but the submitter is. I must be missing something. david.mendenhall@eng 2000-08-01 Reprodcuible on Solaris with 12Dec2000 AWT workspace. david.mendenhall@east 2000-12-14 This is not currently considered a showstopper for merlin. ###@###.### 2001-09-28 Name: dsR10078 Date: 09/08/2003 As of 1.5.0-b17 the hang is not reproducible on Solaris and Linux. It manifests only on Windows. A similar hang is described in the evaluation for 4633417. In this case when the event dispatch thread processes SunDropTargetEvent, it invokes DropTargetListener.dragOver() shows a modal dialog with JOptionPane.showMessageDialog(). JOptionPane.showMessageDialog() blocks until the dialog is closed, so the current SunDropTargetEvent processing will not be completed until the dialog is closed. At the same time, the toolkit thread is waiting until the event dispatch thread processes the SunDropTargetEvent, so the toolkit thread is blocked until the dialog is closed. However, the message dialog is designed to be closed in response to a user, so the respective native message is to be processed on the toolkit thread, so the dialog cannot be closed while the toolkit thread is blocked. ###@###.### 2003-09-05 ======================================================================
05-09-2003

SUGGESTED FIX Name: dsR10078 Date: 09/08/2003 All blocking calls on the event dispatch thread (showing modal dialog or popup menu) start a nested event pump to process events until the blocking method returns. The suggested solution is to detect when the nested event pump is started on the event dispatch thread while it processes a SunDropTargetEvent and stop blocking the toolkit thread in this case. The only side effect in this case is that if the DropTargetListener changes the selected drop action after the blocking call, that change will be lost and the drag source will receive the drop action that was chosen before the blocking call. Since i don't see any other way to resolve this hang, i think this side effect is bearable. It won't cause any regressions, since it will manifest in the situations that currently lead to a hang. ###@###.### 2003-09-05 ======================================================================
05-09-2003