Spring Semester 2002


Lab Twelve: Applet-Servlet Communication

We start by developing an applet.

import java.applet.*; 
import java.awt.*; 
import java.io.*; 
import java.util.*; 

public class DaytimeApplet extends Applet {
    TextField httpText, httpObject, socketText, socketObject, RMIObject; 
    Button refresh; 
    public void init() {
	setLayout(new BorderLayout()); 
	Panel west = new Panel(); 
	west.setLayout(new GridLayout(5, 1)); 

	west.add(new Label("HTTP text: ", Label.RIGHT)); 
	west.add(new Label("HTTP object: ", Label.RIGHT)); 
	west.add(new Label("Socket text: ", Label.RIGHT)); 
	west.add(new Label("Socket object: ", Label.RIGHT)); 
	west.add(new Label("RMI object: ", Label.RIGHT)); 

	add("West", west); 

	Panel center = new Panel(); 
	center.setLayout(new GridLayout(5, 1)); 

	httpText = new TextField();
	httpText.setEditable(false);
	center.add(httpText);  

	httpObject = new TextField();
	httpObject.setEditable(false);
	center.add(httpObject);  

	socketText = new TextField();
	socketText.setEditable(false);
	center.add(socketText);  

	socketObject = new TextField();
	socketObject.setEditable(false);
	center.add(socketObject);  

	RMIObject = new TextField();
	RMIObject.setEditable(false);
	center.add(RMIObject);  

	add("Center", center); 

	Panel south = new Panel(); 
	refresh = new Button("Refresh"); 
	south.add(refresh);
	add("South", south); 

    }

    public void start() { 
	refresh(); 
    }

    private void refresh() {
	httpText.setText(getDateUsingHttpText());
	httpObject.setText(getDateUsingHttpObject());
	socketText.setText(getDateUsingSocketText());
	socketObject.setText(getDateUsingSocketObject());
	RMIObject.setText(getDateUsingRMIObject());
    }

    private String getDateUsingHttpText () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingHttpObject () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingSocketText () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingSocketObject () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingRMIObject () {
	return "unavailable (for now)"; 
    }

    public boolean handleEvent(Event event) {
	// when the refresh button is pushed, refresh the display 
	// use jdk1.0 events for maximum portability

	switch (event.id) {
	    case Event.ACTION_EVENT:
		if (event.target == refresh) {
		    refresh(); 
		    return true; 
		}
	}

	return false; 

    }

}
Here's an HTML for it:
<html>
  <head><title>Daytime Applet</title></head>
  <body>
    <center><h1>Daytime Applet</h1></center>
    <center>
      <applet code=DaytimeApplet 
              width=300
              height=180>

      </applet>
    </center>
  </body>
</html>
We place these (both files, that is) in
$CATALINA_HOME/webapps/one
That's the folder for the web application we created before.

To access the applet (which does not do much at this point) we use

http://burrowww.cs.indiana.edu:36xxx/one/daytime.html
Here's how the folder looks now:
burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/tomcat/jakarta-tomcat-4.0.3/webapps/one
burrowww.cs.indiana.edu% du -a . 
3       ./DaytimeApplet.class
1       ./WEB-INF/classes/HelloWorld.java
1       ./WEB-INF/classes/HelloWorld.class
3       ./WEB-INF/classes
1       ./WEB-INF/lib
1       ./WEB-INF/web.xml
6       ./WEB-INF
3       ./DaytimeApplet.java
1       ./daytime.html
14      .
burrowww.cs.indiana.edu% 
Now we need a servlet.

import java.io.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public class DaytimeServlet extends HttpServlet {

    public Date getDate() {
	return new Date(); 
    }

    public void doGet(HttpServletRequest req, 
		      HttpServletResponse res) throws ServletException, 
		                                      IOException 
    {	res.setContentType("text/plain"); 
	PrintWriter out = res.getWriter(); 
	out.println(getDate().toString()); 
    }

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

    }
}
We place this servlet in
$CATALINA_HOME/one/WEB-INF/classes
We compile it, then we can access it from this URL, directly
http://burrowww.cs.indiana.edu:36xxx/one/servlet/DaytimeServlet
Our goal now is to make the applet contact the servlet.

Here's the applet, with modifications:

import java.net.URL; 

import java.applet.*; 
import java.awt.*; 
import java.io.*; 
import java.util.*; 

public class DaytimeApplet extends Applet {
    TextField httpText, httpObject, socketText, socketObject, RMIObject; 
    Button refresh; 
    public void init() {
	setLayout(new BorderLayout()); 
	Panel west = new Panel(); 
	west.setLayout(new GridLayout(5, 1)); 

	west.add(new Label("HTTP text: ", Label.RIGHT)); 
	west.add(new Label("HTTP object: ", Label.RIGHT)); 
	west.add(new Label("Socket text: ", Label.RIGHT)); 
	west.add(new Label("Socket object: ", Label.RIGHT)); 
	west.add(new Label("RMI object: ", Label.RIGHT)); 

	add("West", west); 

	Panel center = new Panel(); 
	center.setLayout(new GridLayout(5, 1)); 

	httpText = new TextField();
	httpText.setEditable(false);
	center.add(httpText);  

	httpObject = new TextField();
	httpObject.setEditable(false);
	center.add(httpObject);  

	socketText = new TextField();
	socketText.setEditable(false);
	center.add(socketText);  

	socketObject = new TextField();
	socketObject.setEditable(false);
	center.add(socketObject);  

	RMIObject = new TextField();
	RMIObject.setEditable(false);
	center.add(RMIObject);  

	add("Center", center); 

	Panel south = new Panel(); 
	refresh = new Button("Refresh"); 
	south.add(refresh);
	add("South", south); 

    }

