Copyright ©1996, Que Corporation. All rights reserved. No part of this book may be used or reproduced in any form or by any means, or stored in a database or retrieval system without prior written permission of the publisher except in the case of brief quotations embodied in critical articles and reviews. Making copies of any part of this book for any purpose other than your own personal use is a violation of United States copyright laws. For information, address Que Corporation, 201 West 103rd Street, Indianapolis, IN 46290 or at support@mcp .com.

Notice: This material is excerpted from Special Edition Using Java, ISBN: 0-7897-0604-0. The electronic version of this material has not been through the final proof reading stage that the book goes through before being published in printed form. Some errors may exist here that are corrected before the book is published. This material is provided "as is" without any warranty of any kind.

Chapter 26 - Protocol Handlers

by Eric Blossom

The last several chapters have focused on writing applications in Java. One particularly interesting application is the HotJava Web browser. HotJava was built in such a way that you can expand the program's capability. This chapter discusses one way you can extend HotJava. The next chapter discusses another way.

Each document on the web is served up to your browser by a server program. The browser is then a client of the server program. Each server program understands a particular protocol. The protocol is designed so that the server can serve it's clients. The protocol is a rigid structure for the conversation between the client and server programs. HotJava can be extended by writing code to handle a new protocol. This let's HotJava talk to any server that understands the new protocol.

For more information on HotJava you can start with Sun's White Paper on HotJava.

In this chapter, you will


Netscape Plug-ins

Netscape uses a different scheme to extend their Navigator browser. You create something they call aplug-in. Essentially, you write a plug-in in C or some other language. Presumably it could be in Java. Navigator then has an API which the plug-in uses to communicate to Navigator. There is also an API that the plug-in must implement. The first API lets the plug-in use the browser. The second API lets the browser use the plug-in. For more information, see Netscape's Web site.

This chapter discusses how to enable HotJava to talk to new types of servers. When any client program such as HotJava uses a server program, it uses a protocol to give structure to its conversation. Each server has its own protocol and HotJava (and all other browsers) know several protocols. However, HotJava doesn't know some protocols and it therefore can't talk to some servers. By defining a "protocol handler," you can instruct HotJava in the new protocol.

Anatomy of a URL

Documents on the Web are specified by Universal Resource Locators (URLs). They always start with a single word followed by a colon. The most common one is http:, which stands for HyperText Transport Protocol. This first word in the URL is where a browser gets the needed tip about what protocol to use to get the document specified. What follows the protocol in the URL may vary from one protocol to another. This is because different protocols have different needs. Generally, the format is protocol://host:port/document. For more information on URLs you can consult a primer on the web or the standard documents defining URLs.

http://www.ncsa.uiuc.edu/demoweb/url-primer.html ftp://ftp.internic.net/rfc/rfc1738.txt

ftp://ftp.internic.net/rfc/rfc1808.txt

Let's teach HotJava how to use the finger protocol by writing a finger protocol handler. This simple protocol gives information about a user on a particular system.

Open a connection to the other machine on port 79. This will connect you to the finger server. Then send a user name to the finger server. It will then send back some information in plain text about the user you named. If you send the server an empty line, it will send back a list of users currently logged onto the server's system.

For all the details about the finger protocol consult the current standard document on the Web at

ftp://ftp.internic.net/rfc/rfc1288.txt

The finger URL is of the form:

   finger://host/user

where host is the domain name of the server machine and user is the name of the user. user can be blank-if it's blank and the server allows such a query, a list of all users will be returned.

Creating the new finger package

When HotJava encounters a URL such as finger://somehost.somwhere.com/someone it looks for a package named sun.net.www.protocol.finger. The last word in the package must match the protocol at the beginning of the URL. HotJava wants to create an object of the class sun.net.www.protocol.finger.Handler. Your job is to write that class and put it in the right place so that HotJava can find it.

