United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
JDK-4932376 : IOException on drop part of drag'n'drop

Details
Type:
Bug
Submit Date:
2003-10-03
Status:
Closed
Updated Date:
2004-01-07
Project Name:
JDK
Resolved Date:
2003-10-31
Component:
client-libs
OS:
generic
Sub-Component:
java.awt
CPU:
generic
Priority:
P2
Resolution:
Fixed
Affected Versions:
1.4.2_01
Fixed Versions:
1.4.2_04 (04)

Related Reports
Backport:
Relates:
Relates:

Sub Tasks

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


                                     
2004-06-14
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

======================================================================
                                     
2003-10-06



Hardware and Software, Engineered to Work Together