I am trying to implement copy-paste of objects between different instances of same application. Currently it works only in one application (I mean, copy and paste in the same instance of application), but does not work between different instances.
Copying code:
// MyObject is a class of objects I want to copy/paste;
// MyObjectSelection is a class that impements Transferable and ClipboardOwner interfaces
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
MyObject data = new MyObject(selectedItems);
MyObjectSelection dataSelection = new MyObjectSelection(data);
clipboard.setContents(dataSelection, this);
After that, I can check contents of the clipboard, like that:
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable clipboardContent = clipboard.getContents(this);
DataFlavor[] flavors = clipboardContent.getTransferDataFlavors();
System.out.println("flavors.length=" + flavors.length);
for (int i = 0; i < flavors.length; i++){
System.out.println("flavor=" + flavors[i]);
}
If I do this from the same application instance that I copied object, it works: flavors.length
is 1
, mimetype is application/x-java-serialized-object
, and, well, it works.
But if I open application, perform copy, then open the same application again (first one isn't closed, i.e. both instances are running simultaneously), and try to check clipboard contents from there, then flavors.length
is 0
.
I examined the docs and these examples: one, two, but I still can't find what is wrong in my implementation.
Did I miss something?
UPD: I added sscce: clipboard_test.zip.
This is test application (I use Eclipse to build it) with 3 source files:
ClipboardTest.java
- main app classMyObject.java
- class for objects to copy/paste (this class contains just array ofString
)MyObjectSelection.java
- class that implementsTranserable
andClipboardOwner
interfacesThere are two buttons: "copy", "test".
When you press "copy" button, new instance of MyObject is created and put to the clipboard.
When you press "test" button, clipboard contents are checked and echoed to the console (count of supported
DataFlavor
's, and eachDataFlavor
)So, replicate these steps:
Start application
- Press "copy" button: you will see
"object copied"
message in the log Press "test" button: you will see clipboard's contents:
flavors.length = 1 flavor[0] = java.awt.datatransfer.DataFlavor[mimetype=application/x-java-serialized-object;representationclass=MyObject]
Start another instance of the application (don't close the first one)
Press "test" button: you will see that clipboard is empty:
flavors.length = 0
Why is that?
UPD2: sscce posted directly here:
import java.awt.BorderLayout;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.ClipboardOwner;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.Toolkit;
import javax.swing.JButton;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
public final class ClipboardTest implements Runnable, ClipboardOwner {
public static void main(String[] args) {
SwingUtilities.invokeLater (new ClipboardTest());
}
public void run() {
JFrame f = new JFrame ("Clipboard test");
f.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
//-- Create "copy" button.
// When you click it, new object "test_items" becomes created
// and put to the clipboard.
{
JButton button_copy = new JButton("copy");
button_copy.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
String[] test_items = {
"one", "two", "three"
};
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
MyObject data = new MyObject(test_items);
MyObjectSelection dataSelection = new MyObjectSelection(data);
clipboard.setContents(dataSelection, ClipboardTest.this);
System.out.println("object copied");
}
});
f.add(button_copy, BorderLayout.NORTH);
}
//-- Create "test" button.
// When you click it, clipboard contents are checked
// and echoed to the console (count of supported DataFlavor's, and each DataFlavor)
{
JButton button_test = new JButton("test");
button_test.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
Transferable clipboardContent = clipboard.getContents(this);
DataFlavor[] flavors = clipboardContent.getTransferDataFlavors();
System.out.println("flavors.length = " + flavors.length);
for (int i = 0; i < flavors.length; i++){
System.out.println("flavor[" + i + "] = " + flavors[i]);
}
}
});
f.add(button_test, BorderLayout.SOUTH);
}
f.pack();
f.setVisible(true);
}
// ClipboardOwner implementation
@Override
public void lostOwnership(Clipboard clipboard, Transferable transferable){
System.out.println("ClipboardTest: Lost ownership");
}
/* *****************************************************************************************
* Object that I want to copy/paste
* ****************************************************************************************/
public static class MyObject {
private String[] items;
public MyObject(String[] items){
this.setItems(items);
}
public String[] getItems(){
return this.items;
}
public void setItems(String[] items){
this.items = items;
}
}
public static class MyObjectSelection implements Transferable, ClipboardOwner {
private static DataFlavor dmselFlavor = new DataFlavor(MyObject.class, "Test data flavor");
private MyObject selection;
public MyObjectSelection(MyObject selection){
this.selection = selection;
}
// Transferable implementation
@Override
public DataFlavor[] getTransferDataFlavors(){
System.out.println("getTransferDataFlavors");
DataFlavor[] ret = {dmselFlavor};
return ret;
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor){
return dmselFlavor.equals(flavor);
}
@Override
public synchronized Object getTransferData (DataFlavor flavor)
throws UnsupportedFlavorException
{
if (isDataFlavorSupported(flavor)){
return this.selection;
} else {
throw new UnsupportedFlavorException(dmselFlavor);
}
}
// ClipboardOwner implementation
@Override
public void lostOwnership(Clipboard clipboard, Transferable transferable){
System.out.println("MyObjectSelection: Lost ownership");
}
}
}
Quoting this tutorial:
Your
MyObject
is notSerializable
, so it cannot work. The framework apparently detects this fact (as opposed to a situation with an unserializable subclass of a serializable parent class, or similar scenarios where non-serializability is detected only in the process), so it won't even offer that flavor to other processes.In general, two instances of the same java application won't have the same address space, so they can't simply access one another's memory. Therefore everything you transfer has to be serialized.