This chapter demonstrates several techniques by which applets can communicate with servlets. We're going to come at the topic from a slightly different angle than you might expect. Instead of assuming you have an applet and a servlet that need to communicate, we're going assume you have an applet that needs to talk to some entity on the server and explore why sometimes that entity should be a servlet.
To get the ball rolling, let's think about applets that need to communicate with the server. There are a number of good examples. Take a look at the administration applet that manages the Java Web Server. Think about how it works--it executes on the client, but it configures the server. To do this, the applet and the server need to be in near constant communication. As another example, take a look at one of the popular chat applets. One client says something, and all the rest see it. How does that work? They certainly don't communicate applet to applet. Instead, each applet posts its messages to a central server, and the server takes care of updating the other clients. Finally, imagine an applet that tracks the price of a set of stocks and offers continuous updates. How does the applet know the current stock prices, and, more importantly, how does it know when they change? The answer is that it talks with its server.
Our interest in stock trading has risen along with the Dow, so let's continue with this hypothetical stock tracking applet. We should warn you right now that this example will remain hypothetical. We'll use it solely as a reference point for discussing the issues involved in applet-server communication. But don't worry, there's plenty of code later in the chapter that demonstrates the techniques discussed here, just in somewhat simpler examples.
This stock tracking applet of ours needs to get a stock feed from some server machine. Assuming it's a normal, untrusted applet, there's just one choice: the machine from which it was downloaded. Any attempt to connect to another machine results in a SecurityException, so let's assume the applet gets a stock feed from the server machine from which it was downloaded.[1] The question remains: how can the applet and the server communicate?
[1] You may be wondering how the server machine itself got the stock feed. For the purposes of this example, it's magic.
Before JDK 1.1 and servlets, there were two options for applet-server communication:
Have the applet establish an HTTP connection to a CGI program on the server machine. The applet acts like a browser and requests a page, parsing the response for its own use. The applet can provide information using a query string or POST data and can receive information from the returned page.
Have the applet establish a raw socket connection to a non-HTTP server running on the server machine. The non-HTTP server can listen to a particular port and communicate with the applet using whatever custom protocol they agree upon.
Each of these approaches has advantages and disadvantages. Having an applet make an HTTP connection to a CGI program works well for these reasons:
It's easy to write. The applet can take advantage of the java.net.URL and java.net.URLConnection classes to manage the communication channel, and the CGI program can be written like any other.
It works even for applets running behind a firewall. Most firewalls allow HTTP connections but disallow raw socket connections.
It allows a Java applet to communicate with a program written in any language. The CGI program doesn't have to be written in Java. It can be in Perl, C, C++, or any other language.
It works with applets written using JDK 1.0, so it works with all Java-enabled browsers.
It allows secure communication. An applet can communicate with a secure server using the encrypted HTTPS (HTTP + SSL) protocol.
The CGI program can be used by browsers as well as applets. In the case of our stock tracker example, the CGI program can do double duty, also acting as the back-end for an HTML form-based stock quote service. This makes it especially convenient for an applet to leverage existing CGI programs.
But the HTTP connection to a CGI program also has some problems:
It's slow. Because of the HTTP request/response paradigm, the applet and the CGI program cannot communicate interactively. They have to reestablish a new communication channel for each request and response. Plus, there is the standard delay while the CGI program launches and initializes itself to handle a request.
It usually requires requests to be formed as an awkward array of name/value pairs. For example, when our stock tracker applet asks for the daily high for Sun Microsystems' stock, it has to ask with an awkward query string like "stock=sunw&query=dailyhi".
It forces all responses to be formatted using some arbitrary, previously agreed-upon standard. For example, when our stock tracker applet receives the response that contains a stock's daily high price, it needs to know exactly how to parse the data. Does the returned price begin with a dollar sign? Does the response include the time when the high occurred? And if so, where is the time specified and in what format?
Only the applet can initiate communication. The CGI program has to wait passively for the applet to request something before it can respond. If a stock price changes, the applet can find out only when it asks the right question.
An applet and server can also communicate by having the applet establish a socket connection to a non-HTTP server process. This provides the following advantages over the HTTP-based approach:
It allows bidirectional, sustained communication. The applet and servlet can use the same socket (or even several sockets) to communicate interactively, sending messages back and forth. For security reasons, the applet must always initiate the connection by connecting to a server socket on the server machine, but after a socket connection has been established, either party can write to the socket at any time. This allows our stock tracker to receive stock price updates as soon as they are available.
It allows a more efficient program to run on the server side. The non-HTTP server can be written to handle a request immediately without launching an external CGI program to do the work.
But a socket connection also has disadvantages versus the HTTP-based approach:
It fails for applets running behind firewalls. Most firewalls don't allow raw socket connections, and thus they disallow this sort of applet-server communication. Therefore, this mechanism should be used only when an applet is guaranteed to never run on the far side of a firewall, such as for an intranet application.
It can be fairly complicated to write the code that runs on the server. There must always be some process (such as a stock quote server) listening on a well-known port on the server machine. Developing such an application in Java is easier than in C++, but it is still nontrivial.
It may require the development of a custom protocol. The applet and server need to define the protocol they use for the communication. While this protocol may be simpler and more efficient than HTTP, it often has to be specially developed.
The non-HTTP server cannot be conveniently connected to by a web browser. Browsers speak HTTP; they cannot communicate with a non-HTTP server.
The standard historical approach has been for applets to use HTTP to connect to CGI programs on the server. It's easy, and it works for all types of browsers, even browsers running behind firewalls. The use of raw socket connections has generally been reserved for those situations where it's absolutely necessary, such as when the applet and server require bidirectional communication. And, even in those cases, it's often possible to use HTTP connections to simulate bidirectional communication in order to pass through firewalls, as we'll see in a later example.
The recent introduction of Java servlets and object serialization has given new life to these traditional applet-server communication techniques. Servlets are starting to replace slow-starting CGI programs, improving the performance of HTTP-based applet-server communication and making frequent applet-server communication feasible. While it's true in the general case that the applet and the servlet still have to take time to reestablish their connection for each request and response, the applet no longer has to wait as the server launches a CGI program to handle each of its repeated requests.
Java object serialization has simplified the issues involved with formatting responses. With both applets and servlets written in Java, it's only natural that they should communicate by exchanging Java objects. For example, when our hypothetical stock tracking applet asks our stock feed servlet the daily high value for Sun stock, it can receive the response as a serialized StockPrice object. From this, it can get the daily high value as a float and the time of the high value as a Date. It's convenient, and it provides easy type safety. But beware, object serialization works only with applets running inside browsers that support JDK 1.1 or later.
JDK 1.1 includes two additional features that have an impact on applet-server communication: JDBC and RMI. The JDBC (Java database connectivity) API, discussed in Chapter 9, "Database Connectivity", allows a Java program to connect to a relational database on the same machine or on another machine. Java applets written to JDK 1.1 can use JDBC to communicate with a database on the server. This special-purpose communication doesn't generally require applet-servlet communication. However, it is often helpful for an applet (especially one written to JDK 1.0) to forgo connecting straight to the database (or to a pass-through proxy on the web server) and instead connect to a servlet that handles the database communication on the applet's behalf (as explained in the sidebar "Servlets in the Middle Tier" in Chapter 9, "Database Connectivity"). For example, an applet that wants to look up a person's address can connect to a servlet using HTTP, pass the name of the person using HTTP parameters, and then receive the address as either a specially formatted string or a serialized object. This use of applet-servlet communication tends to piggy-back on existing protocols like HTTP, so we aren't going to cover it in any more detail here.
The RMI (Remote Method Invocation) API allows an applet to invoke the methods of a Java object executing on the server machine, and, in some cases, it also allows the object on the server machine to invoke the methods of the applet. The advantages of RMI for applet-server communication are compelling:
It allows applets and server objects to communicate using an elegant high-level, object-oriented paradigm. Requests can be made as method invocations, passing serialized object parameters if necessary. Responses can be received as serialized objects or even references to other remote objects. But to even use the words request and response shows we've been using HTTP too much! With RMI, there are no requests or responses, just method invocations. To go back to our stock tracker example, the applet can get the daily high for Sun stock by calling sunw.getDailyHigh(), where sunw is a Java object that exists on the server.
It allows server objects to make callbacks to the methods of the applet. For example, with our stock tracking example, the server can notify interested applets that a stock price has changed by calling applet.update(stock).
It can be made to work through firewalls (though it doesn't like it, and current browsers don't support it very well). The RMI transport layer normally relies on direct socket connections to perform its work. When an applet executes behind a firewall, however, its socket connections fail. In this case, the RMI transport layer can automatically begin operating entirely within the HTTP protocol.[2] This is not without cost, though. The HTTP overhead affects performance, and the HTTP request/response paradigm cannot support callbacks.
[2] For a description of the system properties necessary for an RMI client application to poke through a firewall see John D. Mitchell's JavaWorld Java Tip 42 at http://www.javaworld.com/javaworld/javatips/jw-javatip42.html. (Unmentioned in the article but also important are the socksProxySet, socksProxyHost, and socksProxyPort properties necessary for SOCKS-based proxies.) All these system properties should be set automatically by web browsers, but unfortunately few web browsers currently do this, leaving their applets with no way to determine the proper settings and no way to use RMI through a firewall.
The disadvantages of RMI are equally concerning:
It's complicated. RMI communication uses special stub and skeleton classes for each remote object, and it requires a naming registry from which clients can obtain references to these remote objects.
It's supported in few browsers. Of all the popular browsers available as of this writing, only Netscape Navigator 4 includes RMI support. Previous Netscape browser versions and all versions of Microsoft's Internet Explorer do not support RMI without installing a special plug-in.
It can be used only by Java clients. The server object can't be shared by a web browser or even a C++ client.
For a more information on RMI programming, see Java Network Programming, by Elliotte Rusty Harold (O'Reilly) and Java Distributed Computing, by Jim Farley (O'Reilly).
CORBA (Common Object Request Broker Architecture) is a technology similar to RMI that enables communication between distributed objects written in various languages. With CORBA and its IIOP (Internet Inter-ORB Protocol) communication protocol, a C++ client can communicate with a Java servlet. Demonstrating this ability extends beyond the scope of this book. For more information, see http://www.acl.lanl.gov/ and http://java.sun.com/products/jdk/idl.
Now that we've examined all the options, the question remains: how should our stock tracking applet communicate with its stock feed server? The answer is: it depends.
If we can guarantee that all our potential clients support it, RMI's elegance and power make it an ideal choice. But currently that's like assuming all your friends enjoy your Star Trek jokes. It can be true if you carefully choose your friends (or your clients), but it's generally not the case in the real world.
When RMI isn't available, the bidirectional capabilities of the non-HTTP socket connection make it look fairly attractive. Unfortunately, that bidirectional communication becomes nonexistent communication when the applet ends up on the far side of a firewall.
There's always the old workhorse, HTTP communication. It's straightforward to implement and works on every Java-enabled client. And if you can guarantee that the client supports JDK 1.1 (and this is easier to guarantee than that the client support RMI), you can use object serialization.
Perhaps the best solution is to use every solution, with servlets. Servlets make it possible to combine the HTTP, non-HTTP, and RMI applet-server communication techniques, supporting them all with a single servlet. That's right: one servlet, multiple access protocols. Why would anyone want to do this? Well, it's a handy technique when an applet wants to communicate using RMI or a non-HTTP protocol but needs to fallback to HTTP when necessary (such as when it finds itself behind a firewall). By using the same servlet to handle every client, the core server logic and the server state can be collected in one place. When you control your environment, of course, you can drop one or more of these protocols. But isn't it nice to know you don't have to?
Copyright © 2001 O'Reilly & Associates. All rights reserved.