    public void start() { 
	refresh(); 
    }

    private void refresh() {
	httpText.setText(getDateUsingHttpText());
	httpObject.setText(getDateUsingHttpObject());
	socketText.setText(getDateUsingSocketText());
	socketObject.setText(getDateUsingSocketObject());
	RMIObject.setText(getDateUsingRMIObject());
    }

    private String getDateUsingHttpText () { 
	try {

	    URL url = new URL(getCodeBase(), 
			      "servlet/DaytimeServlet"); 

	    HttpMessage msg = new HttpMessage(url);

	    InputStream in = msg.sendGetMessage(); 

	    DataInputStream result = 
		new DataInputStream(new BufferedInputStream(in)); 

	    String date = result.readLine(); 

	    in.close(); 

	    return date; 

	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null; 
	} 
    }

    private String getDateUsingHttpObject () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingSocketText () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingSocketObject () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingRMIObject () {
	return "unavailable (for now)"; 
    }

    public boolean handleEvent(Event event) {
	// when the refresh button is pushed, refresh the display 
	// use jdk1.0 events for maximum portability

	switch (event.id) {
	    case Event.ACTION_EVENT:
		if (event.target == refresh) {
		    refresh(); 
		    return true; 
		}
	}

	return false; 

    }

}
This requires a whole new abstraction, class HttpMessage, below:
import java.io.*; 
import java.net.*; 
import java.util.*; 

public class HttpMessage {
    URL servlet = null; 
    Hashtable headers = null; 
    public HttpMessage(URL servlet) {
	this.servlet = servlet; 
    }
    public InputStream sendGetMessage() throws IOException {
	return sendGetMessage(null); 
    }
    public InputStream sendGetMessage(Properties args) throws IOException {
	String argString = ""; 
	if (args != null) {
	    argString = "?" + toEncodedString(args); 
	}
	URL url = new URL(servlet.toExternalForm() + argString); 
	URLConnection con = url.openConnection(); 
	con.setUseCaches(false); 
	sendHeaders(con); 
	return con.getInputStream();
    }
    public InputStream sendPostMessage() throws IOException {
	return sendPostMessage(null); 
    }
    public InputStream sendPostMessage(Properties args) throws IOException {
	String argString = ""; 
	if (args != null) {
	    argString = toEncodedString(args);
	}
	URLConnection con = servlet.openConnection(); 
	con.setDoInput(true); 
	con.setDoOutput(true); 
	con.setUseCaches(false); 
	con.setRequestProperty("Content-Type",
			       "application/x-www-form-urlencoded"); 
	sendHeaders(con); 
	DataOutputStream out = new DataOutputStream(con.getOutputStream()); 
	out.writeBytes(argString); 
	out.flush(); 
	out.close();
	return con.getInputStream(); 
    }
    public InputStream sendPostMessage(Serializable obj) throws IOException {
	URLConnection con = servlet.openConnection(); 
	con.setDoInput(true); 
	con.setDoOutput(true); 
	con.setUseCaches(false); 
	con.setRequestProperty("Content-Type", 
			       "application/x-java-serialized-object"); 
	sendHeaders(con); 
	ObjectOutputStream out = new ObjectOutputStream(con.getOutputStream()); 
	out.writeObject(obj); 
	out.flush(); 
	out.close(); 
	return con.getInputStream(); 
    }
    public void setHeader(String name, String value) {
	if (headers == null) {
	    headers = new Hashtable(); 
	}
	headers.put(name, value); 
    }
    private void sendHeaders(URLConnection con) {
	if (headers != null) {
	    Enumeration enum = headers.keys(); 
	    while (enum.hasMoreElements()) {
		String name = (String) enum.nextElement(); 
		String value = (String) headers.get(name); 
		con.setRequestProperty(name, value);
	    }
	}
    }
    private String toEncodedString(Properties args) {
	StringBuffer buf = new StringBuffer(); 
	Enumeration names = args.propertyNames(); 
	while (names.hasMoreElements()) {
	    String name = (String) names.nextElement();
	    String value = args.getProperty(name); 
	    buf.append(URLEncoder.encode(name) + "=" + URLEncoder.encode(value)); 
	    if (names.hasMoreElements()) buf.append("&"); 
	}
	return buf.toString(); 
    }
}
At this point the applet should be able to report the time in the first field.

We now move to the second method (HTTP object) so we enhance the servlet:

