Lecture note 7
Download
Report
Transcript Lecture note 7
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/mt311201
0/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);
.......