tags:

views:

223

answers:

3

Hi, I want to write an application to share desktop between client and server with java rmi. This is the interface:

public interface IServer extends Remote{

    public void share(BufferedImage image) throws RemoteException;
}
                  -----------------------------

// This is the server side code:

public class ServerFrame extends JFrame implements IServer{
public static void main(String args[]) {
        try {

             ServerFrame frame = new ServerFrame();

             frame.setVisible(true);
             LocateRegistry.createRegistry(1099);
             Naming.bind("test", frame);
             System.out.println("Server Started Successfully...");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
. . . 
public ServerFrame() {

        super();
          try {
            UnicastRemoteObject.exportObject((Remote) this);
        } catch (RemoteException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
  . . .  // bulding the ServerFrame GUI
. . .   
public void share(BufferedImage image) throws RemoteException {
        label.setIcon(new ImageIcon(image));
    }
}
                  ---------------

// And finally this is the client side: there is a button in the ClientFrame which I want the desktop to be shared between client and server after I clicked this button.

public class ClientFrame extends JFrame implements Remote,Serializable{

    IServer serve;
...
public ClientFrame() {
        super();
         try {
            serve = (IServer) Naming.lookup("test");
        } catch (MalformedURLException e) {

            e.printStackTrace();
        } catch (RemoteException e) {

            e.printStackTrace();
        } catch (NotBoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        . . . // codes for building client gui
    final JButton shareDesktopButton = new JButton();
        shareDesktopButton.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent arg0) {
                try {
                    BufferedImage screenShot = new Robot()
                    .createScreenCapture(new Rectangle(Toolkit
                            .getDefaultToolkit().getScreenSize()));
                    label.setIcon(new ImageIcon(screenShot));
                    serve.share(screenShot);
                } catch (HeadlessException e) {

                    e.printStackTrace();
                } catch (AWTException e) {

                    e.printStackTrace();
                } catch (RemoteException e) {

                    e.printStackTrace();
                }

            }
        });
          . . .
     }
// but there is a problem that causes these exceptions that I can not understand it. Please help me to complete my project.

Thanks a lot

Exceptions:

java.rmi.MarshalException: error marshalling arguments; nested exception is: 
    java.io.NotSerializableException: java.awt.image.BufferedImage
    at sun.rmi.server.UnicastRef.invoke(Unknown Source)
    at server.ServerFrame_Stub.share(Unknown Source)
    at client.ClientFrame$1.mouseClicked(ClientFrame.java:86)
    at java.awt.AWTEventMulticaster.mouseClicked(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.io.NotSerializableException: java.awt.image.BufferedImage
    at java.io.ObjectOutputStream.writeObject0(Unknown Source)
    at java.io.ObjectOutputStream.writeObject(Unknown Source)
    at sun.rmi.server.UnicastRef.marshalValue(Unknown Source)
    ... 24 more
A: 

As the stack trace shows, the root cause is:

Caused by: java.io.NotSerializableException: java.awt.image.BufferedImage

which means that you tried to serialize a non-serializable class, namely BufferedImage.

An alternative would be to use and ImageIcon rather than a BufferedImage.

JRL
yes, right. thanks a lot
samuel
+1  A: 

You can't use BufferedImage as a remote parameter as it is neither Remote nor Serializable.

You could wrap the BufferedImage in an ImageIcon but that is very inefficient as it will be converted to a bitmap and transmitted over the network uncompressed.

I would make the argument to Share a byte array representing a compressed image format (e.g. PNG.)

public interface IServer extends Remote{
   public void share(byte[] imagePNGBytes) throws RemoteException;
}

public void mouseClicked(MouseEvent arg0) {
    try {
        BufferedImage screenShot = new Robot()
        .createScreenCapture(new Rectangle(Toolkit
        .getDefaultToolkit().getScreenSize()));
        ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        ImageIO.write(screenShot, "PNG", bytes);
        server.share(bytes.toByteArray());
    } catch (...) {
         // ...
    }
}

public class ServerFrame extends JFrame implements IServer {

    // . . .

    public void share(byte[] imagePNGBytes) throws IOException, RemoteException {
        RenderedImage image = ImageIO.read(new ByteArrayInputStream(imagePNGBytes));
        label.setIcon(new ImageIcon(image));
    }
}
finnw
+1: For including compression as well, that'll be important for remoting
bguiz
A: 

@finnw

Your code works fine except that

label.setIcon(new ImageIcon(image));

ImageIcon class doesnot have a constructor that accepts RenderedImage object.

So you need to convert RenderedImage into a BufferedImage. And code for that goes here

public BufferedImage convertRenderedImage(RenderedImage img) {
    if (img instanceof BufferedImage) {
        return (BufferedImage) img;
    }
    ColorModel cm = img.getColorModel();
    int width = img.getWidth();
    int height = img.getHeight();
    WritableRaster raster = cm.createCompatibleWritableRaster(width, height);
    boolean isAlphaPremultiplied = cm.isAlphaPremultiplied();
    Hashtable properties = new Hashtable();
    String[] keys = img.getPropertyNames();
    if (keys != null) {
        for (int i = 0; i < keys.length; i++) {
            properties.put(keys[i], img.getProperty(keys[i]));
        }
    }
    BufferedImage result = new BufferedImage(cm, raster, isAlphaPremultiplied, properties);
    img.copyData(raster);
    return result;
}

and now changing your code to

label.setIcon(new ImageIcon(convertRenderedImage(image)));

works

rgksugan