3

在 RMI(客户端代码)中,如何使用在服务器端代码中定义的事件?

例如,以下服务器端代码定义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);

}
//-------------------------------------------------------------------

}

4

3 回答 3

0

这就是它的完成方式

抱歉,我的示例已经完成,不完全适合您的代码,但它应该很容易适应

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 的子类,以便它可以通过代理传递给服务器,并且服务器可以远程调用它的方法。
  • 回调应该被异步调用,以防止一个客户端阻塞服务器线程和/或延迟对其他客户端的回调
于 2014-04-10T08:10:35.750 回答
0
于 2013-02-12T06:27:42.457 回答
-1

我可能误解了这个问题。

但据我所知,RMI 并不涉及您所询问的那种事件绑定。

客户端基本上会查找在 RMI Registry 中注册的对象并调用其中的方法。

您必须自己实现事件处理代码。

如果您询问如何调用 RMI 绑定对象的方法,您可以在

http://en.wikipedia.org/wiki/Java_remote_method_invocation

于 2013-02-12T06:23:18.037 回答