import java.io.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public class DaytimeServlet extends HttpServlet {

    public Date getDate() {
	return new Date(); 
    }

    public void doGet(HttpServletRequest req, 
		      HttpServletResponse res) throws ServletException, 
		                                      IOException 
    {	
       // if the client says "format=object" then 
       // return the Date as a serialized object     
       if ("object".equals(req.getParameter("format"))) {

	   ObjectOutputStream out = 
	       new ObjectOutputStream(res.getOutputStream()); 

	   out.writeObject(getDate()); 
	
       } else { 
	   PrintWriter out = res.getWriter(); 
	   out.println(getDate().toString()); 

       }
    }

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

    }
}
The corresponding change in the applet looks like this:
import java.net.URL; 

import java.applet.*; 
import java.awt.*; 
import java.io.*; 
import java.util.*; 

public class DaytimeApplet extends Applet {
    TextField httpText, httpObject, socketText, socketObject, RMIObject; 
    Button refresh; 
    public void init() {
	setLayout(new BorderLayout()); 
	Panel west = new Panel(); 
	west.setLayout(new GridLayout(5, 1)); 

	west.add(new Label("HTTP text: ", Label.RIGHT)); 
	west.add(new Label("HTTP object: ", Label.RIGHT)); 
	west.add(new Label("Socket text: ", Label.RIGHT)); 
	west.add(new Label("Socket object: ", Label.RIGHT)); 
	west.add(new Label("RMI object: ", Label.RIGHT)); 

	add("West", west); 

	Panel center = new Panel(); 
	center.setLayout(new GridLayout(5, 1)); 

	httpText = new TextField();
	httpText.setEditable(false);
	center.add(httpText);  

	httpObject = new TextField();
	httpObject.setEditable(false);
	center.add(httpObject);  

	socketText = new TextField();
	socketText.setEditable(false);
	center.add(socketText);  

	socketObject = new TextField();
	socketObject.setEditable(false);
	center.add(socketObject);  

	RMIObject = new TextField();
	RMIObject.setEditable(false);
	center.add(RMIObject);  

	add("Center", center); 

	Panel south = new Panel(); 
	refresh = new Button("Refresh"); 
	south.add(refresh);
	add("South", south); 

    }

    public void start() { 
	refresh(); 
    }

    private void refresh() {
	httpText.setText(getDateUsingHttpText());
	httpObject.setText(getDateUsingHttpObject());
	socketText.setText(getDateUsingSocketText());
	socketObject.setText(getDateUsingSocketObject());
	RMIObject.setText(getDateUsingRMIObject());
    }

    private String getDateUsingHttpText () {
	try {
	    URL url = new URL(getCodeBase(), 
			      "servlet/DaytimeServlet"); 
	    HttpMessage msg = new HttpMessage(url);
	    InputStream in = msg.sendGetMessage(); 
	    DataInputStream result = 
		new DataInputStream(new BufferedInputStream(in)); 
	    String date = result.readLine(); 
	    in.close(); 
	    return date; 
	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null; 
	}
    }

    private String getDateUsingHttpObject () {

	try {

	    URL url = new URL(getCodeBase(), "servlet/DaytimeServlet"); 

	    HttpMessage msg = new HttpMessage(url); 

	    Properties props = new Properties(); 

	    props.put("format", "object"); 

	    InputStream in = msg.sendGetMessage(props); 

	    ObjectInputStream result = new ObjectInputStream(in); 

	    Object obj = result.readObject(); 

	    Date date = (Date)obj;

	    return date.toString(); 

	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null;
	} 

    }

    private String getDateUsingSocketText () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingSocketObject () {
	return "unavailable (for now)"; 
    }

    private String getDateUsingRMIObject () {
	return "unavailable (for now)"; 
    }

    public boolean handleEvent(Event event) {
	// when the refresh button is pushed, refresh the display 
	// use jdk1.0 events for maximum portability

	switch (event.id) {
	    case Event.ACTION_EVENT:
		if (event.target == refresh) {
		    refresh(); 
		    return true; 
		}
	}

	return false; 

    }

}
The applet now is able to retrieve the date in both ways.

We now turn our attention to Socket connectivity.

The server needs to be changed first:

import java.io.*; 
import java.net.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public class DaytimeServlet extends DaemonHttpServlet {

    public Date getDate() {
	return new Date(); 
    }

    public void init() throws ServletException {
	DEFAULT_PORT = 36600; 
    }

    public void doGet(HttpServletRequest req, 
		      HttpServletResponse res) throws ServletException, 
		                                      IOException 
    {	
       // if the client says "format=object" then 
       // return the Date as a serialized object     
       if ("object".equals(req.getParameter("format"))) {

	   ObjectOutputStream out = 
	       new ObjectOutputStream(res.getOutputStream()); 

	   out.writeObject(getDate()); 
	
       } else {
	   PrintWriter out = res.getWriter(); 
	   out.println(getDate().toString()); 

       }
    }

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

    }

    public void destroy() {
	super.destroy(); 
    }

    public void handleClient(Socket client) {
	new DaytimeConnection(this, client).start(); 

    }
}

class DaytimeConnection extends Thread {

    DaytimeServlet servlet; 
    Socket client; 

    DaytimeConnection(DaytimeServlet servlet, Socket client) {
	this.servlet = servlet; 
	this.client = client;
	setPriority(NORM_PRIORITY - 1); 
    }

