by Joe Weber
One of the advances the Netscape Corporation added to its second generation Web servers--Enterprise Server and FastTrack Server--was the ability to uniquely use the Java and JavaScript languages to perform server-side work. This integration is designed to help you more easily perform the tasks which were previously accomplished through CGI scripts. In addition, Sun has taken up the same quest with its Servlet technology currently available only in its HTTP server (code-named Jeeves).
So why would you want to use Java to perform server-side interaction? First, for developers who are already working in the Java programming language (of which you are now obviously one), and who want to take advantage of the rapid development cycles afforded by Java, server-side Java is a natural extension. Second, it is often advantageous to use the efforts that are put into a client-side Java applet and mitigate that onto the server.
One prime example of where using the code from a standard applet on a server-side program can be extremely helpful is with programs that serve dual purposes. Java can be used not only on the Web, but also on desktops. This combination provides a unique independence. As a result, Java is being used in development in many situations where the medium of delivery will be both CD-ROM and Internet. In other words, with Java it is possible to deliver exactly the same application, first on a Web site as an applet, and then as an application on a CD-ROM. When many of these applications are used on the Internet, though, they suffer badly because of bandwidth restrictions.
The problem is that a CD-ROM can contain a lot of information, more than 600M. Transferring all that data across the low-bandwidth Internet is very problematic. As a result, it would be nice to limit the amount of data sent. By extracting the search portion of the applet for the server (without any other code changes), this limitation is simple and very cost-effective.
In addition to these benefits, you can capture great power from using the client/server abilities of Java. By interacting with a client-side Java applet, a server-side applet can share the load between it and the client. By placing the various portions of your program on two computers you can produce the best solution for your environment. For instance, by limiting the amount of sheer calculations performed by the server, you can reduce your server load. At the same time, by forcing the server to do all the work that requires access to large amounts of data, and having it send only the limited portion of the data the client needs, you end up limiting the amount of bandwidth required by the client. The client can perform most of the calculations, while the server performs all the data searching.
Server-side Java can be done in three ways:
Because the first option--using Java for standard CGI--isn't very practical, ignore that option in this chapter. You'll start by taking a look at how to use the Enterprise and FastTrack Servers with server-side applications.
In order to take advantage of the Java serving capabilities of the Enterprise or FastTrack servers, you must first enable these options on the server.
The controls for the VM are located in the server controls. Netscape's integrated server management system includes controls for a great number of items about your server, and each of these things is placed under different headings. To get to the Java controls:
FIG. 42.1
Select the Programs option in the main window.
FIG. 42.2
Activate the Java interpreter.
Once you choose OK, the server prompts you to confirm the changes and to restart
the server. If all goes as planned, the server notifies you that it has been restarted,
as shown in Figure 42.3. If not, you may need to manually stop and restart your server
in order for these changes to take effect.
FIG. 42.3
The server should confirm that it has restarted.
With the Netscape 2.0 servers, the directory /SERVER-JAVA has been set aside in much the same way that /CGI-BIN has been used on first-generation HTTP servers. Now that you have enabled Java on your Web server, you can confirm that it is working by using some of the Java applications that are included with the Enterprise and FastTrack servers.
NOTE: In order to use the demonstration classes that are shipped with the Enterprise and FastTrack servers, you need to make sure you have entered the correct directory in the Java Applet Directory option during your server configuration. By default, this directory is:
/USR/NS-HOME/PLUGINS/JAVA/APPLETS
However, you will want to change the /USR/NS-HOME to the directory that is appropriate for your installation.
The information that is shown about the browser is the information the browser
provides to the server. There is no magic bullet here; the server must rely on what
and/or who the browser claims to be. As a result, if you use the information, you
should be aware that a browser can provide information that may not seem to be correct.
Some browsers, such as Microsoft Internet Explorer, show themselves as being Mozilla
for compatibility with Netscape, as shown in Figure 42.5.
FIG. 42.5
Microsoft Internet Explorer claims to be Mozilla.
So where does this information come from? First, take a look at the directory
/NETSCAPE-SERVER-DIRECTORY/PLUGINS/JAVA/APPLETS
Make sure you substitute /NETSCAPE-SERVER-DIRECTORY with the directory in which your Netscape 2.0 server resides. In that directory, you will find several files, one of which is called BrowserDataApplet.class.
When you accessed the directory /SERVER-JAVA, the Enterprise server went out and loaded the BrowserDataApplet class into its virtual machine; the results were sent to your browser, just like a CGI program.
Instead of diving into the complete source code for the BrowserDataApplet, take a look at how to do something a bit simpler. In almost every language since Kernighan and Richie invented C, the first program is traditionally one that says "Hello World". Java is a unique language in that it has a number of different Hello World implementations, depending on how it is being used. You have already seen how to write HelloWorld as an application and as an applet. Now write it so that the server sends a document that simply says Hello World to the browser.
Listing 42.1 shows the source code for the server-side applet HelloWorld program.
/* Import the required classes from other packages */ import netscape.server.applet.HttpApplet; import java.io.PrintStream; /* The class which is created is called HelloWorld */ public class HelloWorld extends HttpApplet{ public void run() throws Exception { PrintStream out = getOutputStream(); out.println ("HelloWorld"); } }
In order to understand the source code for the HelloWorld application, take it line by line. The first two lines of the code import two classes. The first class is provided by Netscape, and the second is included with the Java library provided with Sun's Java Developer Kit.
/* Import the required classes from other packages */ import netscape.server.applet.HttpApplet; import java.io.PrintStream;
netscape.server.applet.HttpApplet is probably the most important class included with the API from Netscape. The server expects all the programs that it loads to be HttpApplets, just as Appletviewer or Netscape expect classes to extend java.applet.Applet. As you see in the next line of code, you can make the HelloWorld class into an HttpApplet by extending, or inheriting, the characteristics of HttpApplet.
The next line of code is the class declaration. In order to be usable as a server-applet, all classes must be declared first to be public (so the server can read them) and second to extend HttpApplet.
/* The class which is created is called HelloWorld */ public class HelloWorld extends HttpApplet{
All server applets need to overload the method run(). The run method is the method the server runs when a user attempts to access the URL where the HelloWorld applet is located. In a certain sense, you can think of the run() method as the cousin of the main() function in C or C++.
public void run() throws Exception {
The next two lines perform the actual work of the class. In this case, you want to write the text Hello World out to the browser. In order to do this, you need a place to write the text. This may seem obvious, but remember the class doesn't know what you want to do, and System.out will put it on the server's console, not in the browser window. Fortunately, Netscape has included a method called getOutputStream() that provides you with a means to get to the stream of data going back to the client.
PrintStream out = getOutputStream();
Finally, now that you have access to the stream handler, you can place the text Hello World on the browser screen. To do this, use the println() method provided in the PrintStream class.
out.println ("HelloWorld");
The last two lines could also have been written as:
getOutputStream().out.println("Hello World");
if you would prefer. Because you are used to the syntax of System.out.println(), this may make more sense to you.
To create this application, copy the contents of Listing 42.1 into your favorite text editor. Then compile them using the javac program supplied with Sun's JDK.
The syntax for compiling the HelloWorld application is not quite as simple as for most Java code. The reason is that you need to import a class from the Netscape package. As a result, it is necessary to include the file that contains the package in the CLASSPATH.
Before you can include the file, though, you need to first determine where the file is located. What you need to look for is a file called SERV2_0.ZIP. By default, this file is located at:
/USR/NS-HOME/PLUGINS/JAVA/CLASSES/SERV2_0.ZIP
You need to substitute the /USR/NS-HOME directory with the correct directory for your Enterprise or FastTrack server. As with the rest of this chapter, the location that is used for this installation is /OPTL/ENT-HOME, so the actual path is:
/OPTL/ENT-HOME/PLUGINS/JAVA/CLASSES/SERV2_0.ZIP
but your installation is likely to be different. To obtain the proper file name, substitute /OPTL/ENT-HOME for the directory where you installed the FastTrack or Enterprise server.
CAUTION:
In addition to the SERV2_0.ZIP file, you may need to know the location of the CLASSES.ZIP file included with the JDK. This file is located in the /JAVA/LIB directory. For the purposes of this chapter, the file in this installation is located at:/OPTL/JAVA/LIB/CLASSES.ZIP
But as with the location of the SERV2_0.ZIP file, you need to substitute the actual directory where you have installed the JDK. If you obtain an error such as:
class java.io.PrintStream not found on importthe odds are that you need to also add the CLASSES.ZIP file to your CLASSPATH.
Once you have located the CLASSES.ZIP and SERV2_0.ZIP files, you have two ways to add them to the class path. The first and simplest is to do so on the command line. To compile the HelloWorld file, include the Netscape package in the CLASSPATH as shown in the following command line:
javac -classpath /optl/ent-home/plugins/java/classes/serv2_0.zip:/optl/java/lib/classes.zip:. HelloWorld.java
This should produce a file called HelloWorld.class in the directory where the HelloWorld.java file is located.
If you're like most people, you don't really want to have to type out 104 characters every time you want to compile the HelloWorld program. To eliminate the need to always specify the -CLASSPATH option, you can set an environment variable called CLASSPATH to point to the SERV2_0.ZIP file. Once the CLASSPATH has been set, you can compile a program in much the same way you would any other Java application.
To set the CLASSPATH variable using the corn shell (csh), you would type:
CLASSPATH=/optl/ent-home/plugins/java/classes/serv2_0.zip export CLASSPATH
NOTE: For Windows NT users, the examples given here and throughout this chapter are for UNIX machines. When using an NT machine, the directories should use the backslash (\) instead of the forward slash (/). Also, to set an environment variable you need to use the set command, as in: set classpath=\ns-home\plugins\java\classes\serv2_0.zipOnce the you have set the CLASSPATH variable, you can now compile the HelloWorld application by typing:
javac HelloWorld.java
To be able to run the HelloWorld application, the first step is to copy the HelloWorld.class file into the directory you have specified when configuring the Enterprise or FastTrack server (refer to Figure 42.2). If you placed the HelloWorld.java file in this directory and compiled it there, you don't have to move anything; it is already in the correct location.
To test out the HelloWorld application, open the URL /server-java/HelloWorld in any browser. It does not need to be a Java-enabled browser such as Netscape Navigator. Figure 42.6 shows the page you should see.
As you can see from Figure 42.6, the Java application produced something which,
to your browser, looks exactly like it is coming from any standard HTML file. Of
course, this was a lot of work to produce a simple line of text, but before you learn
to surf the big waves, it is generally a good idea to learn to swim first.
FIG. 42.6
The HelloWorld application should generate a simple page in your browser.
Now take a look at a more advanced Java application similar to the BrowserDataApplet, which shows some information about the incoming browser. Listing 42.2 shows the source code for the new application.
import netscape.server.applet.HttpApplet;
import java.io.PrintStream;
import java.net.Socket;
import java.net.InetAddress;
public class Greetings extends HttpApplet {
public void run() throws Exception {
//Check to find out if browser can accept normal text output
if (returnNormalResponse("text/plain")) {
//Get the output stream to send data to client
PrintStream out = getOutputStream();
//get the socket the browser is connected to
Socket client = getClientSocket();
//find the hostname of the socket
String clientAddress = client.getInetAddress().getHostName();
//Check to see if it's my IP address
if (clientAddress.compareTo ("206.31.43.250")==0)
out.println("Greetings great and wonderful master!");
else
out.println("Nice to meet other netizens");
//Find out what browser the client is using
String browser = getHeader ("user-agent");
if (browser != null){
if (browser.startsWith ("Mozilla"))
out.println ("Your using Netscape... Hey this is a Netscape Server too!");
else
out.println("You're using the" + browser+" browser");
}
else
out.println("Hey, your browser didn't identify itself.");
}
}
} //end class Greetings
Notice that, just like your HelloWorld example, the very first thing that you need to write is:
import netscape.server.applet.HttpApplet;
This includes the class from the Netscape library in the Java environment. Netscape actually provides four classes to assist the construction of server-side applications:
- netscape.server.applet.Server
- netscape.server.applet.ServerApplet
- netscape.server.applet.HttpApplet
- netscape.server.applet.URIUtil
Each of these classes provides a means to access data about the server or the client in order to make writing server applications easier.
Each different class contains various methods to place or get information about the envi-ronment. In the HelloWorld example, the only method used was getOutputStream(). The Greetings shows the use of several more of the methods in HttpApplet.
The first line in the Greetings class shows an example of one such method. Before you blindly write to the browser (like you did with the HelloWorld application), it is better to first make sure that the browser is willing to accept text output. If not, you may have to respond differently, so the first line of the Greetings application checks to see if the browser has a standard "text/plain" response:
if (returnNormalResponse("text/plain")) {
After you have verified the browser response, the application opens the output
stream, just as it did in the HelloWorld application. Once the connection has been
established to the browser, the program proceeds to send data, the same way it did
for HelloWorld.
Now, it's often convenient to know who is accessing your server. Based on who it is, you might want to give a different response to the user, so retrieve some information about the client accessing the Greetings application.
The next line of code defines a Socket variable that points to where the client has attached. If you are familiar with Java programming, this socket can be used just as it would in any other Java application.
Once you have a pointer to the client socket, you can use it to determine the address the client browser is coming from, using the getInetAddress() method. Once you have the client's IP address, you can use it to determine if the client is a valid host or someone else. In this example, the choice is to simply have the application greet my computer differently than other people on the Net:
//get the socket the browser is connected to
Socket client = getClientSocket();
//find the hostname of the socket
String clientAddress = client.getInetAddress().getHostName();
//Check to see if it's my IP address
if (clientAddress.compareTo ("206.31.43.250")==0)
out.println("Greetings great and wonderful master!");
else
out.println("Nice to meet other netizens");
NOTE: For experienced Java programmers, it might be interesting to note that the getClientSocket() method is not one from HttpApplet, but rather from its parent ServerApplet.
The getInetAddress() and getHostName() methods are not part of the Netscape package at all, but rather are members of the java.net package.
//Find out what browser the client is using
String browser = getHeader ("user-agent");
You may initially wonder about the next line of the code, which reads:
if (browser != null){
It's important to make sure that you actually received a string from the getHeader request because, unfortunately, it is not always the case that information obtained using the various methods will actually return data. For instance, some browsers do not return information about who they are. If this is the case, the method will return null and, unless you check for this, you will run into problems using the string.
It was decided that if the browser that was coming in was a Netscape Navigator browser (or any browser claiming to be Netscape), the user would receive a friendly hello. To do this, you need to compare the browser name with Mozilla. In fact, all you really care about is, does the browser's name start with Mozilla? If not, you will simply give them another message.
A complete list of the netscape.server.applet.HttpApplet may be helpful in developing server applications (see Table 42.1).
Methods | Use |
public String getMethod() | Returns the method request header (such as GET, HEAD, or POST). |
public URL getURI() | Returns the relative portion of an URL (for example, if an URL is http://www.magnastar.com/applets, the URI would be /applets). |
public String getProtocol() | Returns the protocol that is being used for transportation (such as HTTP). |
public String getQuery() | Returns the query string from the HTTP request. |
public String getPath() | Returns the path of the HTTP request. |
public void setContentType (String type) | Sets the content type for the HTTP response. |
public getURL() | Returns the URL of the HTTP request. |
public boolean returnNormalResponse(String contentType) | Starts an HTTP response request. The content is set based on the parameter, and headers that have been established by calling setResponseProperty are sent. |
public void returnFile (String contentType, File file) | Opens an HTTP response and sends the of the specified file using the indicated contentType. |
public void returnFile (File file) | Similar to the previous method, but the content type is derived from the extension of the file (for example, MYFILE.GIF) |
public boolean returnErrorResponse(String contentType, int status) | Opens an HTTP error response to the request using the given error status and reason. |
public boolean returnMultipartResponse String subtype, String boundary) | Initiates a multi-part boundary, and explicitly sets the boundary marker. |
public void endMultipart Response() | Ends a multi-part response. This method must be called to close a multi-part response. If multiple multi-part responses are nested, the most recent part is closed. |
public void setStatus (int n) | Specifies the status of the response and the reason. |
public int setFileInfo (File file) | Sets the status based on a given file. If, for instance, the client only wants a changed file (instead of the cached value), this function returns ABORTED. |
public String translateURI (String uri) | Translates the given string into the full path for the given file system. |
public static String uri2url (String prefix, String suffix) | Concatenates the prefix and suffix to form the URL. |
public Hashtable getFormData() | Returns the values of a form. These values are placed in a Hashtable and separated by && characters. The names become the keys to the Hashtable. |
public String getFormField (String fieldName) | Returns the form field of the HTTP request for the given field name. |
The netscape.server.applet.ServerApplet class is at the heart of most
of the activities of Java-based CGI. The HttpApplet class extends this class,
so the methods in Table 42.2 are available to any class that extends HttpApplet as
well.
Methods | Use |
public Socket getClientSocket() | Returns the socket that is connected to the client. The socket can be used to obtain information about the incoming client, including its IP address. |
public String getClientProperty (String name) | Returns a variety of information about the client. The name can be set to several variables, the most common of which are ip and dns. |
public String getConfigProperty (String name) | Returns parameters set by the site administrator for the invocation of server applets. |
public String getHeader (String name) | Returns the header value requested by the name value. The name-value pairs are set based on the RFC822 headers. |
public InputStream getInput Stream() | Returns the InputStream which should be used to read from the client. |
public PrintStream getOutput Stream() | Returns the PrintStream which should be used to write to the client. |
public String getRequestProperty (String name) | Returns properties about the client's request. The request properties include method, uri protocol, and query. |
public String getResponseProperty (String name) | Returns the properties which are to be sent to the client as a result of the client response. |
public static Server getServer() | Returns the Server which may be queried for information about the server. Helpful for use when trying to get information about the current server. |
public String getServerProperty (String name) | Returns properties that represent the working variables of the server. |
public void inform (String error) | Records an informational message in the server's error log. |
public void reportMisconfiguration (String error) | Records an informational message (String error)in the server's error log about missing or illegal parameters. |
public void reportCatastrophe (String error) | Records informational message in the server's error log about a catastrophic error. |
public void reportFailure (String error) | Records informational message in the server's error log about a general failure. |
public void reportSecurity (String error) | Records informational message in the server's error log about security violation, or someone attempting to access a resource they shouldn't. |
public void run() throws Exception | The run method performs the work fulfilling the request that was made to the server. Each applet must override the run method for its own purposes. Any exceptions generated by the run method indicate some sort of failure and are recorded in the server's error log. |
public void setResponseProperty (String name, String value) | Sets properties that should be returned as the result of client responses. |
public void warn(String error) | Records an informational message in the server's error log about warnings. |
Currently code-named Jeeves, Sun's Java-based Internet server is actually a framework for developing Internet services, installing them, and making them secure.
The thing that makes Jeeves the most different is the use of what have been called servlets. Servlets work a lot like SHTML documents. When a request is made to an object that contains a servlet, the servlet is executed and the output is sent to the client. The major difference between standard SHTML documents and servlets, however, is that once a communications socket is opened between the servlet and the client, the servlet can continue to have an ongoing dialog with the client. In this sense, it is very similar to Netscape's technology.
Servlets can also be written to work with server-side includes. So servlets can go either way--SHTML or as a server response to a directory.
In addition, servlets don't have to be loaded separately each time a servlet page is loaded. Once a servlet is placed into memory, it can respond to all requests to that page. This can dramatically save on resources. In fact, Servlets have an init() method, similar to applets. So, if there are some costly operations it will perform for every request, the Servlet can place this in its init() method and maximize performance.
Currently in the servlet specification is the ability to allow digitally signed servlets to be loaded over the Net. This technology could eventually mean that servlet authors could charge for their programs on a per-use basis to individuals whose pages are not accessed frequently, but who would still like to use normally expensive programs on their systems. They would only need to pay for what they use.
Take a look at the HelloWorld example for a Servlet, just as you did for the Netscape example (see Listing 42.3).
/* Import the required classes from other packages */
import java.servlet.*;
import java.io.PrintStream;
/* The class which is created is called HelloWorld */
public class HelloWorld extends Servlet{
public void service(ServletRequest req, ServletResponse resp) throws IOException
{
PrintStream out = new PrintStream (res.getOutputStream());
//Write out standard HTML response header
res.setContentType ("text/html");
res.writeHeaders();
//Write out the response
out.println("<HTML><BODY>");
out.println ("HelloWorld");
out.println("</BODY></HTML>");
}
}
By comparing Listings 42.3 and 42.1, you find that they are surprisingly (or perhaps not so surprisingly) similar. All servlets must extend the java.servlet.Servlet class. The most important method in Servlet is service(). The service() method is called each time the server receives a request for the Servlet page.
If you look at the service() method, you see that just like the run() method in the Netscape HelloWorld program, the first task is to create a PrintStream buffer to print data back to the browser.
The next few lines of code show one of the differences between Servlets and HttpApplets--namely that you need to actually write out the header information. Finally, the next lines are nearly identical to the final line of Listing 42.1.
Once again, as you did with the HttpApplet, take a look at the Greeting example using the Servlet Technology. Listing 42.4 shows the code for doing this.
import java.servlet.*t;
import java.io.PrintStream;
import java.net.Socket;
import java.net.InetAddress;
public class Greetings extends HttpApplet {
public void service (ServletRequest req, ServletResponse res) throws IOException
{
//Get the output stream to send data to client
PrintStream out = new PrintStream (res.getOutputStream();
//find the hostname of the socket
String clientAddress = req.getRemoteAddr();
//Check to see if it's my IP address
if (clientAddress.compareTo ("206.31.43.250")==0)
out.println("Greetings great and wonderful master!");
else
out.println("Nice to meet other netizens");
//Find out what browser the client is using
String browser = req.getHeader ("user-agent");
if (browser != null){
if (browser.startsWith ("Mozilla"))
out.println ("You're using Netscape... Hey this is a Netscape Server too!");
else
out.println("You're using the" + browser+" browser");
}
else
out.println("Hey, your browser didn't identify itself.");
}
}
} //end class Greetings
As with the HelloWorld example, the Greetings example in Listing 42.4 is nearly
identical to its Listing 42.2 counterpart for Netscape server.
As with the HelloWorld example, most of the work for the Servlet is actually being done in the service() method.
The major difference between Listings 42.2 and 42.4 is the fact that java.servlet. ServletRequest includes methods to retrieve information about the request data directly. It's not necessary to get the client socket and then find the address as you did in Listing 42.2. With a servlet, you can simply call the getRemoteAddr() method to obtain this information.