HomeDigital EditionSys-Con RadioSearch Java Cd
Advanced Java AWT Book Reviews/Excerpts Client Server Corba Editorials Embedded Java Enterprise Java IDE's Industry Watch Integration Interviews Java Applet Java & Databases Java & Web Services Java Fundamentals Java Native Interface Java Servlets Java Beans J2ME Libraries .NET Object Orientation Observations/IMHO Product Reviews Scalability & Performance Security Server Side Source Code Straight Talking Swing Threads Using Java with others Wireless XML
 

Java servlets provide a new way to develop server-side solutions. They provide the features of traditional CGI scripts with the additional benefits of efficiency and portability. Currently, major corporations are making the migration from CGI scripts to Java servlets. As a result, the demand for applet and servlet communication is on the rise.

In the February 1998 issue of JDJ (Vol. 3, Issue 2), I presented a three-tier database application that used Java servlets. In this article you will learn how to build a three-tier database application that allows a Java applet to perform two-way communication with a Java servlet. I'll focus on the concepts and techniques of applets communicating with servlets, building on the application presented in the previous article. Don't worry if you missed that article; a review follows.

Reviewing Our Student Tracker Application
The previous article presented a three-tier database application that used Java servlets and the Java Database Connection (JDBC). That application allowed a public speaker to keep track of students who attended the seminars. Students interacted with the application by entering their contact information into an HTML form. Once the form was submitted, the Java servlet used JDBC to store the student information in a database. Afterwards, an updated student list was generated by the servlet and returned to the user as an HTML page.

The application was partitioned into three tiers: user interface layer, the business rules layer and the DataStore layer. Figure 1 illustrates the design.

Figure 1
Figure 1:

The first tier is a Web browser, which serves as our universal client. In the first phase of the application, an HTML front end was used for user input and to display the database query results. The HTML approach was taken because it lowered the requirements of the client's Web browser version. This low-tech approach made the application accessible to users whose browsers were not Java 1.1 enabled.

The second tier of the application was implemented with a Web server capable of executing Java servlets. The Java servlet harnessed the power of JDBC to access the database to store/retrieve information as needed. A dynamic HTML page was generated by the servlet based on the database results.

The third tier was composed of our back-end database server, which stores the information used by the application. Thanks to the JDBC API, however, the servlet can access the database in a portable fashion by using the SQL call-level interface.

Developing an Applet Front End
To enhance the student tracking system, we will develop an applet front end. Students can now enter their contact information into a Java dialog box. Also, an updated student list is displayed in a Java list component. Figure 2 shows the new applet front end.

Figure 2
Figure 2:

Applet-Servlet Communication with HTTP GET and POST
In the previous version the HTML form was used to submit the student's data to the servlet. Accessing the form data on the server side was simple and straightforward. This was accomplished by calling the method HttpRequest.getParameter(<form field name>), which is available in the Java servlet API.

We are now using an applet front end and we need a mechanism for the applet to communicate with the servlet. We need to capture the information a student enters and somehow pass it to the servlet. Since servlets support the HTTP/CGI interface, we can communicate with the servlet over HTTP socket connections. The applet simply opens a connection to the specified servlet URL. Once this connection is made, the applet can get an output or input stream on the servlet.

The applet can send data to the applet by using a GET or a POST method. If a GET method is used, the applet must URL-encode the name/value pair parameters into the actual URL string. For example, if we wanted to send the name/value pair of LastName=Jones, our servlet URL would resemble:

http://www.foo.com/servlet/
TestServlet?LastName=Jones

If you have additional name/value pairs, they are separated by an ampersand (&). If we add a name/value pair of FirstName=Joe, our revised servlet URL would resemble:

http://www.foo.com/servlet/
TestServlet?LastName=Jones&FirstName=Joe

We would have to URL-encode each name/value pair for the student's contact information. To send a GET method to a servlet, the applet can use the java.net.URLConnection class. The code fragment below shows you how.

String location = "http://www.foo.com/servlet/TestServlet?LastName=Jones";

URL testServlet = new URL( location );
URLConnection servletConnection = testServlet.openConnection();

inputStreamFromServlet = servletConnection.getInputStream();

// Read the input from the servlet.
. . .