    public void run() {
	try {
	    // read the first line sent by the client, as Latin-1 text
	    BufferedReader in = 
		new BufferedReader
		    (new InputStreamReader
			(client.getInputStream(), "ISO-8859-1")); 
	    String line = in.readLine(); 
	    // if it was "object" then return the Date as a serialized object
	    if ("object".equals(line)) {
		ObjectOutputStream out = 
		    new ObjectOutputStream
			(client.getOutputStream()); 
		out.writeObject(servlet.getDate()); 
		out.close(); 
	    } else { // otherwise, send the Date as a normal string 
		// wrap a PrintStream around the Socket's OutputStream
		PrintStream out = new PrintStream(client.getOutputStream()); 
		out.println(servlet.getDate().toString()); 
		out.close(); 
	    }
	} catch (IOException e) {
	    servlet.log("IOException while handling client request", e); 
	} catch (Exception e) {
	    servlet.log("Exception while handling client request"); 
	}
    }
    
}
Changes are significant as they are based on these additional classes:
import java.io.*; 
import java.net.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public abstract class DaemonHttpServlet extends HttpServlet {
    protected int DEFAULT_PORT = 36600; // not static or final 
    private Thread daemonThread; 

    public void init(ServletConfig config) throws ServletException {
	super.init(config); 
	// start a daemon thread 
	try {
	    daemonThread = new Daemon(this); 
	    daemonThread.start(); 
	} catch (Exception e) {
	    this.getServletContext().log
		(e, "Problem starting socket server daemon thread"); 
	}
    }

    // returns the socket port on which this servlet will listen
    // a servlet can specify the port in three ways: 
    //  1. by using the socketPort init parameter, 
    //  2. by setting the DEFAULT_PORT variable before calling super.init(), or
    //  3. by overriding this method's implementation
    protected int getSocketPort() {
	try {
	    return Integer.parseInt(getInitParameter("socketPort")); 
	} catch (NumberFormatException e) {
	    return DEFAULT_PORT; 
	}
    }

    abstract public void handleClient(Socket client);

    public void destroy() {
	// stop the daemon thread 
	try {
	    daemonThread.stop();
	    daemonThread = null; 
	} catch (Exception e) {
	    getServletContext().log
		(e, "Problem stopping server socket daemon thread"); 
	}
    }

}

// this work is broken into a helper class so that subclasses of 
// DaemonHttpServlet can define their own run() methods w/out problems...
class Daemon extends Thread {
    private ServerSocket serverSocket; 
    private DaemonHttpServlet servlet;
    public Daemon(DaemonHttpServlet servlet) {
	this.servlet = servlet; 
    }
    public void run() {
	// create a server socket to accept connections
	try {
	    serverSocket = new ServerSocket(servlet.getSocketPort()); 
	} catch (Exception e) {
	    servlet.getServletContext().log
		(e, "Problem establishing server socket"); 
	    return; 
	}

	try {
	    while (true) {
		// as each connection comes in, call the servlet's 
		// handleClient(). Note this method is blocking. It's
		// the servlet responsibility to spawn a handler thread
		// for long-running connections (as DaytimeServlet)
		try { 
		    servlet.handleClient(serverSocket.accept()); 
		} catch(IOException ioe) {
		    servlet.getServletContext()
			.log(ioe,
			     "Problem accepting client's socket connection");

		}
	    }
	} catch (ThreadDeath e) {
	    // when the thread is killed, close the server socket
	    try {
		serverSocket.close(); 
	    } catch (IOException ioe) {
		servlet.getServletContext()
		    .log(ioe,
			 "Problem closing server socket"); 
	    }

	} 
    }
}
It's nice to be able to factor the changes this way.

Now the applet has to be changed:

import java.net.Socket; 

import java.net.URL; 

import java.applet.*; 
import java.awt.*; 
import java.io.*; 
import java.util.*; 

public class DaytimeApplet extends Applet {

    static final int DEFAULT_PORT = 36600; 

    private int getSocketPort() {
	try {
	    return Integer.parseInt(getParameter("socketPort")); 
	} catch (NumberFormatException e) {
	    return DEFAULT_PORT; 
	}
    }

    TextField httpText, httpObject, socketText, socketObject, RMIObject; 
    Button refresh; 
    public void init() {
	setLayout(new BorderLayout()); 
	Panel west = new Panel(); 
	west.setLayout(new GridLayout(5, 1)); 

	west.add(new Label("HTTP text: ", Label.RIGHT)); 
	west.add(new Label("HTTP object: ", Label.RIGHT)); 
	west.add(new Label("Socket text: ", Label.RIGHT)); 
	west.add(new Label("Socket object: ", Label.RIGHT)); 
	west.add(new Label("RMI object: ", Label.RIGHT)); 

	add("West", west); 

	Panel center = new Panel(); 
	center.setLayout(new GridLayout(5, 1)); 

	httpText = new TextField();
	httpText.setEditable(false);
	center.add(httpText);  

	httpObject = new TextField();
	httpObject.setEditable(false);
	center.add(httpObject);  

	socketText = new TextField();
	socketText.setEditable(false);
	center.add(socketText);  

	socketObject = new TextField();
	socketObject.setEditable(false);
	center.add(socketObject);  

	RMIObject = new TextField();
	RMIObject.setEditable(false);
	center.add(RMIObject);  

	add("Center", center); 

	Panel south = new Panel(); 
	refresh = new Button("Refresh"); 
	south.add(refresh);
	add("South", south); 

    }