This new package must contain at least two public classes. One must be named Handler. It must be an extension of the sun.net.ContentHandler class. The other can be any name you choose. The one in this chapter follows (loose) convention and is named fingerConnection. It must be an extension of the sun.net.URLConnection class.

Defining the new Handler class

Listing 26.1 shows the Java code for the new Handler class. It must go in a file named Handler.java. The compiler will insist on it.

Listing 26.1 The Finger Protocol Handler
  1   package sun.net.www.protocol.finger;
  2
  3   // Finger Protocol Handler
  4
  5   import java.net.URL;
  6   import java.net.URLConnection;
  7   import java.net.URLStreamHandler;
  8
  9   import sun.net.www.protocol.finger.fingerConnection;
 10
 11   public class Handler extends URLStreamHandler {
 12
 13      public URLConnection openConnection( URL u ) {
 14
 15         return new fingerConnection( u );
 16
 17      }
 18
 19   }

Now try to compile Handler.java with javac. You will get an error. Javac wants to know what a fingerConnection is. So your job will get a little harder. But, let's look at what you have so far.

  1. First, import three standard objects that you need (lines 5-7). They are all part of the java.net package that comes with the JDK.
  1. Next, import the mysterious fingerConnection (line 9). Later, you will create the fingerConnection class

3. Declare the Handler to be a subclass of URLStreamHandler (line 11). Now you know that you can do anything a generic URLStreamHandler can do. HotJava will be using your Handler class and will expect it to be a URLStreamHandler. This is why your class must extend URLStreamHandler.

  1. Provide a method for other objects to open this connection. All you do here is return a new fingerConnection object. So now you had better look at creating a fingerConnection.

Defining a new type of URLConnection: the fingerConnection

Listing 26.2 shows the Java code for the new fingerConnection class. It must go in a file named fingerConnection.java. Now there are two files, one for each class. Recall that a Java source file can have only one public class. Because both the Handler and fingerConnection classes must be public, they must each have a source file.

Listing 26.2 The fingerConnection subclass of URLConnection
  1   package sun.net.www.protocol.finger;
  2
  3   import java.net.URL;
  4   import java.net.URLConnection;
  5   import java.net.Socket;
  6   import java.io.InputStream;
  7   import java.io.PipedInputStream;
  8   import java.io.SequenceInputStream;
  9   import java.io.DataOutputStream;
 10   import java.io.PipedOutputStream;
 11   import java.io.IOException;
 12
 13   public class fingerConnection extends URLConnection {
 14      Socket socket;
 15
 16      public fingerConnection( URL u ) {
 17
 18         super( u );
 19
 20      }
 21
 22      public String getHeaderField( String label ) {
 23
 24         if ( label.equalsIgnoreCase( "content-type" ) )
 25            return "text/plain";
 26         return null;
 27
 28      }
 29
 30      public void connect() throws IOException {
 31
 32         if ( -1 < this.url.getPort() ) {
 33            this.socket = new Socket( this.url.getHost(), this.url.getPort() );
 34         } else {
 35            this.socket = new Socket( this.url.getHost(), 79 );
 36         }
 37
 38         DataOutputStream out = new DataOutputStream(this.socket.getOutputStream());
 39         out.writeBytes( this.url.getFile().substring( 1 ) + "\r\n" );
 40         out.flush();
 41
 42      }
 43
 44
 45      public InputStream getInputStream() throws IOException {
 46
 47         this.connect();
 48         return this.socket.getInputStream();
 49
 50      }
 51
 52   }

Declaring the fingerConnection Class

Lines 3-11 import the necessary Java objects and line 13 declares the new fingerConnection class. It's a kind of URLConnection. It handles a connection to something else that will serve up the document. You saw one of these in the Handler. Remember how the Handler returned a fingerConnection? If you look at the declaration, you see that it's supposed to return a URLConnection. Well, now this makes sense because a fingerConnection is a kind of URLConnection.

