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
======================================================================
|