Swing GUI 客户端侦听器未响应远程 RMI 属性的更新
Posted
技术标签:
【中文标题】Swing GUI 客户端侦听器未响应远程 RMI 属性的更新【英文标题】:Swing GUI Client Listeners not responding to update of Remote RMI Property 【发布时间】:2012-01-10 17:23:22 【问题描述】:当显式调用 JTree 的模型时,我在重绘 JTree 时遇到了一些麻烦(我在向其添加一些新节点后进行的调用)。
最初运行良好的代码在应用程序导出到 RMI 后失败了。
我将 DefaultTreeModel 对象存储在 Controller 类中,它是一个远程对象。
我使用 tree.addModel(controller.getModel()); 将 DefaultTreeModel 对象添加到客户端中的 JTree;
我使用订阅客户端 GUI 上的按钮的 ActionListener 来调用控制器中的方法,该方法执行“添加新节点”操作。
我使用 TreeModelListener 将消息打印到屏幕以证明模型侦听器已触发。
客户端 Swing 侦听器不能通过 RMI 工作吗?
我已经设法重现了这个问题。我包含完整的代码,但预计有人将能够根据经验得出答案。
服务器驱动类:
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());
服务器控制器实现类:
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);
客户端驱动类:
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");
客户端控制器接口:
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;
客户端界面:
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");
*客户端“添加节点”动作监听器(在控制器中调用添加动作)*
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();
客户端“刷新操作”树侦听器(打印到屏幕以证明侦听器已触发)
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();
【问题讨论】:
【参考方案1】:服务端导出的TreeModel作为客户端自己的副本序列化给客户端。服务器不知道客户端的副本会发生什么,客户端也不知道服务器的副本会发生什么。它们不是同一个对象。
【讨论】:
根据RMI小道:“远程对象本质上是通过引用传递的。远程对象引用是一个存根,它是一个客户端代理,它实现远程对象的完整远程接口集实现”。如果远程对象通过引用传递给存根,那么当它们的状态发生变化时,任何与存根交互的客户端类都可以观察到它? @7SpecialGems TreeModel 不是远程对象;它不是通过引用传递的;它像我上面写的那样被序列化,即按值传递。它们不是同一个对象。【参考方案2】:通过将以下代码添加到订阅 GUI 上的新按钮的 ActionListener,我能够通过单击按钮检查模型的内容:
//Loop contents of model attached to Client Tree
for (int i=0; i<t.getModel().getChildCount(t.getModel().getRoot()); i++)
System.out.println("From Tree: Row #" + i + ": " + t.getModel().getChild(t.getModel().getRoot(), i));
//Loop contents of model object stored in Controller
try
for (int i=0; i<c.getModel().getChildCount(c.getModel().getRoot()); i++)
System.out.println("From Controller: Row #" + i + ": " + c.getModel().getChild(c.getModel().getRoot(), i));
catch (RemoteException e)
// TODO Auto-generated catch block
e.printStackTrace();
我发现对客户端模型的引用与控制器模型对象中维护的状态不同。 Client 循环中没有输出,但 Controller 的循环给出了正确的状态。
我随后在 GUI 中添加了一个 Swing Timer 以刷新树的模型以匹配构造函数的模型。下面是更新的 GUI 类和 GUI 刷新操作,它们可以工作:
更新的用户界面:
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 javax.swing.Timer;
import client.controller.TestTreeControllerService;
import client.view.action.AddNodeAction;
import client.view.action.GUIRefreshAction;
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 listener that doesn't use the Remote object
t.addTreeSelectionListener(new RefreshTreeAction(this));
//Try a property change listener on the TreeModel
t.addPropertyChangeListener("treeModel", new RefreshTreeAction(this));
//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);
//Add a GUI redraw timer
Timer timer = new Timer(1000, new GUIRefreshAction(this));
timer.setInitialDelay(1);
timer.start();
p.add(s);
p.add(addNode);
p.setVisible(true);
f.add(p);
f.setVisible(true);
catch(Exception e)
System.out.println("Splat");
GUI 刷新监听类
package client.view.action;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.rmi.RemoteException;
import client.view.TreeTestClient;
public class GUIRefreshAction implements ActionListener
private TreeTestClient client;
public GUIRefreshAction(TreeTestClient clientIn)
client = clientIn;
@Override
public void actionPerformed(ActionEvent e)
//Update the Tree's Model to match latest on Server
try
client.getTree().setModel(client.getController().getModel());
catch (RemoteException e1)
e1.printStackTrace();
我希望这有助于对 RMI 客户端 Swing GUI 有相同要求的人更新以响应服务器上的更改。
【讨论】:
以上是关于Swing GUI 客户端侦听器未响应远程 RMI 属性的更新的主要内容,如果未能解决你的问题,请参考以下文章