- Special Edition Using Java, 2nd Edition -

Chapter 25

Protocol Handlers


by David W. Baker

An essential feature that Java attempts to impart to computing is extensibility. Over time, newer and better means of communicating will be created. Application protocols will evolve, and programs must be able to adapt. Protocol handlers are a key tool for maintaining an adaptable environment with Java.

Writing a Protocol Handler

Protocol handlers allow you to accomplish two tasks:

See “Creating a TCP Client-Server Application,” Chapter 23 for more information.
 
See “Creating a UDP Client,” Chapter 24 for more information.
 

HotJava can make use of new protocol handlers, part of the vision guiding HotJava's ongoing development. The intention is that programs like HotJava should have very little core intelligence, providing only a framework for extensibility. When new protocols are invented, HotJava will transparently download a new handler, and the user can then access the resource.

You can also use protocol handlers in your own applications. By installing a new protocol handler and using a registration facility termed a factory, URL objects can use these handlers just as the standard protocols are used.

Protocol handlers are not new to your Java programming. In fact, any time a URL object was used to obtain a resource in the previous chapters, such as in Chapter 22, “Communications and Networking,” a handler inherent in the JDK was used. With the following steps, you can create your own handlers to build on this existing set.

The examples within this chapter implement the NICNAME/WHOIS protocol, defined in RFC 954. WHOIS is used by Internic, the domain name registration and directory project, to allow individuals to query their database. WHOIS can be used at rs.internic.net, but other sites use this service. By opening a TCP socket to the standard port 43, a query can be entered and the response read. More information on this protocol is available at:

Step 1: Decide on a Package Name

A protocol handler must be placed into a clearly-defined package. This package must end in protocol.scheme where scheme is the new URL scheme. Generally, new packages also include the domain name and author identifier of the distributor. Thus, in this example, the following package is used:

ORG.netspace.dwb.protocol.whois

Step 2: Create the Directories

The necessary code for the protocol handler must be placed into a directory named in correlation to the package name identified. This directory should reside within a directory into which you own Java code is placed, usually called classes within your home directory.

Here is an example for creating these directories under Windows NT and Windows 95:

cd \users\myid
mkdir classes
mkdir classes\ORG
mkdir classes\ORG\netspace
mkdir classes\ORG\netspace\dwb
mkdir classes\ORG\netspace\dwb\protocol
mkdir classes\ORG\netspace\dwb\protocol\whois

The process is similar under the UNIX operating system:

cd ~
mkdir classes
mkdir classes/ORG
mkdir classes/ORG/netspace
mkdir classes/ORG/netspace/dwb
mkdir classes/ORG/netspace/dwb/protocol
mkdir classes/ORG/netspace/dwb/protocol/whois

Step 3: Set Your CLASSPATH

The CLASSPATH environment variable tells the Java compiler and interpreter where to find Java classes, enabling the dynamic linking feature of the Java execution environment. When installing the JDK, HotJava, or a Java-aware browser, you may have set the CLASSPATH environment variable. If this is so, it is critical that you avoid overwriting that data. Follow these steps:

You must find out what your is CLASSPATH current setting. Under Windows NT/95, type the following command:

Look for the CLASSPATH value. Under UNIX systems, you can display the CLASSPATH value with this command:

Reset your CLASSPATH, including the previous data, if any. Under Windows 95, if your CLASSPATH was

.;C:\JAVA\LIB\CLASSES.ZIP

you can add the below line to your AUTOEXEC.BAT and reboot:

SET CLASSPATH=.;C:\USERS\MYID\CLASSES;C:\JAVA\LIB\CLASSES.ZIP

Under Windows NT, presuming that the CLASSPATH value was the same as under our Windows 95 example, you would use the System Control Panel to add a CLASSPATH environment variable with the value

;C:\USERS\MYID\CLASSES;C:\JAVA\LIB\CLASSES.ZIP

