JDK-4932376 : IOException on drop part of drag'n'drop
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.2_01
  • Priority: P2
  • Status: Closed
  • Resolution: Fixed
  • OS: generic
  • CPU: generic
  • Submitted: 2003-10-03
  • Updated: 2004-01-07
  • Resolved: 2003-10-31
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
1.4.2_04 04Fixed
Related Reports
Relates :  
Relates :  
Description
We are investigating the following exception which occurs in the drop phase of a D'n'D operation. 

******************************************************************************

java.io.IOException: com.nortel.cpt.gui.dnd.TemplateItem 
        at sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy 
.java:61) 
        at java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTar 
getContext.java:359) 
        at com.nortel.cpt.gui.dnd.DropTree.drop(DropTree.java:270) 
        at java.awt.dnd.DropTarget.drop(DropTarget.java:404) 
        at sun.awt.dnd.SunDropTargetContextPeer.processDropMessage(SunDropTargetCon 
textPeer.java:547) 
        at sun.awt.dnd.SunDropTargetContextPeer.access$800(SunDropTargetContextPeer 
.java:52) 
        at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchDropEvent(S 
unDropTargetContextPeer.java:806) 
        at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDr 
opTargetContextPeer.java:744) 
        at sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:29) 
        at java.awt.Component.dispatchEventImpl(Component.java:3456) 
        at java.awt.Container.dispatchEventImpl(Container.java:1623) 
        at java.awt.Component.dispatchEvent(Component.java:3439) 
        at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3450) 
        at java.awt.LightweightDispatcher.processDropTargetEvent(Container.java:323 
6) 
        at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3090) 
        at java.awt.Container.dispatchEventImpl(Container.java:1609) 
        at java.awt.Window.dispatchEventImpl(Window.java:1585) 
        at java.awt.Component.dispatchEvent(Component.java:3439) 
        at java.awt.EventQueue.dispatchEvent(EventQueue.java:450) 
        at com.nortel.inmP.commonuiP.swingP.GUIBlockerC.dispatchEvent(GUIBlockerC.j 
ava:207) 
        at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThrea 
d.java:197) 
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread. 
java:150) 
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:144) 
        at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:136) 
        at java.awt.EventDispatchThread.run(EventDispatchThread.java:99) 

****************************************************************************

This exception only occurs when the transfer object "TemplateItem" (and the DataFlavour) are not in the classpath but have been loaded by a user class loader (URLClassLoader).  In our environment, most classes are loaded this way - the system and bootstrap loaders are not busy.  The problem can be easily seen in the following decompiled code from

sun.awt.datatransfer.TransferableProxy:

    public Object getTransferData(DataFlavor dataflavor) 
        throws UnsupportedFlavorException, IOException 
    { 
        Object obj = transferable.getTransferData(dataflavor); 
        if(obj != null && isLocal &&
dataflavor.isFlavorSerializedObjectType()) 
        { 
            ByteArrayOutputStream bytearrayoutputstream = new
ByteArrayOutputStream(); 
            (new
ObjectOutputStream(bytearrayoutputstream)).writeObject(obj); 
            ByteArrayInputStream bytearrayinputstream = new
ByteArrayInputStream(bytearrayoutputstream.toByteArray()); 
            try 
            { 
                obj = (new
ObjectInputStream(bytearrayinputstream)).readObject(); 
            } 
            catch(ClassNotFoundException classnotfoundexception) 
            { 
                throw new IOException(classnotfoundexception.getMessage());

            } 
        } 
        return obj; 
    } 

In this method, the transfer object obj is being serialized and deserialized. In the deserialization step, the classloader present in obj or dataflavor is not used (and it should be) to recreate obj. When the classloader in obj is different than the classloader in the ObjectInputStream, this code will not work.

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: 1.4.2_04 tiger-beta FIXED IN: 1.4.2_04 tiger-beta INTEGRATED IN: 1.4.2_04 tiger-b28 tiger-beta VERIFIED IN: 1.4.2_04
14-06-2004