There is one more thing that a fingerConnection has that a generic URLConnection doesn't-a Socket. Line 14 declares the Socket attribute. We will use the Socket to send the user name to the server and to get the server's response.

Defining a fingerConnection constructor

Now you need to define a constructor for your new class. Lines 16-20 define a constructor for the fingerConnection. Construct a generic URLConnection and then set the socket to null. You'll get a new socket later.

Setting the content type

Different documents can contain different types of data. The MIME standard has defined seven content types. Each type is extended by several subtypes. The type of a document's content is specified in a Content-type field at the head of the document.

The MIME standard describes the content-type field in detail. The standard is available on the net from:

ftp://ftp.internic.net/rfc/rfc1521

The standard says that the default content type is text/plain. So if a document does not have a Content-type field, it should be considered to be of type text/plain. However, the current version 1.0 Final of the JDK sets it to content/unknown. Hopefully, this will be changed in a future version of the JDK and of HotJava. What this means for us is that you will need to override the method that sets the content type. This is because there is no Content-type field in the reply from the finger server. Hence, you will want to set your content type to text/plain.

Do this by overriding the getHeaderFields() method of a URLConnection class. Lines 22 to 28 of Listing 26.2 do this. The method in the JDK just returns null. Return text/plain when asked for the value of the Content-type field.

Defining a connect Method

One cannot declare an instance of a plain old URLConnection. That is because it is an abstract class. The only way it can be made concrete is to extend it as you did with fingerConnection. What makes it an abstract class is that it contains an abstract method. The connect method of a URLConnection is abstract. It has a declaration but it does not have any implementation. So to make the fingerConnection concrete, you must provide a connect() method that matches the abstract connect() method defined in java.net.URLConnection.

Now you need to show how to connect to the server and send the user name. Lines 30-42 show the connect() method. This is where this.socket is used, along with a local DataOutputStream.

What's a DataOutputStream? Once upon a time, the developers of UNIX thought files were too complicated, partly because of things like record structures and access methods. They decided that it would be simpler to have files just be a sequence of bytes. That way they would look a lot like a sequence of bytes coming in over a communications line or a sequence of bytes going to a terminal screen. This decision simplified a lot of software. Programs no longer needed to be concerned with from where the bytes were coming or to where they were going. If you don't know what "record structure" and "access method" means, don't worry. You don't need to.

A Web browser can make good use of this concept. Sometimes a document is coming from a server far away. Sometimes it's coming from a local file. The protocol handlers deal with this and make an InputStream available to whatever object needs to read the document.

Java Streams fit in with this idea. InputStreams are streams of bytes that come into the program. OutputStreams are bytes going away. You can write() to an OutputStream and read() from an InputStream. A process can produce data by writing to an OutputStream. Another can consume date by reading from an InputStream. An intermediate process can filter data by reading data from an InputStream, modifying it, and writing it to an OutputStream.

You'll be using one to send the user name to the server. Okay. But what's a DataOutputStream? Streams deal in arrays of bytes. A DataOutputStream has additional methods for writing other types of data and converting them automatically. You don't want to write the user name one byte at a time. It is much more convenient to write the whole string at once.

Author: What case is "your case"? (I changed from "our case") Perhaps re-specify what the reader is trying to accomplish?--db Is the above rephrasing more clear? - elb

Getting a socket to the server

Next up is plugging into the server (lines 32 to 36). Sockets connect to a host at a port number. A host has many ports just as Japan has many ports. A different service is available at each port. The finger service is usually available at port 79 if at all. Many folks think it's not a good idea to let others know who is on their systems. So, often the finger service is not available.

There is a java.net.ServerSocket class. Don't be confused. The ServerSocket class is for server programs. HotJava is a client program. Hence, our protocol handler is part of a client program. It should use the Socket class. Not the ServerSocket class.


Sockets

