You are on page 1of 8

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

Implementing Multiple Remote GUIs with RMI


Copyright Gordon R. Durand, 1999-2000 Technical or typographical errors? Please email me. You can download the code for this article.

Introducing RMI
The Remote Method Invocation API allows Java objects to send messages to Java objects running in other Java Virtual Machines anywhere on a TCP/IP network. Declare the remote methods in an interface which extends the Remote interface
import java.rmi.*; public interface HelloIntf extends Remote { public String getGreeting() throws RemoteException; }

Implement the interface in a class that extends UnicastRemoteObject


import java.rmi.*; import java.rmi.server.*; import java.rmi.registry.*; public class HelloRmt extends UnicastRemoteObject implements HelloIntf { public HelloRmt () throws RemoteException {} public String getGreeting() { return "Hello from HelloRmt!"; } public static void main (String[] args) { HelloRmt remote; System.setSecurityManager(new RMISecurityManager()); try { LocateRegistry.createRegistry(Registry.REGISTRY_PORT); remote = new HelloRmt(); Naming.rebind("HelloRmt", remote); } catch (Exception e) { e.printStackTrace(); } System.out.println("HelloRmt bound in registry"); } }

Notice that HelloRmt's constructor throws a RemoteException, but does nothing else. HelloRmt's main method does all the work. First we install a SecurityManager. Then we create a Registry object (most RMI examples run the registry in a separate process, but it's not necessary). Finally, we create the remote object and bind it to a name in the Registry. Now we need a client to test the remote object
public class HelloRMI { public static void main (String[] args) {

1 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

HelloIntf remote = null; try { remote = (HelloIntf) Naming.lookup ("HelloRmt"); System.out.println(remote.getGreeting()); } catch (Exception e) { e.printStackTrace(); } } }

HelloRMI never actually gets instantiated. A static main method does all the work. First it calls Naming.lookup, which returns a reference to a Remote type. It casts that to the HelloIntf type, and call its getGreeting method. The RMI implementation does the rest, calling the remote object's getGreeting method, serializing the returned String, and delivering it back to HelloRMI. Compile the classes with javac
>javac *.java

Generate the stub class with rmic


>rmic -v1.2 HelloRmt

Create a text file called policy with your security policy


grant { // Allow everything for now permission java.security.AllPermission; };

Then start HelloRmt in its own window


>start "HelloRmt" java -Djava.security.policy=policy HelloRmt

Wait until you see the "HelloRmt bound in registry" message and then start HelloRMI
>java HelloRMI Hello from HelloRmt!

You can see that RMI can make remote objects appear to be local. RMI can also serialize objects (such as our return String) and send them over the network as the parameters and return values of remote methods.

You Want Fries With That?


RMI can do even more. It can request the bytecodes for the classes themselves. If an RMI call returns a reference to a class unknown to the local VM, the VM will first try to load the class from the local classpath. Failing that, it will request the class from the remote object's codebase, typically through an HTTP GET. This ability to ship an object's behavior along with the object itself makes RMI an extremely powerful tool for distributed applications. You don't even need a full scale HTTP server. With Java, you can write your own class server with just a few lines of code. A ClassServer would create a ServerSocket to listen on a specified port

2 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

server = new ServerSocket(port);

and then wrap itself in a Thread to wait for a request


private void newListener() { (new Thread(this)).start(); }

The heart of the ClassServer is in its run method


public void run() { Socket socket; try { socket = server.accept(); } catch (IOException e) { e.printStackTrace(); return; } newListener(); try { DataOutputStream out = new DataOutputStream(socket.getOutputStream()); try { BufferedReader in = new BufferedReader( new InputStreamReader(socket.getInputStream())); String path = getPath(in); byte[] bytecodes = getBytes(path); System.out.println("Sending " + socket.getInetAddress() + " " + path); try { out.writeBytes("HTTP/1.0 200 OK\r\n"); out.writeBytes("Content-Length: " + bytecodes.length + "\r\n"); out.writeBytes("Content-Type: application/java\r\n\r\n"); out.write(bytecodes); out.flush(); } catch (IOException ioe) { ioe.printStackTrace(); return; // execution will actually go to finally block first } } catch (Exception e) { out.writeBytes("HTTP/1.0 400 " + e.getMessage() + "\r\n"); out.writeBytes("Content-Type: text/html\r\n\r\n"); out.flush(); } } catch (IOException ex) { ex.printStackTrace(); } finally { try { socket.close(); } catch (IOException e) { } } }

On receiving a request, the ClassServer spins off a new Thread to continue listening, and then parses the input stream for a GET header and a class file name. It reads the class file and send the bytecodes, along with the appropriate header, as a response to the GET.

Skinny Client Fattens Up


Since RMI can request bytecodes when it needs them, a remote GUI only needs to know about a single remote method. Suppose a GUIServer implements

3 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

public interface GUIServerIntf extends Remote { public Panel getGuiPanel() throws RemoteException; }

A GUIClient only needs to request a GuiPanel and display it


Panel panel; try { panel = (Panel) guiServer.getGuiPanel(); } catch (RemoteException e) { e.printStackTrace(); } if (panel != null) { add(panel); panel.setVisible(true); }

When GUIClient request a GUIPanel, the GUIServer instantiates the class, serializes it (and all the objects it references), and returns a reference to GUIClient. GUIClient, finding itself with a reference to an unknown object which itself references other unknown objects, attempts to load the bytecodes for those objects. When it fails to find them in the local classpath, it GETs them from a remote ClassServer. The beauty of this scenario is that the GUIClient always gets fresh bytecodes in their latest revision, without the user ever needing to update GUIClient itself. See Diagram 1: GUI Server

Setting Up Communications
Since GUIPanel brings its own cohorts and their behavior with it, it can look up Remote objects on the server to make calls home, and even set up a remote object on the client side to handle callbacks from the server. A couple minor problems must be overcome. First, because GUIPanel's constructor is actually called on the server side (prior to it being serialized and sent to the client), you don't want to attempt to set up communications in the constructor. Rather, you need a setup() method that the GUIClient can call once GUIPanel gets to the client side. In order to do this you'll have to have your GUI panel extend RemoteGUI, which extends Panel
public abstract class RemoteGUI extends Panel public abstract void setup(String host);

GUIClient can cast the object reference to a RemoteGUI and call its setup method
try { panel = (RemoteGUI) guiServer.getGuiPanel(); } catch (RemoteException e) { e.printStackTrace(); } if (panel != null) { panel.setup(host); add(panel); panel.setVisible(true); }

Second, since GUIPanel extends RemoteGUI which extends Panel, it can't also extend UnicastRemoteObject, so it can't handle remote method calls itself. It might be better, anyway, to encapsulate that functionality in another class

4 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

public class GUIPanelRmt extends UnicastRemoteObject implements GUIPanelIntf { // ... all the gory details here.

And have GUIPanel create GUIPanelRmt in its setup method


public void setup(String host) { try { rmt = new GUIPanelRmt (this, name, host); } catch (RemoteException e) { e.printStackTrace(); } }

Handling Multiple Clients


In order for a server to handle multiple GUIs we must first give each new instance of GUIPanel a unique name
static int panelCount = 0; public Panel getGuiPanel() throws RemoteException { String name = "GUIPanel" + panelCount++; return new GUIPanel(name); }

Then, in GUIPanel's setup routine, when we call GUIPanelRmt's constructor, GUIPanelRmt can register itself in a local registry with its unique name
Naming.rebind(name, this);

and then call a remote method on the server side


String localHost = (InetAddress.getLocalHost()).getHostAddress(); guiServer.addGUI(localHost + "/" + name);

which will add its remote reference to a Vector of remote references


private Vector guis; public void addGUI(String name) throws RemoteException { try { Object gui = Naming.lookup ("rmi://" + name); guis.add(gui); } catch (Exception e) { e.printStackTrace(); } }

Finally, whenever the server side object's state changes, it broadcasts a message to all registered GUIs
public void updateGUIs(int len) { GUIPanelIntf gui; Iterator i = guis.iterator(); while (i.hasNext()) { gui = (GUIPanelIntf) i.next (); try {

5 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

gui.updateGUI(len); } catch (RemoteException e) { i.remove(); } } }

Notice that if we get an exception in the broadcasting loop, we simply remove that reference from our Vector, as a quick-and-dirty way of handling GUI termination without implementing a removeGUI method. See Diagram 2: Multiple Remote GUIs

A Complete Demonstration
You can download the code for a complete demonstration of a GUIServer/GUIClient which supports multiple remote GUIs. On the server side we have a GUIServer and a ClassServer. On the client side we start with a simple GUIClient. The server side has a JBoard object which interacts with remote JBoardGUls. The adapter classes which actually handle the RMI methods are JBoardRmt on the server side and JBoardGUIRmt on the client side. The GUI has a slider which adjusts a value in a text box. Changing the value in the JBoardGUI panel sends setLength messages via JBoardGUIRmt to JBoardRmt, which passes it on to JBoard. JBoard then sends a updateGUls message to JBoardRmt, which broadcasts updateLength messages to all GUIs. See Diagram 3: Remote GUI Classes

Notes
The java commands to start up GUIServer and GUIClient require some additional options. To simplify things, we put these commands into DOS batch files. To simplify further, we can create a shortcut to the batch files. Building the GUIServer and GUIClient class files involves a number of steps, including running the javac compiler, copying files, and running the rmic stub compiler. To simplify things, we put these commands into a batch file. If calls to Naming take longer than one or two seconds, it's not Java, it's a DNS lookup timeout problem. Edit your \winnt\system32\drivers\etc\hosts file to specify IP addresses for each machine involved.

Diagrams
Diagram 1: GUI Server return to text

6 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

Diagram 2: Multiple Remote GUIs

return to text

Diagram 3: Remote GUI Classes

return to text

7 of 8

12/27/2012 7:16 PM

Implementing Multiple Remote GUIs with RMI

http://grdurand.com/java/rmi.html

8 of 8

12/27/2012 7:16 PM

You might also like