EVALUATION Although the exception originates in serialization, this is not a serialization bug. From serialization's standpoint, it is proper behavior--because a "vanilla" ObjectInputStream is user for deserialization, the default ObjectInputStream.resolveClass() implementation is used, which attempts to resolve the class using the closest non-null classloader on the call stack. If different class resolution behavior is needed, then ObjectInputStream should be subclassed and resolveClass() overridden to consult the proper class loader. An example of this is java.rmi.MarshalledObject, which internally uses subclasses of ObjectOutputStream and ObjectInputStream; these subclasses override the ObjectOutputStream.annotateClass and ObjectInputStream.resolveClass methods to send and receive the URLs from which classes of objects to be deserialized should be loaded. Reassigning to java/classes_awt for further evaluation... ###@###.### 2003-10-03 Name: dsR10078 Date: 10/04/2003 Here is a test case for this bug: ------------------------ Test.java ---------------------------------------------- import java.awt.Frame; import java.awt.datatransfer.*; import java.awt.dnd.*; import java.net.URL; import java.net.URLClassLoader; import java.io.File; import java.io.IOException; public class Test { public static class DFTransferable implements Transferable { private final DataFlavor df; private final Object obj; public DFTransferable(DataFlavor df, Object obj) { this.df = df; this.obj = obj; } public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException { if (df.equals(flavor)) { return obj; } else { throw new UnsupportedFlavorException(flavor); } } public DataFlavor[] getTransferDataFlavors(){ return new DataFlavor[] { df }; } public boolean isDataFlavorSupported(DataFlavor flavor) { return df.equals(flavor); } } public static void main(String[] args) throws Exception { final Frame frame = new Frame("Text drag source"); final URL url = new File("./subdir/").toURL(); final ClassLoader classLoader = new URLClassLoader(new URL[] { url }); final Class clazz = Class.forName("TransferableList", true, classLoader); final DataFlavor df = new DataFlavor(clazz, "Transferable List"); final Object obj = clazz.newInstance(); final Transferable t = new DFTransferable(df, obj); final DragGestureListener dgl = new DragGestureListener() { public void dragGestureRecognized(DragGestureEvent dge) { dge.startDrag(null, t); } }; final DragSource ds = DragSource.getDefaultDragSource(); final DragGestureRecognizer dgr = ds.createDefaultDragGestureRecognizer(frame, DnDConstants.ACTION_COPY, dgl); final DropTargetListener dtl = new DropTargetAdapter() { public void drop(DropTargetDropEvent dtde) { dtde.acceptDrop(DnDConstants.ACTION_COPY); Transferable t = dtde.getTransferable(); DataFlavor[] dfs = t.getTransferDataFlavors(); try { if (dfs.length > 0) { System.out.println("Flavor: " + dfs[0]); Object data = t.getTransferData(dfs[0]); } } catch (Exception e) { e.printStackTrace(); } } }; final DropTarget dt = new DropTarget(frame, dtl); frame.setBounds(100, 100, 100, 100); frame.setVisible(true); } } --------------------------------------------------------------------------------- ------------------------- TransferableList.java --------------------------------- import java.util.ArrayList; public class TransferableList extends ArrayList { } --------------------------------------------------------------------------------- Steps to reproduce: 1.Compile the two files above. 2.In the current directory create a subdirectory named "subdir" and move TransferableList.class to that subdirectory. 3.Run Test. 4.A frame will appear. Initiate a drag operation inside a frame and drop within frame bounds. On drop the following exception will be thrown: java.io.IOException: TransferableList at sun.awt.datatransfer.TransferableProxy.getTransferData(TransferableProxy.java:61) at java.awt.dnd.DropTargetContext$TransferableProxy.getTransferData(DropTargetContext.java:359) at Test$2.drop(Test.java:64) at java.awt.dnd.DropTarget.drop(DropTarget.java:430) at sun.awt.dnd.SunDropTargetContextPeer.processDropMessage(SunDropTargetContextPeer.java:500) at sun.awt.dnd.SunDropTargetContextPeer.access$800(SunDropTargetContextPeer.java:53) at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchDropEvent(SunDropTargetContextPeer.java:812) at sun.awt.dnd.SunDropTargetContextPeer$EventDispatcher.dispatchEvent(SunDropTargetContextPeer.java:736) at sun.awt.dnd.SunDropTargetEvent.dispatch(SunDropTargetEvent.java:29) at java.awt.Component.dispatchEventImpl(Component.java:3793) at java.awt.Container.dispatchEventImpl(Container.java:2017) at java.awt.Window.dispatchEventImpl(Window.java:1746) at java.awt.Component.dispatchEvent(Component.java:3770) at java.awt.EventQueue.dispatchEvent(EventQueue.java:463) at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:214) at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157) at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149) at java.awt.EventDispatchThread.run(EventDispatchThread.java:110) ###@###.### 2003-10-04 ====================================================================== Name: dsR10078 Date: 10/06/2003 The suggestion to use the classloader of the data object or dataflavor will not work in all cases. For example, the data object can be a List that contains instances of classes loaded with different loaders, while the List itself and the DataFlavor are created in the system class loader. ###@###.### 2003-10-06 ======================================================================
06-10-2003