Content Handlers - Protocol Handlers
What is it?
The URL class represents an Internet Uniform Resource Locator. It provides a very simple interface to networking. It's then possible to download the content of the object refered to by the URL.
Protocol Handlers handle the connection to the server. They are defined using the URLStreamHandler class. This abstract defines a openConnection() method that basically creates a stream to the resource. A URLStreamHandler is created by a URLStreamHandlerFactory.
ContentHandlers are defined using the same scheme. The ContentHandlerFactory defines a methods that creates and returns the right ContentHandler object for a specified mime type.
When an URL object is created, the system determines the right protocol. The MalformedURL exception signals that an unparseable URL has been used in the URL constructor. The first time a protocol name is encountered when constructing a URL, the appropriate stream protocol handler is automatically loaded.
If everything is correct, a call to the getContent() method returns the content of the URL. As it's not possible to know the data type refered to by the URL (text, zip file, tar file,..), this method returns an Object. The programers must use a cast to convert it to the right object.
Example:
URL u=new URL("zip://inside-java.com/test.zip");
zipFile z=(zipFile) u.getContent();
An example
This example uses the security API and an example of a previous article. You should first read this article to understand how to use digital signatures.
This is a client server application that defines the "signed" protocol. Using it, a client can create an URL of the form: "signed://inside-java.com/test.txt".
This protocol sends a signed object with the content of the file, the server's public key and the signature. The clients then verifies this signature.
There are 7 files in this example. To use send, launch first the Server, and then the Client. Don't forget to create a test.txt file in the directory from where the Server is lauched.
The client requests a file sending a string to the server with a GET keyword. For example, "GET /test.txt". The server parses this command, read the file, and sends back an signedData object.
Concerning the Content Handler and the Protocol Handler, they are defined by the client to understand what says the server. To register them, we simply use:
URL.setURLStreamHandlerFactory(new signedURLStreamHandlerFactory());
URLConnection.setContentHandlerFactory(new signedContentHandlerFactory());
We need to define our own Protocol Handler Factory. It's called signedURLStreamHandlerFactory() and only understands "signed" urls. We also need a ContentHandlerFactory. It's simply called signedContentHandlerFactory().
The protocol handler factory checks the protocol name and basically returns a protocol handler if it is equal to "signed". The content handler factory checks the mime type returns a content handler if it is equal to "application/signed".
Let's have a look now at the client. We won't explain there how to compute a digital signature.
You can download the whole example:
Client.java
import java.net.*;
import java.io.*;
import java.security.*;
class signedURLStreamHandlerFactory implements URLStreamHandlerFactory {
public URLStreamHandler createURLStreamHandler(String protocol) {
if (protocol.equalsIgnoreCase("signed"))
return new net.www.protocol.signed.Handler();
else
return null;
}
}
class signedContentHandlerFactory implements ContentHandlerFactory {
public ContentHandler createContentHandler(String mimetype) {
if (mimetype.equalsIgnoreCase("application/signed"))
return new net.www.content.application.signed();
else
return null;
}
}
class Client {
public static void main(String [] argv)
{
try {
//Defines our own Stream Handler and Content Handler factories
URL.setURLStreamHandlerFactory(new signedURLStreamHandlerFactory());
URLConnection.setContentHandlerFactory(new signedContentHandlerFactory());
System.out.println("Requesting test.txt file");
URL url=new URL("signed://localhost/test.txt");
signedData Data=(signedData)url.getContent();
Signature dsa = Signature.getInstance("SHA/DSA");
dsa.initVerify(Data.pub);
dsa.update(Data.b);
boolean verifies = dsa.verify(Data.sig);
System.out.println(verifies?"Signature is correct!":"Signature is not valid!");
System.out.println("Content of the file:");
System.out.println(new String(Data.b));
}
catch(Exception e) {};
}
}
Handler.java
This file must be in the net/www/protocol/signed directory.
package net.www.protocol.signed;
import java.io.*;
import java.net.*;
public class Handler extends URLStreamHandler {
protected URLConnection openConnection(URL url) throws IOException {
return new signedURLConnection(url);
}
}
class signedURLConnection extends URLConnection {
signedInputStream sis;
signedURLConnection (URL url) throws IOException {
super(url);
try {
sis=(signedInputStream)Class.forName("net.www.protocol.signed.signedInputStream").newInstance();
} catch (Exception e) {System.out.println("Class net.protocol.signed.signedInputStream not found!");}
}
synchronized public void connect() throws IOException {
Socket s=new Socket(url.getHost(),4096);
OutputStream server=s.getOutputStream();
new PrintStream(server).println("GET "+url.getFile());
sis.set(s.getInputStream(),server);
connected=true;
}
synchronized public InputStream getInputStream() throws IOException {
if (!connected) connect();
return sis;
}
public String getContentType() {
return new String("application/signed");
}
}
The Handler class is our StreamHandler. It returns a signedURLConnection object when the URL is created.
This object is a URLConnection object. This class opens the socket, dialogs with the server and implements the protocol.
We define there a new stream called signedInputStream. It is instanciated in the signedURLConnection constructor. This is a simple stream with a set method that allows us to set the InputStream and the OutputStream given by the socket when it is connected to the server.
The connect method opens a socket on the server, and sends the GET command. It also calls the set method defined in the signedInputStream class.
The getContentType returns application/signed and is used to determine the content handler that should be use with this protocol.
signedInputStream.java
package net.www.protocol.signed;
import java.io.*;
class signedInputStream extends InputStream {
protected InputStream is;
protected OutputStream os;
public void set(InputStream is,OutputStream os) {
this.is=is;
this.os=os;
}
public int read() throws IOException
{
return is.read();
}
}
signed.java
Let's proceed now with the Content Handler protocol. The signed class receives a signedData object from the server using serialization and returns it with the getContent. It's called by the URL.getContent method.
This file must go into the net.www.content.application directory.
package net.www.content.application;
import java.io.*;
import java.net.*;
public class signed extends ContentHandler
{
public Object getContent(URLConnection uc) throws IOException {
signedData Data;
ObjectInputStream ois=new ObjectInputStream(uc.getInputStream());
try {
Data=(signedData)ois.readObject();
} catch (Exception e) {System.out.println("Erreur");Data=null;};
return Data;
}
}
signedData.java
import java.io.*;
import java.security.*;
public class signedData implements Serializable
{
byte[] b;
byte[] sig;
PublicKey pub;
signedData(byte b[],byte [] sig ,PublicKey pub) {
this.b=b;
this.sig=sig;
this.pub=pub;
}
void printKey() {
System.out.println("Key dump:");
System.out.println(pub);
}
}
This object was defined in a previous article.
Server.java
This class is similar to the server class defined in a previous article.
import java.net.*;
import java.io.*;
import java.security.*;
class Server implements Runnable
{
ServerSocket server=null;
Socket data=null;
Thread t=null;
KeyPairGenerator keyGen;
KeyPair pair;
Signature dsa;
PublicKey pub;
PrivateKey priv;
public static void main(String [] argv) throws IOException
{
try {
Server s=new Server();
s.startServer();
} catch (IOException e) {};
}
public synchronized void startServer() throws IOException {
if (t==null)
{
server=new ServerSocket(4096);
System.out.println("Generating keys...");
//Generates keys
try {
KeyPairGenerator keyGen = KeyPairGenerator.getInstance("DSA");
keyGen.initialize(256);
KeyPair pair = keyGen.generateKeyPair();
System.out.println("Keys generated.");
//Generates signature object
dsa = Signature.getInstance("SHA/DSA");
pub=pair.getPublic();
priv=pair.getPrivate();
dsa.initSign(priv);
}
catch (Exception e) {};
System.out.println("Signature object generated.");
t=new Thread(this);
t.start();
}
}
public synchronized void stopServer() throws IOException {
if (t!=null)
{
t=null;
server.close();
}
}
public void run()
{
Thread thisThread=Thread.currentThread();
System.out.println("Ready to accept connections.");
while (t==thisThread) {
try {
Socket data=server.accept();
process(data);
} catch( Exception e) {}
}
}
private void process(Socket data) throws IOException
{
System.out.println("Accepting connexion...");
try {
DataInputStream dis=new DataInputStream(data.getInputStream());
ObjectOutputStream oos=new ObjectOutputStream(data.getOutputStream());
//Parse the GET request
String req=dis.readLine();
req=req.substring(5);
System.out.println("Requete:"+req);
//Read the file and sign it
FileInputStream fis = new FileInputStream(req);
byte[] b=new byte[fis.available()];
fis.read(b);
dsa.update(b);
//Build the signedData object and send it
signedData Data=new signedData(b,dsa.sign(),pub);
System.out.println("Sending signed Object from file: "+req);
oos.writeObject(Data);
//Close streams
dis.close();
oos.close();
data.close();
} catch (Exception e) {System.out.println(e);}
}
}
This page was last updated 06 March 1998