Under UNIX, assume that your old CLASSPATH was .:/usr/java/lib. If you are using the C shell, place the following into you .cshrc file:

setenv CLASSPATH .:/home/myid/classes:/usr/java/lib

If you are on a UNIX system using the Korn or a POSIX-compliant shell, add this line to whatever file your ENV environment variable points. If ENV is unset, you could add this line to your ~/.profile file:

CLASSPATH=.:/home/myid/classes:/usr/java/lib

Step 4: Implement the Protocol

Within the directory created in step 2, create a class that extends URLConnection. This class should have a constructor that takes a URL object as an argument and calls its superclass with that object. In addition, most protocol handlers should extend the following methods.

public void connect() throws IOException;

connects to the remote resource and performs the network transaction, as appropriate.

public String getContentType();

indicates the MIME content type of the data returned by the object.

public synchronized InputStream getInputStream()
throws IOException;

returns an InputStream containing the data from the remote system.

Listing 25.1 shows the class used to implement the WHOIS protocol. This class supports the URL formats shown in table 25.1.

Table 25.1 WHOIS URL Syntax

URL Meaning
whois:query Connect to rs.internic.net and submit query.
whois:/query Identical to whois:query.
whois://host/query Instead of connecting to rs.internic.net, connect to WHOIS service on host and submit query.
whois://host:port/query Instead of using port 43, connect to host at the specified port and submit query.

Listing 25.1 whoisURLConnection.java

