Jigsaw

Writing a new resource class

This tutorial explains you how to write a new resource, by walking through a complete example. It is assumed that you are familiar with Jigsaw architecture, and that you have understand the configuration tutorial.

The resource we will write here, will display a message along with a set of customizable request headers. The tutorial will go through the following steps:

Writing the resource class

Before actually writing a new resource, some decisions must be made about:

Picking a super-class

Deciding for the super-class of your resource is a pretty simple process right now. Here are the rule of thumbs:

Given these short rules, it should be obvious that for our sample resource, what we want to do is to subclass the HTTPResource. So right now, we can start writing the following piece of code (we will keep in bold the additional code we add as we walk through the example):

import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*;
import w3c.tools.store.*; 

public class FancyResource extends HTTPResource {

}

Note that we don't know yet were to put this file until we have selected an appropriate package for our resource.

Selecting a package

There is no particular problem with regard to the package your resource belong to: Jigsaw impose no constraint on this. The only thing you should be aware of is your CLASSPATH environment variable. This variable setting is particularly crucial in Jigsaw since it may impact its security: you don't want anyone to be able to plug new resource classes in the server !

For our sample resource, we can create a new package, let's call it tutorial, under the Jigsaw classes directory. We want this package to be under the w3c.jigsaw package. We can now create the appropriate directory (src/classes/w3c/jigsaw/tutorial), and write in it the following FancyResource.java file:

package w3c.jigsaw.tutorials ;

import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*;
import w3c.tools.store.*; 

public class FancyResource extends HTTPResource {

}


Defining the attributes

The next thing we have to figure out, is the list of attributes for our new resource. The HTTPResource  already defines a number of attributes (see the reference manual). Defining the set of attributes of a resource also defines the way the resource will be configured (since a resource is configured by editing its attribute values). Here, we want to be able to configure:

The message emited by the resource can be described as an editable StringAttribute, which defaults to Hello. The name of the request headers to dump can be encoded as a StringArrayAttribute, each entry in the array giving the name of a header. If this last attribute is undefined, then we will emit all of the request headers in our reply.

Now that we now the attributes our resource is to have, we should declare them to the AttributeRegistry. This Registry keeps track of all the attributes of all resource classes. For each class it knows of, it maintains an ordered list of the attribute it defines. The fact that this list is ordered is important, since it allows for fast attribute value access (through a simple indirection in the attribute value array of each resource instance). Attribute declaration should be done at class initialization time, so we introduce a static statement in the class, whose purpose is to declare our attributes:

package w3c.jigsaw.tutorials ;

import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*; 
import w3c.tools.store.*;

public class FancyResource extends HTTPResource {

	// The message attribute index
	protected static int ATTR_MESSAGE = -1 ;
	// The headers attribute index
	protected static int ATTR_HEADERS = -1 ;

	static {
		Attribute a   = null ;
		Class     cls = null ;
		
		try {
			cls = Class.forName("w3c.jigsaw.tutorials.FancyResource");
		} catch (Exception ex) {
			ex.printStackTrace() ;
			System.exit(1) ;
		}
		// Register the message attribute:
		a = new StringAttribute("message", "Hello", Attribute.EDITABLE);
		ATTR_MESSAGE = AttributeRegistry.registerAttribute(cls, a) ;
		// Register the headers attribute:
		a = new StringArrayAttribute("headers", null, Attribute.EDITABLE) ;
		ATTR_HEADERS = AttributeRegistry.registerAttribute(cls, a) ;
	}

}

Notice how the attribute Registry returns back an attribute index for each of the attribute we register. This index can then be used as a parameter to the setValue and getValue methods of AttributeHolder instances, to describe the attribute they should act on.

At this point, we have declared the set of attributes that our resource defines, the attribute Registry knows about it, we can now focus on the actual behavior of the resource. The only HTTP method that our resource will support is the GET method, which will synthetise a reply on the fly for each specific request. Jigsaw comes with a simple HtmlGenerator class for generating HTML that we want to use for this purpose. The actual implementation of the method is the following:

package w3c.jigsaw.tutorials ;

import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.jigsaw.resources.*; 
import w3c.tool.store.*;

import w3c.jigsaw.html.* ;

public class FancyResource extends HTTPResource {
	
	// The message attribute index
	protected static int ATTR_MESSAGE = -1 ;
	// The headers attribute index
	protected static int ATTR_HEADERS = -1 ;

	static {
		Attribute a   = null ;
		Class     cls = null ;
		
		try {
			cls = Class.forName("w3c.jigsaw.tutorials.FancyResource");
		} catch (Exception ex) {
			ex.printStackTrace() ;
			System.exit(1) ;
		}
		// Register the message attribute:
		a = new StringAttribute("message", "Hello", Attribute.EDITABLE);
		ATTR_MESSAGE = AttributeRegistry.registerAttribute(cls, a) ;
		// Register the headers attribute:
		a = new StringArrayAttribute("headers", null, Attribute.EDITABLE) ;
		ATTR_HEADERS = AttributeRegistry.registerAttribute(cls, a) ;
	}