Once the applet has opened a connection to the URL, the input stream from the servlet is accessed. The applet can read this input stream and process the data accordingly. The type and format of the data returned depend on the servlet. If the servlet is returning custom information, a custom messaging protocol has to be created for the applet and servlet to communicate. I won't go into the details of a custom protocol, however, as I'll present an elegant solution later in the article.

To POST data to a servlet, the java.net.URLConnection class is used again. This time, however, we must inform the URL connection that we will send data over the output stream. The POST method is powerful because you can send any form of data (plain text, binary, etc.). All you do is set the content type in the HTTP request header. The servlet must be able to handle the type of data that the applet sends, however.

The code in Listing 1 shows how to send a POST method to a servlet URL. The details of transmitting the data are discussed later in the article.

As you can see, applets can communicate with servlets using the GET and POST method.

Communicating with Object Serialization
In our application we want to provide a higher level of abstraction. Instead of passing each parameter of student information (e.g., last name, first name) as name/value pairs, we'd like to send it as a true Java object. Our Java application already has a Student class that encapsulates all of the information about a student (see Listing 2). This information is gathered from the New Student dialog box and a Student object is created. When we register a new student, we'd simply like to send the Student object to the servlet. Upon receipt of the Student object, the servlet would add the new student to the database. We also want the servlet to send the applet an updated student list as a vector of student objects. This will allow the applet to display the student list quickly and easily.

You may ask how we can accomplish this. It's easy, thanks to Java 1.1's object serialization, which allows an object to be flattened and saved as a binary file. The values of the data members are saved so the state of the object is in fact persistent or serialized. The object can be loaded or deserialized later from the binary file with the values of its data members intact. Object serialization is fascinating because it frees the developer from the low-level details of saving and restoring the object.

How does this relate to applet-servlet communication? Well, object serialization is not limited to binary disk files. Objects can also be serialized to any output stream. This even includes an output stream based on a socket connection. So you can serialize an object over a socket output stream! As you've probably guessed by now, a Java object can also be deserialized or loaded from a socket input stream.

For a Java object to be serializable, its class must implement the java.io.Serializable interface. You won't have to actually implement any methods for this interface, however, because the interface is empty. The java.io.Serializable interface is simply a tag for the Java Virtual Machine. We can create a custom class as follows:

class Foo implements java.io.Serializable
{
// normal declaration of data members,
// constructors and methods
}

The following code fragment shows you how to serialize an object to an output stream. In this example we already have a socket connection to a host machine and are simply serializing the object, myFoo.

outputToHost = new ObjectOutputStream(hostConnection.getOutputStream());

// serialize the object
Foo myFoo = new Foo();
outputToHost.writeObject(myFoo);

outputToHost.flush();
outputToHost.close();

Notice in this example that an ObjectOutputStream is created. This class is responsible for serializing an object. The object is actually serialized when the writeObject() method is called with the target object as its parameter. At this time a binary image of the object is written to the output stream. In this case the output stream is based on a socket connection.

This example wouldn't be complete, however, without code on the host machine to read the serialized object. The next code fragment shows you how to deserialize an object from an input stream.

inputFromClient = new ObjectInputStream(clientConnection.getInputStream());

// deserialize the object, note the cast
Foo theData = (Foo) inputFromClient.readObject();

inputFromClient.close();

An ObjectInputStream is created based on the client's socket connection. The object is deserialized by simply calling the readObject() method. But we must cast the object to its appropriate class, in this case the class Foo. At this point the object is available for normal use.

As you can see, object serialization is very straightforward and easy. Now we'll use the technology to pass objects back and forth between our applet and servlet.

Sending Objects from an Applet to a Servlet
With the information presented so far, we can send a Java object to a servlet. In our Student Tracking application the applet sends a Student object to the servlet when a new student is registered. Figure 3 displays the object interaction between the servlet and the applet.

Figure 3
Figure 3:

The code fragment shown in Listing 3 is used by the applet to send the Student object to the servlet. The applet is actually sending a POST method to the servlet. This client-side code fragment opens a URL connection to the servlet URL. We inform the servlet connection that we're sending output data over the connection and receiving input. Other methods are called so the connection will not use cached versions of the URL. An important call in this code fragment is setRequestProperty(). This method sets the content type in the HTTP request header to the MIME-type application/octet-stream, which allows us to send binary data, in this case our serialized Student object. The next couple of statements create an ObjectOutputStream that actually writes the object to the connection stream.