// This is the package identified for this protocol handler.
package ORG.netspace.dwb.protocol.whois;
import java.io.*; // Import the package names used.
import java.net.*;
/**
* This class implements a connection to the new "whois"
* URL scheme.
* @author David W. Baker
* @version 1.0
*/
class whoisURLConnection extends URLConnection {
// Some defaults for the WHOIS protocol implementation:
// site defaults to rs.internic.net
// port defaults to 43
// query defaults to QUIT
private static final String DEF_SITE = "rs.internic.net";
private static final int DEF_PORT = 43;
private static final String DEF_QUERY = "QUIT";
private static final String CONT_TYPE = "text/html";
static final int URL_BASE = 16;
InputStream fromHandler; // Input from the handler
Socket whoisSocket; // Socket for communication
boolean gotQuery = false; // Did we get the data?
/**
* Given a URL will instantiate a whoisURLConnection
* object.
* @param getURL The URL to contact.
*/
whoisURLConnection(URL getURL) {
super(getURL); // Call superclass with the URL.
}
/**
* Connect to the WHOIS server, obtain, and format the
* data.
* @exception java.io.IOException Indicates a problem
* connecting to URL.
*/
public void connect() throws IOException {
String whoisSite;
int whoisPort;
String whoisQuery;
PrintStream toApp; // Send data to app using handler.
DataInputStream fromWhois = null;
PrintStream toWhois = null;
String dataLine;
// Set up piped streams for communication between
// this handler and the application using it.
PipedOutputStream pipe = new PipedOutputStream();
toApp = new PrintStream(pipe);
fromHandler = new PipedInputStream(pipe);

// Get host from the URL, using default if omitted.
if (url.getHost().length() == 0) {
whoisSite = DEF_SITE;
} else {
whoisSite = url.getHost();
}
// Get port from the URL, using default if omitted.
if (url.getPort() < 1) {
whoisPort = DEF_PORT;
} else {
whoisPort = url.getPort();
}
// Get file from the URL, using default is "/"
if (url.getFile().equals("/")) {
whoisQuery = DEF_QUERY;
} else {
whoisQuery = url.getFile().substring(1);
}
// Decode the query from the URL.
whoisQuery = decodeURL(whoisQuery);
// Open a socket to the whois server.
whoisSocket = new Socket(whoisSite,whoisPort);
// Open streams to communicate with the whois server.
fromWhois =
new DataInputStream(whoisSocket.getInputStream());
toWhois =
new PrintStream(whoisSocket.getOutputStream());
// Send the query to the server.
toWhois.println(whoisQuery);
// Print out some HTML.
toApp.println("<!DOCTYPE HTML PUBLIC \"-//IETF//DTD "
+ "HTML//EN\">");
toApp.println("<HTML>");
toApp.println("<HEAD>");
toApp.println("<TITLE>Whois Query for: " + url
+ "</TITLE>");
toApp.println("</HEAD>");
toApp.println("<BODY>");
toApp.println("<H1>Whois Query for : " + url
+ "</H1>");
toApp.println("<PRE>");
// Loop through the data from the whois server,
// printing it all out within the preformatted
// text section.
while ((dataLine = fromWhois.readLine()) != null) {
toApp.println(dataLine);
}
// Some last HTML.
toApp.println("</PRE>");
toApp.println("</BODY>");
toApp.println("</HTML>");
toApp.flush(); // Flush the pipe.
toWhois.close(); // Close the streeams.
fromWhois.close();
whoisSocket.close(); // Close the socket.
toApp.close(); // Close one end of the pipe.
gotQuery = true; // Data has been obtained.
}
/**
* Determine the content type of the data returned.
* @return The content type.
*/
public String getContentType() {
return CONT_TYPE;
}
/**
* Obtain a stream to get data from this protocol handler.
* @return The stream to read data.
*/
public synchronized InputStream getInputStream()
throws IOException {
// If where has not been obtained, connect()
if (!gotQuery) {
connect();
}
// Return the stream.
return fromHandler;
}
/**
* This method decodes the URL encoded format.
* i.e. %XX -> char and + to space
* @param decode The String to decode.
* @return The decoded String.
*/
protected String decodeURL(String decode) {
StringBuffer decoded = new StringBuffer();
char nextChar;
String encString;
Integer encInteger;
// Go through the String character by character.
for(int index=0; index < decode.length(); index++) {
// Get the next character in the String.
nextChar = decode.charAt(index);
// If the character is +, then convert it to
// a space.
if (nextChar == '+') {
decoded.append(" ");
}
// If the character is a %, then the next two
// characters store the value of the encoded
// character.
else if (nextChar == '%') {
// Create an Integer object containing the
// integer value of the next two characters
// in the string, assuming a base 16 notation.
encInteger = Integer.valueOf(
decode.substring(index+1,index+3),URL_BASE);
// Increment our counter by 2 - we just read
// two characters.
index += 2;
// Return the int value within the Integer
// and then cast that into a char type.
nextChar = (char)encInteger.intValue();
// Add the coded character.
decoded.append(nextChar);
}
// Otherwise, just add the character.
else {
decoded.append(nextChar);
}
}
// Return the decoded string.
return decoded.toString();
}
}

The whoisURLConnection constructor merely makes the appropriate call to the URLConnection superclass, passing it the URL object. The connect() method is where all of the work is done, in the following order:

The getContentType() method is used to indicate that the returned data is an HTML document. getInputStream() returns the PipedOutputStream, into which this protocol handler has pushed the formatted data. The Java application uses this stream to access the data obtained by the new protocol handler.

The whoisURLConnection has an additional protected method called decodeURL(). Certain characters such as spaces and other special characters cannot be represented literally within URLs. These characters are encoded with a special format—for example, a space is encoded as %20. Because our WHOIS query may need to use such characters, this handler must have a method to decode this data. decodeURL() is called from the connect() method, and examines a string byte by byte. When it encounters a percent sign, it uses the next two characters as the Unicode value to the encoded character.

Step 5: Create the Handler Class

Within the same directory, you must create a file called Handler.java. The Handler class must extend the URLSreamHandler class and return an instance of the class created in step 4. Listing 25.2 shows the Handler class for the WHOIS protocol.

Listing 25.2 Handler.java

