Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java: use clipboard to copy-paste java objects between different instances of same application

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 class
  • MyObject.java - class for objects to copy/paste (this class contains just array of String)
  • MyObjectSelection.java - class that implements Transerable and ClipboardOwner interfaces

    There 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 each DataFlavor)

    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");
        }

     }

  }
like image 825
Dmitry Frank Avatar asked Jan 11 '13 10:01

Dmitry Frank


People also ask

How to get copied data from clipboard in Java?

The setContents() method is used to set contents to Clipboard, like cut-copy operations, and the getContents() method is used to retrieve the content from Clipboard, like the paste operation. A data package to be set in the Clipboard must be wrapped in a Transferable object.

What is system clipboard in Java?

A class that implements a mechanism to transfer data using cut/copy/paste operations. FlavorListener s may be registered on an instance of the Clipboard class to be notified about changes to the set of DataFlavor s available on this clipboard (see addFlavorListener(java. awt.

How to access clipboard in Java?

To access the general system clipboard, use the following code: Clipboard clipboard = Clipboard. getSystemClipboard();


1 Answers

Quoting this tutorial:

Transferring the data using this mechanism uses Object serialization, so the class you use to transfer the data must implement the Serializable interface, as must anything that is serialized with it. If everything is not serializable, you will see a NotSerializableException during drop or copy to the clipboard.

Your MyObject is not Serializable, 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.

like image 157
MvG Avatar answered Oct 28 '22 21:10

MvG