350 likes | 484 Views
COMPS311F. Li Tak Sing. RMI callbacks. In previous example, only the client can initiate a communication with the server. The server can only response to a server's request.
E N D
COMPS311F Li Tak Sing
RMI callbacks • In previous example, only the client can initiate a communication with the server. The server can only response to a server's request. • For example, if we want to use RMI to write a chat server, when a client send in a message, we need to send it to all connecting clients. In this case, we need the server to be able to initiate the communication with the clients. • In order for the server to initiate a request, the client should also be a Remote object. The server should have a copy of the stub. This can be done by invoking a remote method by the client which passes itself as a parameter to the server.
Passing the client to the server • The first step is to create a client interface: • public interface Client extends Remote { public void callBack(...) throws RemoteException; } • The server should have a method which allows the client to pass itself as the parameter: • public interface Server extends Remote { public void connect(Client client) throws RemoteException; .......}
Implementation of the client • The implementation of the client should consists of a statement that invoke the connect method of the server that passes itself to the server:....server.connect(this); // A statement in the client....
Implementation of the server • The server should use an attribute to store the connected client: public class ServerImp implements Server, Serializable { private client; public void connect(Client c) throws RemoteException { client=c;} .... //whenever there is a need to contact the client, we can: client.callBack(..); .... }
A Chat server using RMI • You can download the program at • http://plbpc001.ouhk.edu.hk/~mt311f/examples/mt3112010/src/r.zip • The server is stored in the package RMIChat • In the above file, there is another server in the package RMIChat2 which we will talk about later. • The program has four files: • ChatServer: interface for the chat server • ChatServerImp: implementation of the chat server • ChatClient: interface for the chat client • ChatClientImp: implementation of the chat client
ChatServer public interface ChatServer extends java.rmi.Remote { public void send(String st) throws java.rmi.RemoteException; //the client uses it to send a //message to the server public void connect(ChatClient c) throws java.rmi.RemoteException; //the client sends itself to the //server public void disconnect(ChatClient c) throws java.rmi.RemoteException; //the client disconnect from the //server }
ChatServerImp • It contains a JFrame and a JTextArea for the displaying all messages from the clients. • The bottom line displays all connected clients.
Declaration of ChatServerImp public class ChatServerImp extends UnicastRemoteObject implements ChatServer{ ..... }
Some Attributes of ChatServerImp • Vector<ChatClient> clients; //this stores all connected clients
Some Methods of ChatServerImp public void send(String st) throws RemoteException { Vector<ChatClient> problem = new Vector<ChatClient>(); textarea.append(st+"\n"); synchronized (clients) { for (ChatClient c : clients) { try { c.toClient(st); } catch (Exception e) { problem.add(c); }
Some Methods of ChatServerImp } for (ChatClient c : problem) { clients.remove(c); } displayClients(); } }
Some Methods of ChatServerImp public void connect(ChatClient c) throws RemoteException { clients.add(c); displayClients(); }
Some Methods of ChatServerImp public void disconnect(ChatClient c) throws RemoteException { clients.remove(c); displayClients(); }
The main method of ChatServerImp public static void main(String st[]) throws RemoteException { ChatServer s = new ChatServerImp(); java.rmi.registry.Registry r = java.rmi.registry.LocateRegistry.getRegistry(); r.rebind("chat", s); }
ChatClient public interface ChatClient extends java.rmi.Remote { public void toClient(String st) throws java.rmi.RemoteException; //this is the callback for the //server to send a string to the client public String name() throws java.rmi.RemoteException; //this is another callback for the server to get the name of //the client. }
ChatClientImp • The bottom textfield is for the user to type in the name. • The connect bottom is used to connect to the chat server. • Once the connection is made, the button becomes the disconnection button.
ChatClientImp • There is a textfield at the top to allow the user to type a message. • When the user presses the send button, the message would be sent to the server. • The message broadcasted from the server would be displayed on the middle textarea.
Declaration of ChatClientImp public class ChatClientImp extends UnicastRemoteObject implements ChatClient { ...
Some Attributes of ChatClientImp • JTextField name; //the textfield at the bottom of the //window that allows the user to type in the name • JTextArea textarea; //the textarea for the display of //messages broadcasted from the server. • ChatServer server; //the chat server
The action listener of the send button public void actionPerformed(ActionEvent e) { try { server.send(name.getText() + ":" + textfield.getText()); textfield.setText(""); } catch (RemoteException ex) { } }
The action listener of the connect button public void actionPerformed(ActionEvent e) { try { if (connect.getText().equals("connect")) { Registry registry = java.rmi.registry.LocateRegistry.getRegistry("localhost"); server = (ChatServer) registry.lookup("chat"); server.connect(ChatClientImp.this); ...
The action listener of the connect button } else { connect.setText("connect"); server.disconnect(ChatClientImp.this); } } catch (NotBoundException ex) { }
Some methods of ChatClientImp public String name() throws RemoteException { return name.getText(); } public void toClient(String st) throws java.rmi.RemoteException { textarea.append(st + "\n"); }
The main method of ChatClientImp public static void main(String st[]) throws RemoteException { new ChatClientImp(); } • Note that we do not need to register the client with any RMI registries. The client would be sent to the server via a method call. So the server would not need to get the client from an RMI registry.
Some features of this RMI server • There is no need to handle multithreading in the RMI version of the server. Multithreading is handled by the internal mechanism of RMI. • Although we do not create threads, we still need to make the server thread safe as RMI is multithreaded. Therefore, we use the synchronized keyword whenever needed.
Problems of the RMI ChatServer • Although we have a button for the user to disconnect from the server, it has problem for the server to keep track of clients that have quit execution without informing the server. • In the socket version of the ChatServer, when a client quit execution, a network exception would be thrown at the server side so that the server would know that the client has quit. This is not the case for RMI. The server would not know that a client has quit execution.
The java.rmi.server.Unreferenced interface • This interface has one method: • public void unreferenced() throws RemoteException • After a remote object has implemented this interface, then the unreferenced method will be invoked when it is not referenced anywhere.
The java.rmi.server.Unreferenced interface • There are sever ways that an external reference to a remote object disappear: • The client quits execution. • The reference to the object is set to null. For example: • ChatServer server=registry.lookup("chat");.....server=null; //the remote object is not referenced by server • The variable that references the object is garbage collected.
How to know that a client has quit? • For every client that is connected to the server, we pass to it a newly created object that has implemented the Unreferenced interface. Then, when the client has quit, the object is not reference anywhere as we only pass the object to one client. In this way, the corresponding unreferenced method would be invoked.
But there is a delay before unreferenced is called • Note that the unreferenced method would only be invoked for a delay of 10 minutes after all referencing clients have quit.
Modified Chat server • You can find the server in the same file for the original server. But this time, the server is in the RMIChat2 package. • A new Contact interface is needed for this purpose: public interface Contact extends java.rmi.Remote { } • The interface has no method. It only use is to have the object implementing this also implements the Unreferenced interface.
Implementation of Contact public class ContactImp extends UnicastRemoteObject implements Contact, Unreferenced { private ChatClient client; public ContactImp(ChatClient c) throws RemoteException { client = c; } public void unreferenced() { clients.remove(client); displayClients(); } }
Changes to ChatServerImp The connect method is changed as following: public Contact connect(ChatClient c) throws RemoteException { clients.add(c); displayClients(); return new ContactImp(c); }
Changes to ChatClient • The following attribute is added: • private Contact contact; • The code to connect to the server is changed as following: Registry registry = java.rmi.registry.LocateRegistry.getRegistry("localhost"); server = (ChatServer) registry.lookup("chat2"); contact=server.connect(ChatClientImp.this); .......