Sockets are an abstraction developed at the University of California at Berkeley to make network programming easier. UCB had added networking code to UNIX and needed a way for programmers to use it. Theoretically, it can be independent of the underlying network protocols. Practically, it has been tied to TCP/IP. It has been very successful as an API. One can find implementations of the UCB sockets API in just about any operating system that supports TCP/IP. In the early 90's a version of the sockets API was created for Microsoft's Windows operating environment. It is called WinSock. This has helped in porting UNIX networking programs to the PC. Currently work is underway to define a second version of WinSock which hopes to fulfill the promise of lower layer independence. Java's Socket class is an even more convenient way to do network programming.

The URL may have a port specified, in which case we use that port. Otherwise we use the standard port 79. If the URL was something like "finger://somehost.somewhere.com:2088/someone," we would use port 2088.

A Java URL has methods to reveal the host and port. In this chapter's example self.url.getHost() returns somehost.somewhere.com and self.url.getPort() returns 2088. If the port number was not assigned self.url.getPort() returns -1.

Getting an OutputStream to the server

To send bytes to the server, you want an OutputStream. A Socket object provides just such a thing. This is what line 38 of listing 26.2 shows.

Actually, you need to get a DataOutputStream to the Socket. A Java Socket will kindly provide any kind of OutputStream with its getOutputStream() method. You then "wrap" it in a DataOutputStream so that you can write a String all in one go. Otherwise you'd have to send the bytes out one-by-one.

Sending the user name to the server

Line 39 of listing 26.2 sends the user name to the server. The writeChars() method of a DataOutputStream object will write a String. The DataOutputStream will, in turn, send the bytes to the Socket's OutputStream. The Socket will then pass them on to the server.

Remember the format of a finger URL: finger://somehost.somewhere.com/someone The user name someone is where the file name would be in an HTTP or FTP URL. Use the getFile() method to find out the user's name. That method, however, returns the slash before the name as well. That's why you need to use the substring() method to get just the bytes after the first. Don't forget the carriage return and line feed characters after the user name.

Now you just writeBytes() on the DataOutputStream and off they go to the server. Only, not quite. The flush() method makes sure they go right away rather than waiting around for other things in the system to settle.

Making the document available to the browser

Lines 45-50 of listing 26.2 define the getInputStream() method. This makes the document available to the browser. HotJava will be calling the getInputStream() method of the fingerConnection. The browser will then be able to read bytes on the InputStream returned by this method. Of course, you just get it from your socket. Use your connect() method to get the socket connected to the server and then return the socket's InputStream.

This makes a lot of sense. Our protocol handler should handle the protocol entirely. No other object should need to know anything about it. This handler can talk to the server and just deliver the document. Luckily, the finger protocol is a very simple one.

Putting it all together

In this section, you will put the package together so that HotJava can use it. There are several steps you need to take to accomplish this.

  1. 1. You need a directory for the finger package.
  2. 2. You need to compile the classes that make up the package.
  3. 3. You need to put the classes in the directory you created in step 1.
  4. 4. You need to modify your CLASSPATH environment variable to let Java know where you put the package.

Creating a directory for the new package

The classes that came with the JDK are kept in a file named classes.zip. This file is in the subdirectory "lib" of the directory where you installed the JDK. So if you installed the JDK in /usr/local/java, for example, the classes would be in /usr/local/java/lib/classes.zip. This is one place you can put your new package. It has the advantage that Java already knows about this directory and will look for your package there.

An alternative is to create another directory for your own packages. This has the advantage of making it clearer which packages came with Java and which you added. On a multi-user system, you may have do take this alternative as you might not have access to the directory where the JDK was installed. You might create a subdirectory called myclasses in your home directory.

In either case you will need to create a chain of subdirectories of the directory you have chosen for your packages. Because HotJava is looking for the package sun.net.www.protocol.finger, you need to have a new directory sun/net/www/protocol/finger/. This is the directory where you will put the .class files.

For example, suppose you have chosen the second option above on your UNIX system. The command "mkdir -p myclasses/sun/net/www/protocol/finger" issued from your home directory should do the trick.