    public void start() { 
	refresh(); 
    }

    private void refresh() {
	httpText.setText(getDateUsingHttpText());
	httpObject.setText(getDateUsingHttpObject());
	socketText.setText(getDateUsingSocketText());
	socketObject.setText(getDateUsingSocketObject());
	RMIObject.setText(getDateUsingRMIObject());
    }

    private String getDateUsingHttpText () {
	try {
	    URL url = new URL(getCodeBase(), 
			      "servlet/DaytimeServlet"); 
	    HttpMessage msg = new HttpMessage(url);
	    InputStream in = msg.sendGetMessage(); 
	    DataInputStream result = 
		new DataInputStream(new BufferedInputStream(in)); 
	    String date = result.readLine(); 
	    in.close(); 
	    return date; 
	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null; 
	}
    }

    private String getDateUsingHttpObject () {
	try {
	    URL url = new URL(getCodeBase(), "servlet/DaytimeServlet"); 
	    HttpMessage msg = new HttpMessage(url); 
	    Properties props = new Properties(); 
	    props.put("format", "object"); 
	    InputStream in = msg.sendGetMessage(props); 
	    ObjectInputStream result = new ObjectInputStream(in); 
	    Object obj = result.readObject(); 
	    Date date = (Date)obj;
	    return date.toString(); 
	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null;
	}

    }

    private String getDateUsingSocketText () {

	Socket socket = null; 

	try {

	    // establish a socket connection with the servlet 
	    socket = new Socket("burrowww.cs.indiana.edu", getSocketPort());

	    // print an empty line, indicating we want the time as plain text
	    PrintStream out = new PrintStream(socket.getOutputStream()); 
	    out.print("\r\n"); // since println() behaviour varies by system 
	    out.flush(); 

	    // read the first line of the response, it should contain the current time 
	    InputStream in = socket.getInputStream();
	    DataInputStream result =
		new DataInputStream(new BufferedInputStream(in)); 
	    String date = result.readLine(); 

	    // return the retrieved string
	    return date; 

	} catch (Exception e) {

	    // if there is a problem, print to System.out 
	    // (typically the Java console) and return null 
	    e.printStackTrace(); 
	    return null; 

	} finally {

	    // always close the connection (this code
	    // executes no matter how the try block completes)
	    if (socket != null) {

		try { 
		    socket.close(); 

		} catch (IOException ignored) {

		}

	    }
	}
    }

    private String getDateUsingSocketObject () {

	Socket socket = null; 

	try {

	    // establish a socket connection with the servlet 
	    socket = new Socket(getCodeBase().getHost(), getSocketPort()); 

	    // print a line saying "object", indicating we want 
	    // the time as a serialized Date object 
	    PrintStream out = new PrintStream(socket.getOutputStream()); 
	    out.print("object\r\n");
	    out.flush(); 

	    // create an ObjectInputStream to read the response 
	    InputStream in = socket.getInputStream(); 
	    ObjectInputStream result = 
		new ObjectInputStream(new BufferedInputStream(in)); 

	    // read an object, and cast it to be a date 
	    Object obj = result.readObject(); 
	    Date date = (Date)obj; 

	    // return a String representation of the retrieved Date 
	    return date.toString(); 

	} catch (Exception e) {

	    // if there is a problem, print to System.out
	    // (typically to Java console) and return null 
	    e.printStackTrace(); 
	    return null; 

	} finally {

	    // always close the connection (this code
	    // executes no matter how the try block completes)

	    if (socket != null) {

		try {
		    socket.close(); 

		} catch (IOException ignored) {

		}
	    }

	}
    }

    private String getDateUsingRMIObject () {
	return "unavailable (for now)"; 
    }

    public boolean handleEvent(Event event) {
	// when the refresh button is pushed, refresh the display 
	// use jdk1.0 events for maximum portability

	switch (event.id) {
	    case Event.ACTION_EVENT:
		if (event.target == refresh) {
		    refresh(); 
		    return true; 
		}
	}

	return false; 

    }

}
We also make a change in the HTML file (to match the servlet's port) and we are done.
<html>
  <head><title>Daytime Applet</title></head>
  <body>
    <center><h1>Daytime Applet</h1></center>
    <center>
      <applet code=DaytimeApplet 
              width=300
              height=180>

        <param name=socketPort value=36600>

      </applet>
    </center>
  </body>
</html>
Four out of the five fields are now reporting the date correctly.

For the last step we use RMI.

We already need a new port for Sockets, we try to avoid needing another one for RMI.

First we need a DaytimeServer interface.

import java.util.Date;
import java.rmi.Remote;
import java.rmi.RemoteException;

public interface DaytimeServer extends Remote {

    public Date getDate() throws RemoteException; 

}
Then we need to add a few things to the servlet.

import java.io.*; 
import java.net.*; 
import java.util.*; 
import javax.servlet.*; 
import javax.servlet.http.*; 

public class DaytimeServlet    extends RemoteDaemonHttpServlet
                            implements DaytimeServer 
{    
    public Date getDate() {
	return new Date(); 
    }

    public void init() throws ServletException {
	DEFAULT_PORT = 36600; 
    }

    public void init(ServletConfig config) throws ServletException {
	super.init(config); 
    }

    public void doGet(HttpServletRequest req, 
		      HttpServletResponse res) throws ServletException, 
		                                      IOException 
    {	
       if ("object".equals(req.getParameter("format"))) {

	   ObjectOutputStream out = 
	       new ObjectOutputStream(res.getOutputStream()); 

	   out.writeObject(getDate()); 
	
       } else {

	   PrintWriter out = res.getWriter(); 
	   out.println(getDate().toString()); 

       }
    }

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

    }

    public void destroy() {
        // if you override destroy() you have to call super.destroy()
	super.destroy(); 
    }

    public void handleClient(Socket client) {
	new DaytimeConnection(this, client).start(); 

    }
}