We're not finished yet. Recall that our application is in the process of registering a new student. The servlet has to read this student object and update the database accordingly. Thus we need code on the server side to receive a serialized Student object.

The code fragment in Listing 4 displays the servlet code for reading a Student object from an applet. The servlet handles POST methods by implementing the doPost() method and acquires an ObjectInputStream from the requesting applet. From there it is simply a matter of reading the Student object from the stream. At this point the Student object is loaded and available for registration in the database. Please note the small number of statements on the server side for reading in a serialized object. You have to agree it's quite simple and straightforward.

Sending Objects from a Servlet to an Applet
The servlet in our Student Tracking application is now capable of receiving a student object and registering it in the database. Now the servlet has to return an updated list of registered students that is then returned as a vector of student objects. This interaction is also illustrated in Figure 3.

When the servlet returns the vector, there's no need to iterate through the vector and serialize each Student object individually. The servlet can serialize the entire vector in one step since the class java.util.Vector also implements the java.io.Serializable interface.

The code fragment shown in Listing 5 is used by the servlet to send a vector of Student objects to the applet. The sendStudentList() method is passed to an HttpResponse parameter and a vector of Student objects. Since the applet initiated the HttpRequest, the servlet can respond to the applet by using the HttpResponse parameter. Thus an ObjectOutputStream to the applet is created based on the HttpResponse object. The student vector is actually serialized and sent to the vector with a call to outputToApplet.writeObject(studentVector).

As we've seen before, code is needed by the applet to handle the data sent from the servlet. The applet uses the code fragment shown in Listing 6 to read in a vector the Student objects from the servlet. The applet opens a URL connection to the servlet's location. Appropriate methods are called to ensure that the applet doesn't use cached versions of the URL connection. Next, an ObjectInputStream is created based on the servlet's input stream socket connection. All the switches have been flipped now and we can easily read in our vector of Student objects. Again, remember that we have to cast the object to the appropriate type.

Congratulations! You've successfully read in a vector of student objects. This vector is now available for refreshing the AWT List component.

Conclusion
This article goes beyond the normal method of sending name/value pairs over the HTTP/CGI protocol. The techniques presented leveraged the features of Java object serialization, providing an elegant way to transmit serialized Java objects over network connections.

I must inform you, however, that this article discussed communication using the HTTP/CGI protocol only. There are a number of other mechanisms for applets to communicate with server-side processes - Java's Remote Method Invocation (RMI), for example.

RMI allows a client application to call methods on a remote object as if the object was local. In fact, RMI uses object serialization to pass objects back and forth between the client application and the remote object. All the low-level details of network connections and serialization are hidden from the developer using RMI. If your project requires a large amount of applet-servlet communication, I'd recommend that you take a close look at RMI and the features it has to offer.

The second mechanism of communicating with server-side process is CORBA (Common Object Request Broker Architecture). Like RMI, CORBA allows you to make method calls on remote objects. If you have legacy server-side code written in a different language, you can wrap it as a CORBA object and expose its functionality. CORBA provides a rich framework of services and facilities for distributing objects on the network.

By now, you should understand the concepts and techniques for communication between applets and servlets. If you'd like to get more information on distributed computing with RMI and CORBA, visit the Web sites listed below. In this article I demonstrated how an applet uses a POST method to send a serialized object to a servlet. The appropriate server-side code for the servlet was provided for reading in a serialized object. Our Student Tracking applet used this communication method to send a true Java object to a servlet. The servlet was also enhanced to return a vector of student objects to the applet. Likewise, the appropriate applet code was provided for reading in a vector of student objects.

As you can see, applet and servlet communication is straightforward with the techniques presented in this article. You can now add an applet front end to your servlet-based application.

URL References:
Remote Method Invocation (RMI):
www.javasoft.com/products/rmi
CORBA: www.omg.org
Student Tracker Source Code: www.j-nine.com/pubs/applet2servlet

About the Author
Chád (shod) Darby, a Java consultant for J9 Consulting, specializes in developing server-side Java and database applications. You can e-mail him at [email protected].Carnegie Mellon University.

	

Listing 1.
 
// connect to the servlet 
String location = "http://www.foo.com/servlet/TestServlet"; 
URL testServlet = new URL( servletLocation ); 
URLConnection servletConnection = testServlet.openConnection(); 