// This is the package identified for this protocol handler.
package ORG.netspace.dwb.protocol.whois;
import java.net.*; // Import the package names used.
/**
* This class is a subclass of URLStreamHandler and provides
* an implementation of the abstract openConnection() method
* to support the "whois" scheme.
* @author David W. Baker
* @version 1.0
*/
public class Handler extends URLStreamHandler {
/**
* Given a URL return an appropriate URLConnection.
* @param requestedURL The URL instance to contact.
* @return The connection to the resource.
*/
public synchronized URLConnection
openConnection(URL requestedURL) {
return new whoisURLConnection(requestedURL);
}
}

Step 6: Compile the Sources

Use javac to compile the sources created in steps 4 and 5, leaving the compiled class files in the originating directory.

Using Protocol Handlers with HotJava

Eventually, as part of HotJava's vision, protocol handlers will be dynamically downloaded and used. At this time of this writing, this feature is not supported by HotJava. Protocol handlers must be manually installed, as previously described, in order for HotJava to use them.

Netscape and Microsoft Explorer Extensibility

The Netscape Navigator supports a different mechanism for extending the browser. A Netscape plug-in can implement a new application protocol using a special API. No specific plans seem to have been stated by Netscape regarding the support of protocol handlers as HotJava. More information on Netscape Plug-ins is available from:

http://home.netscape.com/comprod/products/navigator/version_2.0/plugins/

The Microsoft Explorer also supports add-ins and ActiveX, OLE Controls. ActiveX can accomplish many of the same tasks as Netscape Plug-ins and Java. As with Netscape, no stated plans are apparent with respect to protocol handlers and the Explorer. More information on ActiveX is available from:

http://www.microsoft.com/intdev/controls/controls-f.htm

Once a protocol handler has been installed, the following additional steps are necessary to use it within your HotJava browser.

JavaSoft makes the HotJava browser and instructions for its installation available at http://www.javasoft.com/java.sun.com/HotJava/CurrentRelease/installation.html.
 

Step 1: Update the Properties File

You must instruct HotJava to load additional protocol handlers. This is done by updating the properties file, which is located within the HOTJAVA directory within your home directory.

Within this file, you must set the java.protocol.handler.pkgs property to include the new protocol package. The value to add should be everything from the protocol token leftward. Thus, you want to add the following line for your WHOIS protocol handler:

java.protocol.handler.pkgs=ORG.netspace.dwb.protocol

If you already have this property set within your HotJava properties file, append a pipe character (|) to the end of the line and then ORG.netspace.dwb.protocol. This syntax allows you to install several new protocol handlers. For instance:

java.protocol.handler.pkgs=COM.company.protocol|ORG.netspace.dwb.protocol

Step 2: Run HotJava

Start up HotJava and test out the new protocol handler. Choose File, Open Page, and into the URL field type:

Figure 25.1 shows what should appear within the HotJava window.


FIG. 25.1

After installing the WHOIS protocol handler, HotJava can be used to easily retrieve domain name information.

Using Protocol Handlers with Your Own Applications

The usefulness of new protocol handlers extends beyond the HotJava browser. They can also be used within your own applications, the key step being a method to register the new protocol handler using a concept known as a factory.

As an example, a simple application will be developed to make use of the WHOIS protocol handler. FetchWhois sends its arguments as a query to the WHOIS service at rs.internic.net. The source of this example is shown in listing 25.3.

Listing 25.3 FetchWhois.java

