Search code examples
swingcallbacklistenerrmijtree

Swing GUI Client Listeners not responding to update of Remote RMI Property


I am having some trouble with getting a JTree to redraw when an explicit call is made to its model (a call which I make once I have added some new nodes to it).

The code, which initially worked fine, fails now that the application is exported to RMI.

I store the DefaultTreeModel object in the Controller class, which is a Remote Object.

I add the DefaultTreeModel object to the JTree in my Client, using tree.addModel(controller.getModel());

I use an ActionListener subscribed to a button on the Client GUI to call a method in the Controller which performs the "Add new node" action.

I use a TreeModelListener to print a message to screen to prove that the Model Listener has fired.

Do Client side Swing listeners not work over RMI?

I have managed to reproduce the problem. I include the code for completeness but anticipate that someone will be able to reel off the answer based on experience.

Server Driver Class:

package server;
import java.io.IOException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;

import client.controller.TestTreeControllerService;

import server.controller.TestTreeControllerImpl;


public class TestTreeServerStart {

    /**
     * @param args
     */
    public static void main(String[] args) {
        new TestTreeServerStart();

    }

    public TestTreeServerStart() {

        try {

            LocateRegistry.createRegistry(1099);

            TestTreeControllerService c = new TestTreeControllerImpl();

             Registry registry = LocateRegistry.getRegistry();
             registry.rebind("TestTreeControllerService", c);

            System.out.println("Started the RMI Server");
        }
        catch (RemoteException e) {
            System.out.println(e.getMessage());
        }
    }

}

Server Controller Implementation Class:

package server.controller;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import client.controller.TestTreeControllerService;



@SuppressWarnings("serial")
public class TestTreeControllerImpl extends UnicastRemoteObject implements TestTreeControllerService {

    /**
     * 
     */
    //private static final long serialVersionUID = -8137864611400855504L;
    private DefaultTreeModel m ;


    public DefaultTreeModel getModel() {

        return m;
    }


    public TestTreeControllerImpl() throws RemoteException {

        super();
        m = new DefaultTreeModel(new DefaultMutableTreeNode("Root"));

    }

    public void addNodeAction() throws RemoteException {
        DefaultTreeModel m = (DefaultTreeModel) getModel();
        DefaultMutableTreeNode newNode = new DefaultMutableTreeNode("New Node");
        DefaultMutableTreeNode root = (DefaultMutableTreeNode) m.getRoot();
        root.add(newNode);
        //m.insertNodeInto(newNode, (DefaultMutableTreeNode) m.getRoot(), m.getChildCount(m.getRoot()));
        m.nodeStructureChanged(root);

    }

}

Client Driver Class:

package client;
import java.rmi.Naming;
import java.rmi.RemoteException;

import client.controller.TestTreeControllerService;
import client.view.TreeTestClient;



public class TreeTestClientStart {

    /**
     * @param args
     */
    public static void main(String[] args) {


            try {
                TestTreeControllerService c = (TestTreeControllerService) Naming.lookup("rmi://localhost:1099/TestTreeControllerService");
                 new TreeTestClient(c);
            }
            catch(RemoteException e) {
                System.out.println("Remote service not found: " + e.getLocalizedMessage());
            }
            catch (Exception e) {
                System.out.println("Splat");
            }
    }

}

Client Controller Interface:

package client.controller;
import javax.swing.tree.DefaultTreeModel;


public interface TestTreeControllerService extends java.rmi.Remote {


    public DefaultTreeModel getModel() throws java.rmi.RemoteException;

    public void addNodeAction() throws java.rmi.RemoteException;
}

Client UI:

package client.view;
import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;

import client.controller.TestTreeControllerService;
import client.view.action.AddNodeAction;
import client.view.action.RefreshTreeAction;



public class TreeTestClient {

    private JTree t;
    private TestTreeControllerService c;

    public JTree getTree() {
        return t;
    }

    public TestTreeControllerService getController() {
        return c;
    }

    public void setTree(JTree tIn) {
        t = tIn;
    }

    public TreeTestClient(TestTreeControllerService cIn) {

        //Add controller
        try {
            c = cIn;

            //Draw Frame & Panel  - set dimensions
            JFrame f = new JFrame();
            f.setSize(new Dimension(800,600));
            JPanel p = new JPanel();
            p.setSize(new Dimension(800,600));

            //Create a tree and add the Model from the Controller to it
            t = new JTree();
            t.setModel(c.getModel());


            //Try a Tree Model Listener
            t.getModel().addTreeModelListener(new RefreshTreeAction(this));

            //Add listener to a button which adds nodes to the tree when clicked
            JButton addNode = new JButton("Add node");
            addNode.addActionListener(new AddNodeAction(this));

            JScrollPane s = new JScrollPane(t);


            p.add(s);
            p.add(addNode);

            p.setVisible(true);
            f.add(p);
            f.setVisible(true);
    }
    catch(Exception e) {
        System.out.println("Splat");
    }
    }

}

*Client "Add Node" Action Listener (invokes Add Action in Controller) *

package client.view.action;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;

import javax.swing.table.DefaultTableModel;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;

import client.view.TreeTestClient;


public class AddNodeAction implements ActionListener {

    private TreeTestClient treeTest;

public AddNodeAction(TreeTestClient treeTestIn) {
    treeTest=treeTestIn;
}
@Override
public void actionPerformed(ActionEvent arg0) {

    try {
        treeTest.getController().addNodeAction();
    } catch (RemoteException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
}

}

Client "Refresh Action" Tree Listener (Prints to Screen to prove that Listener fired)

package client.view.action;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.event.TreeModelEvent;
import javax.swing.event.TreeModelListener;

import client.view.TreeTestClient;


public class RefreshTreeAction implements PropertyChangeListener, TreeModelListener {

    private TreeTestClient treeTest;

    public RefreshTreeAction(TreeTestClient treeTestIn) {
        treeTest = treeTestIn;
    }
    private void refreshTree() {
        System.out.println("Refresh tree fired");

    }
    @Override
    public void treeNodesChanged(TreeModelEvent arg0) {
        refreshTree();

    }

    @Override
    public void treeNodesInserted(TreeModelEvent arg0) {
        refreshTree();
    }

    @Override
    public void treeNodesRemoved(TreeModelEvent arg0) {
        refreshTree();

    }

    @Override
    public void treeStructureChanged(TreeModelEvent arg0) {
            refreshTree();

    }
    @Override
    public void propertyChange(PropertyChangeEvent arg0) {
        refreshTree();

    }

}

Solution

  • The TreeModel exported by the server is serialized to the client as the client's own copy. The server doesn't know anything about what happens to the client's copy, and the client doesn't know anything about what happens to the server's copy. They are not the same object.