Compiling the Classes

You need to compile both your classes but you must compile the fingerConnection class before the Handler class. This is because the Handler class uses a fingerConnection object. If the Handler class has not been compiled the compiler will not be able to find it when you try to compile the fingerConnection class.

Putting the .class files in the package directory

The 1.0 release of the JDK has all the classes zipped up in an archive named classes.zip. On UNIX systems this will often be in /usr/local/java/lib. Don't zip your new classes into this archive. Use the directory you created in step 1. Copy the files Handler.class and fingerConnection.class to this directory..

Modifying Your CLASSPATH

Your operating system needs to know where to look for programs to run. You let the operating system know where to look by specifying the PATH environment variable. Similarly Java needs to know where to look for classes. You let Java know where to look by specifying the CLASSPATH environmental variable.

Set it to include the classes that came with the JDK and your classes. Another convenience, especially when developing and testing, is to specify that the current directory is also a place to look for classes.

Suppose you've chosen /usr/local/java/classes to put your packages in. This means that the classes will go in /usr/local/java/classes/sun/net/www/protocol/finger. You need to set your CLASSPATH this way:

 export CLASSPATH=.:/usr/local/java/classes:/usr/local/java/lib/.classes.zip

Notice that there are three components to the CLASSPATH, separated by colons.

  1. A period (.) This means look in the current directory.
  2. Where you expect to find your package (and, perhaps, other classes and/or packages you may have defined for other purposes). Do not specify the sun/net/www/protocol/finger portion of the path. This is because that is your package name. Java knows to tack on the rest of the full path name to each path it finds in the CLASSPATH environment variable.
  3. The compressed classes that you got with the JDK.

The order of these components is significant. You can keep newer versions of packages and classes under your current directory. They will be found before the other class libraries are searched. This lets you work on experimental versions without disturbing more stable versions in your classes directory.

Testing the new handler

As mentioned in "Choosing your API", Sun has not yet released the new version of HotJava. The current version of HotJava uses the 1.0 Alpha3 API. This leaves you with a problem-how to test. You can't just tap your foot until HotJava 1.0 Final is released. A solution is to build a trivial browser to stand in for HotJava.

Building a trivial browser

You need a trivial browser that will work with your new classes. It uses the same URL object that HotJava will use. Hopefully, if your protocol handler works with this, it will work with HotJava.

Toward that end, create a file named GoGet.java. It's contents are listed in listing 26.3.

Listing 26.3 The Trivial Browser - GoGet
  1   import java.net.URL;
  2   import java.net.MalformedURLException;
  3   
  4   public class GoGet {
  5   
  6      public static void main( String args[] ) {
  7   
  8         URL url = null;
  9         Object o = null;
 10   
 11         try {
 12            url = new URL( args[0] );
 13            o = url.getContent();
 14            System.out.println( o.toString() );
 15   
 16         } catch (ArrayIndexOutOfBoundsException e) {
 17            System.err.println( "usage: java tester URL " );
 18   
 19         } catch (MalformedURLException e) {
 20            System.err.println( "Malformed URL: " + args[0] );
 21            System.err.println( e.toString() );
 22   
 23         } catch (Exception e) {
 24            System.err.println( e.toString() );
 25   
 26         }
 27   
 28      }
 29   
 30   }

The trivial browser is an application run through the Java interpreter. Its usage is:

 java GoGet URL

Notice is that this listing never called anything in the finger package. It doesn't importing anything from it either. Behind the scenes, the URL takes apart the String passed to it. It finds the protocol and looks for a package that implements it. Guess where it looks? That's right, in sun/net/www/protocol/finger. Just where you put it. When you think about it, that had to be the case. Sun isn't going to change HotJava for every oddball protocol someone wants to write. So they found a general way for you to plug something in.

Line 12 creates a URL; line 13 gets the content in; line 14 converts it to a String (with the toString() method) and puts it out on stdout. The rest of it catches exceptions.