	/**
	 * Display the resource message with the request headers.
	 * @param request The request to handle.
	 * @exception HTTPException If processing the request failed.
	 */

	public Reply get(Request request) 
		throws HTTPException
	{
		// Create the HTML generator, and set titles:
		HtmlGenerator g = new HtmlGenerator("FancyResource");
		g.append("<h1>FancyResource output</h1>");
		// Emit the message:
		g.append("<p>"+getValue(ATTR_MESSAGE, null));
		// Now emit the requested headers:
		String headers[] = (String[]) getValue(ATTR_HEADERS, null) ;
		if ( headers != null ) {
			g.append("<hr><h2>Request headers:</h2><dl>");
			for (int i = 0 ; i < headers.length ; i++) {
				g.append("<dt>"+headers[i]+"</dt><dd>");
				String value = request.getValue(headers[i]);
				g.append((value == null) 
					 ? "<em>undefined</em>"
					 : value) ;
			}
		}
		Reply reply = createDefaultReply(request, HTTP.OK) ;
		reply.setStream(g) ;
		return reply ;
	}

}

We are done, you can now compile the above code, which should produce a FancyResource.class file, the next section will explain how to install it in the information space. Before switching to this, it is worth pointing out that the instances of our new resource class will be made persistent by the Jigsaw runtime: this means that once plugged into the resource space, a new instance of the FancyResource class will keep its attribute values across Jigsaw invocations.

Being smart with caches

Before continuing into the installation of that new resources, we would like to look into caching issues. As written above, the output generated by our resource doesn't say nothing about wether it's cachable or not. Under these circumstances, most existing caches will try to cache the resource content.

Although what we really want is to prevent any sort of caching for that particular resource, since its output depends on the request it answers too. To handle this situation, we can use the newly defined request headers in HTTP/1.1. What we really want is to tell any caches between our resource and the client to keep out of the way. To do this, we need to set the no-store cache control directive, defined in HTTP/1.1.

This can be done by redefining the createDefaultReply method of our superclass in the following way:

package w3c.jigsaw.tutorials ;

import w3c.www.http.*;
import w3c.jigsaw.http.*;
import w3c.tools.store.*;
import w3c.jigsaw.resources.*.*;

import w3c.jigsaw.html.* ;

public class FancyResource extends HTTPResource {
	...

    public Reply createDefaultReply(Request request, int status) {
        Reply reply = super.createDefaultReply(request, status);
        reply.setNoStore(true);
        // Be friendly to HTTP/1.0 caches too:
        reply.addPragma("no-cache");
        return reply;
    }
	
}

This set the no-store cache control directive, and it also set the no-cache pragma, in case there is an HTTP/1.0 proxy between our resource and the client.

Installing the resource

You are not supposed to have compiled the fancy resource class. What we want to do here, is actually using it in our exported space. For the sake of this example, we will just attach an instance of the fancy resource to Jigsaw documentation directory (the /User directory). Don't be afraid, you will also learn how to remove it from there by the end of this tutorial).

As you might have guess after reading the configuration tutorial, what we are going to do here, is bring up the generic resource editor on the /User directory resource. Point your favorite browser to /Admin/Editor/User, and follow the link to AddingResource. This shows up a form prompting us for the resource name (i.e. the name of the resource in the URL space), and the resource class. Let's call our resource fancy, and the class, of course, is w3c.jigsaw.tutorials.FancyResource. We press the OK button to create the resource, then we follow the ExistingResources link. This link pops up  the list of resources of the /User directory, we click on the fancy one. This brings up the editor for the attribute value of our fancy resource. We are mostly interested by our specific attributes: message and headers. Let's have fun here, first set the message to whatever you think is appropriate, then type in the name of the request header you want the resource to display (lower case, one per line). In my configuration I have set the message to Testing the fancy resource, and I have selected the user-agent and  accept headers. Press the OK button: we are all set, let's test this by visiting the /User/fancy resource. Here is what I get with my configuration:

FancyResource output

Testing the fancy resource


Request headers:

user-agent
Mozilla/2.0 (X11; I; SunOS 5.3 sun4m)
accept
image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*

The example we have been walking through is probably one of the simplest one, however, by now, you should be able to read and understand the basic resource classes provided by Jigsaw. I would recommend reading them in the following order:

Enjoy !


Jigsaw Team
$Id: resource.html,v 1.14 1997/07/31 08:26:40 ylafon Exp $