Writing a Protocol Handler

To demonstrate a complete protocol handler, let’s write one for the finger protocol, which is defined in RFC 1288 and was introduced in Chapter 10. 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 were listed, a list of the currently logged in users. For example:

% telnet rama.poly.edu 79
Trying 128.238.10.212...
Connected to rama.poly.edu.
Escape character is '^]'.

Login       Name            TTY    Idle    When    Where
jacola   Jane Colaginae    *pts/7       Tue 08:01  208.34.37.104
marcus   Marcus Tullius     pts/15  13d Tue 17:33  farm-dialup11.poly.e
matewan  Sepin Matewan     *pts/17  17: Thu 15:32  128.238.10.177
hengpi   Heng Pin          *pts/10      Tue 10:36  128.238.18.119
nadats   Nabeel Datsun      pts/12   56 Mon 10:38  128.238.213.227
matewan  Sepin Matewan     *pts/8     4 Sun 18:39  128.238.10.177
Connection closed by foreign host.

Or, requesting information about a specific user:

% telnet rama.poly.edu 79
Trying 128.238.10.212...
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:

finger://hostname:port/username

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.guess Content TypeFromName(String name) and URLConnection.guessContent TypeFromStream(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 e) {
          result = names.getBytes(  );  
        }
        out.write(result);
      }
      out.write('
'),
      out.write('
'),
      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, that 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, then 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 so that it is similar to an http 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, so 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:

java.protocol.handler.pkgs=com.macfaq.net.www.protocol

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. Note that 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.3b1, I was able to put my classes in the jdk1.3/jre/lib/classes folder. Your mileage may vary depending on what 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.

HotJava using the finger protocol handler

Figure 16-1. HotJava using the finger protocol handler

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.109.30