CSCI A348/A548

Lecture Notes 26

Fall 1999


Revisiting the weather applet application with RMI and servlets. Automating server startup.

We now understand RMI pretty well. There's not much we need to know in terms of how we think about our application: we do it as if the programs were all running on the same machine. That's the beauty of RMI. The difficulty is in the ancillary bookeeping: interface files, the need to run the RMI compiler, the necessity of being aware of the files that it creates in the process (and through which networking is actually done). All of this is apparent in the homework 7 exercise, where the client distribution and server start-up have to be done by hand: the client needs to get her/his client software, the server administrator needs to start the server before the clients can communicate with each other. Let's now revisit the weather applet application posted in lecture notes 23. The client (being an applet) will be distributed autimatically. The server-side will be implemented with servlets, and thus we won't need to worry about it having to be started by hand any longer. The first time the servlet is needed it will be started and will make the state of the weather on our host available to the world by RMI. That's the plan for today's lecture.

The first thing we need to do is to:

1. Create an interface that defines the exported methods that the remote server will implement. Let's call this the Weather Server Interface (WSI) and place it in the servlet area.

Here's the code:

import java.rmi.*;

public interface WSI 
  extends Remote 
{ 
  public String read() throws RemoteException; 
}
This file's name is WSI.java and we place it in the servlet area. Now we need to write the servlet.

2. Define a servlet that implements the interface just described. The servlet should either subclass UnicastRemoteObject or implement the Remote interface. Other than declaring its remote methods to throw RemoteException objects the remote object does not need to do anything special to allow its methods to be invoked remotely.

Here's the code:

import java.io.*; 
import java.net.*; 
import java.rmi.*; 
import java.rmi.server.*; 
import java.rmi.registry.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public class WS 
  extends HttpServlet 
  implements Remote, WSI 
{ 
  public String read() 
  { 
    return "The time is:\n  ***(" 
    + new java.util.Date().toString()  
    + ")***\n... and the weather is fine.";    
  } 

  public void init(ServletConfig conf) 
    throws ServletException 
  {
    super.init(conf); 
    try {
      Registry reg;
      UnicastRemoteObject.exportObject(this); 
      try {
        reg = LocateRegistry.getRegistry(39904); 
        reg.list(); 
      } catch (Exception e) { 
        reg = null; 
      } 
      if (reg == null) {
        try { 
          reg = LocateRegistry.createRegistry(39904); 
        } catch (Exception e) { } 
      } 
      try { 
        reg.rebind("Letterman", this); 
      } catch (Exception e) { 

      } 
    } catch (Exception e) { 
      System.out.println("Error: " + e); 
    } 
  } 

  public void doGet(HttpServletRequest req, 
                    HttpServletResponse resp) 
    throws ServletException, IOException 
  {
    resp.setContentType("text/html"); 
    resp.getWriter().println(
      "<html><head><title>Letterman's Applet</title></head>"
    + "<body bgcolor=white>" 
    + "<applet codebase=http://tucotuco.cs.indiana.edu:59904/lab14" 
    + "\ncode=WA width=400 height=300> </applet></body></html>" 
    ); 
  } 

  public void doPost(HttpServletRequest req, 
                     HttpServletResponse resp) 
    throws ServletException, 
           IOException 
  {
    doGet(req, resp); 
  } 

}
We need to note that: This means we don't need to write any file anywhere for client distribution. It also gives us a nicer way to register with a registry that may or may not have been started, and we notice that we also save an additional step in the RMI development process, step that was listed as:

3. Write a program that creates an instance of your remote server. Export the object making it available for use by clients by registering the object by name with a registry service.

We've done this already in init() because we don't create an instance of the servlet, the web server does, and when it creates it that's the method that it calls: init(). So we move to the next step:

4. Compile the servlet (with javac) and use rmic to generate the stub and skeleton for the remote object. Invoke rmic on the remote object class (not interface). It creates and compiles two new classes, with the suffixes _Stub and _Skel.

IMPORTANT NOTE

Set your CLASSPATH variable now to:

setenv CLASSPATH .:/l/jdk1.1/lib/classes.zip:/l/jsdk2.0/lib/jsdk.jar
in your .login file and source it (or log out and log back in after you make the change). Now compile the files as discussed (with javac) then run rmic on the server class.

5. The next step in the RMI registry is a comment on the registry used. You can either run the rmiregistry program (not recommended here) or have the remote server object act as its own registry server. It actually does it nicely, and we'll discuss that in class. So as far as this step is related again we don't need to do anything more than what we've already done. Now it's time to write the applet.

6. Now you can write a client program that uses the remote object that is exported by the server. This will be an applet and we need to look into the servlet's doGet() method to find out the name of the class and the location, relative to the web server's htdocs directory.

import java.rmi.*;
import java.applet.*;
import java.awt.*;
import java.awt.event.*; 

public class WA 
  extends Applet 
  implements ActionListener 
{
  private TextArea status;
  WSI server;
  private Button request; 

  public void init() 
  {
    add (status = new TextArea(5, 50)); 
    add (request = new Button("Retrieve")); 
    status.setEditable(false);
    status.setText("Starting Connection");
    request.addActionListener(this); 

    try {

      server = (WSI)Naming.lookup("rmi://" 
                            // protocol used  
                                + getCodeBase().getHost() 
                            // location of registry server  
                                + ":39904" 
                            // your 3990x port here  
                                + "/Letterman" 
                            // remote server's name   
                                 ); 
    } catch (Exception e) {
      System.out.println("Error: " + e.toString()); 
      status.setText("Error: " + e.toString()); 
    } 
  }
  public void actionPerformed (ActionEvent e) {
    try {
      status.setText(server.read() + "\n"); // replaced append
    } catch (RemoteException ex) {
      status.setText("Error: " + ex.toString()); 
    } 
  }
}
The applet needs to have access to the _Stub class and the server interface so we either copy them to the applet's location or create soft links to them there. Then compile the applet.

Now we're ready for any incoming calls.


Summary of steps taken:

  1. create WSI.java in jserv/servlets

  2. create WS.java in jserv/servlets

  3. compile them (javac W*.java in jserv/servlets)

  4. run rmic WI (in jserv/servlets) to get WS_Skel.class and WS_Stub.class

  5. create WA.java in apache_1.3.9/htdocs/lab14

  6. create symbolic links in apache_1.3.9/htdocs/lab14
    ln -s /u/dgerman/November/jserv/servlets/WS_Stub.class WS_Stub.class
    ln -s /u/dgerman/November/jserv/servlets/WSI.class WSI.class
    to the classes that the client class needs to use

  7. compile (javac WA.java) the client applet in apache_1.3.9/htdocs/lab14

  8. call the servlet from your browser
    http://tucotuco.cs.indiana.edu:59904/servlet/WS
    and work with the resulting client applet


Last updated: November 30, 1999 by Adrian German