class DaytimeConnection extends Thread {

    DaytimeServlet servlet; 
    Socket client; 

    DaytimeConnection(DaytimeServlet servlet, Socket client) {
	this.servlet = servlet; 
	this.client = client;
	setPriority(NORM_PRIORITY - 1); 
    }

    public void run() {
	try {
	    // read the first line sent by the client, as Latin-1 text
	    BufferedReader in = 
		new BufferedReader
		    (new InputStreamReader
			(client.getInputStream(), "ISO-8859-1")); 
	    String line = in.readLine(); 
	    // if it was "object" then return the Date as a serialized object
	    if ("object".equals(line)) {
		ObjectOutputStream out = 
		    new ObjectOutputStream
			(client.getOutputStream()); 
		out.writeObject(servlet.getDate()); 
		out.close(); 
	    } else { // otherwise, send the Date as a normal string 
		// wrap a PrintStream around the Socket's OutputStream
		PrintStream out = new PrintStream(client.getOutputStream()); 
		out.println(servlet.getDate().toString()); 
		out.close(); 
	    }
	} catch (IOException e) {
	    servlet.log("IOException while handling client request", e); 
	} catch (Exception e) {
	    servlet.log("Exception while handling client request"); 
	}
    }
    
}
The superclass has changed, so we provide it here.

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 abstract class RemoteDaemonHttpServlet    extends DaemonHttpServlet 
                                              implements Remote 
{
    protected Registry registry; 

    public void init(ServletConfig config) throws ServletException {

	super.init(config); 

	try {

	    // export ourself 
	    UnicastRemoteObject.exportObject(this); 

	    // register ourself
	    bind(); 

	} catch(RemoteException e) {

	    getServletContext().log(e, "Problem binding to RMI registry."); 

	}
    }

    public void destroy() {

	super.destroy(); 

	// unregister yourself 
	unbind(); 

    }

    // returns the name under which we are to be registered
    protected String getRegistryName() {

	// first name choice is the "registryName" init parameter
	String name = getInitParameter("registryName"); 
	if (name != null) return name; 

	// fallback choice is the name of this class followed by your username
	return this.getClass().getName() + "ByDGerman"; 

    }

    // returns the port on which the registry server 
    // is listening (or should be listening)
    protected int getRegistryPort() {

	// first port choice is the "registryPort" init parameter 
	String name = getInitParameter("registryName"); 
	try { 

	    return Integer.parseInt(getInitParameter("registryPort")); 

	} catch (NumberFormatException e) {

	    // fallback choice is the default registry port (1099) 
	    return Registry.REGISTRY_PORT; 
	}

    }

    protected void bind() {

	// try to find the appropriate registry already running 
	try {

	    registry = LocateRegistry.getRegistry(getRegistryPort()); 
	    registry.list(); // verify it's alive and well 
	} catch (Exception e) {

	    // couldn't get a valid registry
	    registry = null; 
	}

	// if we couldn't find it, we need to create it 
	// (equivalent to running "rmiregistry")
	if (registry == null) {
	    try {

		registry = LocateRegistry.createRegistry(getRegistryPort()); 

	    } catch (Exception e) {

		log("Could not get or create RMI registry on port " + 
		    getRegistryPort() + ": " + e.getMessage()); 

		return; 
	    }

	}

	// if we get here, we must have a valid registry.
	// now register this servlet instance with that registry.
	// "rebind" to replace any other objects using our name

	try {

	    registry.rebind(getRegistryName(), this); 

	} catch (Exception e) {

	    log("Could not bind to RMI registry: " + e.getMessage()); 
	    return; 
	}

    }

    protected void unbind() {

	try {

	    if (registry != null) registry.unbind(getRegistryName()); 

	} catch (Exception e) {

	    getServletContext().log(e, "Problem unbinding from RMI registry");

	}

    }  
}
Compile the servlet, then use rmic to create the stub and the skeleton files.

The stub file and the interface will have to be placed next to the applet.

burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/tomcat/jakarta-tomcat-4.0.3/webapps/one/WEB-INF/classes
burrowww.cs.indiana.edu% javac DaytimeServlet.java
burrowww.cs.indiana.edu% rmic DaytimeServlet 
burrowww.cs.indiana.edu% ls -ld Daytime*.class
-rw-r--r--   1 dgerman  faculty      1668 Mar 28 15:15 DaytimeConnection.class
-rw-r--r--   1 dgerman  faculty       247 Mar 28 15:15 DaytimeServer.class
-rw-r--r--   1 dgerman  faculty      1796 Mar 28 15:15 DaytimeServlet.class
-rw-r--r--   1 dgerman  faculty      1422 Mar 28 15:16 DaytimeServlet_Skel.class
-rw-r--r--   1 dgerman  faculty      2887 Mar 28 15:16 DaytimeServlet_Stub.class
burrowww.cs.indiana.edu% cp DaytimeServer.class ../..
burrowww.cs.indiana.edu% cp DaytimeServlet_Stub.class ../..
burrowww.cs.indiana.edu% cd ../..
burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/tomcat/jakarta-tomcat-4.0.3/webapps/one
burrowww.cs.indiana.edu% 
The applet changes a bit, as indicated below.

import java.rmi.*; 
import java.rmi.registry.*; 

import java.net.Socket; 
import java.net.URL; 
import java.applet.*; 
import java.awt.*; 
import java.io.*; 
import java.util.*; 

public class DaytimeApplet extends Applet {

    private String getRegistryHost() {
	return getCodeBase().getHost(); 
    }

    private int getRegistryPort() {
	try {

	    return Integer.parseInt(getParameter("registryPort")); 
	} catch (NumberFormatException e) {

	    return Registry.REGISTRY_PORT;
	}
    }

    private String getRegistryName() {

	String name = getParameter("registryName"); 

	if (name == null) {

	    name = "DaytimeServlet" // default 
	 	 + "ByDGerman";     // add your username 
	} 

	return name;
    }

    static final int DEFAULT_PORT = 36600; 

    private int getSocketPort() {
	try {
	    return Integer.parseInt(getParameter("socketPort")); 
	} catch (NumberFormatException e) {
	    return DEFAULT_PORT; 
	}
    }

    TextField httpText, httpObject, socketText, socketObject, RMIObject; 
    Button refresh; 
    public void init() {
	setLayout(new BorderLayout()); 
	Panel west = new Panel(); 
	west.setLayout(new GridLayout(5, 1)); 

	west.add(new Label("HTTP text: ", Label.RIGHT)); 
	west.add(new Label("HTTP object: ", Label.RIGHT)); 
	west.add(new Label("Socket text: ", Label.RIGHT)); 
	west.add(new Label("Socket object: ", Label.RIGHT)); 
	west.add(new Label("RMI object: ", Label.RIGHT)); 

	add("West", west); 

	Panel center = new Panel(); 
	center.setLayout(new GridLayout(5, 1)); 

	httpText = new TextField();
	httpText.setEditable(false);
	center.add(httpText);  

	httpObject = new TextField();
	httpObject.setEditable(false);
	center.add(httpObject);  

	socketText = new TextField();
	socketText.setEditable(false);
	center.add(socketText);  

	socketObject = new TextField();
	socketObject.setEditable(false);
	center.add(socketObject);  

	RMIObject = new TextField();
	RMIObject.setEditable(false);
	center.add(RMIObject);  

	add("Center", center); 

	Panel south = new Panel(); 
	refresh = new Button("Refresh"); 
	south.add(refresh);
	add("South", south); 

    }

    public void start() { 
	refresh(); 
    }

    private void refresh() {
	httpText.setText(getDateUsingHttpText());
	httpObject.setText(getDateUsingHttpObject());
	socketText.setText(getDateUsingSocketText());
	socketObject.setText(getDateUsingSocketObject());
	RMIObject.setText(getDateUsingRMIObject());
    }

    private String getDateUsingHttpText () {
	try {
	    URL url = new URL(getCodeBase(), 
			      "servlet/DaytimeServlet"); 
	    HttpMessage msg = new HttpMessage(url);
	    InputStream in = msg.sendGetMessage(); 
	    DataInputStream result = 
		new DataInputStream(new BufferedInputStream(in)); 
	    String date = result.readLine(); 
	    in.close(); 
	    return date; 
	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null; 
	}
    }

    private String getDateUsingHttpObject () {
	try {
	    URL url = new URL(getCodeBase(), "servlet/DaytimeServlet"); 
	    HttpMessage msg = new HttpMessage(url); 
	    Properties props = new Properties(); 
	    props.put("format", "object"); 
	    InputStream in = msg.sendGetMessage(props); 
	    ObjectInputStream result = new ObjectInputStream(in); 
	    Object obj = result.readObject(); 
	    Date date = (Date)obj;
	    return date.toString(); 
	} catch (Exception e) {
	    e.printStackTrace(); 
	    return null;
	}

    }

