Java 如何在RMI(客户端代码)中使用在服务器端代码中定义的事件?
在RMI(客户端代码)中,如何使用服务器端代码中定义的事件 例如,以下服务器端代码定义了Java 如何在RMI(客户端代码)中使用在服务器端代码中定义的事件?,java,events,rmi,Java,Events,Rmi,在RMI(客户端代码)中,如何使用服务器端代码中定义的事件 例如,以下服务器端代码定义了PropertyChangeSupport事件 如何在客户端实现它? package rmiservice.services.calculator; import java.beans.PropertyChangeSupport; import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.server.UnicastRe
PropertyChangeSupport
事件
如何在客户端实现它?
package rmiservice.services.calculator;
import java.beans.PropertyChangeSupport;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedList;
import java.util.Queue;
public class CalculatorService extends UnicastRemoteObject implements ICalculator {
private Queue<Integer> numbers = new LinkedList<Integer>();
private Integer result;
***private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);***
public CalculatorService() throws RemoteException {
super();
}
public void start() throws Exception {
java.rmi.registry.LocateRegistry.createRegistry(1099);
Naming.bind("CalculatorService", this);
System.out.println("Calculator Service is Run . . .");
}
public void stop() throws Exception {
Naming.unbind("CalculatorService");
UnicastRemoteObject.unexportObject(this, true);
System.out.println("Calculator Service is Stop . . .");
}
//-------------------------------------------------------------------
//------------------------------ Implements ICalculator -------------
public void addNumber(Integer number) throws Exception {
numbers.add(number);
}
public Integer getResult() throws Exception {
return this.result;
}
public void setResult(Integer result) throws Exception {
Integer oldResult = this.getResult();
this.result = result;
***propertyChangeSupport.firePropertyChange("result", oldResult, result);***
}
public void calculate(Operation operation) throws Exception {
Integer _result = 0;
if (numbers.size() < 2)
return;
switch (operation) {
case Add: {
_result = 0;
while (numbers.size() > 0) {
_result += numbers.poll();
}
break;
}
case Substract: {
_result = numbers.poll();
while (numbers.size() > 0) {
_result -= numbers.poll();
}
break;
}
}
this.setResult(_result);
}
//-------------------------------------------------------------------
包rmiservice.services.calculator;
导入java.beans.PropertyChangeSupport;
导入java.rmi.Naming;
导入java.rmi.RemoteException;
导入java.rmi.server.UnicastRemoteObject;
导入java.util.LinkedList;
导入java.util.Queue;
公共类计算器服务扩展了UnicastRemoteObject实现了ICalculator{
私有队列号=新的LinkedList();
私有整数结果;
***私有属性更改支持属性更改支持=新属性更改支持(此)***
公共计算器服务()引发RemoteException{
超级();
}
public void start()引发异常{
java.rmi.registry.LocateRegistry.createRegistry(1099);
Naming.bind(“CalculatorService”,this);
System.out.println(“计算器服务正在运行…”);
}
public void stop()引发异常{
命名。解除绑定(“计算器服务”);
unexportObject(this,true);
System.out.println(“计算器服务停止…”);
}
//-------------------------------------------------------------------
//------------------------------电子计算器-------------
public void addNumber(整数)引发异常{
数字。添加(数字);
}
公共整数getResult()引发异常{
返回此结果;
}
public void setResult(整数结果)引发异常{
整数oldResult=this.getResult();
this.result=结果;
***propertyChangeSupport.firePropertyChange(“结果”,旧结果,结果)***
}
公共无效计算(操作)引发异常{
整数_结果=0;
if(number.size()<2)
返回;
开关(操作){
案例补充:{
_结果=0;
while(numbers.size()>0){
_结果+=数字。poll();
}
打破
}
案例摘要:{
_结果=数字。poll();
while(numbers.size()>0){
_结果-=number.poll();
}
打破
}
}
这个.setResult(_result);
}
//-------------------------------------------------------------------
}我可能误解了这个问题 但据我所知,RMI并不参与你所问的那种事件绑定 客户端基本上会查找在RMI注册表中注册的对象,并调用其中的方法 您必须自己实现事件处理代码 如果您询问如何调用RMI绑定对象的方法,您可以在
RMI不支持通知。但是,您可以使用带有事件支持的JMXbean,它可以在RMI上使用 为此,您的MBean接口必须进行扩展。就是这样做的 很抱歉,我的示例已经制作好了,不能完全适合您的代码,但它应该很容易适应 1.共享代码(向任何客户端公开服务器接口的jar) 2.服务器代码(启动和接口实现) 因此:
- 您的服务器通过共享jar公开其远程接口
- 其中一个将在服务器端实现,以提供事件触发(这将是您的计算器服务)
- 另一个将在客户端实现,以提供事件处理(我想这就是您的PropertyChangeSupport)
- 客户端事件处理程序还必须是UnicastRemoteObject的子类,以便可以通过代理将其传递给服务器,并且服务器可以远程调用其上的方法
- 回调应该异步调用,以防止一个客户端阻塞服务器线程和/或延迟对其他客户端的回调
UnicastRemoteObject的子类。
它可以手动导出。我真的没有得到反对票:这是一个实际可行的解决方案,详细解释,而且没有解释为什么这会很糟糕!
//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;
}
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");
}
}
}
}
// 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);
}
}
}