import java.net.*; // Import the package names used.
import java.io.*;
/**
* This is an application which uses our new whois
* protocol handler to obtain information.
* @author David W. Baker
* @version 1.1
*/
public class FetchWhois {
/**
* The method launches the application.
* @param args Arguments which are the query string.
*/
public static void main (String args[]) {
if (args.length < 1) {
System.err.println(
"usage: java FetchWhois query string");
System.exit(1);
}
FetchWhois app = new FetchWhois(args);
}
/**
* This constructor does all of the work of obtaining
* the data from the server.
* @param args The tokens of the query string.
*/
public FetchWhois(String args[]) {
String encodedString; // Hold the URL encoded query.
String nextLine; // Line from the handler.
URL whoisURL; // URL to whois resource
URLConnection whoisAgent; // Connection to whois.
DataInputStream input; // Stream from whois.
// Create a buffer to place in all of the query
// string tokens.
StringBuffer buffer = new StringBuffer();
// Append all of the tokens to the buffer.
for(int index = 0; index < args.length; index++) {
buffer.append(args[index]);
if (index < args.length-1) {
buffer.append(" ");
}
}
// URL encode the query buffer.
encodedString = URLEncoder.encode(buffer.toString());
// Set the factory to register the whois handler.
URL.setURLStreamHandlerFactory(new whoisUSHFactory());
try {
// Create the whois URL object.
whoisURL = new URL("whois:" + encodedString);
// Open the connection.
whoisAgent = whoisURL.openConnection();
// Get an input stream from the whois server.
input =
new DataInputStream(whoisAgent.getInputStream());
// Print out the data line-by-line.
while((nextLine = input.readLine()) != null) {
System.out.println(nextLine);
}
input.close(); // Close the stream.
} catch(MalformedURLException excpt) {
System.err.println("Mailformed URL: " + excpt);
} catch(IOException excpt) {
System.err.println("Failed I/O: " + excpt);
}
}
}
/**
* This class implements the URLStreamHandlerFactory
* interface to register the whois protocol handler.
* @see java.net.URLStreamHandlerFactory
*/
class whoisUSHFactory implements URLStreamHandlerFactory {
/**
* This method returns the protocol handler to the
* calling object.
* @param scheme The URL scheme to be obtained.
* @return The protocol handler.
* @see java.net.URLStreamHandlerFactory#createURLStreamHandler
*/
public URLStreamHandler
createURLStreamHandler(String scheme) {
// Make sure that this is for a whois URL
if (scheme.equalsIgnoreCase("whois")) {
// If so, create the handler and return it.
return
new ORG.netspace.dwb.protocol.whois.Handler();
// Otherwise print an error message and return null.
} else {
System.err.println("Unknown protocol: " + scheme);
return null;
}
}
}

The main() Method: Starting FetchWhois

The simple main() method checks to see that the program was invoked properly. It then creates a FetchWhois object, passing it the array of command line arguments.

The FetchWhois Constructor: Where the Work Gets Done

The constructor is where the connection to rs.internic.net is opened through the use of our new protocol handler. The key line is where is uses the setURLStreamHandlerFactory() static method from the URL class. This is where FetchWhois directs new URL instances to the new WHOIS protocol handler, using the whoisUSHFactory described later in “The whoisUSHFactory Class: Registering the Protocol Handler.”

The constructor uses a StringBuffer to consolidate the array of command line arguments into a single String, and then uses the static encode() method of URLEncoder to properly format the data. Then, it creates a URL object with the new whois URL scheme. Finally, it opens a connection to the URL and receives the WHOIS data.

The whoisUSHFactory Class: Registering the Protocol Handler

The URL class uses a URLStreamHandlerFactory implementation to choose an appropriate protocol handler. In order to use the WHOIS protocol handler, you must implement the URLStreamHandlerFactory interface. The constructor should take a String that contains the scheme of the URL object being instanciated. whoisUSHFactory checks to ensure that the scheme is whois, and if so, returns an instance of the WHOIS Handler. Otherwise, it returns null.

Running FetchWhois

Compile FetchWhois with javac and then try running it by using the following command. The program should print an HTML document that contains the same data as when you used the whois URL within HotJava.

java FetchWhois internic.net


Previous Page TOC Next Page

| Previous Chapter | Next Chapter |

|Table of Contents | Book Home Page |

| Que Home Page | Digital Bookshelf | Disclaimer |


To order books from QUE, call us at 800-716-0044 or 317-361-5400.

For comments or technical support for our books and software, select Talk to Us.

© 1996, QUE Corporation, an imprint of Macmillan Publishing USA, a Simon and Schuster Company