// inform the connection that we will send output and accept input 
servletConnection.setDoInput(true); 
servletConnection.setDoOutput(true); 

// Don't use a cached version of URL connection. 
servletConnection.setUseCaches (false); 
servletConnection.setDefaultUseCaches (false); 

// Specify the content type that we will send binary data 
servletConnection.setRequestProperty 
("Content-Type", "<insert favorite mime type>"); 

// get input and output streams on servlet 
. . . 

// send your data to the servlet 
. . . 

Listing 1.
 
// 

import java.sql.*; 
import javax.servlet.http.*; 

/** 
* The Student class has data members to describe 
* a student. String methods are available to 
* display the data members to the console or web page. 
* 
* @author Chad (shod) Darby, [email protected] 
* @version 0.6, 5 Jan 1998 
* 
*/ 
public class Student implements java.io.Serializable 
{ 
// data members 
protected String lastName; 
protected String firstName; 
protected String company; 
protected String email; 
protected String courseTitle; 
protected String courseLocation; 
protected String expectations; 
protected java.sql.Date courseDate; 

protected final String CR = "\n"; // carriage return 

// constructors 
public Student() 
{ 
} 

public Student(String aLastName, String aFirstName, String aEmail, 
String aCompany, String aDate, String aCourseTitle, 
String aCourseLocation, String aExpectation) 
{ 
lastName = aLastName; 
firstName = aFirstName; 
email = aEmail; 
company = aCompany; 

courseDate = java.sql.Date.valueOf(aDate); 
courseTitle = aCourseTitle; 
courseLocation = aCourseLocation; 
expectations = aExpectation; 
} 

public Student(HttpServletRequest request) 
{ 
lastName = request.getParameter("LastName"); 
firstName = request.getParameter("FirstName"); 
email = request.getParameter("Email"); 
company = request.getParameter("Company"); 

String dateString = request.getParameter("CourseStartDate"); 
courseDate = java.sql.Date.valueOf(dateString); 

courseTitle = request.getParameter("CourseTitle"); 
courseLocation = request.getParameter("CourseLocation"); 
expectations = request.getParameter("Expectations"); 
} 

public Student(ResultSet dataResultSet) 
{ 

try { 
// assign data members 
lastName = dataResultSet.getString("LastName"); 
firstName = dataResultSet.getString("FirstName"); 
email = dataResultSet.getString("Email"); 
company = dataResultSet.getString("Company"); 
expectations = dataResultSet.getString("CourseExpectations"); 
courseTitle = dataResultSet.getString("CourseTitle"); 
courseLocation = dataResultSet.getString("CourseLocation"); 
courseDate = dataResultSet.getDate("CourseStartDate"); 
} 
catch (SQLException e) 
{ 
e.printStackTrace(); 
} 
} 

// accessors 
public String getLastName() 
{ 
return lastName; 
} 

public String getFirstName() 
{ 
return firstName; 
} 

public String getEmail() 
{ 
return email; 
} 

public String getCompany() 
{ 
return company; 
} 

public String getExpectations() 
{ 
return expectations; 
} 

public String getCourseTitle() 
{ 
return courseTitle; 
} 

public String getCourseLocation() 
{ 
return courseLocation; 
} 

public Date getCourseDate() 
{ 
return courseDate; 
} 
  

// methods 
// normal text string representation 
public String toString() 
{ 
String replyString = ""; 

replyString += "Name: " + lastName + ", " + firstName + CR; 
replyString += "E-mail: " + email + CR; 
replyString += "Company: " + company + CR; 
replyString += "Course Expectations: " + expectations + CR; 
replyString += "Course Title: " + courseTitle + CR; 
replyString += "Course Location: " + courseLocation + CR; 
replyString += "Course Start Date: " + courseDate + CR + CR; 

return replyString; 
} 

// returns data as HTML formatted un-ordered list 
public String toWebString() 
{ 
String replyString = "<ul>"; 

replyString += "<li><B>Name:</B> " + lastName + ", " + firstName + CR; 
replyString += "<li><B>E-mail:</B> " + email + CR; 
replyString += "<li><B>Company:</B> " + company + CR; 
replyString += "<li><B>Course Expectations:</B> " + expectations + CR; 
replyString += "<li><B>Course Title:</B> " + courseTitle + CR; 
replyString += "<li><B>Course Location:</B> " + courseLocation + CR; 
replyString += "<li><B>Course Start Date:</B> " + courseDate + CR; 

replyString += "</ul>" + CR; 

return replyString; 
} 

// returns data formatted for an HTML table row 
public String toTableString(int rowNumber) 
{ 
String replyString = ""; 
String tdBegin = "<td>"; 
String tdEnd = "</td>" + CR; 

replyString += "<tr>" + CR; 
replyString += tdBegin + rowNumber + tdEnd; 
replyString += tdBegin + lastName + ", " + firstName + tdEnd; 
replyString += tdBegin + "<a href=mailto:" + email + "> " 
+ email + "</a>" + tdEnd; 

replyString += tdBegin + company + tdEnd; 
replyString += tdBegin + expectations + tdEnd; 
replyString += "</tr>" + CR; 

return replyString; 
} 
}
 