If no URL is specified on the command line, the args[] array will be empty. This will throw an ArrayIndexOutOfBoundsException. So line 16 catches that exception; line 17 gives a hint about usage.

If the URL specified on the command line is bogus, the URL constructor will throw a MalformedURLException. Lines 19 to 21 report that.

Lines 23 and 24 catch and report any other Exception that might get thrown.

Trying It Out

Now try it out. Listing 26.4 shows what happened when the author tried it out on his system. First he tried it with a URL of just finger:. The host portion should default to the local host. An empty finger command should return all the users logged in at the current time.

Listing 26.4 Just the Protocol Part of the URL
 bash$ java GoGet finger:
 Login    Name                 Tty  Idle  Login Time   Office     Office Phone
 eric     Eric Blossom          1   4:48  Feb 25 08:40 West       510 841-3338
 eric     Eric Blossom          p0        Feb 25 08:40 [ :0.0 ]

Listing 26.5 shows an attempt using the author's user name. Again the default host should be taken.

Listing 26.5 The Protocol and User Name
 bash$ java GoGet finger:eric
 Login: eric                             Name: Eric Blossom
 Directory: /home/eric                   Shell: /bin/bash
 Office: West, 510 841-3338
 On since Sun Feb 25 08:40 (PST) on tty1,  idle 4:49
 On since Sun Feb 25 08:40 (PST) on ttyp0 from :0.0
 No Mail.
 Plan:
 to (dare I say it?) rule the world!

Listing 26.6 shows an attempt with a different host. The author's user name on that host is "elb".

Listing 26.6 The Full URL, Protocol, Host, and User
 bash$ java GoGet finger://shell/elb
 Login: elb                              Name: Eric Blossom
 Directory: /home/elb                    Shell: /bin/tcsh
 Never logged in.
 No Mail.
 No Plan.

Listing 26.7 shows what happened when the author tried to see who else was using another host.

Listing 26.7 Just the Protocol and Host Portions of the URL
 bash$ java GoGet finger://shell
 must provide username

Remember, that the administrators of some machines think it's not such a good idea to run a finger server. Others will run finger but only reply if a user name is asked for. The machine named "shell" in the author's domain is such a machine. The response "must provide username" is from the finger server on shell.

Comparing protocol handlers and applets

Another way to connect to a server using another protocol is to write an applet. This has some advantages over writing a protocol handler and some disadvantages.

With a protocol handler, your web page would have a link that looks like any other link. It is only the URL it points to that is different. You could write an applet that functions like a link and even looks like a link (or like a button). This applet would have to be downloaded and run before the user can decide to click on it to get the document to which it points.

An applet can be used in any browser that supports Java, not just the HotJava browser. It can also have a better, more lively appearance, perhaps downloading the refered to document without requiring (or allowing) user action.

A protocol handler is not restricted by the security constraints of an applet. It can connect to servers other than the one from which it got the containing page. This gives you far more flexibility in pointing to other resources.

A protocol handler must be installed on the client machine where an applet can be downloaded over the net. The HotJava White Paper talks about protocol handlers being downloaded on demand. However, the version 1.0 Alpha3 of HotJava does not support this. It doesn't look like the next version of HotJava will either.

Security Considerations

Using a custom protocol handler should be about as risky as installing a browser. If you trust the producer or can examine the source code, you will likely be able to trust the software.

Having the protocol handler downloaded automatically is another question. This makes it roughly as risky as an applet. If the protocol handler is still free of an applet's security restrictions, it would be riskier than an applet. However, as mentioned above, this may not be a worry if HotJava doesn't support downloadable protocol handlers.

Some Unanswered Questions

Protocol handlers have not yet been a widely used feature of HotJava. There are still questions about their use and value that need to be answered. Here are some of them.

QUE Home Page

For technical support for our books and software contact support@mcp.com

Copyright ©1996, Que Corporation