Scone Tutorial: An Example Plugin

Lets say your old granny complains about her stupid browser and asks you to implement the following: Whenever the mousepointer is over a link to a page she has visited before, the title of this page should be displayed in the browser's statusbar.

This is an example how granny wants her browser. She hates URLs:

This simple task normally includes a lot of complicated programming but do not worry: It is fairly easy with Scone. We will first have a look at a HtmlTokenEditor (the code that works on the Html documents), then we will setup a plugin for Scone in order to use our HtmlTokenEditor and finally we will perform some nice finetuning.

The HtmlTokenEditor

An HtmlTokenEditor is an object which is plugged into the datastream from a webserver to your browser. Usually you don't want to anaylze an Html document bytewise so Scone parses the byte stream for you and provides you with nice clean Html tokens.

StatusBarWizard.java
import scone.*;
import scone.proxy.*;
import scone.netobjects.*;
import scone.util.*;
import scone.util.tokenstream.*;

public class StatusBarWizard extends HtmlTokenEditor{

   public void handleRequest(SconePipe pipe){
      try{
         
         TokenInputStream in=pipe.getTokenInputStream();
         TokenOutputStream out=pipe.getTokenOutputStream();
         
         Token t=null;
         LinkToken linkToken=null;
         
         while((t=in.read())!=null){
            
            if(t instanceof LinkToken){
               linkToken=(LinkToken)t;
               
               //work with the LinkToken
            
            }
            out.write(t);
         }
      }catch(Exception exc){
         ErrorLog.log(this,"handleRequest()","",exc);
      }
   }
}      

What happens is obvious: Scone provides you with a TokenInputStream from which you can read Token objects and a TokenOutputStream to which you can write Token objects. The while loop reads from the input stream and writes to the output stream until there is nothing more to read. If one of these Token objects happens to be a LinkToken it has to undergo some special treatment since we have to add some javascript code to the links of the document. And this is what happens to the LinkToken:

The treatment of LinkTokens
if(t instanceof LinkToken){
   linkToken=(LinkToken)t;
   HtmlNode doc=HtmlNodeCache.check(linkToken.getLink().getToNode());
   
   if(doc!=null){
      String jsEvent="status='"+doc.getTitle()+"';return true";
      linkToken.setParam("onMouseOver",jsEvent);
      linkToken.setParam("onMouseOut","status='';return true;");
   }
}

To understand this piece of code we have to cast a look at how Scone sees the Internet: The Scone framework automatically analyzes incoming Html documents and stores some of their entities as objects. An HtmlNode for example contains information about an Html document, a NetNode is a resource within the Internet and a Link is a link from one NetNode to another one. There are many more object types and they are all persistent within the Scone framework and accessible through their caches (for performance reasons). To understand the line

HtmlNode doc=HtmlNodeCache.check(linkToken.getLink().getToNode());
we split it:

A LinkToken contains not only a tag which defines a link, but also a Link object.

linkToken.getLink()
returns this Link object. A Link object basically contains references to two NetNode objects and the method
getToNode()
returns the Node which is linked to. Knowing the NetNode object of the page that is linked to we know its URI but not its title. If the page belonging to this NetNode has been loaded through Scone before, there must be an HtmlNode which we receive through the HtmlNodeCache.
HtmlNodeCache.check(NetNode node)
returns this HtmlNode if it exists or null otherwise. A long way to go but now we have our HtmlNode and
doc.getTitle()
simply returns the title of our page (if specified).

linkToken.setParam("onMouseOver",jsEvent);
now adds an attribute to the link tag and we are done! This code will transform a tag like this
<A HREF="manual/">
to a tag like this
<A HREF="manual/" onMouseOut="status='';return true;" 
onMouseOver="status='Apache 1.3 documentation ';return true">

The Plugin

And here's a simple plugin for our StatusBarWizard:

StatusBarWizardPlugin.java
import scone.*;

public class StatusBarWizardPlugin extends Plugin{
   
   //requirements
   public int getRequirements(){
      return PARSEDOCUMENT | CONSIDERLINKS;
   }
   
   public void init(){
   	StatusBarWizard sbw=new StatusBarWizard();
   	sbw.setup("StatusBarWizard",HTDOCCONDITION,60);
   	addMeg(sbw);      
   }
}

The requirements tell Scone which features have to be available at runtime. We need the information from the HtmlNode objects and the PARSEDOCUMENT feature creates these objects. The CONSIDERLINKS feature creates the LinkToken and Link objects.

In the init() method we setup our "wizard". The String "StatusBarWizard" is helpful for debugging, HTDOCCONDITION specifies that only real Html documents should be edited by StatusBarWizard and 60 is a priority in case two or more HtmlTokenEditors run under the same conditions.

This plugin can now be registered with

runscone -register StatusBarWizardPlugin
and we are done :-)

Configuration and Fine-Tuning of Scone Plugins

Of course there is still a lot of room for improvement, the browser caches the Html documents and so there is not always the newest information displayed in the status bar. But nothing is impossible... You are using Scone and so there is an easy way to do this. Just add

setResponseHeaderField("Pragma","no-cache"); 

to the StatusBarWizard code. This line must be added before the tokenstreams are requested from the pipe. Now the browser no longer caches the Html documents and everything is displayed correctly. Of course this adds the drawback of slower navigation and your granny might not be too happy about this. To give her the freedom of choice it is a good idea to make our plugin configurable which is fairly esay with Scone.

Our plugin inherits the method getProperties() which returns a PersistentProperties object. This object automatically corresponds to the properties set in a file called config/properties/scone.example.statusbar.StatusBarWizard.xml.

This file could look like this:

config/properties/scone.example.statusbar.StatusBarWizard.xml
<?xml version="1.0" encoding="UTF-8"?>
<properties>
  <property name="no-cache" value="true" type="boolean"></property>
</properties>

To make these properties accessible in StatusBarWizard, we write a constructor for it where we pass on the plugin object and store it locally in a variable "plugin". Then we can add these lines in handleRequest():

if(plugin.getProperties().get("no-cache").equals("true"))
   setResponseHeaderField("Pragma","no-cache"); 

The Scone configuration GUI will now automatically render a properties configuration dialog:

Voilą! We have our reliable futuristic configurable plugin :-)


For more Information please contact Volkert Buchmann
Last Update: 3-Aug-2002