Listing 2.
 
// 
// Applet client-side code to send a student object 
// to a servlet in a serialized fashion. 
// 
// A POST method is sent to the servlet. 
// 

URL studentDBservlet = new URL( webServerStr ); 
URLConnection servletConnection = studentDBservlet.openConnection(); 

// inform the connection that we will send output and accept input 
servletConnection.setDoInput(true); 
servletConnection.setDoOutput(true); 

// Don't use a cached version of URL connection. 
servletConnection.setUseCaches (false); 
servletConnection.setDefaultUseCaches (false); 

// Specify the content type that we will send binary data 
servletConnection.setRequestProperty ("Content-Type", "application/octet-stream"); 

// send the student object to the servlet using serialization 
outputToServlet = new ObjectOutputStream(servletConnection.getOutputStream()); 

// serialize the object 
outputToServlet.writeObject(theStudent); 

outputToServlet.flush(); 
outputToServlet.close(); 

Listing 3.
 
// 
// Servlet server-side code to read a serialized 
// student object from an applet. 
// 
// The servlet code handles a POST method 
// 
public void doPost(HttpServletRequest request, 
HttpServletResponse response) 
throws ServletException, IOException 
{ 
ObjectInputStream inputFromApplet = null; 
Student aStudent = null; 
PrintWriter out = null; 
BufferedReader inTest = null; 

try 
{ 
// get an input stream from the applet 
inputFromApplet = new ObjectInputStream(request.getInputStream()); 

// read the serialized student data from applet 
aStudent = (Student) inputFromApplet.readObject(); 

inputFromApplet.close(); 

// continue the process for registering the student object 

} 
catch(Exception e) 
{ 
// handle exception 
} 
} 

Listing 4.
 
// 
// Servlet server-side code to send a serialized 
// vector of student objects to an applet. 
// 
// 

public void sendStudentList(HttpServletResponse response, Vector studentVector) 
{ 
ObjectOutputStream outputToApplet; 

try 
{ 
outputToApplet = new ObjectOutputStream(response.getOutputStream()); 

System.out.println("Sending student vector to applet..."); 
outputToApplet.writeObject(studentVector); 
outputToApplet.flush(); 

outputToApplet.close(); 
System.out.println("Data transmission complete."); 
} 
catch (IOException e) 
{ 
e.printStackTrace(); 
} 
} 

Listing 5.
 
// 
// Applet client-side code to read a serialized 
// vector of student objects from a servlet. 
// 
// 

// connect to the servlet 
URL studentDBservlet = new URL( servletLocation ); 
URLConnection servletConnection = studentDBservlet.openConnection(); 

// Don't used a cached version of URL connection. 
servletConnection.setUseCaches (false); 
servletConnection.setDefaultUseCaches(false); 

// Read the input from the servlet. 
// 
// The servlet will return a serialized vector containing 
// student entries. 
// 
inputFromServlet = new ObjectInputStream(servletConnection.getInputStream()); 
studentVector = (Vector) inputFromServlet.readObject(); 

Content-Type: application/rtf; name="Applet2Servlet.rtf" 
Content-Disposition: inline; filename="Applet2Servlet.rtf" 

  
      
 

All Rights Reserved
Copyright ©  2004 SYS-CON Media, Inc.
  E-mail: [email protected]

Java and Java-based marks are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. SYS-CON Publications, Inc. is independent of Sun Microsystems, Inc.