这就是它的完成方式
抱歉,我的示例已经完成,不完全适合您的代码,但它应该很容易适应
1. 共享代码(将服务器接口暴露给任何客户端的 jar)
//The interface that RMI will use to pass event handlers between client and server
public interface ServerEventHandler extends Remote {
//This is not actually required (using it for testing)
public void setId(int id) throws RemoteException;
//This is not actually required (using it for testing)
public int getId() throws RemoteException;
// Here we use String as event type.
// Could be any number of Serializable or Remote arguments
public void handle(String message) throws RemoteException;
}
// A simple interface that will allow us to remotely (un)register event handlers
public interface ServerObjectWithCallback extends Remote {
public void addServerEventHandler(ServerEventHandler handler) throws RemoteException;
public void removeServerEventHandler(ServerEventHandler handler) throws RemoteException;
}
2. 服务器代码(启动和接口实现)
public class ServerObjectWithCallBackImpl implements ServerObjectWithCallback {
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss,SSS", Locale.ROOT);
// A counter to automatically assign unique ids to new event handlers
private int handCount = 0;
// This will provide references to client-side event handlers and a way to
// access their ids with no remote call
private HashMap<Integer, ServerEventHandler> handlers = new LinkedHashMap<Integer, ServerEventHandler>();
// A fixed pool of 10 threads for asynchronous event handling
private ExecutorService threads = Executors.newFixedThreadPool(10);
// A simple counter that will allow printing a different message each time
// (for the sake of this test only)
private int eventcounter = 0;
@Override
public synchronized void addServerEventHandler(ServerEventHandler handler) throws RemoteException {
// Assign a new id to handler and keep a reference to it
handler.setId(++handCount);
handlers.put(handCount, handler);
System.out.println("New handler added with id " + handCount);
}
@Override
public synchronized void removeServerEventHandler(ServerEventHandler handler) throws RemoteException {
try {
// Get handler id and forget about it
int id = handler.getId();
handlers.remove(id);
System.out.println("Handler with id " + id + " removed");
} catch (RemoteException e) {
System.err.println("#Could not retrieve id for handler to unregister");
}
// TODO safer method "removeById" that will avoid unnecessary remote call to getId()
}
public synchronized void onServerEvent(Object event) {
// This is where the remote callbacks take place
// This method is called from server side and performs asynchronous
// callback on each registered client
// TODO event should actually be of a more meaningfull type than Object
System.out.println(sdf.format(new Date()) + "> Firing event #" + ++eventcounter + ": " + event + " (" + handlers.size()
+ " registered handlers)");
for (int id : handlers.keySet()) {
try {
ServerEventHandler handler = handlers.get(id);
threads.execute(new EventRunnable(handler, id, event, eventcounter));
} catch (Exception e) {
System.err.println("#Could not execute async callback on handler " + id);
e.printStackTrace();
}
}
}
// A private runnable that will suit our needs to perform callbacks asynchronously
// If we didn't, server might hang because of client behavior or missing client
// Moreover, one client being slow would delay event dispatch to other clients
private static class EventRunnable implements Runnable {
private ServerEventHandler handler;
private int handlerId;
private Object event;
private int eventNum;
public EventRunnable(ServerEventHandler handler, int handlerId, Object event, int eventNum) {
this.handler = handler;
this.handlerId = handlerId;
this.event = event;
this.eventNum = eventNum;
}
@Override
public void run() {
try {
handler.handle("message #" + eventNum + " sent on " + sdf.format(new Date()) + " = " + event);
} catch (Exception e) {
// TODO Better exception management : react depending on cause
System.err.println("handler " + handlerId + " seems to have gone away: " + e.toString());
// TODO Self-unregister handler after some unavailability time
// and possibly destroy client session as well
}
}
}
}
public class MainCallback {
private static ServerObjectWithCallBackImpl soc;
private static ServerObjectWithCallback stub;
public static void main(String[] args) throws RemoteException, AlreadyBoundException, InterruptedException {
Registry reg = null;
try {
// Startup RMI registry
reg = LocateRegistry.createRegistry(1099);
System.out.println("RMI started");
// Instantiate the RMI entry-point for the client, which will also
// be the object sending events
soc = new ServerObjectWithCallBackImpl();
stub = (ServerObjectWithCallback) UnicastRemoteObject.exportObject(soc, 0);
// Bind the remote object's stub in the registry
reg.bind("CallbackServer", stub);
System.out.println("ServerObjectWithCallback bound to RMI (CallbackServer). Waiting for client");
// This will be our event object : a counter
int count = 0;
while (true) {
// Wait between 1 and 5 seconds
Thread.sleep((int) (Math.random() * 4000 + 1000));
// Fire event
soc.onServerEvent(++count);
}
} finally {
try {
// Close up registry
UnicastRemoteObject.unexportObject(reg, true);
System.out.println("RMI registry destroyed");
} catch (Exception e) {
System.out.println("Could not destroy RMI registry");
}
}
}
}
3. 客户端代码(启动和处理程序实现)
// This is our event handler implementation
// Note how it extends UnicastRemoteObject:
// this is what allows the magic of calling client methods from server,
// along with the fact that it implements ServerEventHandler, which is a Remote interface known from server
public class ClientSideEventHandler extends UnicastRemoteObject implements ServerEventHandler {
private static final long serialVersionUID = 5094195049935134358L;
private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss,SSS", Locale.ROOT);
// id is just a way of discriminating clients for this test
private int id;
public ClientSideEventHandler() throws RemoteException {
super();
}
// Make id available to server so it can number clients as it wishes
@Override
public int getId() throws RemoteException {
return id;
}
@Override
public void setId(int id) throws RemoteException {
this.id = id;
}
// This is the actual callback method
@Override
public void handle(String message) throws RemoteException {
System.out.println(sdf.format(new Date()) + "> Message from server: " + message);
}
// Overriding toString allows testing whether the handler is a reference or
// a serialized copy on server side
@Override
public String toString() {
return this.getClass().getSimpleName() + "[" + id + "]";
}
}
public class MainCallback {
public static void main(String[] args) throws InterruptedException, RemoteException {
// Connect to RMI registry on server
Registry registry = null;
try {
registry = LocateRegistry.getRegistry("localhost", 1099);
System.out.println("Connected to server");
} catch (RemoteException e) {
System.out.println("Error connecting to RMI");
e.printStackTrace();
return;
}
ServerObjectWithCallback soc = null;
// Create an event handler on our side
ClientSideEventHandler handler = new ClientSideEventHandler();
try {
// Get RMI server entry-point from remote RMI registry
soc = (ServerObjectWithCallback) registry.lookup("CallbackServer");
System.out.println("CallbackServer recovered from server");
// Register for server events
soc.addServerEventHandler(handler);
System.out.println("Handler registered. Waiting for events");
} catch (RemoteException | NotBoundException e) {
System.out.println("Error getting MyRemoteInterface");
e.printStackTrace();
return;
}
// Just wait indefinitely for an event to happen
while (true) {
Thread.sleep(1000);
}
}
}
所以:
- 您的服务器通过共享 jar 公开其远程接口
- 其中之一将在服务器端实现以提供事件触发(即您的 CalculatorService)
- 另一个将在客户端实现以提供事件处理(我猜这将是您的 PropertyChangeSupport)
- 客户端事件处理程序也必须是 UnicastRemoteObject 的子类,以便它可以通过代理传递给服务器,并且服务器可以远程调用它的方法。
- 回调应该被异步调用,以防止一个客户端阻塞服务器线程和/或延迟对其他客户端的回调