    private String getDateUsingSocketText () {

	Socket socket = null; 

	try {

	    // establish a socket connection with the servlet 
	    socket = new Socket("burrowww.cs.indiana.edu", getSocketPort());

	    // print an empty line, indicating we want the time as plain text
	    PrintStream out = new PrintStream(socket.getOutputStream()); 
	    out.print("\r\n"); // since println() behaviour varies by system 
	    out.flush(); 

	    // read the first line of the response, it should contain the current time 
	    InputStream in = socket.getInputStream();
	    DataInputStream result =
		new DataInputStream(new BufferedInputStream(in)); 
	    String date = result.readLine(); 

	    // return the retrieved string
	    return date; 

	} catch (Exception e) {

	    // if there is a problem, print to System.out 
	    // (typically the Java console) and return null 
	    e.printStackTrace(); 
	    return null; 

	} finally {

	    // always close the connection (this code
	    // executes no matter how the try block completes)
	    if (socket != null) {

		try { 
		    socket.close(); 

		} catch (IOException ignored) {

		}

	    }
	}
    }

    private String getDateUsingSocketObject () {

	Socket socket = null; 

	try {

	    // establish a socket connection with the servlet 
	    socket = new Socket(getCodeBase().getHost(), getSocketPort()); 

	    // print a line saying "object", indicating we want 
	    // the time as a serialized Date object 
	    PrintStream out = new PrintStream(socket.getOutputStream()); 
	    out.print("object\r\n");
	    out.flush(); 

	    // create an ObjectInputStream to read the response 
	    InputStream in = socket.getInputStream(); 
	    ObjectInputStream result = 
		new ObjectInputStream(new BufferedInputStream(in)); 

	    // read an object, and cast it to be a date 
	    Object obj = result.readObject(); 
	    Date date = (Date)obj; 

	    // return a String representation of the retrieved Date 
	    return date.toString(); 

	} catch (Exception e) {

	    // if there is a problem, print to System.out
	    // (typically to Java console) and return null 
	    e.printStackTrace(); 
	    return null; 

	} finally {

	    // always close the connection (this code
	    // executes no matter how the try block completes)

	    if (socket != null) {

		try {
		    socket.close(); 

		} catch (IOException ignored) {

		}
	    }

	}
    }

    private String getDateUsingRMIObject () {

	try {

	    Registry registry = 
		LocateRegistry.getRegistry(getRegistryHost(), 
					   getRegistryPort()); 

	    DaytimeServer daytime = 
		(DaytimeServer)registry.lookup(getRegistryName()); 
	    
	    return daytime.getDate().toString(); 

	} catch (ClassCastException e) {

	    System.out.println("Retrieved object was not a DaytimeServer: " + 
			       e.getMessage()); 
	} catch (NotBoundException e) {

	    System.out.println(getRegistryName() + " not bound: " + e.getMessage()); 
	} catch (RemoteException e) {

	    System.out.println("Hit remote exception: " + e.getMessage()); 
	} catch (Exception e) {

	    System.out.println("Problem getting DaytimeServer reference: " + 
			       e.getClass().getName() + ":" + e.getMessage()); 
	}

	return null; 

    }

    public boolean handleEvent(Event event) {
	// when the refresh button is pushed, refresh the display 
	// use jdk1.0 events for maximum portability

	switch (event.id) {
	    case Event.ACTION_EVENT:
		if (event.target == refresh) {
		    refresh(); 
		    return true; 
		}
	}

	return false; 

    }

}
And now we are done.

The applet opens up five different connections to the servlet and obtains information in five different ways (or formats). Our next goal, next week, is to make sure that each one of these connections could be made bidirectional, so that information can travel both ways

As a summary, here's what we worked on today:
burrowww.cs.indiana.edu% pwd
/nfs/paca/home/user1/dgerman/tomcat/jakarta-tomcat-4.0.3/webapps/one
burrowww.cs.indiana.edu% du -a .
6       ./DaytimeApplet.class
1       ./WEB-INF/classes/HelloWorld.java
2       ./WEB-INF/classes/DaemonHttpServlet.class
2       ./WEB-INF/classes/Daemon.class
3       ./WEB-INF/classes/DaemonHttpServlet.java
3       ./WEB-INF/classes/DaytimeServlet.java
2       ./WEB-INF/classes/DaytimeServlet.class
2       ./WEB-INF/classes/DaytimeConnection.class
1       ./WEB-INF/classes/HelloWorld.class
1       ./WEB-INF/classes/DaytimeServer.java
1       ./WEB-INF/classes/DaytimeServer.class
3       ./WEB-INF/classes/DaytimeServlet.java~
3       ./WEB-INF/classes/RemoteDaemonHttpServlet.class
3       ./WEB-INF/classes/RemoteDaemonHttpServlet.java
3       ./WEB-INF/classes/RemoteDaemonHttpServlet.java~
3       ./WEB-INF/classes/DaytimeServlet_Stub.class
2       ./WEB-INF/classes/DaytimeServlet_Skel.class
36      ./WEB-INF/classes
1       ./WEB-INF/lib
1       ./WEB-INF/web.xml
39      ./WEB-INF
4       ./HttpMessage.class
7       ./DaytimeApplet.java
3       ./DaytimeServlet_Stub.class
6       ./DaytimeApplet.java~
1       ./daytime.html
3       ./HttpMessage.java
1       ./DaytimeServer.class
71      .
burrowww.cs.indiana.edu% 
You should be able to describe each one of these files and its contents.


Last updated on Mar 28, 2002, by Adrian German for A348/A548