16.3 Writing a Protocol Handler


To demonstrate a complete protocol handler, let's write one for the finger protocol defined in RFC 1288 and introduced in Chapter 9. Finger is a relatively simple protocol compared to JDK-supported protocols such as HTTP and FTP. The client connects to port 79 on the server and sends a list of usernames followed by a carriage return/ linefeed pair. The server responds with ASCII text containing information about each of the named users or, if no names are listed, a list of the currently logged in users. For example:

 %  telnet rama.poly.edu 79  Trying Connected to rama.poly.edu. Escape character is '^]'. Login       Name            TTY    Idle    When    Where jacola   Jane Colaginae    *pts/7       Tue 08:01 marcus   Marcus Tullius     pts/15  13d Tue 17:33  farm-dialup11.poly.e matewan  Sepin Matewan     *pts/17  17: Thu 15:32 hengpi   Heng Pin          *pts/10      Tue 10:36 nadats   Nabeel Datsun      pts/12   56 Mon 10:38 matewan  Sepin Matewan     *pts/8     4 Sun 18:39 Connection closed by foreign host. 

Or to request information about a specific user :

 %  telnet rama.poly.edu 79  Trying Connected to rama.poly.edu. Escape character is '^]'. marcus Login       Name            TTY    Idle    When    Where marcus   Marcus Tullius     pts/15  13d Tue 17:33  farm-dialup11.poly.e 

Since there's no standard for the format of a finger URL, we will start by creating one. Ideally, this should look as much like an http URL as possible. Therefore, we will implement a finger URL like this:


Second, we need to determine the content type returned by the finger protocol's getContentType() method. New protocols such as HTTP use MIME headers to indicate the content type; in these cases, you do not need to override the default getContentType( ) method provided by the URLConnection class. However, since most protocols precede MIME, you often need to specify the MIME type explicitly or use the static methods URLConnection.guessContentTypeFromName(String name) and URLConnection.guessContentTypeFromStream(InputStream in) to make an educated guess. This example doesn't need anything so complicated, however. A finger server returns ASCII text, so the getContentType() method should return the string text/plain . The text/plain MIME type has the advantage that Java already understands it. In the next chapter, you'll learn how to write content handlers that let Java understand additional MIME types.

Example 16-2 is a FingerURLConnection class that subclasses URLConnection . This class overrides the getContentType( ) and getInputStream( ) methods of URLConnection and implements connect( ) . It also has a constructor that builds a new URLConnection from a URL.

Example 16-2. The FingerURLConnection class
 package com.macfaq.net.www.protocol.finger; import java.net.*; import java.io.*; public class FingerURLConnection extends URLConnection {   private Socket connection = null;      public final static int DEFAULT_PORT = 79;   public FingerURLConnection(URL u) {     super(u);   }   public synchronized InputStream getInputStream( ) throws IOException {        if (!connected) this.connect( );     InputStream in = this.connection.getInputStream( );     return in;        }   public String getContentType( ) {     return "text/plain";   }   public synchronized void connect( ) throws IOException {        if (!connected) {       int port = url.getPort( );       if ( port < 1  port > 65535) {         port = DEFAULT_PORT;       }       this.connection = new Socket(url.getHost( ), port);       OutputStream out = this.connection.getOutputStream( );       String names = url.getFile( );       if (names != null && !names.equals("")) {         // delete initial /         names = names.substring(1);         names = URLDecoder.decode(names);         byte[] result;         try {           result = names.getBytes("ASCII");         }         catch (UnsupportedEncodingException ex) {           result = names.getBytes( );           }         out.write(result);       }       out.write('\r');       out.write('\n');       out.flush( );       this.connected = true;     }    } } 

This class has two fields. connection is a Socket between the client and the server. Both the getInputStream( ) method and the connect() method need access to this field, so it can't be a local variable. The second field is DEFAULT_PORT , a final static int , which contains the finger protocol's default port; this port is used if the URL does not specify the port explicitly.

The class's constructor holds no surprises . It just calls the superclass's constructor with the same argument, the URL u . The connect( ) method opens a connection to the specified server on the specified port or, if no port is specified, to the default finger port, 79. It sends the necessary request to the finger server. If any usernames were specified in the file part of the URL, they're sent. Otherwise, a blank line is sent. Assuming the connection is successfully opened (no exception is thrown), it sets the boolean field connected to true . Recall from the previous chapter that connected is a protected field in java.net.URLConnection , which is inherited by this subclass. The Socket that connect( ) opens is stored in the field connection for later use by getInputStream( ) . The connect( ) and getInputStream( ) methods are synchronized to avoid a possible race condition on the connected variable.

The getContentType( ) method returns a String containing a MIME type for the data. This is used by the getContent( ) method of java.net.URLConnection to select the appropriate content handler. The data returned by a finger server is almost always ASCII text or some reasonable approximation thereof, so this getContentType( ) method always returns text/plain . The getInputStream( ) method returns an InputStream , which it gets from the Socket that connect created. If the connection has not already been established when getInputStream( ) is called, the method calls connect( ) itself.

Once you have a URLConnection , you need a subclass of URLStreamHandler that knows how to handle a finger server. This class needs an openConnection() method that builds a new FingerURLConnection from a URL. Since we defined the finger URL as a hierarchical URL, we don't need to implement a parseURL() method. Example 16-3 is a stream handler for the finger protocol. For the moment, we're going to use Sun's convention for naming protocol handlers; we call this class Handler and place it in the package com.macfaq.net.www.protocol.finger .

Example 16-3. The finger handler class
 package com.macfaq.net.www.protocol.finger; import java.net.*; import java.io.*; public class Handler extends URLStreamHandler {   public int getDefaultPort( ) {     return 79;   }   protected URLConnection openConnection(URL u) throws IOException {     return new FingerURLConnection(u);   } } 

You can use HotJava to test this protocol handler. Add the following line to your .hotjava/properties file or some other place from which HotJava will load it:


Some (but not all) versions of HotJava may also allow you to set the property from the command line:

 %  hotjava -Djava.protocol.handler.pkgs=com.macfaq.net.www.protocol  

You also need to make sure that your classes are somewhere in HotJava's class path . HotJava does not normally use the CLASSPATH environment variable to look for classes, so just putting them someplace where the JDK or JRE can find them may not be sufficient. Using HotJava 3.0 on Windows with the JDK 1.3, I was able to put my classes in the jdk1.3/jre/lib/classes folder. Your mileage may vary depending on the version of HotJava you're using with which version of the JDK on which platform.

Run it and ask for a URL of a site running finger, such as utopia.poly.edu . Figure 16-1 shows the result.

Figure 16-1. HotJava using the finger protocol handler

Java Network Programming
Java Network Programming, Third Edition
ISBN: 0596007213
EAN: 2147483647
Year: 2003
Pages: 164

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net